【问题标题】:Pushing ActualWidth/ActualHeight from a custom control to TemplateBinding将 ActualWidth/ActualHeight 从自定义控件推送到 TemplateBinding
【发布时间】:2026-02-14 04:50:02
【问题描述】:

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

但是,尽管我看到在大小观察器中更新了依赖属性中的代码,但绑定的 viewmodel 属性的 setter 并没有使用这些值进行设置。我的绑定有问题,我不知道是什么问题。

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

【问题讨论】:

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


    【解决方案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。因此,它们仅在源属性 (RenderedWidth, RenderedHeight) 更改时更新目标属性 (ObservedWidth, ObservedHeight)。

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

    【讨论】:

    • 嘿,谢谢!这行得通!通过代码阅读,我无法确定将ObservedWidthObservedHeight 默认为OneWay 的原因,但同样,我不太了解这个WPF 接线(必须努力改变这个:) )。