更改自定义属性不会触发绑定更新

本文关键字:绑定 更新 自定义属性 | 更新日期: 2023-09-27 17:50:59

我尝试创建一个名为MyComboBox的自定义组合框。它有一个在上一项和下一项之间切换的按钮。

我将底色存储在BaseBackground中。这是有用的,因为我不希望FrontGlyph从模板化的父级继承Background

这是我的WPF代码:
<Style x:Key="{x:Type local:MyComboBox}" TargetType="{x:Type local:MyComboBox}">
    </Style.Resources>
    <Setter Property="Focusable" Value="False" />
    <Setter Property="SnapsToDevicePixels" Value="True" />
    <Setter Property="OverridesDefaultStyle" Value="True" />
    <Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto" />
    <Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto" />
    <Setter Property="ScrollViewer.CanContentScroll" Value="true" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type local:MyComboBox}">
                <Grid Cursor="Hand">
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="Auto" />       <!-- A glyph will come here -->
                        <ColumnDefinition Width="*" />          <!-- The base combo box can take as much space as it can. -->
                    </Grid.ColumnDefinitions>
                    <Path x:Name="FrontGlyph" Grid.Column="0" Data="M 0.0 16.0 L 6.0 0.0 L 6.0 16.0 Z" Fill="{TemplateBinding BaseBackground}" Stretch="Fill" />
                    <Grid x:Name="BaseComboBox" Grid.Column="1" Background="{TemplateBinding BaseBackground}">
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="Auto" />   <!-- Previous item -->
                            <ColumnDefinition Width="Auto" />   <!-- Next item -->
                            <ColumnDefinition Width="*" />      <!-- Content Presenter -->
                            <ColumnDefinition Width="Auto" />   <!-- Drop down button -->
                        </Grid.ColumnDefinitions>
                        <Button x:Name="Prev" Grid.Column="0" Background="{TemplateBinding Background}" Style="{StaticResource MyUIButton}">
                            <Path VerticalAlignment="Center" Data="M 4.5 0.5 L 0.5 4.5 L 4.5 8.5 Z" Fill="Black" />
                        </Button>
                        <Button x:Name="Next" Grid.Column="1" Background="{TemplateBinding Background}" Style="{StaticResource MyUIButton}">
                            <Path VerticalAlignment="Center" Data="M 0.5 0.5 L 4.5 4.5 L 0.5 8.5 Z" Fill="Black" />
                        </Button>
                        <ContentPresenter x:Name="ContentSite" Grid.Column="2" IsHitTestVisible="False" Content="{TemplateBinding SelectionBoxItem}" ContentTemplate="{TemplateBinding SelectionBoxItemTemplate}" ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}" VerticalAlignment="Stretch" HorizontalAlignment="Left" />
                        <ToggleButton x:Name="ToggleButton" Template="{StaticResource ComboBoxToggleButton}" Grid.Column="3" Focusable="false" ClickMode="Press" IsChecked="{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}" Background="{TemplateBinding Background}" />
                        <Popup x:Name="Popup" Placement="Bottom" IsOpen="{TemplateBinding IsDropDownOpen}" AllowsTransparency="True" Focusable="False" PopupAnimation="Slide">
                            <Grid x:Name="DropDown" SnapsToDevicePixels="True" MinWidth="{TemplateBinding ActualWidth}" MaxHeight="{TemplateBinding MaxDropDownHeight}">
                                <Border x:Name="DropDownBorder" BorderThickness="1" BorderBrush="{TemplateBinding Background, Converter={StaticResource LightenBrushColor}, ConverterParameter=0.5}" Background="{TemplateBinding Background}" />
                                <ScrollViewer Margin="4,6,4,6" SnapsToDevicePixels="True">
                                    <StackPanel IsItemsHost="True" KeyboardNavigation.DirectionalNavigation="Contained" />
                                </ScrollViewer>
                            </Grid>
                        </Popup>
                    </Grid>
                </Grid>
                <ControlTemplate.Triggers>
                    <Trigger Property="IsMouseOver" Value="True">
                        <Setter Property="BaseBackground" Value="Goldenrod" />  <!-- This does not work -->
                        <!--<Setter TargetName="FrontGlyph" Property="Fill" Value="Goldenrod" />
                        <Setter TargetName="BaseComboBox" Property="Background" Value="{Binding Path=Fill, ElementName=FrontGlyph}" />-->   <!-- These 2 lines do work. -->
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

