[翻譯] WPF 中用戶控制元件 DataContext/Binding 和依賴屬性的問題
目錄 提問 回答User Control DataContext/Binding Issue with Dependency Property WPF
[譯者] 獨立觀察員 2022 年 3 月 24 日
提問
ProgrammingDude(asked Dec 8, 2015 at 21:24)
Ok, so my problem is I have a user control. In the xaml I bind some colors to color properties that I have created as shown below.
好,我的問題是,我有一個用戶控制元件,在 Xaml 中,我系結了一些顏色到顏色屬性,如下所示:
<GradientStop x:Name="stop1" Color="{Binding Color1}" Offset="0"/> <GradientStop x:Name="stop2" Color="{Binding Color2}" Offset="1"/>
In my code behind I have a DependencyProperty that I have declared as shown below.
在后臺代碼中,我宣告了一個依賴屬性,如下所示:
public static readonly DependencyProperty IsActiveProperty = DependencyProperty.Register("IsActive", typeof(bool), typeof(Bin), new PropertyMetadata(new PropertyChangedCallback(Bin.IsActivePropertyChanged)));
The dependency property has a PropertyChangedCallback that it calls called IsActivePropertyChanged as shown below.
該依賴屬性有一個 PropertyChangedCallback 方法,名稱為 IsActivePropertyChanged,如下所示:
private static void IsActivePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { Bin b = (Bin)d; if((bool)e.NewValue) { b.Color1 = Color.FromArgb(0xFF, 0x3E, 0x3E, 0x3E); b.Color2 = Colors.Red; b.Color3 = Colors.Red; b.Color4 = Color.FromArgb(0xFF, 0xFF, 0xFF, 0xFF); } else { b.Color1 = Color.FromArgb(0xFF, 0x3E, 0x3E, 0x3E); b.Color2 = Color.FromArgb(0xFF, 0x83, 0x83, 0x83); b.Color3 = Color.FromArgb(0xFF, 0x63, 0x63, 0x63); b.Color4 = Color.FromArgb(0xFF, 0xFF, 0xFF, 0xFF); } }
If I use the constructor below, the color changes inside of the constructor work fine, however, my IsActivePropertyChangedEvent never gets fired. I am assuming because of the DataContext assignment in the constructor.
如果我使用下面的建構式,在建構式中的顏色改變作業正常,然而,我的 IsActivePropertyChangedEvent 從未被觸發,我估計是因為在建構式中指定了 DataContext,
public Bin() { Color1 = Color.FromArgb(0xFF, 0x3E, 0x3E, 0x3E); Color2 = Color.FromArgb(0xFF, 0x83, 0x83, 0x83); Color3 = Color.FromArgb(0xFF, 0x63, 0x63, 0x63); Color4 = Color.FromArgb(0xFF, 0xFF, 0xFF, 0xFF); InitializeComponent(); DataContext = this; }
If I comment out the DataContext assignment and use the constructor below, my Color assignments do not work, but the IsActivePropertyChanged event fires fine.
如果我注釋掉 DataContext 賦值,使用如下的建構式,我的顏色賦值就沒起作用,但 IsActivePropertyChanged 事件能夠被觸發,
public Bin() { Color1 = Color.FromArgb(0xFF, 0x3E, 0x3E, 0x3E); Color2 = Color.FromArgb(0xFF, 0x83, 0x83, 0x83); Color3 = Color.FromArgb(0xFF, 0x63, 0x63, 0x63); Color4 = Color.FromArgb(0xFF, 0xFF, 0xFF, 0xFF); InitializeComponent(); //DataContext = this; }
My question is how can i get the binding to work correctly and have my event fire as well. I have tried setting the
DataContext="{Binding RelativeSource={RelativeSource Self}}"(instead of setting the DataContext in the code behind) of the items that are bound to the Color Properties in XAML, a rectangle and a polygon, but that didn't seem to work. Thanks in advance for any help.
我的問題就是,怎樣能讓系結和事件觸發都正常作業,我嘗試了為 Xaml 中系結到 Color 屬性的元素設定 DataContext="{Binding RelativeSource={RelativeSource Self}}" (而不是在后臺代碼中設定 DataContext),一個矩形和一個多邊形,但似乎不起作用,提前感謝任何提供的幫助,
回答
vesan(answered Dec 9, 2015 at 22:48)
When writing your own control, you shouldn't mess with the
DataContextof the control itself.Instead, on the binding of the
GradientStop, you can useRelativeSource={RelativeSource AncestorType=Bin}(assuming Bin is your control). Or you can define a template and use TemplateBinding. Check this answer I wrote a while back for a similar question - it has more detailed description of how this works.
在寫你自己的控制元件時,你不應當把控制元件自身的 DataContext 弄亂,
取而代之的是,在系結 GradientStop 時,你可以使用 RelativeSource={RelativeSource AncestorType=Bin} (假設 Bin 是你的控制元件),或者,你可以定義一個模板并且使用 TemplateBinding,查看我不久之前為一個類似的問題寫的 這個回答 ,其中有關于這如何作業的更多詳細描述,
(譯者注:他上面提到的詳細回答如下)
vesan(answered May 28, 2015 at 2:26)
Although this has now been solved there seems to be some, in my opinion, inappropriate use of the
DataContext.When developing a custom reusable control, you should not set
DataContextat all. What theDataContextwill be, that is for the user of the control to decide, not for the developer. Consider the following common pattern of code:
雖然這個現在已經被解決了(譯者注:另一個回答被標記為答案),在我看來,似乎有一些,對 DataContext 的不正確使用,
當開發一個定制的可復用的控制元件,你根本不應該設定 DataContext ,DataContext 將會是什么,應當是控制元件使用者來決定的,而不是開發者,思考下面的常見模式代碼:
<Grid DataContext="{Binding Data}"> <TextBox Text="{Binding TextValue1}" /> <!-- Some more controls --> </Grid>
Notice that here, you are using the
Gridcontrol. The developer of the control (in this case, the WPF team), didn't touch theDataContextat all - that is up to you. What does it mean for you as a control developer? YourDependencyPropertydefinition is fine, but you shouldn't touch theDataContext. How will you then bind something inside your control to theDependencyPropertyvalue? A good way is using a template (namespaces omitted):
注意這里,你正在使用 Grid 控制元件,控制元件的開發者(這個例子中,是 WPF 團隊),根本沒有碰 DataContext —— 這個是你來用的,那么這對于作為控制元件開發者的你來說意味著什么呢?你的依賴屬性定義是沒問題的,但你不應該碰 DataContext ,那么你之后怎么將控制元件中的一些東西系結到依賴屬性的值呢?一個好的方式就是使用模板(命名空間省略了):
<MyTimePicker>
<MyTimePicker.Template>
<ControlTemplate TargetType="MyTimePicker">
<!-- Stuff in your control -->
<TextBlock Text="{TemplateBinding Time}" />
<TextBox Text="{Binding Time, RelativeSource={RelativeSource TemplatedParent}}" />
</ControlTemplate>
<MyTimePicker.Template>
</MyTimePicker>
Note that
TemplateBindingis always one-way only, so if you need any editing at all, you need to use normal binding (as you can see on theTextBoxin the example).This only means that the TextBlock/Box inside your control will get its
Timevalue from your custom control itself, ignoring anyDataContextyou might have set.
注意 TemplateBinding 總是 one-way 的,所以如果你需要任何編輯,你需要使用普通的 binding (正如你在 TextBox 示例中看到的那樣),
這僅僅意味著你的控制元件內部的 TextBlock/Box 會從你自定義控制元件自身獲得它的 Time 值,忽略任何你可能設定的 DataContext ,
Then, when you use the control, you do it like this (added to my first example):
然后,當你使用這個控制元件時,你會這樣做(被添加到我的第一個示例):
<Grid DataContext="{Binding Data}"> <TextBox Text="{Binding TextValue1}" /> <!-- Some more controls --> <MyTimePicker Time="{Binding TimeValue}" /> </Grid>
What just happened here is that the
MyTimePickerdoes not haveDataContextset anywhere at all - it gets it from the parent control (theGrid). So the value goes like this:Data-->(binding)-->MyTimePicker.Time-->(template binding)-->TextBlock.Text.
這里發生的是, MyTimePicker 的 DataContext 根本沒有在任何地方被設定,而是從父控制元件(Grid)獲取,所以數值(譯者注:這里是 TimeValue)像這樣流轉:Data-->(binding)-->MyTimePicker.Time-->(template binding)-->TextBlock.Text ,
And above all, avoid doing this in the constructor of your custom control:
首要的是,避免在你的自定義控制元件的建構式中這樣做:
public MyTimePicker() { InitializeComponent(); DataContext = this; }
This will override any
DataContextset in XAML, which will make binding a huge pain (because you'll have to always setSourcemanually). The previous example would not work, and this wouldn't work either:
這會覆寫在 Xaml 中設定的任何 DataContext ,會使得系結變成一個大痛苦(因為你將不得不總是手動設定 Source ),之前的示例將不能作業,下面這個也不能作業:
<MyTimePicker DataContext="{Binding Data}" Time="{Binding TimeValue}" />
You would think this is OK, but the
DataContextwill be resolved in theInitializeComponent()call, so the value will be immediately overwritten. So the binding toTimeValuewill look for it in the control instead (which will, of course, fail).
你可能認為這是可以的,但 DataContext 會在 InitializeComponent() 呼叫中被重新處理,所以值會被立即重寫,所以目標為 TimeValue 的系結反而會在控制元件中搜尋(這個當然會失敗),
Just don't touch the
DataContextwhen developing a control and you'll be fine.
(總之,)開發一個控制元件時就別碰 DataContext ,你將會一切順利,
原創文章,轉載請注明: 轉載自 獨立觀察員
本文鏈接地址: [翻譯] WPF 中用戶控制元件 DataContext/Binding 和依賴屬性的問題 [http://dlgcy.com/translate-user-control-datacontext_binding-issue-with-dependency-property-wpf/]
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/449017.html
標籤:WPF
上一篇:dnSpy反編譯除錯
