The following forum(s) are migrating to a new home on Microsoft Q&A (Preview): Developing Universal Windows apps!

Ask new questions on Microsoft Q&A (Preview).
Interact with existing posts until December 13, 2019, after which content will be closed to all new and existing posts.

Learn More

 none
[UWP][C#] Trouble Databinding A Combobox To <list> RRS feed

  • Question

  • I can't figure out how to databind my combobox to a list I have. The program has a list of petbrands. Each pet brand has a BrandName, BrandID, and BrandClass(premium, store, name).

    Here is the code to get the pet brands from my local SQL server db. The UI should display each brand name only. The class name that this is in is PetItemManager.cs.

     public List<PetBrands> petBrands = new List<PetBrands>(); //PetBrands will hold all information for pet brands
    
            public void UpdatePetBrands() //Method to retrieve all brands from server
            {
                using (SqlConnection connection = new SqlConnection(PetItemDB.ConnectionString))
                {
                    SqlCommand command = new SqlCommand(BrandsQueryString, connection);
                    //command.Connection = connection;
                    //command.CommandText = BrandsQueryString.ToString();
                    
                   try
                    {
                        connection.Open();
                        SqlDataReader DataReader;
                        DataReader = command.ExecuteReader();
                        while (DataReader.Read())
                        {
                            PetBrands brand = new PetBrands()
                            {
                                BrandID = (int)DataReader["BrandID"],
                                BrandName = DataReader["BrandName"].ToString(),
                                BrandClass = (int)DataReader["BrandClass"],
                            };
                            petBrands.Add(brand);
                        }
                        DataReader.Close();
                    }
                    catch (SqlException ex)
                    {
                        Console.WriteLine(ex.Message);
                    }
                    finally
                    {
                        connection.Close();
                    }
                }
    <StackPanel Margin="100,0,14.2,0" Orientation="Vertical" Grid.Row="2">
                <TextBlock Text="Brand" FontSize="18"/>
                <ComboBox Name="PetBrandComboBox" ItemsSource="{Binding BrandName}" FontSize="24" Width="300" SelectionChanged="PetBrandComboBox_SelectionChanged"/>
            </StackPanel>

    Is there a way to use an indexer to find BrandName within the List<PetBrands>petBrands? Like... ItemsSource={Binding petBrands[BrandName]}?

    Additionally, the datacontext of this information would be the class name that List<PetBrands>petBrands instantiates in right? Which would be PetItemManager.cs... So would I put this after the xaml page initializes?

    Thursday, February 8, 2018 2:02 AM

Answers

  • Hi PumpedUpKicks91,

    Ok. I think your code is almost all perfect. When you give ItemsSource through DataContext, you don't need to set property-path in the {Binding}. And you may not give DataContext="manager" to the StackPanel (PetBrandComboBox's parent) there.

    After all, modifying the XAML just as follows, your code would work well. Maybe.

            <StackPanel Margin="100,0,14.2,0" Orientation="Vertical" Grid.Row="2"> <!-- DataContext="manager" -->
                <TextBlock Text="Brand" FontSize="18"/>
                <ComboBox Name="PetBrandComboBox" ItemsSource="{Binding}" FontSize="24" Width="300" 
                          SelectionChanged="PetBrandComboBox_SelectionChanged" DisplayMemberPath="BrandName" >
                </ComboBox>
            </StackPanel>

    Friday, February 9, 2018 5:19 AM

All replies

  • Hello PumpedUpKicks91,

    If you just want to display a specific data member in the ComboBox, there is a compact solution to use ComboBox.DisplayMemberPath property.

    e.g.

    1. Specify DisplayMemberPath in the XAML.

    <StackPanel Margin="100,0,14.2,0" Orientation="Vertical" Grid.Row="2">
        <TextBlock Text="Brand" FontSize="18"/>
        <ComboBox Name="PetBrandComboBox" DisplayMemberPath="BrandName" FontSize="24"
                Width="300" SelectionChanged="PetBrandComboBox_SelectionChanged"/>
    </StackPanel>

    2. And set ItemsSource in the code behind.

    PetItemManager manager = new PetItemManager();
    
    public MainPage()
    {
        this.InitializeComponent();
    
        PetBrandComboBox.ItemsSource = manager.petBrands;
        manager.UpdatePetBrands();
    }
    
    
    private void PetBrandComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        if (e.AddedItems.Count > 0)
        {
            PetBrands brands = e.AddedItems[0] as PetBrands;
            Debug.WriteLine(brands.BrandName + " Selected!");
        }
    }

    Appendix: How to get a PetBrands item by BrandName.

    In case you need to obtain PetBrands by BrandName, it can be done with the help of Linq. Try appending a method to the PetItemManager class;

    public PetBrands GetPetBrandsByName(string name)
    {
        return petBrands.FirstOrDefault(x => (x.BrandName == name));
    }
    • Edited by FEC-4RP Thursday, February 8, 2018 8:50 AM
    • Marked as answer by PumpedUpKicks91 Thursday, February 8, 2018 11:53 AM
    • Unmarked as answer by PumpedUpKicks91 Friday, February 9, 2018 2:18 AM
    Thursday, February 8, 2018 7:42 AM
  • Thanks for the reply. Unfortunately after I added the code for DisplayMemberPath and PetBrandComboBox.ItemsSource = manager.petBrands, the combobox had 4 empty selections. I'm not sure why they are blank. There are 4 brands in the local database, so it would make sense to have 4 empty brands instead of say 5 or 6... but I don't understand why they are empty. A picture is shown below. 

    It may appear like I am selecting from the Pet Type Combobox but that's the Pet Brand Combobox. Any suggestions as to why it's blank?

    Edit: I also tried calling the manager.UpdatePetBrands() method before setting the PetBrandComboBox ItemsSource. I also modified the databinding to see if I would get a different result, and I got an error that may reveal the issue. The combobox returns only 1 item that is really small and blank when I ran the code below. Below is all of the information.empty when run.

    Code, Binding, Error Below

    Xaml

    <Page
        x:Class="TestProgram2.CalculatorPage"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:TestProgram2"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d">
    
    
        <Grid Background="{ThemeResource SystemControlAcrylicWindowBrush}" Margin="7,5,0,0">
            <Grid.RowDefinitions>
                <RowDefinition/>
                <RowDefinition/>
                <RowDefinition/>
                <RowDefinition Height="140"/>
                <RowDefinition/>
            </Grid.RowDefinitions>
    
            <Button Grid.Row="0" Content="Update Brands" Width="250" Name="BrandsButton" Click="BrandsButton_Click"/>
    
            <StackPanel Margin="100,0,14.2,0" Orientation="Vertical" Grid.Row="1">
                <TextBlock Text="Pet Type" FontSize="18"/>
                <ComboBox Name="PetTypeComboBox" FontSize="24" Width="150" Margin="0,0,0,0" SelectionChanged="PetTypeComboBox_SelectionChanged">
                    <ComboBoxItem>Cat</ComboBoxItem>
                    <ComboBoxItem>Dog</ComboBoxItem>
                </ComboBox>
            </StackPanel>
    
            <StackPanel Margin="100,0,14.2,0" Orientation="Vertical" Grid.Row="2" DataContext="manager">
                <TextBlock Text="Brand" FontSize="18"/>
                <ComboBox Name="PetBrandComboBox" ItemsSource="{Binding BrandName}" FontSize="24" Width="300" 
                          SelectionChanged="PetBrandComboBox_SelectionChanged" DisplayMemberPath="BrandName" >
                </ComboBox>
            </StackPanel>
    
            <StackPanel Margin="100,0,14.2,0" Orientation="Vertical" Grid.Row="3">
                <TextBlock Text="Size" FontSize="18"/>
                <TextBox Name="SizeTextBox" FontSize="24" Width="150" HorizontalAlignment="Left" />
                
                <TextBlock Text="Units" FontSize="18" />
                <ComboBox Name="PetUnitsComboBox" FontSize="24" Width="200" SelectionChanged="PetUnitsComboBox_SelectionChanged"/>
            </StackPanel>
    
            <StackPanel Grid.Row="4" Margin="100,0,14.2,0" Orientation="Vertical">
                <TextBlock Text="Price" FontSize="18" />
                <TextBox FontSize="24" Width="150" HorizontalAlignment="Left" />
            </StackPanel>
    
    
        </Grid>
    </Page>


    Xaml.cs

    namespace TestProgram2
    {
        /// <summary>
        /// An empty page that can be used on its own or navigated to within a Frame.
        /// </summary>
        public sealed partial class CalculatorPage : Page
        {
            public PetItemManager manager = new PetItemManager();
            public CalculatorPage()
            {
                this.InitializeComponent();
                manager.UpdatePetBrands();
                DataContext = manager.petBrands;
                //PetBrandComboBox.ItemsSource = manager.petBrands;
               // manager.UpdatePetBrands();
                
                //PetBrandComboBox.DisplayMemberPath = "BrandName";
    
    
            }
    
            private void PetTypeComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
            {
                //CurrentItem.Type = PetTypeComboBox.SelectedItem.ToString();
            }
    
            private void PetBrandComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
            {
                //CurrentItem.Brand = PetBrandComboBox.SelectedItem.ToString();
                if (e.AddedItems.Count>0)
                {
                    PetBrands brands = e.AddedItems[0] as PetBrands;
                    Debug.WriteLine(brands.BrandName + "Selected");
                }
            }
    
            private void PetUnitsComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
            {
                //CurrentItem.Brand = PetUnitsComboBox.SelectedItem.ToString();
            }
    
            private void BrandsButton_Click(object sender, RoutedEventArgs e)
            {
                manager.UpdatePetBrands();
                
                        foreach (var Brand in manager.petBrands)
                        {
                            PetBrandComboBox.Items.Add(Brand.BrandName);
                        }
                
            }
        }
    }

    PetBrand.cs

    namespace TestProgram2
    {
        public class PetBrands
        {
            public string BrandName { get; set; }
            public int BrandClass {get; set; }
            public int BrandID {get; set; }
        }
    }


    PetItemManager.cs

    using System.Data.SqlClient;
    
    namespace TestProgram2
    {
        public class PetItemManager
        {
            DBManager PetItemDB = new DBManager(); //PetItemDB holds connection string to connect to server
            public string BrandsQueryString = @"SELECT * FROM Brands ORDER BY BrandName;"; //Query string to get brands from server
            public ObservableCollection<PetBrands> petBrands = new ObservableCollection<PetBrands>(); //PetBrands will hold all information for pet brands
    
            public void UpdatePetBrands() //Method to retrieve all brands from server
            {
                using (SqlConnection connection = new SqlConnection(PetItemDB.ConnectionString))
                {
                    SqlCommand command = new SqlCommand(BrandsQueryString, connection);
                    //command.Connection = connection;
                    //command.CommandText = BrandsQueryString.ToString();
                    
                   try
                    {
                        connection.Open();
                        SqlDataReader DataReader;
                        DataReader = command.ExecuteReader();
                        while (DataReader.Read())
                        {
                            PetBrands brand = new PetBrands()
                            {
                                BrandID = (int)DataReader["BrandID"],
                                BrandName = DataReader["BrandName"].ToString(),
                                BrandClass = (int)DataReader["BrandClass"],
                            };
                            petBrands.Add(brand);
                        }
                        DataReader.Close();
                    }
                    catch (SqlException ex)
                    {
                        Console.WriteLine(ex.Message);
                    }
                    finally
                    {
                        connection.Close();
                    }
                }
            }
        }
    }


    Error:

    Error: BindingExpression path error: 'BrandName' property not found on 'System.Collections.ObjectModel.ObservableCollection`1[TestProgram2.PetBrands]'. BindingExpression: Path='BrandName' DataItem='System.Collections.ObjectModel.ObservableCollection`1[TestProgram2.PetBrands]'; target element is 'Windows.UI.Xaml.Controls.ComboBox' (Name='PetBrandComboBox'); target property is 'ItemsSource' (type 'Object')

    Error: BindingExpression path error: 'BrandName' property not found on 'Windows.Foundation.IReference`1<String>'. BindingExpression: Path='BrandName' DataItem='System.Collections.ObjectModel.ObservableCollection`1[TestProgram2.PetBrands]'; target element is 'Windows.UI.Xaml.Controls.ComboBox' (Name='PetBrandComboBox'); target property is 'ItemsSource' (type 'Object')

    How is BrandName not a property of petBrands? 

    Also... the button to add the items manually worked so I don't see any server issues or anything on that end of things.




    Friday, February 9, 2018 1:40 AM
  • Hi PumpedUpKicks91,

    Ok. I think your code is almost all perfect. When you give ItemsSource through DataContext, you don't need to set property-path in the {Binding}. And you may not give DataContext="manager" to the StackPanel (PetBrandComboBox's parent) there.

    After all, modifying the XAML just as follows, your code would work well. Maybe.

            <StackPanel Margin="100,0,14.2,0" Orientation="Vertical" Grid.Row="2"> <!-- DataContext="manager" -->
                <TextBlock Text="Brand" FontSize="18"/>
                <ComboBox Name="PetBrandComboBox" ItemsSource="{Binding}" FontSize="24" Width="300" 
                          SelectionChanged="PetBrandComboBox_SelectionChanged" DisplayMemberPath="BrandName" >
                </ComboBox>
            </StackPanel>

    Friday, February 9, 2018 5:19 AM
  • Thanks for your help! I put in the suggested code, and I updated the DataContext to "DataContext = manager.petBrands" in the xaml.cs after the page initializes. The binding works now :).

    I think my next challenge is turning the PetBrandComboBox into an ObservableCollection so that it automatically updates if a brand is added to the database, changed, etc.

    Thanks again.

    Friday, February 9, 2018 10:06 PM