【问题标题】:WPF Attached Property triggering twiceWPF附加属性触发两次
【发布时间】:2018-03-15 18:40:52
【问题描述】:

我正在尝试学习依赖属性和附加属性,所以如果您发现我正在尝试做的事情没有用,请原谅我。

我有一个通常的 MVVM 方法,其中一个窗口的数据上下文设置为一个 VM,而视图是一个包含针对此类 VM 的用户控件的数据模板。

我试图使窗口容器尽可能地笨拙,因此我试图通过使用附加属性的用户控件定义一些通常驻留在窗口 XAML 中的参数(例如高度)。

为此,我创建了以下类,并在其中定义了附加属性:

public static class WpfExtensions
{
    public static readonly DependencyProperty ContainerHeightProperty = DependencyProperty.RegisterAttached(
      "ContainerHeight",
      typeof(double),
      typeof(WpfExtensions),
      new FrameworkPropertyMetadata(300.0, FrameworkPropertyMetadataOptions.AffectsParentArrange | FrameworkPropertyMetadataOptions.AffectsParentMeasure | FrameworkPropertyMetadataOptions.AffectsRender, ContainerHeight)
    );
    public static void SetContainerHeight(UIElement element, double value)
    {
        element.SetValue(ContainerHeightProperty, value);
    }
    public static double GetContainerHeight((UIElement element)
    {
        return (double)element.GetValue(ContainerHeightProperty);
    }

    private static void ContainerHeight(DependencyObject d, DependencyPropertyChangedEventArgs e)  
    {  
       if (d is UserControl)
       {
           UserControl l_Control = (UserControl)d;

           Binding l_Binding = new Binding();

           l_Binding.RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, typeof(Window), 1);

           l_Binding.Path = new PropertyPath("Height");

           l_Binding.Mode = BindingMode.OneWayToSource;

           BindingOperations.SetBinding(d, e.Property, l_Binding);           
       }
    }  
}

如您所见,为了实现对容器窗口高度的控制,我在代码中创建了一个从附加属性到容器窗口的绑定。

但是,ContainerHeight 更改会被触发两次。我第一次从 300(默认值)更改为 1024(在 XAML 中定义)。然后我立即收到另一个从 1024 回到 300 的消息,我不明白为什么。

窗口代码很简单:

<Window x:Class="WpfApplication.DialogWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:lcl="clr-namespace:WpfApplication"
        Title="DialogWindow">
    <Window.Resources>
        <DataTemplate DataType="{x:Type lcl:Dialog_VM}">
            <lcl:Dialog_VM_View />
        </DataTemplate>
    </Window.Resources>

    <ContentPresenter Content="{Binding }" />

</Window>

最后是简单的 ViewModel

<UserControl x:Class="WpfApplication.Dialog_VM_View"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:lcl="clr-namespace:WpfApplication"
    mc:Ignorable="d" 
    d:DesignHeight="300" d:DesignWidth="300"
    lcl:WpfExtensions.ContainerHeight="1024">

    <StackPanel>
        <TextBlock Text="This is a test" />
    </StackPanel>

</UserControl>

【问题讨论】:

  • 您的ContainerHeight 财产在哪里?
  • WpfExtensions,我粘贴的第一段代码,与 UserControl 一起使用,我粘贴的第三段代码。
  • 您是否考虑过简单地设置 Window 的 SizeToContent 属性,而不是这种奇怪的方法?
  • 不,这不是问题的范围。

标签: wpf c#-4.0 attached-properties


【解决方案1】:

您应该将父窗口的Height 属性绑定到附加属性,而不是相反,不是吗?

这会将父窗口的Height 设置(绑定)为1024,这是UserControl 中的依赖属性的值:

private static void ContainerHeight(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    if (d is UserControl)
    {
        UserControl l_Control = (UserControl)d;
        if (!l_Control.IsLoaded)
        {
            l_Control.Loaded += L_Control_Loaded;
        }
        else
        {
            Bind(l_Control);
        }
    }
}

private static void L_Control_Loaded(object sender, RoutedEventArgs e)
{
    UserControl l_Control = (UserControl)sender;
    Bind(l_Control);
}

private static void Bind(UserControl l_Control)
{
    Window window = Window.GetWindow(l_Control);

    Binding l_Binding = new Binding();
    l_Binding.Path = new PropertyPath(WpfExtensions.ContainerHeightProperty);
    l_Binding.Source = l_Control;

    BindingOperations.SetBinding(window, Window.HeightProperty, l_Binding);
}

【讨论】:

  • 这完全达到了我的预期。我试图将目标绑定到源并使用 onewaytosource 与目标作为控件的事情变得复杂,而正如您所展示的,以窗口为目标且控件为源的绑定是正确的方法。我达到了被视觉层次蒙蔽的脑锁。谢谢。
  • @user1464603 您可能还会发现根本不再需要绑定。只需在“Bind”方法中将 Window 的 Height 设置为当前 ContentHeight 属性值(只要 ContentHeight 以后不改变)。
  • 对不起,stackoverflow 菜鸟。
  • @Clemens。我知道那行得通。但是,如果我想在 UserControl 中保持逻辑自包含,包括进一步绑定(即从 textbox.Text)到同一用户控件中的附加属性以影响容器高度,则使用直接值将无济于事。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-03-08
  • 1970-01-01
  • 1970-01-01
  • 2012-04-25
  • 1970-01-01
  • 2018-11-24
相关资源
最近更新 更多