在 DependencyProperty 的 PropertyChangedCallback 中从代码隐藏设置绑定
Setting a binding from code-behind in PropertyChangedCallback of a DependencyProperty
我正在尝试实现一个 UserControl
来承载其他控件,这些控件绑定到它的某些属性。在我的例子中,它会在 NumericUpDown
旁边显示一个滑块 - 两个控件都将绑定到 UserControl
的 Value
属性。我的方法是为每个控件定义一个 DependencyProperty
,因此在使用此控件时,Minimum
、TickPlacement
等属性仍然可以通过将新滑块设置为相应 属性:
<!-- Displays a slider with default properties next to a text block-->
<Foo/>
<!-- Displays a sider with custom properties -->
<Foo>
<Foo.Slider>
<Slider Maximum="500" TickPlacement="Both" TickFrequency="20"/>
</Foo.Slider>
</Foo>
代码隐藏看起来像这样:
public partial class Foo : UserControl
{
public Foo()
{
InitializeComponent();
Slider = new Slider();
}
public static readonly DependencyProperty ValueProperty = DependencyProperty.Register(nameof(Value), typeof(double), typeof(Foo), new PropertyMetadata(0.0));
public double Value
{
get => (double)GetValue(ValueProperty);
set => SetValue(ValueProperty, value);
}
public static readonly DependencyProperty SliderProperty = DependencyProperty.Register(nameof(Slider), typeof(Slider), typeof(Foo),
new PropertyMetadata(
new PropertyChangedCallback((obj, e) =>
{
Slider slNew = (Slider)e.NewValue;
if (slNew == null)
obj.SetValue(SliderProperty, new Slider());
else
slNew.SetBinding(ValueProperty,
new Binding("Value")
{
Source = obj,
UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged,
Mode = BindingMode.TwoWay
});
})));
public Slider Slider
{
get => (Slider)GetValue(SliderProperty);
set => SetValue(SliderProperty, value ?? new Slider());
}
}
XAML 代码的相关部分如下所示:
<UserControl x:Name="ctrlBase" [...]>
<Grid [...]>
<Grid.ColumnDefinitions [...]/>
<ContentControl DataContext="{Binding ElementName=ctrlBase}"
Content="{Binding Path=Slider}"/>
<!-- NumericUpDown to be implemented -->
<TextBlock Grid.Column="1"
Text="{Binding ElementName=ctrlBase, Path=Value}/>
</Grid>
</UserControl>
实际问题是从代码隐藏创建的绑定似乎不起作用。我认为这是由于以下原因:
- 移动滑块不会改变文本块显示的值
在滑块旁边。
- 将
Value
从 XAML 更改为 属性
文本块显示的值而不是滑块的值。
你用错了ValueProperty
。它应该是滑块 class 之一,而不是 Foo.ValueProperty
:
slNew.SetBinding(Slider.ValueProperty, new Binding("Value") { ... });
如果您使用 Slider.ValueProperty.AddOwner
而不是 DependencyProperty.Register
就可以了。
也就是说,一个更简单的方法可能是简单地在您的 UserControl XAML 中声明一个滑块,并在实例化期间在 Foo 资源中提供一个默认的滑块样式:
<Foo ...>
<Foo.Resources>
<Style TargetType="Slider">
...
</Style>
</Foo.Resources>
</Foo>
我正在尝试实现一个 UserControl
来承载其他控件,这些控件绑定到它的某些属性。在我的例子中,它会在 NumericUpDown
旁边显示一个滑块 - 两个控件都将绑定到 UserControl
的 Value
属性。我的方法是为每个控件定义一个 DependencyProperty
,因此在使用此控件时,Minimum
、TickPlacement
等属性仍然可以通过将新滑块设置为相应 属性:
<!-- Displays a slider with default properties next to a text block-->
<Foo/>
<!-- Displays a sider with custom properties -->
<Foo>
<Foo.Slider>
<Slider Maximum="500" TickPlacement="Both" TickFrequency="20"/>
</Foo.Slider>
</Foo>
代码隐藏看起来像这样:
public partial class Foo : UserControl
{
public Foo()
{
InitializeComponent();
Slider = new Slider();
}
public static readonly DependencyProperty ValueProperty = DependencyProperty.Register(nameof(Value), typeof(double), typeof(Foo), new PropertyMetadata(0.0));
public double Value
{
get => (double)GetValue(ValueProperty);
set => SetValue(ValueProperty, value);
}
public static readonly DependencyProperty SliderProperty = DependencyProperty.Register(nameof(Slider), typeof(Slider), typeof(Foo),
new PropertyMetadata(
new PropertyChangedCallback((obj, e) =>
{
Slider slNew = (Slider)e.NewValue;
if (slNew == null)
obj.SetValue(SliderProperty, new Slider());
else
slNew.SetBinding(ValueProperty,
new Binding("Value")
{
Source = obj,
UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged,
Mode = BindingMode.TwoWay
});
})));
public Slider Slider
{
get => (Slider)GetValue(SliderProperty);
set => SetValue(SliderProperty, value ?? new Slider());
}
}
XAML 代码的相关部分如下所示:
<UserControl x:Name="ctrlBase" [...]>
<Grid [...]>
<Grid.ColumnDefinitions [...]/>
<ContentControl DataContext="{Binding ElementName=ctrlBase}"
Content="{Binding Path=Slider}"/>
<!-- NumericUpDown to be implemented -->
<TextBlock Grid.Column="1"
Text="{Binding ElementName=ctrlBase, Path=Value}/>
</Grid>
</UserControl>
实际问题是从代码隐藏创建的绑定似乎不起作用。我认为这是由于以下原因:
- 移动滑块不会改变文本块显示的值 在滑块旁边。
- 将
Value
从 XAML 更改为 属性 文本块显示的值而不是滑块的值。
你用错了ValueProperty
。它应该是滑块 class 之一,而不是 Foo.ValueProperty
:
slNew.SetBinding(Slider.ValueProperty, new Binding("Value") { ... });
如果您使用 Slider.ValueProperty.AddOwner
而不是 DependencyProperty.Register
就可以了。
也就是说,一个更简单的方法可能是简单地在您的 UserControl XAML 中声明一个滑块,并在实例化期间在 Foo 资源中提供一个默认的滑块样式:
<Foo ...>
<Foo.Resources>
<Style TargetType="Slider">
...
</Style>
</Foo.Resources>
</Foo>