【问题标题】:Inherited WPF custom control does not inherit parent Command继承的 WPF 自定义控件不继承父命令
【发布时间】:2021-05-06 21:29:33
【问题描述】:

我创建了一个用于 WPF/XMAML 的 IconButton。它应该能够在顶部显示 Icon MDL2 Assets 字体,在底部显示文本。它应该具有默认 WPF 工具栏按钮的外观。我决定创建一个继承自默认 WPF 按钮的自定义控件。

所以我创建了自定义控件并添加了 Text 的依赖属性和有点神秘的 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 通常不应该包含另一个Button。您是否考虑过将您的Style 基于ToolBar.ButtonStyleKey 并设置Content 属性?

标签: c# wpf xaml custom-controls controltemplate


【解决方案1】:

将控件模板内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>

但它应该可以工作,根据继承它仍然是一个按钮..

没有。控件模板内的Button 不会神奇地绑定到其模板化父级的相应属性,无论它是从Button 或任何其他控件派生的。对于 CommandParameter 等其他依赖项属性,您也必须这样做。

还请注意,TemplateBinding 是一种优化的绑定,它不具备更强大的Binding 标记扩展的所有功能。因此,当TemplateBinding 不起作用时,例如在双向绑定场景中,您可以像这样使用TemplatedParent:

{Binding RelativeSource={RelativeSource TemplatedParent}, Path=MyDependencyProperty}

更多信息可以参考TemplateBinding文档。

【讨论】:

  • 谢谢你,命令模板绑定终于可以工作了。
  • 但现在我注意到它只有在 XAML 设计器中为 IconButton 添加文本时才有效。当我将 Text 绑定到 ViewModel 属性时,它不起作用。 Ìs INotifyPropertyChanged ControlTemplate 绑定不支持?
  • 使用TemplatedParent 没有解决这个问题。
【解决方案2】:

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}" />

【讨论】:

    猜你喜欢
    • 2016-10-11
    • 1970-01-01
    • 1970-01-01
    • 2012-12-17
    • 1970-01-01
    • 1970-01-01
    • 2020-06-10
    • 2014-07-04
    • 1970-01-01
    相关资源
    最近更新 更多