Joe
Joe

Reputation: 1326

Retrieve the string value of a WPF ComboBox’s selected item in the code behind

I’m having trouble retrieving the string value of a WPF ComboBox’s selected item in the code behind.

I have set up three different types of array which provide the ItemSources for three ComboBoxes. I’ve bound each ComboBox selected item to a property of a class called DataBase. I verify the binding works by echoing the value of each DataBase property back to a TextBox.

In the code behind I want to retrieve the string value of each ComboBox’s selected item. I can do this for the systemComboBox where SelectedItem and SelectedValue both return the string value of the selected item.

I can’t get it to work for the oneDComboBox where SelectedItem and SelectedValue return “ComboBoxes.OneD” or for the twoDComboBox which correctly returns the SelectedValue but returns “ComboBoxes.TwoD” as the value for the SelectedItem

Can anyone tell me how to get the string value of the oneDComboBox and twoDComboBox selected items?

In Visual Studio I’ve set the Output Type of the code below Assembly to “Console Application” so I can write to the console.

WPF

 <Window x:Class="ComboBoxes.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:ComboBoxes"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:sys="clr-namespace:System;assembly=mscorlib" Title="MainWindow" Width="1200" Height="450"
        mc:Ignorable="d">

    <Window.Resources>
        <x:Array x:Key="OneDArray" Type="{x:Type local:OneD}">
            <local:OneD OneDName="OneD-0" />
            <local:OneD OneDName="OneD-1" />
            <local:OneD OneDName="OneD-2" />
            <local:OneD OneDName="OneD-3" />
            <local:OneD OneDName="OneD-4" />
        </x:Array>

        <x:Array x:Key="TwoDArray" Type="{x:Type local:TwoD}">
            <local:TwoD TwoDName="TwoD-0" TwoDNumber="0" />
            <local:TwoD TwoDName="TwoD-1" TwoDNumber="1" />
            <local:TwoD TwoDName="TwoD-2" TwoDNumber="2" />
            <local:TwoD TwoDName="TwoD-3" TwoDNumber="3" />
            <local:TwoD TwoDName="TwoD-4" TwoDNumber="4" />
        </x:Array>

        <x:Array x:Key="SystemStringArray" Type="sys:String">
            <sys:String>SystemString-0</sys:String>
            <sys:String>SystemString-1</sys:String>
            <sys:String>SystemString-2</sys:String>
            <sys:String>SystemString-3</sys:String>
            <sys:String>SystemString-4</sys:String>
        </x:Array>

    </Window.Resources>
    <Grid>
        <Grid.Resources>
            <local:DataBase x:Key="dataBase" />
        </Grid.Resources>
        <Grid.DataContext>
            <Binding Source="{StaticResource dataBase}" />
        </Grid.DataContext>

        <Grid.RowDefinitions>
            <RowDefinition Height="10" />
            <RowDefinition Height="auto" />
            <RowDefinition Height="20" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="10" />
        </Grid.RowDefinitions>

        <Grid Grid.Row="1" ShowGridLines="True">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="20" />
                <ColumnDefinition Width="120" />
                <ColumnDefinition Width="120" />
                <ColumnDefinition Width="40" />
                <ColumnDefinition Width="120" />
                <ColumnDefinition Width="120" />
                <ColumnDefinition Width="40" />
                <ColumnDefinition Width="120" />
                <ColumnDefinition Width="120" />
                <ColumnDefinition Width="*" />
                <ColumnDefinition Width="20" />
            </Grid.ColumnDefinitions>
            <Label Grid.Row="1" Grid.Column="1" Content="OneDComboBox:" HorizontalAlignment="Right"/>
            <ComboBox x:Name="oneDComboBox" Grid.Row="1" Grid.Column="2" Width="120" DisplayMemberPath="OneDName"
                      ItemsSource="{StaticResource OneDArray}"
                      SelectedItem="{Binding Path=DataBaseOneDName, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}"
                      SelectionChanged="ComboBox_SelectionChanged"
                      />
            <Label Grid.Row="1" Grid.Column="4" Content="SystemComboBox:" HorizontalAlignment="Right"/>
            <ComboBox x:Name="systemComboBox" Grid.Row="1" Grid.Column="5" Width="120"
                      ItemsSource="{StaticResource SystemStringArray}"
                      SelectedItem="{Binding Path=DataBaseSystemString, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}"
                      SelectionChanged="ComboBox_SelectionChanged"                      
                      />
            <Label Grid.Row="1" Grid.Column="7" Content="TwoDComboBox:" HorizontalAlignment="Right"/>
            <ComboBox x:Name="twoDComboBox" Grid.Row="1" Grid.Column="8" Width="120" DisplayMemberPath="TwoDName"
                      ItemsSource="{Binding Source={StaticResource TwoDArray}}"
                      SelectedValue="{Binding Path=DataBaseTwoDNumber, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}"
                      SelectedValuePath="TwoDNumber" SelectionChanged="ComboBox_SelectionChanged"
                      />
        </Grid>
        <Grid Grid.Row="3" ShowGridLines="True">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="20" />
                <ColumnDefinition Width="120" />
                <ColumnDefinition Width="120" />
                <ColumnDefinition Width="40" />
                <ColumnDefinition Width="120" />
                <ColumnDefinition Width="120" />
                <ColumnDefinition Width="40" />
                <ColumnDefinition Width="120" />
                <ColumnDefinition Width="120" />
                <ColumnDefinition Width="*" />
                <ColumnDefinition Width="20" />
            </Grid.ColumnDefinitions>
            <Label Grid.Row="2" Grid.Column="1" Content="BoundOneDName:" HorizontalAlignment="Right"/>
            <TextBox x:Name="oneDComboBoxEcho" Grid.Row="3" Grid.Column="2" Width="120"
                     Text="{Binding Path=DataBaseOneDName, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}"
                     TextWrapping="Wrap" />
            <Label Grid.Row="2" Grid.Column="4" Content="BoundSystemString:" HorizontalAlignment="Right"/>
            <TextBox x:Name="systemComboBoxEcho" Grid.Row="5" Grid.Column="5" Width="120"
                     Text="{Binding Path=DataBaseSystemString, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}"
                     TextWrapping="Wrap" />
            <Label Grid.Row="2" Grid.Column="7" Content="BoundTwoDNumber:" HorizontalAlignment="Right"/>
            <TextBox x:Name="twoDComboBoxEcho" Grid.Row="5" Grid.Column="8" Width="120"
                     Text="{Binding Path=DataBaseTwoDNumber, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}"
                     TextWrapping="Wrap" />

        </Grid>
    </Grid>
