需求场景
列表类控件,如 ListBox、ListView、DataGrid等。显示的行数据中,部分内容依靠选中时触发控制,例如选中行时行记录复选,部分列内容控制显隐。
案例源码以ListView
为例。
Xaml
部分
xaml
<ListView ItemsSource="{Binding MyPropertys}" IsManipulationEnabled="False">
<ListView.View>
<GridView>
<!--该列用于自定义行逻辑-->
<GridViewColumn Header="操作列" >
<GridViewColumn.CellTemplate>
<DataTemplate>
<!--该列用于自定义行逻辑-->
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="内容列" DisplayMemberBinding="{Binding MyProperty}"/>
<GridViewColumn Header="内容列" DisplayMemberBinding="{Binding MyProperty1}"/>
<GridViewColumn Header="内容列" DisplayMemberBinding="{Binding MyProperty2}"/>
<GridViewColumn Header="内容列" DisplayMemberBinding="{Binding MyProperty3}"/>
</GridView>
</ListView.View>
</ListView>
ViewModel
部分
CaseItemViewModel
作为数据项
cs
public class CaseItemViewModel
{
public string MyProperty { get; set; }
public string MyProperty1 { get; set; }
public string MyProperty2 { get; set; }
public string MyProperty3 { get; set; }
}
MainWindowViewModel
作为上层ViewModel
cs
public class MainWindowViewModel
{
public List<CaseItemViewModel> MyPropertys { get; set; }
public MainWindowViewModel()
{
MyPropertys = new List<CaseItemViewModel>
{
new CaseItemViewModel { MyProperty = "1", MyProperty1 = "1", MyProperty2 = "1", MyProperty3 = "1" },
new CaseItemViewModel { MyProperty = "2", MyProperty1 = "2", MyProperty2 = "2", MyProperty3 = "2" },
new CaseItemViewModel { MyProperty = "3", MyProperty1 = "3", MyProperty2 = "3", MyProperty3 = "3" },
new CaseItemViewModel { MyProperty = "4", MyProperty1 = "4", MyProperty2 = "4", MyProperty3 = "4" },
new CaseItemViewModel { MyProperty = "5", MyProperty1 = "5", MyProperty2 = "5", MyProperty3 = "5" }
};
}
}
设置MainWindowViewModel
到上层DataContext
cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
// 设置上下文
DataContext = new MainWindowViewModel();
}
}
分析思路
ItemsControl
的子类控件,对应数据项多为xxxItem
,该控件继承关系如下:
继承
[Object]-->[DispatcherObject]-->[DependencyObject]-->[Visual]-->[UIElement]-->[FrameworkElement]-->[Control]-->[ContentControl]-->[ListBoxItem]
派生
--->[ComboBoxItem]
--->[ListViewItem]
排查
通过Vs2022
自带工具,查看对应的选中行页面对象。
选中行,开启三项。
鼠标悬浮,确认选择的是该元素节点。
点击转到实时可视化树,定位元素。弹出实时可视化树窗口。
可以看到已经选中节点,单击右键【显示属性】。
显示出对应的选中项实际UI
元素当前属性。
其中属性关联项是ListBoxItem
对应为IsSelected
。是否可以考虑直接通过在数据模板中获取到UI
元素xxxItem
的IsSelected
较少ViewModel
中添加额外属性。
cs
public class ListViewItem : ListBoxItem
{
}
public class ListBoxItem : ContentControl
{
public bool IsSelected { get; set; }
}
解决办法
方式一
如果是使用的MvvM
架构设计,可以为控件的子项ViewModel
添加 IsSelected
属性,从数据的维度去控制数据模板内的具体操作,此处不展开细说,主要以方式二为主。
cs
public class CaseItemViewModel
{
// 省略重复项
public bool IsSelected { get; set; }
}
方式二(推荐)
纯UI层级处理,通过Binding
机制中提供的FindAncestor
方式,直接获取上级 Item
控件项属性。好处是ViewModel
中,不需要再为了页面交互添加额外属性。
xaml
<ListView ItemsSource="{Binding MyPropertys}" IsManipulationEnabled="False">
<ListView.View>
<GridView>
<!--该列获取ListViewItem中的IsSelected属性-->
<GridViewColumn Header="操作列" >
<GridViewColumn.CellTemplate>
<DataTemplate>
<!--使用Binding机制中的FindAncestor,查找到ListViewItem的IsSelected属性-->
<CheckBox Content="操作项"
IsChecked="{Binding IsSelected, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListViewItem}}}"
Foreground="{Binding Path=Foreground, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListViewItem}}}"></CheckBox>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<!--省略重复内容-->
</GridView>
</ListView.View>
</ListView>
运行效果
非选中效果。
选中行效果。