WPF中繼承ItemsControl子類控制元件資料模板獲取選中屬性

关关长语發表於2024-03-30

blog-hbh-hc-header

需求場景

列表類控制元件,如 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 元素xxxItemIsSelected 較少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>

執行效果

非選中效果。

選中行效果。

相關文章