</Window>   

C#

using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;


namespace ComboBoxes
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// 
    /// The code below reflects the suggestions made by:
    /// 1  Ben Broadley (to use e.AddedItems[0]) and Benny (To look in the ItemsSource object) in the responses to https://stackoverflow.com/questions/4351603/get-selected-value-from-combo-box-in-c-sharp-wpf.
    /// 2  Adam Nathan in the FAQ box on page 266 of his book "WPF 4.5 Unleashed".
    /// 
    /// 
    /// 
    /// </summary>
    public partial class MainWindow : Window
    {
        //public MainWindow mainWindow;
        DataBase dataBase;
        public MainWindow()
        {
            InitializeComponent();
            dataBase = new DataBase();
            //DataContext = this;
        }
        private void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (e.AddedItems.Count > 0)                                                 //  Test to ensure that an item has been selected.
             {
                    ComboBox sourceComboBox = (ComboBox)sender;
                    int selectedIndex = -1;
                    string selectedItem = "???";
                    string selectedValue = "???";
                    string displayMemberPath = "???";
                    if (sourceComboBox.Name == "oneDComboBox")
                    {
                    //  Because this ComboBox.ItemsSource is bound to an array of types the selected value has to be unbundled from the selected type.  
                    if (oneDComboBox.SelectedValue is OneD)                                 //  Verify ComboBox.ItemsSource is bound to what we think its bound to.
                        {
                            selectedIndex = sourceComboBox.SelectedIndex;
                            selectedItem = sourceComboBox.SelectedItem.ToString();
                            selectedValue = (oneDComboBox.SelectedValue as OneD).OneDName;
                            displayMemberPath = sourceComboBox.DisplayMemberPath.ToString();
                        }
                    }
                    else
                    if (sourceComboBox.Name == "systemComboBox")
                    {
                        selectedIndex = sourceComboBox.SelectedIndex;
                        selectedItem = sourceComboBox.SelectedItem.ToString();
                        selectedValue = sourceComboBox.SelectedValue.ToString();
                        displayMemberPath = sourceComboBox.DisplayMemberPath.ToString();
                    }
                    else
                    if (sourceComboBox.Name == "twoDComboBox")
                    {
                    //  Because this ComboBox.ItemsSource is bound to an array of types the selected value has to be unbundled from the selected type.  
                    if (e.AddedItems[0] is TwoD)                                 //  Verify ComboBox.ItemsSource is bound to what we think its bound to.
                        {
                            selectedIndex = sourceComboBox.SelectedIndex;
                            selectedItem = sourceComboBox.SelectedItem.ToString();
                            selectedValue = (e.AddedItems[0] as TwoD).TwoDName;
                            //selectedValue = (twoDComboBox.SelectedValue as TwoD).TwoDName;  //  Why doesn't this work?  It works for oneDComboBox.
                        displayMemberPath = sourceComboBox.DisplayMemberPath.ToString();
                        }
                    }
                Console.WriteLine($"\nComboBox Name = {sourceComboBox.Name}");
                Console.WriteLine($"Selected Index = {selectedIndex}  Selected Item = {selectedItem}  Selected Value = {selectedValue}  DisplayMemberPath = {displayMemberPath}");
             }
        }
    }
    public partial class DataBase : INotifyPropertyChanged
    {
        private string _dataBaseOneDName = "OneDArray";
        public string DataBaseOneDName
        {
            get { return _dataBaseOneDName; }
            set
            {
                if (_dataBaseOneDName != value)
                {
                    _dataBaseOneDName = value;
                    NotifyPropertyChanged("DataBaseOneDName");
                }
            }
        }


        private string _dataBaseSystemString = "System_String";

        public string DataBaseSystemString                         // String property used in binding examples.
        {
            get { return _dataBaseSystemString; }
            set
            {
                if (_dataBaseSystemString != value)
                {
                    _dataBaseSystemString = value;
                    NotifyPropertyChanged("DataBaseSystemString");
                }
            }
        }

        private int _dataBaseTwoDNumber = 99;


        public int DataBaseTwoDNumber
        {
            get { return _dataBaseTwoDNumber; }
            set
            {
                if (_dataBaseTwoDNumber != value)
                {
                    _dataBaseTwoDNumber = value;
                    NotifyPropertyChanged("DataBaseTwoDNumber");
                }
            }
        }                           // Int property used in binding examples.


        #region INotifyPropertyChanged Members

        /// Need to implement this interface in order to get data binding
        /// to work properly.
        private void NotifyPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        #endregion
    }


    public partial class OneD                           // This class supports the "OneDArray" in the XAML.
    {
        public string OneDName { get; set; }
    }
    public partial class TwoD                         // This class supports the "TwoDArray" in the XAML.
    {
        public int TwoDNumber { get; set; }
        public string TwoDName { get; set; }
    }

}

