将 ActualWidth/ActualHeight 从自定义控件推送到 TemplateBinding
Pushing ActualWidth/ActualHeight from a custom control to TemplateBinding
我已经设置了一个简单的示例来尝试实现一件简单的事情:在自定义控件中公开依赖项 属性,从而在该自定义控件中公开控件的 ActualWidth/ActualHeight。
为了尝试实现这一点,我有:
customcontrol.cs
public class CustomControl : ContentControl
{
static CustomControl()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomControl), new FrameworkPropertyMetadata(typeof(CustomControl)));
}
public static readonly DependencyProperty RenderedWidthProperty = DependencyProperty.Register(
"RenderedWidth", typeof (double), typeof (CustomControl), new PropertyMetadata(default(double)));
public double RenderedWidth
{
get { return (double) GetValue(RenderedWidthProperty); }
set { SetValue(RenderedWidthProperty, value); }
}
public static readonly DependencyProperty RenderedHeightProperty = DependencyProperty.Register(
"RenderedHeight", typeof (double), typeof (CustomControl), new PropertyMetadata(default(double)));
public double RenderedHeight
{
get { return (double) GetValue(RenderedHeightProperty); }
set { SetValue(RenderedHeightProperty, value); }
}
}
generic.xaml
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Custom_Control_Pushing_ActualWidth">
<Style TargetType="{x:Type local:CustomControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:CustomControl}">
<Grid local:SizeObserver.Observe="True"
local:SizeObserver.ObservedWidth="{Binding RenderedWidth, RelativeSource={RelativeSource TemplatedParent}}"
local:SizeObserver.ObservedHeight="{Binding RenderedHeight, RelativeSource={RelativeSource TemplatedParent}}">
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
viewmodel.cs
class ViewModel
{
private double width;
private double height;
public double Width
{
get { return width; }
set
{
width = value;
Console.WriteLine("Width: {0}", value);
}
}
public double Height
{
get { return height; }
set
{
height = value;
Console.WriteLine("Height: {0}", value);
}
}
}
mainwindow.xaml
<Window x:Class="Custom_Control_Pushing_ActualWidth.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Custom_Control_Pushing_ActualWidth"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<local:ViewModel />
</Window.DataContext>
<Grid>
<local:CustomControl RenderedWidth="{Binding Width, Mode=OneWayToSource}" RenderedHeight="{Binding Height, Mode=OneWayToSource}" />
</Grid>
</Window>
我使用 this SO answer 的 SizeObserver。
然而,虽然我看到依赖项 属性 中的代码在大小观察器中被更新,但绑定的视图模型 属性 的 setter 没有设置值。我的绑定有问题,我不知道它是什么。
如何正确绑定 DependencyProperty 与 ViewModel 的 属性?
改变这个:
<Grid local:SizeObserver.Observe="True"
local:SizeObserver.ObservedWidth="{Binding RenderedWidth, RelativeSource={RelativeSource TemplatedParent}}"
local:SizeObserver.ObservedHeight="{Binding RenderedHeight, RelativeSource={RelativeSource TemplatedParent}}">
</Grid>
为此:
<Grid local:SizeObserver.Observe="True"
local:SizeObserver.ObservedWidth="{Binding RenderedWidth, RelativeSource={RelativeSource TemplatedParent}, Mode=OneWayToSource}"
local:SizeObserver.ObservedHeight="{Binding RenderedHeight, RelativeSource={RelativeSource TemplatedParent}, Mode=OneWayToSource}">
</Grid>
所以在绑定的末尾添加Mode=OneWayToSource
。
这样,ViewModel 中的宽度 属性 就可以为我正确更新。
这背后的原因对我来说并不完全清楚,但我认为 ObservedWidth
和 ObservedHeight
附加属性的默认绑定模式是 OneWay。因此,他们仅在源属性 (RenderedWidth, RenderedHeight
) 更改时更新目标属性 (ObservedWidth, ObservedHeight
)。
你想要的恰恰相反。
通过 OneWayToSource
修改,ActualWidth
和 ActualHeight
属性中的更改将很好地传播到您的 ViewModel。
我已经设置了一个简单的示例来尝试实现一件简单的事情:在自定义控件中公开依赖项 属性,从而在该自定义控件中公开控件的 ActualWidth/ActualHeight。
为了尝试实现这一点,我有:
customcontrol.cs
public class CustomControl : ContentControl
{
static CustomControl()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomControl), new FrameworkPropertyMetadata(typeof(CustomControl)));
}
public static readonly DependencyProperty RenderedWidthProperty = DependencyProperty.Register(
"RenderedWidth", typeof (double), typeof (CustomControl), new PropertyMetadata(default(double)));
public double RenderedWidth
{
get { return (double) GetValue(RenderedWidthProperty); }
set { SetValue(RenderedWidthProperty, value); }
}
public static readonly DependencyProperty RenderedHeightProperty = DependencyProperty.Register(
"RenderedHeight", typeof (double), typeof (CustomControl), new PropertyMetadata(default(double)));
public double RenderedHeight
{
get { return (double) GetValue(RenderedHeightProperty); }
set { SetValue(RenderedHeightProperty, value); }
}
}
generic.xaml
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Custom_Control_Pushing_ActualWidth">
<Style TargetType="{x:Type local:CustomControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:CustomControl}">
<Grid local:SizeObserver.Observe="True"
local:SizeObserver.ObservedWidth="{Binding RenderedWidth, RelativeSource={RelativeSource TemplatedParent}}"
local:SizeObserver.ObservedHeight="{Binding RenderedHeight, RelativeSource={RelativeSource TemplatedParent}}">
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
viewmodel.cs
class ViewModel
{
private double width;
private double height;
public double Width
{
get { return width; }
set
{
width = value;
Console.WriteLine("Width: {0}", value);
}
}
public double Height
{
get { return height; }
set
{
height = value;
Console.WriteLine("Height: {0}", value);
}
}
}
mainwindow.xaml
<Window x:Class="Custom_Control_Pushing_ActualWidth.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Custom_Control_Pushing_ActualWidth"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<local:ViewModel />
</Window.DataContext>
<Grid>
<local:CustomControl RenderedWidth="{Binding Width, Mode=OneWayToSource}" RenderedHeight="{Binding Height, Mode=OneWayToSource}" />
</Grid>
</Window>
我使用 this SO answer 的 SizeObserver。
然而,虽然我看到依赖项 属性 中的代码在大小观察器中被更新,但绑定的视图模型 属性 的 setter 没有设置值。我的绑定有问题,我不知道它是什么。
如何正确绑定 DependencyProperty 与 ViewModel 的 属性?
改变这个:
<Grid local:SizeObserver.Observe="True"
local:SizeObserver.ObservedWidth="{Binding RenderedWidth, RelativeSource={RelativeSource TemplatedParent}}"
local:SizeObserver.ObservedHeight="{Binding RenderedHeight, RelativeSource={RelativeSource TemplatedParent}}">
</Grid>
为此:
<Grid local:SizeObserver.Observe="True"
local:SizeObserver.ObservedWidth="{Binding RenderedWidth, RelativeSource={RelativeSource TemplatedParent}, Mode=OneWayToSource}"
local:SizeObserver.ObservedHeight="{Binding RenderedHeight, RelativeSource={RelativeSource TemplatedParent}, Mode=OneWayToSource}">
</Grid>
所以在绑定的末尾添加Mode=OneWayToSource
。
这样,ViewModel 中的宽度 属性 就可以为我正确更新。
这背后的原因对我来说并不完全清楚,但我认为 ObservedWidth
和 ObservedHeight
附加属性的默认绑定模式是 OneWay。因此,他们仅在源属性 (RenderedWidth, RenderedHeight
) 更改时更新目标属性 (ObservedWidth, ObservedHeight
)。
你想要的恰恰相反。
通过 OneWayToSource
修改,ActualWidth
和 ActualHeight
属性中的更改将很好地传播到您的 ViewModel。