我试图创建一些WPF用户控件以包含在库中以与团队共享,但是只读属性对我的工作方式有问题。
对于这个问题,我做了一个非常简单的带有两个DependencyProperty的用户控件。一个基于enum
,另一个基于所选的enum
执行操作。 enum
用于选择按钮将使用的样式。
该应用程序是一个常规Wpf应用程序,以Wpf用户控件库为参考。我怀疑控制库可能可能导致了该问题,因此我认为它与示例有关。
Wpf控制库1
Dictionary1.xaml:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfControlLibrary1">
<Style x:Key="SampleStyle-Font1">
<Setter Property="TextElement.FontFamily" Value="Wingdings" />
<Setter Property="TextElement.FontSize" Value="30" />
</Style>
<Style x:Key="SampleStyle-Font2">
<Setter Property="TextElement.FontFamily" Value="Elephant" />
<Setter Property="TextElement.FontSize" Value="30" />
</Style>
<Style x:Key="SampleStyle-Font3">
<Setter Property="TextElement.FontFamily" Value="Times New Roman" />
<Setter Property="TextElement.FontSize" Value="30" />
</Style>
</ResourceDictionary>
UserControl1.xaml:
<UserControl x:Class="WpfControlLibrary1.UserControl1"
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:local="clr-namespace:WpfControlLibrary1"
mc:Ignorable="d"
d:DesignHeight="100" d:DesignWidth="200">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Dictionary1.xaml"></ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<UserControl.Template>
<ControlTemplate>
<Button Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:UserControl1}}, Path=Command}">
<StackPanel>
<Label Style="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:UserControl1}}, Path=ReadOnlyStyle}" Content="{Binding Path=Content, RelativeSource={x:Static RelativeSource.TemplatedParent}}"></Label>
</StackPanel>
</Button>
</ControlTemplate>
</UserControl.Template>
</UserControl>
UserControl1.xaml.cs
namespace WpfControlLibrary1 {
using System.Windows;
using System.Windows.Controls;
/// <summary>
/// Interaction logic for UserControl1.xaml
/// </summary>
public partial class UserControl1 : UserControl {
public enum StyleSelector {
Style1,
Style2,
Style3
}
public static DependencyProperty SelectedStyleProperty =
DependencyProperty.Register("SelectedStyle", typeof(StyleSelector), typeof(UserControl1), new PropertyMetadata(ReadOnlyStyle_Changed));
private static readonly DependencyPropertyKey ReadOnlyStylePropertyKey =
DependencyProperty.RegisterReadOnly("ReadOnlyStyle", typeof(Style),
typeof(UserControl1), null);
public UserControl1() {
InitializeComponent();
}
public StyleSelector SelectedStyle {
get => (StyleSelector)GetValue(SelectedStyleProperty);
set => SetValue(SelectedStyleProperty, value);
}
public Style ReadOnlyStyle => (Style)GetValue(ReadOnlyStylePropertyKey.DependencyProperty);
private static void ReadOnlyStyle_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e) {
if (!(d is UserControl1 userControl1)) {
return;
}
Style style;
switch (userControl1.SelectedStyle) {
case StyleSelector.Style1:
style = (Style)userControl1.FindResource("SampleStyle-Font1");
break;
case StyleSelector.Style2:
style = (Style)userControl1.FindResource("SampleStyle-Font2");
break;
case StyleSelector.Style3:
style = (Style)userControl1.FindResource("SampleStyle-Font3");
break;
default:
style = (Style)userControl1.FindResource("SampleStyle-Font1");
break;
}
userControl1.SetValue(ReadOnlyStylePropertyKey, style);
}
}
}
Wpf应用程序
MainWindow.xaml:
<Window x:Class="ReadOnlyDependencyPropertiesWithUserControls.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:ReadOnlyDependencyPropertiesWithUserControls"
xmlns:wpfControlLibrary1="clr-namespace:WpfControlLibrary1;assembly=WpfControlLibrary1"
mc:Ignorable="d"
Title="Example" Height="200" Width="400">
<StackPanel>
<wpfControlLibrary1:UserControl1 SelectedStyle="Style1" Content="This is the first control"></wpfControlLibrary1:UserControl1>
<wpfControlLibrary1:UserControl1 SelectedStyle="Style2" Content="This is the second control"></wpfControlLibrary1:UserControl1>
<wpfControlLibrary1:UserControl1 SelectedStyle="Style3" Content="This is the third control"></wpfControlLibrary1:UserControl1>
</StackPanel>
</Window>
以下输出显示第一个控件未未显示Style
。如果我运行该应用程序,请使用“实时”编辑器将其切换为Style2,然后再切换回Style1,WingDings字体会接管,但在全新运行时则不会。显然,这似乎是一个依赖项属性问题,但据我所知,我的设置正确,尤其是因为其他两个控件似乎都起作用。
答案 0 :(得分:2)
之所以不起作用,是因为您在ReadOnlyStyle
属性更改处理程序中分配了SelectedStyle
属性值。由于SelectedStyle
的类型为StyleSelector
,类型为enum
,并且您没有为此属性显式分配默认值,因此它将具有由default(StyleSelector)
分配的默认值StyleSelector.Style1
。框架,恰好是ReadOnlyStyle
。即使您在第一个控件上将该值显式分配给此属性,该值也不会真正改变,因此不会调用处理程序ergo,ergo null
仍为Label
,ergo您会得到(具有默认样式的ReadOnlyStyle
)。
为了解决这个问题,您应该分配初始值protected override void OnInitialized(EventArgs e)
{
base.OnInitialized(e);
var style = (Style)userControl1.FindResource("SampleStyle-Font1");
SetValue(ReadOnlyStylePropertyKey, style);
}
。但是,由于样式保留在资源字典中,因此您不能在构造函数中这样做。分配初始值的一个好方法是:
public partial class UserControl1 : UserControl
{
public enum StyleSelector
{
Style1,
Style2,
Style3
}
public static DependencyProperty SelectedStyleProperty =
DependencyProperty.Register("SelectedStyle", typeof(StyleSelector), typeof(UserControl1));
public UserControl1()
{
InitializeComponent();
}
public StyleSelector SelectedStyle
{
get => (StyleSelector)GetValue(SelectedStyleProperty);
set => SetValue(SelectedStyleProperty, value);
}
}
实现目标的“ WPF 方法”是使用触发器。因此,首先您可以从控件中删除不必要的代码:
<ControlTemplate>
<Button Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:UserControl1}}, Path=Command}">
<StackPanel>
<Label x:Name="PART_Label" Content="{Binding Path=Content, RelativeSource={x:Static RelativeSource.TemplatedParent}}" />
</StackPanel>
</Button>
<ControlTemplate.Triggers>
<Trigger Property="local:UserControl1.SelectedStyle" Value="Style1">
<Setter TargetName="PART_Label" Property="Style" Value="{StaticResource SampleStyle-Font1}" />
</Trigger>
<Trigger Property="local:UserControl1.SelectedStyle" Value="Style2">
<Setter TargetName="PART_Label" Property="Style" Value="{StaticResource SampleStyle-Font2}" />
</Trigger>
<Trigger Property="local:UserControl1.SelectedStyle" Value="Style3">
<Setter TargetName="PART_Label" Property="Style" Value="{StaticResource SampleStyle-Font3}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
然后修改您的temlpate:
Label
这里有两个重要的事情:
x:Name
必须具有Setter.TargetName
,以便可以在Trigger.Property
中引用它ControlTemplate
的值必须完全合格,因为TargetType
没有设置TextBox
。