Upvotes: 0

Views: 2355

Answers (1)

mm8
mm8

Reputation: 169150

Set the SelectedValuePath property to OneDName and bind the SelectedValue value property to DataBaseOneDName in your XAML markup:

<ComboBox x:Name="oneDComboBox" Grid.Row="1" Grid.Column="2" Width="120" DisplayMemberPath="OneDName"
            ItemsSource="{StaticResource OneDArray}"
            SelectedValuePath="OneDName"
            SelectedValue="{Binding Path=DataBaseOneDName, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}"
            SelectionChanged="ComboBox_SelectionChanged"
            />

Then you cast SelectedItem to a OneD and then access its OneDName property:

private void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    ComboBox sourceComboBox = (ComboBox)sender;
    int selectedIndex = sourceComboBox.SelectedIndex;
    OneD oneD = sourceComboBox.SelectedItem as OneD;
    if (oneD != null)
    {
        string name = oneD.OneDName;
    }
    string displayMemberPath = sourceComboBox.DisplayMemberPath.ToString();
}

The same thing for TwoD.

The other option would be to override the ToString() method of your classes, e.g.:

public partial class OneD
{
    public string OneDName { get; set; }

    public override string ToString()
    {
        return OneDName;
    }
}

Upvotes: 1

Related Questions