继承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文档。

ButtonControlTemplate 不应包含另一个 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}" />