MyComboBox的源代码:

class MyComboBox : ComboBox
{
    public static readonly DependencyProperty BaseBackgroundProperty;
    public SolidColorBrush BaseBackground { get { return (SolidColorBrush)GetValue(BaseBackgroundProperty); } set { SetValue(BaseBackgroundProperty, value); } }
    static MyComboBox()
    {
        BaseBackgroundProperty = DependencyProperty.Register("BaseBackground", typeof(SolidColorBrush), typeof(MyComboBox), new FrameworkPropertyMetadata(Brushes.Lime, FrameworkPropertyMetadataOptions.AffectsRender, OnBaseBackgroundPropertyChanged));
    }
    public MyComboBox()
    {
        DefaultStyleKey = typeof(MyComboBox);
    }
    private static void OnBaseBackgroundPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
    {
        // This is not called when the trigger tries to set BaseBackground when the mouse is over the control
    }
}

当我将鼠标悬停在控件上时,它应该改变颜色。我应该能够通过改变BaseBackground来做到这一点,因为FrontGlyphBaseComboBox都有它们的背景色绑定在那里。尽管如此,代码可以编译,但更改并没有发生。不仅在UI上,而且如果我调试代码,我也看不到c#中的变化。

如果我改变FrontGlyph的背景颜色,并将BaseComboBox.BackgroundColor绑定到它,它会很好地工作。

有人能解释为什么我的自定义属性BaseBackground的变化没有注册?它一定不是标准的"实现INotifyPropertyChanged接口"问题,因为我使用Brush作为我的属性,画笔在其他地方工作得很好。:)

我的实现可能看起来很傻。嗯,我对WPF有点陌生。另外,我不想让您负担整个实现,只是试图复制关键部分。

更新

我发现,在我的源代码我设置BaseBackground = new SolidColorBrush(...),如果一些条件得到满足。如果我删除这行代码,现在触发器工作,BaseBackground被分配为Goldenrod颜色。

但是我想知道,为什么从c#代码更改DependencyProperty会阻止它从XAML标记工作。此外,我需要他们两个都工作。

谢谢。

更改自定义属性不会触发绑定更新

摘要

我认为你可能要做的是注册BaseBackgroundProperty下的名称"BaseBackground"(而不是"Background"),因为你的xaml正试图设置一个属性与该名称。

查看您的代码,我认为您的MyComboBox.BaseBackground属性从未设置的原因是因为它正在使用名称"Background"注册依赖属性系统。这意味着在基类中有一个注册为"Background" (Control.BackgroundProperty)的属性,在派生类中有一个注册为"Background" (MyComboBox.BaseBackgroundProperty)的属性。所以理论上,在xaml中设置"Background"应该最终设置MyComboBox.BaseBackgroundProperty,而设置"Control.Background"应该最终设置Control.Background属性。

虽然理论上可行,但我不知道它在实践中是否可行。这也不是真正的做事方式。如果你想以某种方式修改你的类的现有属性,你可以在你的类型初始化器(又名静态构造函数)中覆盖Control.BackgroundProperty的元数据,但我不认为这是你的意图在这里。您的xaml试图设置不存在的名为"BaseBackground"的属性。

根据你的问题更新,看起来你正在做的是在依赖属性上设置一个本地值,这可能会破坏模板绑定。在通过其他方式设置目标的特定情况下,单向绑定可能会发生这种情况。在这种情况下,似乎您正在设置源,但有可能导致绑定中断。

您可以通过设置presentationtracesource来调试绑定。TraceLevel属性设置为"High"。(您可能需要使用标准的{Binding RelativeSource={RelativeSource TemplatedParent}, Mode=OneWay}语法而不是{TemplateBinding}快捷语法来附加属性-我不确定。)然后,运行应用程序并查看调试输出。如果绑定被破坏了,它会告诉你。

如果绑定实际上被破坏了,根据你的用例,你可以做不同的事情来修复它。

  1. 您可以将绑定模式设置为TwoWay,以防止其破坏。
  2. 你可以尝试只从代码中使用SetCurrentValue而不是SetValue来设置它,因为许多控件在从代码中修改自己的依赖属性时往往会这样做。

潜在相关信息:

  • DependencyProperty值优先级
  • 在WPF中为什么TemplateBinding不起作用?
  • 调试数据绑定
  • 控制创作概述