tl; dr:強制值不跨數據綁定傳播。當代碼隱藏不知道綁定的另一端時,如何強制跨越數據綁定進行更新?脅迫值的強制傳播
我使用一個WPF依賴屬性CoerceValueCallback
和我被困在那個被迫值不通過對綁定得到傳播的問題。
Window1.xaml.cs
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Media;
namespace CoerceValueTest
{
public class SomeControl : UserControl
{
public SomeControl()
{
StackPanel sp = new StackPanel();
Button bUp = new Button();
bUp.Content = "+";
bUp.Click += delegate(object sender, RoutedEventArgs e) {
Value += 2;
};
Button bDown = new Button();
bDown.Content = "-";
bDown.Click += delegate(object sender, RoutedEventArgs e) {
Value -= 2;
};
TextBlock tbValue = new TextBlock();
tbValue.SetBinding(TextBlock.TextProperty,
new Binding("Value") {
Source = this
});
sp.Children.Add(bUp);
sp.Children.Add(tbValue);
sp.Children.Add(bDown);
this.Content = sp;
}
public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value",
typeof(int),
typeof(SomeControl),
new PropertyMetadata(0, ProcessValueChanged, CoerceValue));
private static object CoerceValue(DependencyObject d, object baseValue)
{
if ((int)baseValue % 2 == 0) {
return baseValue;
} else {
return DependencyProperty.UnsetValue;
}
}
private static void ProcessValueChanged(object source, DependencyPropertyChangedEventArgs e)
{
((SomeControl)source).ProcessValueChanged(e);
}
private void ProcessValueChanged(DependencyPropertyChangedEventArgs e)
{
OnValueChanged(EventArgs.Empty);
}
protected virtual void OnValueChanged(EventArgs e)
{
if (e == null) {
throw new ArgumentNullException("e");
}
if (ValueChanged != null) {
ValueChanged(this, e);
}
}
public event EventHandler ValueChanged;
public int Value {
get {
return (int)GetValue(ValueProperty);
}
set {
SetValue(ValueProperty, value);
}
}
}
public class SomeBiggerControl : UserControl
{
public SomeBiggerControl()
{
Border parent = new Border();
parent.BorderThickness = new Thickness(2);
parent.Margin = new Thickness(2);
parent.Padding = new Thickness(3);
parent.BorderBrush = Brushes.DarkRed;
SomeControl ctl = new SomeControl();
ctl.SetBinding(SomeControl.ValueProperty,
new Binding("Value") {
Source = this,
Mode = BindingMode.TwoWay
});
parent.Child = ctl;
this.Content = parent;
}
public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value",
typeof(int),
typeof(SomeBiggerControl),
new PropertyMetadata(0));
public int Value {
get {
return (int)GetValue(ValueProperty);
}
set {
SetValue(ValueProperty, value);
}
}
}
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
}
}
}
Window1.xaml
<Window x:Class="CoerceValueTest.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="CoerceValueTest" Height="300" Width="300"
xmlns:local="clr-namespace:CoerceValueTest"
>
<StackPanel>
<local:SomeBiggerControl x:Name="sc"/>
<TextBox Text="{Binding Value, ElementName=sc, Mode=TwoWay}" Name="tb"/>
<Button Content=" "/>
</StackPanel>
</Window>
即,兩個用戶控件,一個嵌套在另一個內,和那些在一個窗口的外層一個。內部用戶控件具有Value
依賴項屬性,該屬性綁定到外部控件的依賴項屬性Value
。在窗口中,TextBox.Text
屬性綁定到外部控件的Value
屬性。
內部控制有一個CoerceValueCallback
註冊其Value
屬性,其效果是這個Value
屬性只能分配偶數。
請注意,爲了演示目的,此代碼被簡化。真正的版本不會在構造函數中初始化任何東西;這兩個控件實際上都有控制模板,它們完成了這裏各個構造函數中所做的所有事情。也就是說,在真實的代碼中,外部控制不知道內部控制。
當向文本框中寫入偶數並更改焦點(例如,通過將文本框下面的虛擬按鈕聚焦)時,兩個Value
屬性都會得到適時更新。但是,在向文本框中寫入奇數時,內部控件的Value
屬性不會更改,而外部控件的Value
屬性以及TextBox.Text
屬性顯示奇數。
我的問題是:如何在文本框中強制更新(理想情況下還在外部控件的Value
屬性中,而我們在此)?
我發現an SO question on the same problem,但並沒有真正提供解決方案。它暗示使用屬性更改事件處理程序來重置值,但據我所知,這意味着將評估代碼複製到外部控制...這不是真正可行的,因爲我的實際評估代碼依賴於某些基本上只知道(沒有太多努力)內部控制的信息。
此外,this blogpost建議在TextBox.Text
在CoerceValueCallback
的結合,但首先調用UpdateTarget
,如上提示過,我內心的控制不可能有關於文本框的任何知識,第二,我可能會叫UpdateSource
第一對內部控制的屬性的綁定。但我不知道該怎麼做,因爲在CoerceValue
方法中,強制值尚未設置(因此更新綁定還爲時過早),而在值爲CoerceValue
的情況下,屬性值將保持原來的狀態,因此屬性更改回調將不會被調用(也暗示在this discussion中)。
一種可能的解決方法我曾認爲用常規的特性和INotifyPropertyChanged
執行替換SomeControl
依賴項屬性的(所以我可以手動觸發即使該值已經被裹挾着PropertyChanged
事件)。但是,這意味着我不能再聲明對該屬性的綁定,所以它不是一個真正有用的解決方案。
讓我知道,如果你找到這個答案=) –