将 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 中的宽度 属性 就可以为我正确更新。

这背后的原因对我来说并不完全清楚,但我认为 ObservedWidthObservedHeight 附加属性的默认绑定模式是 OneWay。因此,他们仅在源属性 (RenderedWidth, RenderedHeight) 更改时更新目标属性 (ObservedWidth, ObservedHeight)。

你想要的恰恰相反。 通过 OneWayToSource 修改,ActualWidthActualHeight 属性中的更改将很好地传播到您的 ViewModel。