Search code examples
c#wpfdata-bindingcomboboxoxyplot

Binding DisplayMemberPath of ComboBox to a string method


I need to get a full collection of OxyPlot colors and bind them to a ComboBox of DataGrid for future selection. I managed to do it, but the names displayed in ComboBox are "0x00ff", "0x0001", etc. Unfortunately, I can only get an actual color name ("Green", "Blue", etc.) using the GetColorName() method.

The DisplayMemberPath works perfect with public properties, but when I tried to use a method, my "0x00ff" were replaced with empty strings.

Is there a way to use a method in DisplayMemberPath instead of a public property or is there an another way?

My C# code:

using System.Collections.ObjectModel;
using System.Linq;
using System.Reflection;
using System.Windows;

using OxyPlot;

namespace WpfTestApp
{
    public partial class MainWindow : Window
    {
        public class Data
        {
            public OxyColor Color { set; get; } = OxyColors.Automatic;
        }

        public ObservableCollection<OxyColor> Colors { set; get; } = new ObservableCollection<OxyColor>(
            typeof(OxyColors).GetFields(BindingFlags.Static | BindingFlags.Public).Where(f => f.FieldType == typeof(OxyColor)).
            Select(f => f.GetValue(null)).Cast<OxyColor>().ToList());

        public ObservableCollection<Data> ColorData { set; get; } = new ObservableCollection<Data>();

        public MainWindow()
        {
            InitializeComponent();

            DataContext = this;
        }
    }
}

My XAML:

    <Window.Resources>
        <CollectionViewSource x:Key="ColorsItems" Source="{Binding Colors}" />
    </Window.Resources>

    <Grid>

        <DataGrid x:Name="dataGrid" ItemsSource="{Binding ColorData}" AutoGenerateColumns="False" CanUserSortColumns="False" Margin="44,29,507,230">

            <DataGrid.Columns>
                <DataGridComboBoxColumn Header="OxyColors" ItemsSource="{Binding Source={StaticResource ColorsItems}}"
                                        DisplayMemberPath="GetColorName"
                                        SelectedValueBinding="{Binding Color}"/>
            </DataGrid.Columns>

        </DataGrid>

    </Grid>

Solution

  • In WPF you cannot bind directly to methods.

    Use a IValueConverter:

    OxyColorToColorNameConverter.cs

    [ValueConversion(typeof(OxyColor), typeof(string))]
    class OxyColorToColorNameConverter : IValueConverter
    {
      public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 
        => value is IEnumerable<OxyColor> colors
          ? colors.Select(color => color.GetColorName()) 
          : value is OxyColor color
              ? color.GetColorName()
              : Binding.DoNothing;
    
      public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 
        => value is IEnumerable<string> colorNames
          ? colorNames.Select(colorName => OxyColor.Parse(colorName)) 
          : value is string colorName 
              ? OxyColor.Parse(colorName)
              : Binding.DoNothing;
    }
    

    MainWindow.xaml

    <Window>
      <Window.Reources>
        <OxyColorToColorNameConverter x:Key="OxyColorToColorNameConverter" />
      </Window.Resources>
    
      <DataGrid ItemsSource="{Binding ColorData}">
        <DataGrid.Columns>
          <DataGridComboBoxColumn Header="OxyColors">
            <DataGridComboBoxColumn.EditingElementStyle>
              <Style TargetType="ComboBox">
                <Setter Property="ItemsSource"
                        Value="{Binding RelativeSource={RelativeSource AncestorType=DataGrid}, 
                                Path=DataContext.Colors, 
                                Converter={StaticResource OxyColorToColorNameConverter}}" />
              </Style>
            </DataGridComboBoxColumn.EditingElementStyle>
            <DataGridComboBoxColumn.ElementStyle>
              <Style TargetType="ComboBox">
                <Setter Property="ItemsSource"
                        Value="{Binding RelativeSource={RelativeSource AncestorType=DataGrid}, 
                                Path=DataContext.Colors, 
                                Converter={StaticResource OxyColorToColorNameConverter}}" />
              </Style>
            </DataGridComboBoxColumn.ElementStyle>
          </DataGridComboBoxColumn>
        </DataGrid.Columns>
      </DataGrid>
    </Window>