WPF自定義控件綁定數(shù)據(jù)對(duì)象的最佳實(shí)踐
引言
在 WPF 中開(kāi)發(fā)自定義控件時(shí),如何優(yōu)雅地綁定數(shù)據(jù)對(duì)象,是一個(gè)經(jīng)常遇到的問(wèn)題。最近在實(shí)現(xiàn)一個(gè)自定義的 ImageView 控件時(shí),我遇到了一個(gè)典型場(chǎng)景:
- 控件內(nèi)部需要使用第三方控件
HSmartWindowControlWPF來(lái)顯示圖像; - 控件需要和業(yè)務(wù)對(duì)象
GraphicInfo綁定,并且要把自身引用回寫(xiě)到GraphicInfo.View; - 控件還要支持在
ItemList(例如ListBox)中批量使用。
本文就以這個(gè)案例為例,來(lái)總結(jié)下最佳實(shí)踐。

1. 直接使用 DataContext 的問(wèn)題
最初的寫(xiě)法是這樣的:
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
var hSmart = (HSmartWindowControlWPF)GetTemplateChild("PART_hSmart");
hSmart.Loaded += Hsmart_Loaded;
hSmart.HMouseMove += HSmart_HMouseMove;
if (DataContext is GraphicInfo info)
{
info.View = this; // 子類(lèi)控件回寫(xiě)到數(shù)據(jù)對(duì)象
}
}
在 XAML 里:
<local:ImageView DataContext="{Binding MyGraphic}" />
這種方式雖然能用,但有幾個(gè)問(wèn)題:
- 控件內(nèi)部和外部公用了同一個(gè)
DataContext,如果控件內(nèi)部還需要 MVVM 綁定,會(huì)和外部沖突。 - 在
ItemList中使用時(shí)不夠直觀,容易出錯(cuò)。
2. 定義依賴(lài)屬性(推薦)
更好的做法是為控件定義一個(gè)依賴(lài)屬性,例如 Graphic:
public class ImageView : Control
{
static ImageView()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(ImageView),
new FrameworkPropertyMetadata(typeof(ImageView)));
}
// 定義依賴(lài)屬性
public GraphicInfo Graphic
{
get => (GraphicInfo)GetValue(GraphicProperty);
set => SetValue(GraphicProperty, value);
}
public static readonly DependencyProperty GraphicProperty =
DependencyProperty.Register(nameof(Graphic), typeof(GraphicInfo), typeof(ImageView),
new PropertyMetadata(null, OnGraphicChanged));
private static void OnGraphicChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is ImageView view && e.NewValue is GraphicInfo info)
{
info.View = view; // 在綁定時(shí)回寫(xiě)
}
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
var hSmart = (HSmartWindowControlWPF)GetTemplateChild("PART_hSmart");
if (hSmart != null)
{
hSmart.Loaded += Hsmart_Loaded;
hSmart.HMouseMove += HSmart_HMouseMove;
}
}
private void Hsmart_Loaded(object sender, RoutedEventArgs e) { }
private void HSmart_HMouseMove(object sender, HMouseEventArgs e) { }
}
這樣,控件就有了一個(gè)獨(dú)立的 Graphic 屬性,外部綁定時(shí)可以很清晰地寫(xiě):
<local:ImageView Graphic="{Binding MyGraphic}" />
3. 在 ItemList 中使用
在 ListBox 中批量展示多個(gè) GraphicInfo 時(shí),就非常自然:
<ListBox ItemsSource="{Binding Graphics}">
<ListBox.ItemTemplate>
<DataTemplate>
<local:ImageView Graphic="{Binding}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
這里的 Binding 就是每個(gè) GraphicInfo,通過(guò)依賴(lài)屬性綁定到控件,不會(huì)和 DataContext 混用。
4. 對(duì)比與總結(jié)
使用 DataContext 的方式
? 簡(jiǎn)單,少寫(xiě)一個(gè)依賴(lài)屬性
? 容易和控件內(nèi)部 MVVM 沖突
? 在 ItemList 中語(yǔ)義不清晰
使用依賴(lài)屬性的方式
? 更加清晰,控件的 DataContext 可以獨(dú)立使用
? 在 ItemList 中使用更自然
? 可以在依賴(lài)屬性的回調(diào)里處理邏輯(如回寫(xiě) View)
最佳實(shí)踐
- 如果控件只是簡(jiǎn)單的 UI 展示,內(nèi)部不需要額外綁定,可以用 DataContext。
- 如果控件要在 ItemList 中使用,或者內(nèi)部還需要用自己的 DataContext,推薦使用依賴(lài)屬性。
我個(gè)人在項(xiàng)目里最終采用了 依賴(lài)屬性模式,不僅解耦了數(shù)據(jù)和視圖,還能方便在列表場(chǎng)景下使用。
以上就是WPF自定義控件綁定數(shù)據(jù)對(duì)象的最佳實(shí)踐的詳細(xì)內(nèi)容,更多關(guān)于WPF自定義控件綁定數(shù)據(jù)對(duì)象的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
基于WPF編寫(xiě)一個(gè)串口轉(zhuǎn)UDP工具
串口是設(shè)備和上位機(jī)通信的常用接口,UDP則是網(wǎng)絡(luò)通信常用的通信協(xié)議,串口和UDP之間的相互轉(zhuǎn)換是非常有意義的。本文就利用WPF編寫(xiě)一個(gè)串口轉(zhuǎn)UDP工具,需要的可以參考一下2023-04-04
C#實(shí)現(xiàn)帶進(jìn)度條的ListView
這篇文章主要介紹了C#實(shí)現(xiàn)帶進(jìn)度條的ListView 的相關(guān)資料,需要的朋友可以參考下2016-02-02
C#實(shí)現(xiàn)一個(gè)Word保護(hù)性模板文件
這篇文章主要為大家詳細(xì)介紹了C#如何實(shí)現(xiàn)一個(gè)Word保護(hù)性模板文件,文中的示例代碼講解詳細(xì),具有一定的借鑒價(jià)值,需要的小伙伴可以參考一下2024-01-01
C# Onnx CenterNet實(shí)現(xiàn)目標(biāo)檢測(cè)的示例詳解
這篇文章主要為大家詳細(xì)介紹了C# Onnx CenterNet實(shí)現(xiàn)目標(biāo)檢測(cè)的相關(guān)知識(shí),文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2023-12-12
C#實(shí)現(xiàn)Bitmap類(lèi)型與Byte[]類(lèi)型相互轉(zhuǎn)化的示例詳解
在C#編程中,Bitmap類(lèi)型和Byte[]類(lèi)型之間的相互轉(zhuǎn)化是圖像處理和數(shù)據(jù)傳輸中常見(jiàn)的需求,Bitmap類(lèi)型表示一個(gè)位圖圖像,而B(niǎo)yte[]類(lèi)型則是一個(gè)字節(jié)數(shù)組,本文將詳細(xì)介紹如何在這兩種類(lèi)型之間進(jìn)行相互轉(zhuǎn)化,需要的朋友可以參考下2024-07-07

