首页 > 解决方案 > WPF - 将面板切换到编辑模式,但使用样式排除特定部分

问题描述

我在应用程序中使用的面板有许多不同的部分。当按下按钮时,我想将面板/画布/用户控件等的某些部分置于编辑模式,而我可以根据我创建的附加属性排除其他部分。在我的 ViewModel 中,我使用了一个布尔属性“IsEnabled”,我在该属性上绑定了一个全局样式(编辑:在本示例中为文本框在资源字典中定义),如下所示:

<Style TargetType="TextBox">
<Style.Triggers>
        <DataTrigger Binding="{Binding Path=IsEnabled}" Value="False">
            <Setter Property="Properties:Properties.Editable" Value="False"/>
        </DataTrigger>
        <DataTrigger Binding="{Binding Path=IsEnabled}" Value="True">
            <Setter Property="Properties:Properties.Editable" Value="True"/>
        </DataTrigger>
        <Trigger Property="Properties:Properties.Editable" Value="False">
            <Setter Property="Background" Value="Red"/> //For visibility to see if its working
            <Setter Property="IsReadOnly" Value="True"/>
        </Trigger>
        <Trigger Property="Properties:Properties.Editable" Value="True">
            <Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"/>
            <Setter Property="IsReadOnly" Value="False"/>
        </Trigger>
</Style.Triggers>
</Style>

附加属性定义为:

static Properties()
    {
        EditableProperty = DependencyProperty.RegisterAttached("Editable", typeof(bool), typeof(EasyProperties), new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.Inherits));

    }

    public static readonly DependencyProperty EditableProperty; 
    public static void SetEditable(DependencyObject dependencyObject, bool value)
    {
        dependencyObject.SetValue(EditableProperty, value);
    }

    public static bool GetEditable(DependencyObject dependencyObject)
    {
        return (bool)dependencyObject.GetValue(EditableProperty);
    }

一个示例 XAML 看起来像这样,我想通过将容器中的可编辑属性设置为 false 来排除堆栈面板下的用户控件的一部分:

    <ScrollViewer Padding="5,5,5,5" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto">
    <Grid x:Name="_MainGrid" Background="{DynamicResource {x:Static SystemColors.ControlLightLightBrushKey}}" MinWidth="400">
        <Grid.RowDefinitions>
            <RowDefinition Height="5"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="Auto"/>
        </Grid.ColumnDefinitions>

        <TextBox x Width="40" Text="Test"  Grid.Row="1"/>
        <StackPanel Properties:Properties.Editable="False" Grid.Column="2" Grid.Row="1">
            <TextBox  Width="40" Text="Test" Grid.Column="2" Grid.Row="1"/>
            <TextBox  Width="40" Text="Test" Grid.Column="2" Grid.Row="1"/>
        </StackPanel>
    </Grid>
    </Stackpanel>

我期望会发生什么:

从 ViewModel 的 isEnabled 属性开始,设置为“true”-> 因此所有控件都应该是可编辑的。但是 Stackpanel 下的控件(仅在这种情况下为文本框)应该是可编辑的,并且带有红色背景,因为附加属性在它们所属的 Stackpanel 中设置为 false。

发生什么了:

触发器在堆栈面板下的控件中不起作用,它们是可编辑的。

我认为正在发生的事情:

Style 再次为每个 Textbox 声明属性,这与它们从 Stackpanel 继承的属性不同。因此,根据样式,应启用控件。

我可以调整样式中的触发器,使其首先查找继承的属性,然后查找我根据我的视图模型的 IsEnabled 属性声明的默认值吗?是否有另一种我没有看到的方法来做到这一点?

编辑:澄清标题(我希望:D)

标签: c#wpfxaml

解决方案


因此,经过更多的研究和测试(非常感谢@Sinatr 为我指明了正确的方向),我找到了答案:

在附加属性上使用 MultiDatatrigger 并结合使用枚举而不是布尔值作为附加属性。

枚举:

public enum EnabledEnum
    {
     NotSet, 
     False, 
     True
    }

附加属性类型更改为 typeof(EditableEnum) 并使用 NotSet 进行初始化:

static Properties()   
    {
        EditableProperty = DependencyProperty.RegisterAttached("Editable", typeof(EditableEnum), typeof(EasyProperties), new FrameworkPropertyMetadata(EditableEnum.NotSet, FrameworkPropertyMetadataOptions.Inherits));

    }

    public static readonly DependencyProperty EditableProperty; 
    public static void SetEditable(DependencyObject dependencyObject, EditableEnum value)
    {
        dependencyObject.SetValue(EditableProperty, value);
    }

    public static EditableEnum GetEditable(DependencyObject dependencyObject)
    {
        return (EditableEnum)dependencyObject.GetValue(EditableProperty);
    }

样式的新代码:

    <Style.Triggers>
        <MultiDataTrigger>
            <MultiDataTrigger.Conditions>
                <Condition Binding="{Binding Path=IsEnabled}" Value="False"/>
                <Condition Binding="{Binding Path=(Properties:Properties.Editable),RelativeSource={RelativeSource Self}}" Value="NotSet"/>
            </MultiDataTrigger.Conditions>
            <Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.ControlBrush}}"/>
            <Setter Property="IsReadOnly" Value="True"/>
        </MultiDataTrigger>
        <MultiDataTrigger>
            <MultiDataTrigger.Conditions>
                <Condition Binding="{Binding Path=IsEnabled}" Value="True"/>
                <Condition Binding="{Binding Path=(Properties:Properties.Editable),RelativeSource={RelativeSource Self}}" Value="NotSet"/>
            </MultiDataTrigger.Conditions>
            <Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"/>
            <Setter Property="IsReadOnly" Value="False"/>
        </MultiDataTrigger>

        <Trigger Property="EasyProperties:EasyProperties.Editable" Value="False">
              <Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.ControlBrush}}"/>
            <Setter Property="IsReadOnly" Value="True"/>
        </Trigger>
        <Trigger Property="Properties:Properties.Editable" Value="True">
            <Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"/>
            <Setter Property="IsReadOnly" Value="False"/>
        </Trigger>
    </Style.Triggers>

这可以按预期与我的问题中的示例代码一起使用。唯一仍然困扰我的是 Style 中的重复。我尝试在 Multidatatrigger 中设置“Properties:Properties.Editable”,但这导致了 Stackoverflow。


推荐阅读