【问题标题】:TemplateBinding not working on INotifyPropertyChanged in WPF custom controlTemplateBinding 在 WPF 自定义控件中的 INotifyPropertyChanged 上不起作用
【发布时间】:2021-05-09 11:16:19
【问题描述】:

我最近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",
                                                       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>

但它只工作了一半。我很快意识到绑定到 DependencyProperties 仅适用于 XAML 设计器,但不适用于 ViewModel。因此,当我在设计器中设置 Text 属性时,它可以工作。但是从 ViewModel 绑定到它,该属性既不是最初设置也不是在 INotifyPropertyChanged 事件上更新。

所以作为测试,我将 Text 属性的 TemplateBinding 更改为

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

但这并没有帮助。

我的代码可能有什么问题?

带有 TemplateBinding 的 WPF 自定义控件是否完全支持 INotifyPropertyChanged?

【问题讨论】:

    标签: c# wpf xaml custom-controls inotifypropertychanged


    【解决方案1】:

    问题是您设置 TextMDL2IconCode 的新值的方式破坏了 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);
    }
    

    但是如果你在OnTextChangedOnIconTextChanged中没有任何特殊的逻辑,那么你可以去掉PropertyChangedCallbacks,它仍然可以工作。

    TextProperty = DependencyProperty.Register("Text",
                                                       typeof(string),
                                                       typeof(IconButton),
                                                       new PropertyMetadata("Button text"));
    
    MDL2IconCodeProperty = DependencyProperty.Register("MDL2IconCode",
                                                               typeof(string),
                                                               typeof(IconButton),
                                                               new PropertyMetadata("\uf13e"));
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-09-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-03-08
      相关资源
      最近更新 更多