Search code examples
c#wpfdata-binding

WPF bind ComboBox ItemSouce to an ObservableCollection and the SelectedItem to a DataContext model property


I am new to WPF, and I am interested can this be done or not. I hava a string datasource for my combobox controll defined in the MainWindow lie this:

public readonly ObservableCollection<string> _roles = new ObservableCollection<string>() { "User", "Admin", "Developer" };

I have a model defined:

public class LoginRequest : ValidationBase
    {
        private string _email;
        private string _password;
        private string _role;

        [Required(ErrorMessage = "Email address is required")]
        public string Email
        {
            get { return _email; }
            set { SetValue(ref _email, value); }
        }

        [Required(ErrorMessage = "Password is required")]
        [MinLength(8, ErrorMessage = "Password minimum length is 8 charachters.")]
        public string Password
        {
            get { return _password; }
            set { SetValue(ref _password, value); }
        }

        [Required(ErrorMessage = "Role is required")]
        public string Role
        {
            get { return _role; }
            set { SetValue(ref _role, value); }
        }
    }

This model is defines in xaml as my DataContext

<Window.DataContext> <model:LoginRequest /> </Window.DataContext>

In the form I have a ComboBox where I bind the Role property of my object. That works perfect if I set the combobox ItemSource property form the code like this:

cbRoles.ItemsSource = _roles;

But I am interested, can I do this i xaml? Here is my code snippet for my combobox.

<StackPanel Grid.Row="2"
                    Margin="5"
                    Orientation="Vertical">
            <Label x:Name="lblRole" 
                   Content="Role" />
            <ComboBox ItemsSource="{Binding Path=Role,
                                    UpdateSourceTrigger=PropertyChanged,
                                    NotifyOnValidationError=True,
                                    ValidatesOnDataErrors=True}"
                 DisplayMemberPath="{Binding SimpleStringProperty}"
                 SelectedValuePath="{Binding SimpleStringProperty}"
                 SelectedItem="{Binding SimpleStringProperty}"
                 IsSynchronizedWithCurrentItem="True" 
                 Text="Select Option"
                 Margin="1"
                 Height="20"
                 x:Name="cbRoles"/>
        </StackPanel>

Solution

  • In order to set the ComboBox.ItemsSource from XAML you must use data binding. To use data binding, the source must be a public property, which requires to convert the _roles field to a public property.

    Since the items are plain string, it doesn't have any property and therefore no path. You can't select a property to provide the display value or selection value. ComboBox.DisplayMemberPath and ComboBox.SelectedValuePath must be removed.

    Binding.UpdateSourceTrigger is set to UpdateSourceTrigger.PropertyChanged by default.

    SimpleStringProperty does not exist on LoginRequest. SelectedItem should bind to LoginRequest.Role instead.

    MainWindow.xaml.cs

    partial class MainWindow : Window
    {
      public ObservableCollection<string> Roles { get; }
    
      public MainWindow()
      {
        InitializeComponent();
        this.Roles = new ObservableCollection<string>() { "User", "Admin", "Developer" };
      }
    }
    

    MainWindow.xaml

    <ComboBox ItemsSource="{Binding Path=Roles, 
                                    RelativeSource={RelativeSource AncestorType=Window}},
                                    NotifyOnValidationError=True,
                                    ValidatesOnDataErrors=True}"
              SelectedItem="{Binding Role}"
              IsSynchronizedWithCurrentItem="True" />