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

时间:2015-05-28 17:13:57

标签: c# wpf triggers dependency-properties

我尝试创建一个名为MyComboBox的自定义ComboBox。它有一个按钮,用于在上一个和下一个项目之间切换。

我将基础的背景颜色存储在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标记中运行。此外,我需要他们两个工作。

谢谢。

2 个答案:

答案 0 :(得分:1)

<强>摘要

我认为您可能要做的是在名称BaseBackgroundProperty(而非"BaseBackground")下注册"Background",因为您的xaml正在尝试使用该名称设置属性。< / p>

<强>详情

查看代码,我认为永远不会设置MyComboBox.BaseBackground属性的原因是因为它是使用名称"Background"向依赖属性系统注册的。这意味着您在基类中注册了"Background"Control.BackgroundProperty)的属性,以及在派生类中注册为"Background"MyComboBox.BaseBackgroundProperty)的属性。因此,理论上,在xaml中设置"Background"最终应设置为MyComboBox.BaseBackgroundProperty,而设置"Control.Background"时最终应设置Control.Background属性。

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

答案 1 :(得分:1)

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

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

如果绑定实际上已被破坏,您可以采取不同的措施来修复它,具体取决于您的使用情况。

  1. 您可以将绑定模式设置为TwoWay,以防止其破坏。
  2. 您可以尝试仅使用SetCurrentValue代替SetValue从代码设置它,因为许多控件在从代码修改自己的依赖项属性时往往会这样做。
  3. 潜在相关信息: