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>
すなわち2つのユーザーコントロール、他の内部にネスト1、およびウィンドウ内のそれらの外側の。内部ユーザーコントロールには、Value
依存プロパティがあり、これは外部コントロールの依存関係プロパティValue
にバインドされています。このウィンドウでは、TextBox.Text
プロパティが外部コントロールのValue
プロパティにバインドされています。
内部コントロールにValue
プロパティで登録されたCoerceValueCallback
プロパティがあります。これは、このValue
プロパティにのみ偶数を割り当てることができます。
このコードはデモンストレーションの目的で単純化されています。実際のバージョンはコンストラクタ内の何も初期化しません。 2つのコントロールには、それぞれのコンストラクターで行われたすべての操作を行うコントロールテンプレートが実際にあります。つまり、実際のコードでは、外部コントロールは内部コントロールを認識しません。
テキストボックスに偶数番号を書き込み、フォーカスを変更する場合(たとえば、ダミーボタンをテキストボックスの下に合わせる)、両方ともValue
のプロパティが正しく更新されます。ただし、奇数をテキストボックスに書き込むと、内部コントロールのValue
プロパティは変更されず、外部コントロールのValue
プロパティとTextBox.Text
プロパティの奇数が表示されます。
私の質問は:テキストボックス内の更新を強制するにはどうすればいいですか(外部コントロールのValue
プロパティでも理想的です)。
私はan SO question on the same problemを見つけましたが、解決策は実際にはありません。それはプロパティ変更イベントハンドラを使って値をリセットすることを暗示しますが、私が見る限り、評価コードを外部コントロールに複製することを意味します...実際の評価コードは、情報は基本的に(多くの労力を要することなく)内部コントロールにのみ知られています。
また、this blogpostは、上記の暗示のように、最初のCoerceValueCallback
にTextBox.Text
で結合が、上UpdateTarget
を呼び出す示唆して、私の内側の制御は、おそらく、テキストボックスについての知識を持つことができない、そして第二に、私はおそらく最初UpdateSource
を呼び出す必要があります内側のコントロールのプロパティーのバインドについて。しかし、私はそれを行う場所がわかりませんが、CoerceValue
メソッドの中では、強制的な値はまだ設定されていません(バインディングを更新するには早すぎます)。値がCoerceValue
でリセットされた場合、プロパティ値はそのまま残っているので、プロパティ変更コールバックは呼び出されません(this discussionにも暗示されています)。私は、従来のプロパティとINotifyPropertyChanged
実装とSomeControl
に依存プロパティを交換したと考えた
一つの可能な回避策は、(私は手動で値が強制されている場合でもPropertyChanged
イベントをトリガすることができます)。しかし、これは、そのプロパティにバインディングを宣言することができないため、本当に便利なソリューションではありません。
あなたがこの回答を見つけたら教えてください) –