Reputation: 171
I recently created an IconButton in WPF as CustomControl. It is using TemplateBinding for the DependencyProperties:
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",
typeof(string),
typeof(IconButton),
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>
But it works only half. I soon realized that binding to the DependencyProperties only works from the XAML designer but not from a ViewModel. So, when I set the Text property in the designer it works. But binding to it from a ViewModel, the property is neither set initially nor updated on INotifyPropertyChanged events.
So as a test I changed the TemplateBinding of Text property to
{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Text}
But it didn’t help.
What could be the problem in my code?
Is INotifyPropertyChanged supported by WPF custom controls with TemplateBinding at all?
Upvotes: 0
Views: 307
Reputation: 20778
The issue is that you set new values of Text
and MDL2IconCode
in a way which breaks the Binding
, so changes are not propagated to UI.
iconButton.Text = newText;
....
iconButton.MDL2IconCode = newText;
The correct way is to use SetCurrentValue
method that changes the effective value of the property, but existing triggers, data bindings, and styles will continue to work.
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);
}
But if you don't have any special logic in OnTextChanged
and OnIconTextChanged
then you can get rid of PropertyChangedCallbacks
and it will still work.
TextProperty = DependencyProperty.Register("Text",
typeof(string),
typeof(IconButton),
new PropertyMetadata("Button text"));
MDL2IconCodeProperty = DependencyProperty.Register("MDL2IconCode",
typeof(string),
typeof(IconButton),
new PropertyMetadata("\uf13e"));
Upvotes: 1