WPF 附加 属性 触发两次
WPF Attached Property triggering twice
我正在尝试学习依赖属性和附加属性,所以如果您发现我正在尝试做的事情没有用处,请原谅我。
我有一个常用的 MVVM 方法,其中 Window 的数据上下文设置为 VM,View 是一个包含针对此类 VM 的用户控件的数据模板。
我试图让 window 容器尽可能地笨拙,因此我试图定义一些通常驻留在 window XAML 中的参数(例如高度)通过用户控件使用附加属性。
为此,我创建了以下 class,其中我定义了附加的 属性:
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);
}
}
}
如您所见,为了实现对容器 window 高度的控制,我在代码中创建了一个从附加的 属性 到容器 window 的绑定。
但是 ContainerHeight 更改被触发了两次。我第一次从 300(默认值)更改为 1024(XAML 中定义的值)。然后我立即收到另一个从 1024 回到 300,我不明白为什么。
window代码很简单:
<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>
您应该将父 window 的 Height
属性 绑定到附加的 属性 而不是相反,不是吗?
这会将父 window 的 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);
}
我正在尝试学习依赖属性和附加属性,所以如果您发现我正在尝试做的事情没有用处,请原谅我。
我有一个常用的 MVVM 方法,其中 Window 的数据上下文设置为 VM,View 是一个包含针对此类 VM 的用户控件的数据模板。
我试图让 window 容器尽可能地笨拙,因此我试图定义一些通常驻留在 window XAML 中的参数(例如高度)通过用户控件使用附加属性。
为此,我创建了以下 class,其中我定义了附加的 属性:
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);
}
}
}
如您所见,为了实现对容器 window 高度的控制,我在代码中创建了一个从附加的 属性 到容器 window 的绑定。
但是 ContainerHeight 更改被触发了两次。我第一次从 300(默认值)更改为 1024(XAML 中定义的值)。然后我立即收到另一个从 1024 回到 300,我不明白为什么。
window代码很简单:
<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>
您应该将父 window 的 Height
属性 绑定到附加的 属性 而不是相反,不是吗?
这会将父 window 的 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);
}