Search code examples
wpfbindingcomboboxelement-binding

Access ComboBoxItem DisplayValue


I have a comboBox that has an ItemsSource of a list of objects. So the DisplayMemberPath is set to a particular property of the object. Of course this means that the correct value is displayed in the ComboBoxItem.

My issue is that I would like to be able to get that "Value" that is returned by the DisplayMemberPath in XAML so that I can bind it to something else. i.e. I would like to have a "DisplayText" property on the ComboBoxItem.

Of course I don't have this, so, does anyone know of a way to get this value without traversing down into the template of the ComboBoxItem looking for the ContentHost?

If you're interested in my specific use of this, I'm trying to do this on the style of the ComboBox:

....
<Setter Property="ItemContainerStyle">
   <Setter.Value>
      <Style>
        <Setter 
             Property="AutomationProperties.AutomationId" 
             Value="{Binding RelativeSource={RelativeSource Self}, Path=MagicPathForDisplayedText}"/>
....

Of course Path=Content works just fine if you're just binding your ItemsSource to properties, but when it's an Object with a DisplayMemberPath, Content will be that Object.

Thanks for any help or re-framing of the problem.


Solution

  • The easiest way to handle problems like this is usually Attached Properties and Behaviors.

    You could create two attached properties called DisplayMemberPath and DisplayText, then you bind DisplayMemberPath to the parent ComboBox DisplayMemberPath and in the PropertyChangedCallback you set up a binding of your own with the same path for DisplayText. After that you have a property which you can bind to

    <Style x:Key="ComboBoxStyle" TargetType="ComboBox">
        <Setter Property="ItemContainerStyle">
            <Setter.Value>
                <Style TargetType="ComboBoxItem">
                    <Setter Property="behaviors:DisplayTextBehavior.DisplayMemberPath"
                            Value="{Binding RelativeSource={RelativeSource AncestorType={x:Type ComboBox}},
                                            Path=DisplayMemberPath}"/>
                    <Setter Property="AutomationProperties.AutomationId"
                            Value="{Binding RelativeSource={RelativeSource Self},
                                            Path=(behaviors:DisplayTextBehavior.DisplayText)}"/>
                </Style>
            </Setter.Value>                    
        </Setter>
    </Style>
    

    DisplayTextBehavior

    public class DisplayTextBehavior
    {
        public static DependencyProperty DisplayMemberPathProperty =
            DependencyProperty.RegisterAttached("DisplayMemberPath",
                                                typeof(string),
                                                typeof(DisplayTextBehavior),
                                                new FrameworkPropertyMetadata("", DisplayMemberPathChanged));
        public static string GetDisplayMemberPath(DependencyObject obj)
        {
            return (string)obj.GetValue(DisplayMemberPathProperty);
        }
        public static void SetDisplayMemberPath(DependencyObject obj, string value)
        {
            obj.SetValue(DisplayMemberPathProperty, value);
        }
    
        private static void DisplayMemberPathChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            ComboBoxItem comboBoxItem = sender as ComboBoxItem;
            string displayMemberPath = GetDisplayMemberPath(comboBoxItem);
            comboBoxItem.SetBinding(DisplayTextProperty, new Binding(displayMemberPath));
        }
    
        public static DependencyProperty DisplayTextProperty =
            DependencyProperty.RegisterAttached("DisplayText",
                                                typeof(string),
                                                typeof(DisplayTextBehavior),
                                                new FrameworkPropertyMetadata(""));
        public static string GetDisplayText(DependencyObject obj)
        {
            return (string)obj.GetValue(DisplayTextProperty);
        }
        public static void SetDisplayText(DependencyObject obj, string value)
        {
            obj.SetValue(DisplayTextProperty, value);
        }
    }