需求場景
列表類控制元件,如 ListBox、ListView、DataGrid等。顯示的行資料中,部分內容依靠選中時觸發控制,例如選中行時行記錄複選,部分列內容控制顯隱。
案例原始碼以ListView
為例。
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
作為資料項
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
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
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
中新增額外屬性。
public class ListViewItem : ListBoxItem
{
}
public class ListBoxItem : ContentControl
{
public bool IsSelected { get; set; }
}
解決辦法
方式一
如果是使用的MvvM
架構設計,可以為控制元件的子項ViewModel
新增 IsSelected
屬性,從資料的維度去控制資料模板內的具體操作,此處不展開細說,主要以方式二為主。
public class CaseItemViewModel
{
// 省略重複項
public bool IsSelected { get; set; }
}
方式二(推薦)
純UI層級處理,透過Binding
機制中提供的FindAncestor
方式,直接獲取上級 Item
控制元件項屬性。好處是ViewModel
中,不需要再為了頁面互動新增額外屬性。
<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>
執行效果
非選中效果。
選中行效果。