将ActualWidth / ActualHeight从自定义控件推送到TemplateBinding

时间:2015-04-23 18:09:58

标签: c# .net wpf xaml custom-controls

我已经设置了一个简单的示例来尝试实现一个简单的事情:在自定义控件中公开依赖项属性,该控件公开此自定义控件中控件的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。

然而,虽然我看到依赖项属性中的代码在size观察者中被更新,但绑定的viewmodel属性的setter不会被设置为值。我的绑定有问题,我不知道它是什么。

如何正确地将DependencyProperty与ViewModel的属性绑定?

1 个答案:

答案 0 :(得分:1)

改变这个:

<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中的Width属性正在为我正确更新。

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

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