简体   繁体   English

触发器设置器更改后未将新值设置为DependencyProperty

[英]Trigger setter doesn't set new value to DependencyProperty after it changed

I really tried to find the solution but I failed. 我确实试图找到解决方案,但失败了。 So I have ResourceDictionary in separate xaml file and Control class in another cs file. 所以我在单独的xaml文件中有ResourceDictionary,在另一个cs文件中有Control类。 Here is xaml code: 这是xaml代码:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:local="clr-namespace:WpfApp1" x:Class="Control1">
    <Style TargetType="{x:Type local:Control1}">
        <Setter Property="GridColor" Value="Red"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:Control1}">
                    <Grid>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition/>
                            <ColumnDefinition/>
                        </Grid.ColumnDefinitions>
                        <Grid x:Name="PART_Grid" Background="{TemplateBinding GridColor}" 
                              Height="{TemplateBinding Height}" Width="{TemplateBinding Width}"/>
                        <Button Grid.Column="1" x:Name ="PART_Button" Width="50" Height="50"/>
                    </Grid>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsChecked" Value="True">
                            <Setter Property="GridColor" Value="Black"/>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

In cs file I change IsChecked by OnMouseEnter and OnMouseLeave event handlers. 在cs文件中,我通过OnMouseEnterOnMouseLeave事件处理程序更改IsChecked It works fine. 工作正常。 The problem is that when I change GridColor in OnButtonClick event handler for example, it changes, but after that the trigger setter doesn't work (but another setters still work fine). 问题是,例如,当我在OnButtonClick事件处理程序中更改GridColor时,它会更改,但是此后触发器设置器不起作用(但是另一个设置器仍然可以正常工作)。 No exceptions, no messages in output. 没有例外,输出中没有消息。 Did I miss something? 我错过了什么?

Here is cs code if somebody needs it 这是如果有人需要的cs代码

  public class Control1: Control
    {
        public static readonly DependencyProperty GridColorProperty = 
            DependencyProperty.Register("GridColor", typeof(Brush), typeof(Control1), new PropertyMetadata(new SolidColorBrush(Colors.Red)));

        public static readonly DependencyProperty IsCheckedProperty = 
            DependencyProperty.Register("IsChecked", typeof(bool), typeof(Control1), new PropertyMetadata(false));

        public Brush GridColor
        {
            get { return (Brush)GetValue(GridColorProperty); }
            set { SetValue(GridColorProperty, value); }
        }

        public bool IsChecked
        {
            get { return (bool)GetValue(IsCheckedProperty); }
            set { SetValue(IsCheckedProperty, value); }
        }

        public override void OnApplyTemplate()
        {
            ((Grid)GetTemplateChild("PART_Grid")).MouseEnter += Grid_MouseEnter;
            ((Grid)GetTemplateChild("PART_Grid")).MouseLeave += Grid_MouseLeave;
            ((Button)GetTemplateChild("PART_Button")).Click += Button_Click;
            base.OnApplyTemplate();
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            GridColor = new SolidColorBrush(Colors.DarkBlue);
        }

        private void Grid_MouseLeave(object sender, System.Windows.Input.MouseEventArgs e)
        {
            IsChecked = false;
        }

        private void Grid_MouseEnter(object sender, System.Windows.Input.MouseEventArgs e)
        {
            IsChecked = true;
        }
    }

You seem to have run into a problem with dependency property value precedence : It looks to me like you're setting a local value (#3 in the linked precedence list) in the click handler, and that overrides the value set by the control template trigger (#4a in the precedence list). 您似乎遇到了依赖项属性值优先级的问题:在我看来,您正在单击处理程序中设置本地值(链接的优先级列表中的#3),并且覆盖了控件模板设置的值触发器(优先级列表中的#4a)。

The only time you want to mix precedences is when you explicitly want to replace something done in a default style or a base class or something of that nature. 唯一要混合优先级的时间是显式地替换默认样式或基类中已完成的操作或该性质的操作。

But your approach isn't the best idea anyway: Ideally, you write WPF controls as if a stranger with no access to the source will have to write the default style and default template. 但是您的方法仍然不是最好的主意:理想情况下,您编写WPF控件就像无权访问源代码的陌生人必须编写默认样式和默认模板一样。 And then you write your default template as if you were a stranger. 然后,您就像是一个陌生人一样编写默认模板。 Anything the template may need to know about should be cleanly exposed as a property. 模板可能需要了解的所有内容都应作为属性完全暴露。

That's not always possible, but I'll demonstrate how you can do it in this case. 这并非总是可能的,但在这种情况下,我将向您展示如何做到这一点。

The blue brush indicates a state: The button has been clicked. 蓝色画笔指示状态:已单击该按钮。 That state isn't explicitly stored or exposed anywhere, it's just implicit in the value of the GridColor property (which you really should have named GridBrush or GridBackground , since it's not a Color ). 该状态没有显式存储或公开在任何地方,它只是隐含在GridColor属性的值中(您实际上应该将其命名为GridBrushGridBackground ,因为它不是Color )。

So let's add a read-only dependency property for that state, and push the decision about brush color off to the template, where it belongs. 因此,让我们为该状态添加一个只读依赖项属性,并将有关画笔颜色的决定推到它所属的模板中。 This is really powerful, because if you want some sibling control to change its state depending on our HasBeenClicked state, you can just add a binding in the parent view. 这确实非常强大,因为如果您希望某些同级控件根据我们的HasBeenClicked状态更改状态,则只需在父视图中添加一个绑定即可。

You could also make HasBeenClicked a regular read/write dependency property, if you wanted a consumer of the control to be able to change that state programmatically or via a binding. 如果希望控件的使用者能够以编程方式或通过绑定更改状态,则还可以将HasBeenClicked为常规的读/写依赖项属性。

//  This is not a good name, but I don't know what your semantics are. 
public bool HasBeenClicked
{
    get { return (bool)GetValue(HasBeenClickedProperty); }
    protected set { SetValue(HasBeenClickedPropertyKey, value); }
}

internal static readonly DependencyPropertyKey HasBeenClickedPropertyKey =
    DependencyProperty.RegisterReadOnly(nameof(HasBeenClicked), typeof(bool), typeof(Control1),
        new PropertyMetadata(false));

public static readonly DependencyProperty HasBeenClickedProperty = HasBeenClickedPropertyKey.DependencyProperty;

private void Button_Click(object sender, RoutedEventArgs e)
{
    HasBeenClicked = true;
}

And in the control template: 并在控制模板中:

<ControlTemplate.Triggers>
    <!-- 
    The last applicable trigger wins: If both are true, GridColor will be Black 
    -->
    <Trigger Property="HasBeenClicked" Value="True">
        <Setter Property="GridColor" Value="DarkBlue"/>
    </Trigger>
    <Trigger Property="IsChecked" Value="True">
        <Setter Property="GridColor" Value="Black"/>
    </Trigger>
</ControlTemplate.Triggers>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM