Search code examples
c#wpfdependency-propertiesonrender

OnRender method doesn't display a Rectangle with the thickness set to a dependency property in WPF


I've defined two UserControls :

  • Drawing: contains a CustomCanvas that derives from Canvas.
  • Control: contains a Button and is used to change the GlobalThickness property in MyViewModel.cs

The CustomCanvas has a custom dependency property named Thickness. This is bound to GlobalThickness in XAML.

I have also overridden the OnRender method in CustomCanvas to draw a Rectangle using a Pen its thickness is set to Thickness.

When I click the Button, the GlobalThickness changes and the Thickness which is bound to it changed as well. But I don't get a Rectangle with a new Thickness.

Here is all the code I've put together so far.

<Window x:Class="WpfApplication23.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525"
        xmlns:local="clr-namespace:WpfApplication23">
    <Window.DataContext>
        <local:MyViewModel></local:MyViewModel>
    </Window.DataContext>
    <StackPanel>
        <local:Drawing/>
        <local:Control/>
    </StackPanel>
</Window>

<UserControl x:Class="WpfApplication23.Drawing"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:wpfApplication23="clr-namespace:WpfApplication23">
    <Grid>
        <wpfApplication23:CustomCanvas Thickness="{Binding GlobalThickness}"
                                       Height="100" 
                                       Width="100" 
                                       Background="Blue"/>
    </Grid>
</UserControl>

<UserControl x:Class="WpfApplication23.Control"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <StackPanel>
        <Button Content="Change Thickness" 
                Width="200" 
                Height="30"
                Click="ButtonBase_OnClick"/>
    </StackPanel>
</UserControl>


public partial class Control
{
    public Control()
    {
        InitializeComponent();
    }

    private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
    {
        var vm = (MyViewModel)DataContext;
        vm.GlobalThickness = 10;
    }
}


public class CustomCanvas : Canvas
{
    public int Thickness
    {
        private get { return (int)GetValue(ThicknessProperty); }
        set
        {
            SetValue(ThicknessProperty, value); 
            InvalidateVisual();
        }
    }

    public static readonly DependencyProperty ThicknessProperty =
        DependencyProperty.Register("Thickness", typeof(int), typeof(CustomCanvas), new PropertyMetadata(0));

    protected override void OnRender(DrawingContext dc)
    {
        var myPen = new Pen(Brushes.Red, Thickness);
        var myRect = new Rect(0, 0, 400, 400);
        dc.DrawRectangle(null, myPen, myRect);
    }
}


public class MyViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private double _globalThickness = 1;
    public double GlobalThickness
    {
        get { return _globalThickness; }
        set
        {
            _globalThickness = value;
            RaisePropertyChanged("GlobalThickness");
        }
    }

    private void RaisePropertyChanged(string propertyName)
    {
        var handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

Solution

  • This alternative might be more efficient. Instead of frequently calling OnRender and re-rendering everything each time the Pen Thickness changed, it just changes the Pen's Thickness of an existing rendering which is made only once. The visual output will be updated automatically by WPF.

    public class CustomCanvas : Canvas
    {
        private readonly Pen pen = new Pen(Brushes.Red, 0d);
    
        public static readonly DependencyProperty ThicknessProperty =
            DependencyProperty.Register(
                "Thickness", typeof(double), typeof(CustomCanvas),
                new PropertyMetadata(ThicknessPropertyChanged));
    
        public double Thickness
        {
            get { return (double)GetValue(ThicknessProperty); }
            set { SetValue(ThicknessProperty, value); }
        }
    
        protected override void OnRender(DrawingContext drawingContext)
        {
            var myRect = ...
            drawingContext.DrawRectangle(null, pen, myRect);
        }
    
        private static void ThicknessPropertyChanged(
            DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            ((CustomCanvas)obj).pen.Thickness = (double)e.NewValue;
        }
    }