Gre*_*g D 13 c# data-binding wpf xaml dependency-properties
我正在编写一个真正的NumericUpDown/Spinner控件作为练习来学习自定义控件创作.我已经得到了我正在寻找的大部分行为,包括适当的强制.然而,我的一项测试显示出一个缺陷.
我的控制有3个依赖属性:Value,MaximumValue,和MinimumValue.我使用强制来确保Value在最小值和最大值之间保留,包括在内.例如:
// In NumericUpDown.cs
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register("Value", typeof(int), typeof(NumericUpDown),
new FrameworkPropertyMetadata(0, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault | FrameworkPropertyMetadataOptions.Journal, HandleValueChanged, HandleCoerceValue));
[Localizability(LocalizationCategory.Text)]
public int Value
{
get { return (int)this.GetValue(ValueProperty); }
set { this.SetCurrentValue(ValueProperty, value); }
}
private static object HandleCoerceValue(DependencyObject d, object baseValue)
{
NumericUpDown o = (NumericUpDown)d;
var v = (int)baseValue;
if (v < o.MinimumValue) v = o.MinimumValue;
if (v > o.MaximumValue) v = o.MaximumValue;
return v;
}
Run Code Online (Sandbox Code Playgroud)
我的测试只是为了确保数据绑定符合我的预期.我创建了一个默认的wpf windows应用程序,并引入了以下xaml:
<Window x:Class="WpfApplication.MainWindow" x:Name="This"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:nud="clr-namespace:WpfCustomControlLibrary;assembly=WpfCustomControlLibrary"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<nud:NumericUpDown Value="{Binding ElementName=This, Path=NumberValue}"/>
<TextBox Grid.Row="1" Text="{Binding ElementName=This, Path=NumberValue, Mode=OneWay}" />
</Grid>
</Window>
Run Code Online (Sandbox Code Playgroud)
非常简单的代码隐藏:
public partial class MainWindow : Window
{
public int NumberValue
{
get { return (int)GetValue(NumberValueProperty); }
set { SetCurrentValue(NumberValueProperty, value); }
}
// Using a DependencyProperty as the backing store for NumberValue. This enables animation, styling, binding, etc...
public static readonly DependencyProperty NumberValueProperty =
DependencyProperty.Register("NumberValue", typeof(int), typeof(MainWindow), new UIPropertyMetadata(0));
public MainWindow()
{
InitializeComponent();
}
}
Run Code Online (Sandbox Code Playgroud)
(我省略了控件演示的xaml)
现在如果我运行它,我会NumericUpDown在文本框中看到适当反映的值,但是如果我输入的值超出范围,则超出范围值会显示在测试文本框中,同时NumericUpDown显示正确的值.
这是强制价值应该如何行动的吗?在ui中强制它是好的,但我也期望强制值也能通过数据绑定.
Qua*_*ter 13
哇,这很令人惊讶.在依赖项属性上设置值时,绑定表达式会在值强制运行之前更新!
如果查看Reflector中的DependencyObject.SetValueCommon,可以在方法的中途看到对Expression.SetValue的调用.在绑定已经更新之后,对调用CoerceValueCallback的UpdateEffectiveValue的调用就在最后.
您也可以在框架类上看到这一点.从新的WPF应用程序中,添加以下XAML:
<StackPanel>
<Slider Name="Slider" Minimum="10" Maximum="20" Value="{Binding Value,
RelativeSource={RelativeSource AncestorType=Window}}"/>
<Button Click="SetInvalid_Click">Set Invalid</Button>
</StackPanel>
Run Code Online (Sandbox Code Playgroud)
和以下代码:
private void SetInvalid_Click(object sender, RoutedEventArgs e)
{
var before = this.Value;
var sliderBefore = Slider.Value;
Slider.Value = -1;
var after = this.Value;
var sliderAfter = Slider.Value;
MessageBox.Show(string.Format("Value changed from {0} to {1}; " +
"Slider changed from {2} to {3}",
before, after, sliderBefore, sliderAfter));
}
public int Value { get; set; }
Run Code Online (Sandbox Code Playgroud)
如果您拖动滑块然后单击按钮,您将收到一条消息,如"值从11更改为-1;滑块从11更改为10".
旧问题的新答案:-)
在登记ValueProperty一个FrameworkPropertyMetadata实例被使用.UpdateSourceTrigger将此实例的属性设置为Explicit.这可以在构造函数重载中完成.
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register("Value", typeof(int), typeof(NumericUpDown),
new FrameworkPropertyMetadata(
0,
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault | FrameworkPropertyMetadataOptions.Journal,
HandleValueChanged,
HandleCoerceValue,
false
UpdateSourceTrigger.Explicit));
Run Code Online (Sandbox Code Playgroud)
现在,绑定源ValueProperty将不会自动更新PropertyChanged.在您的HandleValueChanged方法中手动更新(请参阅上面的代码).只有在调用了强制方法后,才会对属性的"实际"更改调用此方法.
你可以这样做:
static void HandleValueChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
NumericUpDown nud = obj as NumericUpDown;
if (nud == null)
return;
BindingExpression be = nud.GetBindingExpression(NumericUpDown.ValueProperty);
if(be != null)
be.UpdateSource();
}
Run Code Online (Sandbox Code Playgroud)
通过这种方式,您可以避免使用DependencyProperty的非强制值更新绑定.
| 归档时间: |
|
| 查看次数: |
1849 次 |
| 最近记录: |