views:

507

answers:

2

Hi,

so, I have templated combobox that is basically acting as a simple colour palette. It is populated with a list of SolidColorBrush objects. Fine.

Now, I have some data that holds the hex value of the current colour. I have a converter that converts the hex into a SolidColorBrush. Also fine.

Now, I want to set the SelectedItem property of the combobox based on the colour from my datasource. Since my combo is populated with objects of type SolidColourBrush, and my binding converter is returning a SolidColorBrush, I assumed it would be as simple as saying:

SelectedItem="{Binding Color, Converter={StaticResource StringToBrush}}"

However... it doesn't work :(

I've tested that the binding is working behind the scenes by using the exact same value for the Background property of the combobox. It works fine.

So, clearly I can't just say SelectedItem = [something] where that [something] is basically an object equal to the item I want to be selected.

What is the right way to do this? Surely it's possible in a XAML-only styley using binding, and I don't have to do some nasty C# iterating through all items in the combobox trying to find a match (that seems awfully old-school)...?

Any help appreciated. Many thanks!

AT

A: 

Hi!

I you are using the MVVM pattern, then add a property SelectedHexColor to your view model and let the view model search the correct solid color brush in your list from which the color brush combobox is being populated and update the SelectedBrush property of your view model to which the selected item of the combobox is bound.

[edit] I just read that you want to avoid C# code because you think it is old-school. But this is a strength of the MVVM pattern because you can unit test exactly this code which already is complex business logic. Putting this in the XAML is quite unsafe because you cannot unit test it and therefore you cannot garantuee correct functionality (except with automated ui tests which are way more complex to implement than unit tests).

Best Regards,
Oliver Hanappi

Oliver Hanappi
Hmmm... OK. But I think I'm looking for a more generalised answer. If I have a combobox with a list of items of type Foo, and I also have another instance of Foo held somewhere else (let's say, in myFoo), surely there's a XAML-only way to say, "The selected item of the combobox is myFoo", or more accurately, "The selected item of the combobox is the Foo that matches myFoo"... right?
Andy T
In that case you will need to implement value equality logic. For that purpose you can wrap the `SolidColorBrush` in an object which implements value equality (overriding GetHashCode and Equals).
Oliver Hanappi
+1  A: 

You can databind SelectedItem on a combobox. If I understand your description you have a collection of hex strings and a SolidColorBrush property called Color. The below code updates Color property when selected item is changed.

XAML:

<Window x:Class="SelItemTest.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:SelItemTest="clr-namespace:SelItemTest"
    Title="Window1" Height="300" Width="300">

    <Window.Resources>        
        <SelItemTest:StringToBrushConverter x:Key="StringToBrush" />        
    </Window.Resources>

    <StackPanel Background="{Binding Path=Color}">
        <ComboBox
            ItemsSource="{Binding Path=Colors}"
            SelectedItem="{Binding Path=Color, Converter={StaticResource StringToBrush}}">
            <ComboBox.ItemTemplate>
                <DataTemplate>
                    <Border Background="{Binding}" Height="20" Width="100" />
                </DataTemplate>
            </ComboBox.ItemTemplate>
        </ComboBox>
    </StackPanel>

</Window>

Code behind:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows;
using System.Windows.Data;
using System.Windows.Media;

namespace SelItemTest
{
    public partial class Window1 : Window, INotifyPropertyChanged
    {
        public Window1()
        {
            InitializeComponent();

            Colors = new List<string>();
            Colors.Add(Brushes.Red.ToString());
            Colors.Add(Brushes.Blue.ToString());
            Colors.Add(Brushes.Yellow.ToString());

            Color = Brushes.Yellow;

            DataContext = this;
        }

        public List<string> Colors { get; set;}

        private SolidColorBrush _color;
        public SolidColorBrush Color
        {
            get { return _color; }
            set
            {
                _color = value;
                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs("Color"));
                }
            }
        }

        #region INotifyPropertyChanged Members
        public event PropertyChangedEventHandler PropertyChanged;
        #endregion
    }

    public class StringToBrushConverter : IValueConverter
    {
        #region IValueConverter Members
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            return value.ToString();
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            Color color = (Color)ColorConverter.ConvertFromString(value.ToString());
            SolidColorBrush scb = new SolidColorBrush(color);
            return scb;
        }
        #endregion
    }
}
Wallstreet Programmer