gartenriese
gartenriese

Reputation: 4356

Using a dependency property with a view model to change the Fill of a Rectangle

I have View with a Rectangle and depending on a boolean variable I want to set the Fill to a specific brush. For this I have added a DependencyProperty to the ViewModel.

public static readonly DependencyProperty FalseColorProperty = DependencyProperty.Register(
            "FalseColor",
            typeof(Brush),
            typeof(BooleanRectangleView),
            new FrameworkPropertyMetadata(Application.Current.FindResource("LightGreyBrush"), FrameworkPropertyMetadataOptions.AffectsRender));

public Brush FalseColor
{
    get { return (Brush)GetValue(FalseColorProperty); }
    set { SetValue(FalseColorProperty, value); }
}

In the View, I added a style to the rectangle with the following Trigger

<DataTrigger Binding="{Binding DataContext.Model.Variable, RelativeSource={RelativeSource AncestorType=UserControl}}" Value="false">
                        <Setter Property="Fill" Value="{Binding DataContext.FalseColor, RelativeSource={RelativeSource AncestorType=UserControl}}" />
</DataTrigger>

Since I have the DependencyProperty in the ViewModel, I don't know how to set it when creating the UserControl. For example, the following does not work

<BooleanRectangleView FalseColor="..."/>

Also, when I am running the program, I get an exception in the constructor of the ViewModel:

The default type does not match the type of the property FalseColor (translated from German)

EDIT:

When I cast the FindResource to Brush I get a new exception:

The default value for the property FalseColor cannot be bound to a specific thread

I guess that has something to do with FindResource not necessarily being called from the dispatcher thread?

Upvotes: 1

Views: 1045

Answers (1)

nkoniishvt
nkoniishvt

Reputation: 2521

I don't understand your structure here: You tell that FalseColorProperty is in the ViewModel layer but the class is called BooleanRectangleView which should probably be a name for a View class (UserControl or something like that).

In your case you'll have this DP in your View in addition to a bool named IsVariableTrue for example.

In your ViewModel you'll have the Variable property which makes the Variable property from your Model accessible.

In the BooleanRectangleView XAMl you'll have a Style:

<Rectangle>
    <Rectangle.Style>
        <Style TargetType="{x:Type Rectangle}">
            <Setter Property="Fill" Value="{Binding RelativeSource={RelativeSource Self}, Path=Fill}"/> <!-- Binding to the Rectangle Fill property -->
            <Style.Triggers>
                <DataTrigger Property="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type BooleanRectangleView}}, Path=IsVariableTrue}" Value="False">
                    <Setter Property="Fill" Value="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type BooleanRectangleView}}, Path=FalseColor}"/> <!-- Binding to the DP in the View -->
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </Rectangle.Style>
    ...
</Rectangle>

In the XAML that uses your BooleanRectangleView:

<BooleanRectangleView IsVariableTrue="{Binding Variable}"/> <!-- Binding to the ViewModel -->

On Your DP declaration:

public static readonly DependencyProperty FalseColorProperty = DependencyProperty.Register(
    "FalseColor",
    typeof(Brush),
    typeof(BooleanRectangleView),
    new FrameworkPropertyMetadata(
        Brush.Red, 
        FrameworkPropertyMetadataOptions.AffectsRender
    )
);

public Brush FalseColor
{
    get { return (Brush)GetValue(FalseColorProperty); }
    set { SetValue(FalseColorProperty, value); }
}

You'll want to set the DP value in the constructor of your BooleanRectangleView:

public BooleanRectangleView() {

    InitializeComponents();
    FalseColor = (Brush)Application.Current.FindResource("Infoteam_LightGreyBrush");
}

I'm sure there's a better solution but it should work fine.

Edited

Replaced the TemplateBinding for a Binding with RelativeSource as we're not in a ControlTemplate. Replaced the Trigger with a DataTrigger because the IsVariableTrue doesn't exist in a Rectangle.

Upvotes: 1

Related Questions