Search code examples
wpfdata-bindingcolorsinotifypropertychangeddatacontext

WPF Bind color to ellipse and update it automatically


I have this Ellipse with a RadialGradientBrush, where the colors are bind to the colors Light and Dark:

<Grid x:Name="gridEllipse">
  <Ellipse x:Name="ellipseMPCenter">
    <Ellipse.Fill>
      <RadialGradientBrush GradientOrigin="50,50" Center="50,50" Radius="1">
        <RadialGradientBrush.GradientStops>
          <GradientStop Color="{Binding Light}" Offset="0"/>
          <GradientStop Color="{Binding Dark}" Offset="1"/>
        </RadialGradientBrush.GradientStops>
      </RadialGradientBrush>
    </Ellipse.Fill>
  </Ellipse>
</Grid>

This is my code:

    public class UserControlLED : UserControl, INotifyPropertyChanged
    {
        private readonly Color colorGreenLight = Color.FromRgb(61, 214, 0);
        private readonly Color colorGreenDark = Color.FromRgb(10, 92, 1);
        private readonly Color colorRedLight = Color.FromRgb(235, 0, 0);
        private readonly Color colorRedDark = Color.FromRgb(130, 0, 0);

        private Color light;

        public Color Light
        {
            get
            {
                return light;
            }

            set
            {
                light = value;
                OnPropertyChanged(nameof(Light));
            }
        }

        private Color dark;

        public Color Dark
        {
            get
            {
                return dark;
            }

            set
            {
                dark = value;
                OnPropertyChanged(nameof(Dark));
            }
        }


        public UserControlLED()
        {
            InitializeComponent();

            Light = colorGreenLight;
            Dark = colorGreenDark;

            gridEllipse.DataContext = this;
        }

        private async void BtStartClicked(object sender, RoutedEventArgs args)
        {
            Light = colorRedLight;
            Dark = colorRedDark;
        }

        public event EventHandler<PropertyChangedEventArgs> PropertyChanged;

        protected void OnPropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }

With this code, the Ellipse is colored green, even if I press the button. When I set the DataContext in the button click event again, nothing happens. Or do I need to put this in the OnPropertyChanged method? But this still does not work.

Where and when do I need to set the DataContext?


Solution

  • Fix the Center property of the RadialGradientBrush and use RelativeSource Bindings:

    <Ellipse.Fill>
        <RadialGradientBrush Center="0.5,0.5">
            <RadialGradientBrush.GradientStops>
                <GradientStop
                    Color="{Binding Light,
                        RelativeSource={RelativeSource AncestorType=UserControl}}"
                    Offset="0"/>
                <GradientStop
                    Color="{Binding Dark,
                        RelativeSource={RelativeSource AncestorType=UserControl}}"
                    Offset="1"/>
            </RadialGradientBrush.GradientStops>
        </RadialGradientBrush>
    </Ellipse.Fill>
    

    Do not implement INotifyPropertyChanged, but declare the properties as dependency properties:

    public static readonly DependencyProperty LightProperty =
        DependencyProperty.Register(
            nameof(Light), typeof(Color), typeof(UserControlLED),
            new PropertyMetadata(Colors.White));
    
    public static readonly DependencyProperty DarkProperty =
        DependencyProperty.Register(
            nameof(Dark), typeof(Color), typeof(UserControlLED),
            new PropertyMetadata(Colors.Black));
    
    public Color Light
    {
        get { return (Color)GetValue(LightProperty); }
        set { SetValue(LightProperty, value); }
    }
    
    public Color Dark
    {
        get { return (Color)GetValue(DarkProperty); }
        set { SetValue(DarkProperty, value); }
    }
    

    Do not explicitly set the DataContext, because that would break the standard binding mechanism when you bind the control's properties like

    <local:UserControlLED Light="{Binding SomeColor}" .../>