继承WPF自定义控件不继承父Command
Inherited WPF custom control does not inherit parent Command
我创建了一个用于 WPF/XMAML 的 IconButton。它应该能够在顶部显示 Icon MDL2 Assets 字体,在底部显示文本。它应该具有默认 WPF 工具栏按钮的外观。我决定创建一个继承自默认 WPF 按钮的自定义控件。
所以我创建了自定义控件并添加了文本的依赖属性和有点神秘的 MDL2IconCode:
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 如下所示:
<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}}">
<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>
按钮看起来应该是这样。
但是 XAML 中的命令绑定不再起作用。但它应该可以工作,根据继承它仍然是一个按钮..
也许有人知道要添加什么才能使命令绑定起作用?
将控件模板中 Button
的命令绑定到模板父级。
<ControlTemplate TargetType="{x:Type local:IconButton}">
<Button Style="{StaticResource {x:Static ToolBar.ButtonStyleKey}}"
Command="{TemplateBinding Command}"
CommandParameter="{TemplateBinding CommandParameter}"
CommandTarget="{TemplateBinding CommandTarget}">
<!-- ...other code. -->
</Button>
</ControlTemplate>
But it should work, as per inheritance it still is a button..
没有。控件模板中的 Button
不会神奇地绑定到其模板化父控件的相应属性,无论它是从 Button
还是任何其他控件派生的。对于 CommandParameter
等其他依赖项属性,您也必须这样做。
另请注意 TemplateBinding
is an optimized binding that does not have all capabilities of the more powerful Binding
标记扩展。因此,当 TemplateBinding
不起作用时,例如在双向绑定场景中,你可以这样使用TemplatedParent
:
{Binding RelativeSource={RelativeSource TemplatedParent}, Path=MyDependencyProperty}
更多信息,您可以参考TemplateBinding
文档。
Button
的 ControlTemplate
不应包含另一个 Button
。
您应该将您的控件模板化为 看起来 像 Button
如果那是您想要的:
<Style TargetType="{x:Type local:IconButton}" BasedOn="{StaticResource {x:Static ToolBar.ButtonStyleKey}}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:IconButton}">
<Border Name="Bd" Background="{TemplateBinding Control.Background}"
BorderBrush="{TemplateBinding Control.BorderBrush}"
BorderThickness="{TemplateBinding Control.BorderThickness}"
Padding="{TemplateBinding Control.Padding}" SnapsToDevicePixels="true">
<ContentControl HorizontalAlignment="{TemplateBinding Control.HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding Control.VerticalContentAlignment}"
SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}">
<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>
</ContentControl>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="UIElement.IsMouseOver" Value="true">
<Setter TargetName="Bd" Value="#80DADADA" Property="BorderBrush"/>
<Setter TargetName="Bd" Value="#FFB6BDC5" Property="Background"/>
</Trigger>
<Trigger Property="UIElement.IsKeyboardFocused" Value="true">
<Setter TargetName="Bd" Value="#80DADADA" Property="BorderBrush"/>
<Setter TargetName="Bd" Value="#FFB6BDC5" Property="Background"/>
</Trigger>
<Trigger Property="ButtonBase.IsPressed" Value="true">
<Setter TargetName="Bd" Value="#90006CD9" Property="BorderBrush"/>
<Setter TargetName="Bd" Value="#400080FF" Property="Background"/>
</Trigger>
<Trigger Property="UIElement.IsEnabled" Value="false">
<Setter Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}" Property="Foreground"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
然后它将像任何其他 Button
一样运行,这意味着您可以像往常一样绑定它的 Command
属性:
<local:IconButton Command="{Binding YourCommand}" />
我创建了一个用于 WPF/XMAML 的 IconButton。它应该能够在顶部显示 Icon MDL2 Assets 字体,在底部显示文本。它应该具有默认 WPF 工具栏按钮的外观。我决定创建一个继承自默认 WPF 按钮的自定义控件。
所以我创建了自定义控件并添加了文本的依赖属性和有点神秘的 MDL2IconCode:
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 如下所示:
<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}}">
<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>
按钮看起来应该是这样。
但是 XAML 中的命令绑定不再起作用。但它应该可以工作,根据继承它仍然是一个按钮..
也许有人知道要添加什么才能使命令绑定起作用?
将控件模板中 Button
的命令绑定到模板父级。
<ControlTemplate TargetType="{x:Type local:IconButton}">
<Button Style="{StaticResource {x:Static ToolBar.ButtonStyleKey}}"
Command="{TemplateBinding Command}"
CommandParameter="{TemplateBinding CommandParameter}"
CommandTarget="{TemplateBinding CommandTarget}">
<!-- ...other code. -->
</Button>
</ControlTemplate>
But it should work, as per inheritance it still is a button..
没有。控件模板中的 Button
不会神奇地绑定到其模板化父控件的相应属性,无论它是从 Button
还是任何其他控件派生的。对于 CommandParameter
等其他依赖项属性,您也必须这样做。
另请注意 TemplateBinding
is an optimized binding that does not have all capabilities of the more powerful Binding
标记扩展。因此,当 TemplateBinding
不起作用时,例如在双向绑定场景中,你可以这样使用TemplatedParent
:
{Binding RelativeSource={RelativeSource TemplatedParent}, Path=MyDependencyProperty}
更多信息,您可以参考TemplateBinding
文档。
Button
的 ControlTemplate
不应包含另一个 Button
。
您应该将您的控件模板化为 看起来 像 Button
如果那是您想要的:
<Style TargetType="{x:Type local:IconButton}" BasedOn="{StaticResource {x:Static ToolBar.ButtonStyleKey}}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:IconButton}">
<Border Name="Bd" Background="{TemplateBinding Control.Background}"
BorderBrush="{TemplateBinding Control.BorderBrush}"
BorderThickness="{TemplateBinding Control.BorderThickness}"
Padding="{TemplateBinding Control.Padding}" SnapsToDevicePixels="true">
<ContentControl HorizontalAlignment="{TemplateBinding Control.HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding Control.VerticalContentAlignment}"
SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}">
<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>
</ContentControl>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="UIElement.IsMouseOver" Value="true">
<Setter TargetName="Bd" Value="#80DADADA" Property="BorderBrush"/>
<Setter TargetName="Bd" Value="#FFB6BDC5" Property="Background"/>
</Trigger>
<Trigger Property="UIElement.IsKeyboardFocused" Value="true">
<Setter TargetName="Bd" Value="#80DADADA" Property="BorderBrush"/>
<Setter TargetName="Bd" Value="#FFB6BDC5" Property="Background"/>
</Trigger>
<Trigger Property="ButtonBase.IsPressed" Value="true">
<Setter TargetName="Bd" Value="#90006CD9" Property="BorderBrush"/>
<Setter TargetName="Bd" Value="#400080FF" Property="Background"/>
</Trigger>
<Trigger Property="UIElement.IsEnabled" Value="false">
<Setter Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}" Property="Foreground"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
然后它将像任何其他 Button
一样运行,这意味着您可以像往常一样绑定它的 Command
属性:
<local:IconButton Command="{Binding YourCommand}" />