Search code examples
c#xamlstructuwpproperty-binding

UWP: How to programmatically set value of bound struct property without loosing binding?


I have a ContentDialog with a few controls, one of them a color picker, where the user can pick a color.

When the dialog loads I want to initialize the color picker with a given color (from a previous selection), there is also a rectangle control which shows the selected color.

The color picker is bound to a property in the view model (TwoWay) and the rectangle background color is also bound (OneWay) to the same property.

                <Viewbox Margin="5" MaxWidth="100" Stretch="Fill">
                    <ColorPicker x:Name="fontColorPicker"
                        ColorSpectrumShape="Ring"
                        IsColorPreviewVisible="False"
                        IsColorChannelTextInputVisible="False"
                        IsHexInputVisible="False" Color="{x:Bind ViewModel.SelectedColor, Mode=TwoWay}" ColorChanged="fontColorPicker_ColorChanged" />
                </Viewbox>
                <Rectangle Height="50" Width="50" Margin="10,0,0,0" StrokeThickness="1" Stroke="DarkGray">
                    <Rectangle.Fill>
                        <SolidColorBrush Color="{x:Bind ViewModel.SelectedColor, Mode=OneWay}"/>
                    </Rectangle.Fill>
                </Rectangle>

All is fine until I want to set the initial color for the color picker. As Color is a struct, I cannot change the A, R, G, B properties, I have to instanciate a new struct and assign it to the property. But with this assignment the binding will get removed.

This is how I currently change the value in the view model, to initialize the value (settings.Color is not a Windows.UI.Color, but a class from another imaging library, so I have to convert the values):

            // Font color
            if (settings.Color != null)
            {
                var pickerColor = new Windows.UI.Color
                {
                    A = settings.Color.A,
                    R = settings.Color.R,
                    G = settings.Color.G,
                    B = settings.Color.B
                };
                ViewModel.SelectedColor = pickerColor;
            }

I know, I could re-create the binding after this so make it work again, but I am sure there must be a more clean approach without the need for this to happen.


Solution

  • You can make the ViewModel class implement the INotifyPropertyChanged interface:

    public class ViewModel : INotifyPropertyChanged
    {
        private Color _selectedColor;
    
        public Color SelectedColor
        {
            get => _selectedColor;
            set
            {
                _selectedColor = value;
                OnPropertyChanged();
            }
        }
    
        public event PropertyChangedEventHandler PropertyChanged;
        public void OnPropertyChanged([CallerMemberName] string propertyName = "")
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
    

    In this way, when you bind SelectedColor through OneWay, changes to SelectedColor will notify the UI to also change.