Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to set a WPF usercontrol property from XAML?

I'm trying to set the fill property of several instances of the same usercontrol from XAML in order to distinguish them. I'm using a dependency property in the C# codebehind of the control and referring to that in the XAML when I instantiate the control. Here's a simplified example of what I've tried, first the XAML of the user control:

<UserControl x:Class="RectangleFillUserControlTest.RectangleFillTest"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" 
             d:DesignHeight="50" d:DesignWidth="150">
    <Grid>
        <Rectangle x:Name="rect" HorizontalAlignment="Left" Height="50" Stroke="Black" VerticalAlignment="Top" Width="150"/>
    </Grid>
</UserControl>

Now the codebehind:

namespace RectangleFillUserControlTest
{
    public partial class RectangleFillTest : UserControl
    {
        SolidColorBrush fillBrush;

        public static readonly DependencyProperty FillColourProperty = DependencyProperty.Register
            ("FillColour", typeof(string), typeof(RectangleFillTest), new PropertyMetadata(string.Empty));

        public string FillColour
        {
            get { return (string)GetValue(FillColourProperty); }

            set
            {
                SetValue(FillColourProperty, value);
                if (value == "red") fillBrush = new SolidColorBrush(Colors.Red);
                else fillBrush = new SolidColorBrush(Colors.Green);
                rect.Fill = fillBrush;
            }
        }

        public RectangleFillTest()
        {
            InitializeComponent();
        }
    }
}

I instantiate the control in the main window and try to set the fill colour to red:

<Window x:Class="RectangleFillUserControlTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:RectangleFillUserControlTest"
        Title="MainWindow" Height="350" Width="525">
    <Grid Background="#FF1D2CC3">
        <local:RectangleFillTest FillColour="red"/>
    </Grid>
</Window>

But the rectangle remains unfilled, even when I run the project. Can anyone help please?

Cheers,

Tim

like image 640
Tim Butler Avatar asked Dec 05 '22 21:12

Tim Butler


1 Answers

There are two things wrong with your dependency property.

First, its type should be Brush, not string, because that is the type used by properties of WPF controls like Shape.Fill or Control.Background. WPF provides automatic type conversion from strings like "Red" or "#FFFF0000" in XAML to type Brush.

Second, you should not have anything else than a call to SetValue in the setter method of the CLR wrapper. The reason is explained in the XAML Loading and Dependency Properties article on MSDN:

Because the current WPF implementation of the XAML processor behavior for property setting bypasses the wrappers entirely, you should not put any additional logic into the set definitions of the wrapper for your custom dependency property. If you put such logic in the set definition, then the logic will not be executed when the property is set in XAML rather than in code.

So your dependency property declaration should look like this:

public static readonly DependencyProperty FillBrushProperty =
    DependencyProperty.Register(
        "FillBrush", typeof(Brush), typeof(RectangleFillTest));

public Brush FillBrush
{
    get { return (Brush)GetValue(FillBrushProperty); }
    set { SetValue(FillBrushProperty, value); }
}

To react to property changes, you would now register a PropertyChangedCallback with property metadata. But you don't need to do that here, because you could simply bind the property in the UserControl's XAML like this:

<Rectangle Fill="{Binding FillBrush,
    RelativeSource={RelativeSource FindAncestor, AncestorType=UserControl}}" ... />
like image 196
Clemens Avatar answered Dec 11 '22 07:12

Clemens