How do I bind a "list" of strings to a ComboBox in WPF?

17,403

Solution 1

There are countless ways of doing this. Once you've created the collection in code-behind, you can:

  1. Call Resources.Add to add it to the window's resource dictionary, and then bind to the resource, e.g. ItemsSource="{Binding {DynamicResource MyList}}".

  2. Give the ComboBox a name (using the x:Name attribute) and set its ItemsSource explicitly in code, e.g. MyComboBox.ItemsSource = myCollection;.

  3. Create a class, make the collection a property of the class, and set the window's DataContext to an instance of that class and bind to it directly, e.g. ItemsSource = "{Binding MyCollectionProperty}".

  4. Make the collection a property of the window, set the window's DataContext to this, and bind to the property (this is essentially the same technique as #3, only you're not creating a new class).

  5. Without setting the window's DataContext, you can still reference a property on it using binding as long as you've given it a name, e.g. {Binding ElementName=MyWindow, Path=MyCollection}. (This is the same as Ross's suggestion.)

  6. Or, without giving the window a name, you can use RelativeSource binding to find the ancestor Window and bind to a property on it. I don't have any confidence in my ability to write a working binding expression that uses RelativeSource off the top of my head, so I'll leave that as an exercise for the reader.

  7. You can set the DataContext of the ComboBox to the instance of your collection, and then set itsItemsSource to {Binding}. You probably wouldn't do this in practice; I mention it just because it seems to be a common mistake for people to set the DataContext of a control without also setting a binding, and then wonder why content from the bound object isn't showing up.

(While I've said "window" in the above, everything I've said is also true for user controls.)

I'm sure there are at least five other ways to do this that I'm not thinking of. Binding is really, really flexible.

Solution 2

What have you tried so far?

I would approach it as follows, assuming the combo box is within a UserControl with a code-behind class containing the public property MyObservableCollection:

<UserControl x:Name="MyCollectionOwnerControl">

    <ComboBox ItemsSource="{Binding ElementName=MyCollectionOwnerControl, Path=MyObservableCollection, Mode=OneWay}" />

</UserControl>
Share:
17,403
Yatrix
Author by

Yatrix

The 3 best things in life are Keanu Reeves and dogs.

Updated on June 04, 2022

Comments

  • Yatrix
    Yatrix about 2 years

    I basically want to take a bunch of names in a collection and bind them to a combobox. For example:

    • Bill
    • Jack
    • Bob
    • Kevin

    and have those items in a collection and have it bound to the ComboBox. I'm not sure if the list will be updated dynamically or not, but I prefer to plan for it to be. Any help would be appreciated. I've been trying for a few hours now and can't figure it out. I want to do it in XAML and not the code-behind. In the code-behind,

    MyComboBox.ItemsSource = MyObservableCollection; 
    

    works fine. I don't know how to do that in XAML though with the collection declared in the code-behind.

    Thanks in advance (again), community.

    *EDIT:

    This is how I have the collection declared and accessible.

        public ObservableCollection<string> propertynames 
        { 
            get {return _propertynames;}
        }
        private ObservableCollection<string> _propertynames;
    

    The last thing I tried was this:

    <Window.Resources>     
        <CollectionViewSource Source="{Binding propertynames}" x:Key="srcSort"/>
    </Window.Resources>
    ....
        <ComboBox x:Name="cboSort" HorizontalAlignment="Left" VerticalAlignment="Top" 
                  Width="256" Background="WhiteSmoke" Margin="12,50,0,0" FontSize="12pt" 
                  Height="27.28" 
                  SelectedIndex="0" 
                  SelectionChanged="cboWorkCenters_SelectionChanged"
                  ItemsSource="{Binding Path = {StaticResource srcSort}}">
        </ComboBox>
    ....
    

    I'm a total n00b to this stuff. Been in it about a week now, so I may have done something really obvious to a seasoned user.

    *EDIT #2

    <Window x:Class="WpfApplication1.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:s="clr-namespace:WpfApplication1"
        Title="Window1" Height="226" Width="242"
        DataContext="{Binding RelativeSource={RelativeSource Self}}">
    
        <Grid>
            <ComboBox Margin="43,71,40,77" 
                      Name="comboBox1" 
                      ItemsSource="{Binding ob}" />
        </Grid>
    </Window>
    
    namespace WpfApplication1
    {
        /// <summary>
        /// Interaction logic for Window1.xaml
        /// </summary>
        public partial class Window1 : Window
        {
            public ObservableCollection<string> ob 
            { 
                get 
                {
                    return _ob;
                }
            }
            private ObservableCollection<string> _ob = new ObservableCollection<string>();
    
            public Window1()
            {
                InitializeComponent();
                FillObj();
                //comboBox1.ItemsSource = ob;
            }
    
            private void FillObj()
            {
                for (int i = 1; i < 6; i++)
                {
                    _ob.Add(i.ToString());
                }
            }
        }
    }
    

    Made above real simple project just to see if I was doing it all wrong. This worked fine so something else must be causing it to fail.

    *EDIT #3 *PROBLEM FIXED

    For God's sake, I figured it out. I've been on this for HOURS and it's just silly what's caused it to fail.

    The solution is this: I wasn't instantiating _propertynames when I declared it. I was querying the class properties with Linq to get the list of properties and then created _propertynames by passing ...GetProperties.ToList<...>() to the constructor. Apparently, you have to instantiate the variable so it hits during InitializeComponent. Unreal. Once I did that and then added the items to it after the fact, it worked fine.

    I wish WPF had a face so I could punch it. I know it's my ignorance of how it works, but I really could have used some kind of message.

    Thanks guys for the help. Both of your suggestions were useful once I took care of the root issue.

    private ObservableCollection<string> _propertynames
    

    needs to be

    private ObservableCollection<string> _propertynames = new ObservableCollection<string>()
    
  • Yatrix
    Yatrix almost 13 years
    Edited my original entry with more info. Thanks again, Ross.
  • Yatrix
    Yatrix almost 13 years
    <ComboBox ItemsSource="{Binding ElementName=Window1, Path=propertynames, Mode=OneWay}" /> I've done this and it didn't work.
  • Adrian
    Adrian almost 13 years
    It should work if the window's datacontext is set to itself (put "this.Datacontext = this;" inside the window's constructor).
  • Yatrix
    Yatrix almost 13 years
    <Window x:Class=... ...DataContext="{Binding RelativeSource={RelativeSource Self}}"> Is what I have. That's not accomplishing the same thing?
  • Yatrix
    Yatrix almost 13 years
    Added more to question above. See under Edit#2. That worked fine. The combo populated from the collection as intended.
  • H.B.
    H.B. almost 13 years
    "... countless ways ... <Numbered list with 7 items>"; Anyway, isn't there something missing in the first binding? i.e. Source=
  • Robert Rossney
    Robert Rossney almost 13 years
    You're right about the Source attribute; as to your other observation, the last paragraph is not unimportant.