问题描述
我最近 created an IconButton in WPF 作为 CustomControl。它正在为 DependencyProperties 使用 TemplateBinding:
IconButton.cs
public class IconButton : Button
{
public static readonly DependencyProperty TextProperty;
public static readonly DependencyProperty MDL2IconCodeProperty;
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty,value); }
}
public string MDL2IconCode
{
get { return (string)GetValue(MDL2IconCodeProperty); }
set { SetValue(MDL2IconCodeProperty,value); }
}
static IconButton()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(IconButton),new FrameworkPropertyMetadata(typeof(IconButton)));
TextProperty = DependencyProperty.Register("Text",typeof(string),typeof(IconButton),new PropertyMetadata("Button text",OnTextChanged));
MDL2IconCodeProperty = DependencyProperty.Register("MDL2IconCode",new PropertyMetadata("\uf13e",OnIconTextChanged));
}
static void OnTextChanged(DependencyObject o,DependencyPropertyChangedEventArgs e)
{
var iconButton = o as IconButton;
if (iconButton == null)
{
return;
}
string newText = e.NewValue as string;
iconButton.Text = newText;
}
static void OnIconTextChanged(DependencyObject o,DependencyPropertyChangedEventArgs e)
{
var iconButton = o as IconButton;
if (iconButton == null)
{
return;
}
string newText = e.NewValue as string;
iconButton.MDL2IconCode = newText;
}
}
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:UI.CustomControls">
<Style targettype="{x:Type local:IconButton}"
BasedOn="{StaticResource {x:Type Button}}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate targettype="{x:Type local:IconButton}">
<Button Style="{StaticResource {x:Static ToolBar.ButtonStyleKey}}"
Command="{TemplateBinding Command}"
CommandParameter="{TemplateBinding CommandParameter}"
CommandTarget="{TemplateBinding CommandTarget}">
<StackPanel>
<TextBlock HorizontalAlignment="Center"
Text="{TemplateBinding MDL2IconCode}"
FontFamily="Segoe MDL2 Assets"
FontSize="16"
x:Name="iconTextBlock"/>
<TextBlock HorizontalAlignment="Center"
Text="{TemplateBinding Text}"
x:Name="textTextBlock"/>
</StackPanel>
</Button>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
但它只工作了一半。我很快意识到绑定到 DependencyProperties 仅适用于 XAML 设计器,而不适用于 viewmodel。因此,当我在设计器中设置 Text 属性时,它会起作用。但是从 viewmodel 绑定到它,该属性既不是初始设置也不是在 INotifyPropertyChanged 事件上更新。
因此作为测试,我将 Text 属性的 TemplateBinding 更改为
{Binding RelativeSource={RelativeSource TemplatedParent},Path=Text}
但这没有帮助。
我的代码可能有什么问题?
带有 TemplateBinding 的 WPF 自定义控件是否支持 INotifyPropertyChanged?
解决方法
问题在于您以一种破坏 Text
的方式设置了 MDL2IconCode
和 Binding
的新值,因此更改不会传播到 UI。
iconButton.Text = newText;
....
iconButton.MDL2IconCode = newText;
正确的方法是使用 SetCurrentValue
方法改变属性的有效值,但现有的触发器、数据绑定和样式将继续工作。
static void OnTextChanged(DependencyObject o,DependencyPropertyChangedEventArgs e)
{
var iconButton = o as IconButton;
if (iconButton == null)
{
return;
}
string newText = e.NewValue as string;
iconButton.SetCurrentValue(TextProperty,newText);
}
static void OnIconTextChanged(DependencyObject o,DependencyPropertyChangedEventArgs e)
{
var iconButton = o as IconButton;
if (iconButton == null)
{
return;
}
string newText = e.NewValue as string;
iconButton.SetCurrentValue(MDL2IconCodeProperty,newText);
}
但是如果您在 OnTextChanged
和 OnIconTextChanged
中没有任何特殊逻辑,那么您可以去掉 PropertyChangedCallbacks
并且它仍然可以工作。
TextProperty = DependencyProperty.Register("Text",typeof(string),typeof(IconButton),new PropertyMetadata("Button text"));
MDL2IconCodeProperty = DependencyProperty.Register("MDL2IconCode",new PropertyMetadata("\uf13e"));