0

I have many different parts to a panel I am using in my application. When a button is pressed I want to put certain parts of a panel/Canvas/Usercontrol etc to go an Edit Mode while I can excluse others based on an attached property i created. In my ViewModel I am using a boolean Property "IsEnabled" on which i bind a global style (Edit: Defined in a Resource Dictionary for Textboxes in this Example) as you can see below:

<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>

The attached Property is defined as:

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);
    }

an Example XAML would look like this where I want exclude a part of the userControl under a Stackpanel by setting the Editable Propertis in the Container to 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>

What do I expect to happen:

To start withisEnabled Property of the ViewModel ist set to 'true' -> as such all Controls should be editable. But the Controls (Textboxed in this case only) under the Stackpanel should not be editable and with Red Background as the attached property was set to false in the Stackpanel they are a part of.

What happens:

The trigger does not work in the Controls under the Stackpanel and they are editable.

What I think is happening:

The Style is declaring the property for each Textbox again, which is distinct from the property they inherit from the Stackpanel. As such according to the Style the Controls should be enabled.

Can I adjust the trigger in the style in such a way that it would first look for inherited propertys and then look for the default I declare based on the IsEnabled property of my viewmodel? Is there maybe another way to do this which I am not seeing?

Edit: Clarified Title (I hope :D)

Novolizer
  • 1
  • 2
  • What is "global style"? To what do you apply it and how? I think *attached behavior* will be a better and more clear solution than inheritable attached property + style. Though I am not sure why do you need it: you have edit controls defined in xaml, just bind to `ReadOnly` each `TextBox`. Even better approach would be to generate view dynamically (`PropertyGrid` way) from model containing settings. – Sinatr Aug 06 '21 at 10:11
  • The Style is defined for all Textboxed by targeting them with – Novolizer Aug 06 '21 at 10:18
  • *"a lot of uneccesary xaml code"* - but with a lot more flexibility and less problems to solve like yours. PropertyGrid is a control in winforms ([here](https://docs.devexpress.com/WPF/15640/controls-and-libraries/property-grid) is a DevExpress implementation for WPF) to which you can pass instance of *any* class and edit its properties, using attributes to control the process. – Sinatr Aug 06 '21 at 10:22
  • In this case you probably just need to use [MultiDataTrigger](https://stackoverflow.com/q/905932/1997232). – Sinatr Aug 06 '21 at 10:27
  • "but with a lot more flexibility" - I am not losing the flexibility? Anything defined in the xaml of the Control itself overrides the Style to my knowledge (and tests). This lets me use the Style as I want everywhere - but if I don't want to I can simply state that in the xaml of an control specifically. I will take a look at the PropertyGrid and MultiDataTrigger - Thanks! – Novolizer Aug 06 '21 at 10:39

1 Answers1

0

So after some more research and testing (huge thanks to @Sinatr for pointing me in the right direction) I found an answer:

Using MultiDatatrigger on the attached property in combination with using an Enum instead of a boolean as the attached property.

the enum:

public enum EnabledEnum
    {
     NotSet, 
     False, 
     True
    }

The attached propertys type was changed to typeof(EditableEnum) and initialized with 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);
    }

The new code for the style:

    <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>

This works with the example code in my question as intended. The only thing still bugging me would be the repetition inside the Style. I tried setting the "Properties:Properties.Editable" inside the Multidatatrigger but that led to an Stackoverflow.

Novolizer
  • 1
  • 2