O. *_*per 9 data-binding wpf xaml dependency-properties coerce
tl; dr:强制值不会跨数据绑定传播.当代码隐藏不知道绑定的另一面时,如何强制更新数据绑定?
我正在使用一个CoerceValueCallback
WPF依赖属性,我坚持认为强制值不会传播到绑定的问题.
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();
}
}
}
Run Code Online (Sandbox Code Playgroud)
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>
Run Code Online (Sandbox Code Playgroud)
即两个用户控件,一个嵌套在另一个中,另一个嵌在窗口中.内部用户控件具有Value
依赖项属性,该属性绑定到Value
外部控件的依赖项属性.在窗口中,TextBox.Text
属性绑定到Value
外部控件的属性.
内部控件已CoerceValueCallback
注册其Value
属性,其效果是此Value
属性只能分配偶数.
请注意,此代码已简化用于演示目的.真实版本不会在构造函数中初始化任何内容; 这两个控件实际上有控件模板,可以在这里完成各自构造函数中完成的所有操作.也就是说,在实际代码中,外部控件不知道内部控件.
在文本框中写入偶数并更改焦点时(例如,通过将虚拟按钮聚焦在文本框下方),两个Value
属性都会得到适当更新.但是,当在文本框中写入奇数时,Value
内部控件的属性不会改变,而Value
外部控件的TextBox.Text
属性以及属性显示奇数.
我的问题是:如何在文本框中强制更新(理想情况下,也可以在外部控件的Value
属性中强制更新)?
我在同一个问题上找到了一个SO问题,但并没有真正提供解决方案.它暗示使用属性更改事件处理程序来重置值,但据我所知,这意味着将评估代码复制到外部控件...这实际上不可行,因为我的实际评估代码依赖于某些信息基本上只知道(没有太多努力)内部控制.
此外,该博文暗示调用UpdateTarget
的结合中TextBox.Text
的CoerceValueCallback
,但首先,如上文所暗示的,我内心的控制不可能有关于文本框的任何知识,第二,我可能会叫UpdateSource
先上结合Value
的特性内在的控制.我不知道在哪里这样做,因为在CoerceValue
方法中,强制值尚未设置(因此更新绑定为时尚早),而在重置值的情况下,CoerceValue
属性值将保持原样,因此不会调用属性更改回调(在本讨论中也暗示).
我想到的一个可能的解决方法是SomeControl
使用常规属性和INotifyPropertyChanged
实现替换依赖属性(因此PropertyChanged
即使值已被强制,我也可以手动触发事件).但是,这意味着我不能再声明对该属性的绑定,因此它不是一个非常有用的解决方案.
一段时间以来,我自己一直在寻找这个相当令人讨厌的错误的答案。一种不需要在绑定上强制 UpdateTarget 的方法是:
根据您的代码,您的依赖属性声明将变为:
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register("Value",
typeof(int),
typeof(SomeControl),
new PropertyMetadata(0, ProcessValueChanged, null));
Run Code Online (Sandbox Code Playgroud)
然后,您的 ProcessValueChanged将变成这样:
private static void ProcessValueChanged(object source, DependencyPropertyChangedEventArgs e)
{
int baseValue = (int) e.NewValue;
SomeControl someControl = source as SomeControl;
if (baseValue % 2 != 0)
{
someControl.Value = DependencyProperty.UnsetValue;
}
else
{
someControl.ProcessValueChanged(e);
}
}
Run Code Online (Sandbox Code Playgroud)
我稍微修改了您的逻辑,以防止在需要强制值时引发事件。如前所述,将强制值分配给someControl.Value将导致ProcessValueChanged被连续调用两次。放置 else 语句只会引发一次具有有效值的事件。
我希望这有帮助!
归档时间: |
|
查看次数: |
2363 次 |
最近记录: |