locked
Using a ComboBox in a Grid DataTemplate with a CollectionViewSource to provide the ItemsSource

    Question

  • I have written a small stand alone page for a Windows Store App to show the issue I am having. I can create a XAML ItemsControl with a ComboBox in the DataTemplate. When you create the list for the combobox as ComboBoxItems under the ComoboBox definition, it works as advertised. If you need to have that Item list as configurable, you can bind it to a CollectionViewSource and set that at run time. The problem is that if you change one Item on a Record, it changes all of them. I wrote a simple app page to show this behavior:

    XAML

    <Page
        x:Name="pageRoot"
        x:Class="App2.MainPage"
        DataContext="{Binding DefaultViewModel, RelativeSource={RelativeSource Self}}"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:App2"
        xmlns:common="using:App2.Common"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d">
    
        <Page.Resources>
            <!-- TODO: Delete this line if the key AppName is declared in App.xaml -->
            <x:String x:Key="AppName">My Application</x:String>
        </Page.Resources>
    
        <!--
            This grid acts as a root panel for the page that defines two rows:
            * Row 0 contains the back button and page title
            * Row 1 contains the rest of the page layout
        -->
        <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
            <Grid.ChildrenTransitions>
                <TransitionCollection>
                    <EntranceThemeTransition/>
                </TransitionCollection>
            </Grid.ChildrenTransitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="140"/>
                <RowDefinition Height="*"/>
            </Grid.RowDefinitions>
    
            <!-- Back button and page title -->
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="120"/>
                    <ColumnDefinition Width="*"/>
                </Grid.ColumnDefinitions>
                <Button x:Name="backButton" Margin="39,59,39,0" Command="{Binding NavigationHelper.GoBackCommand, ElementName=pageRoot}"
                            Style="{StaticResource NavigationBackButtonNormalStyle}"
                            VerticalAlignment="Top"
                            AutomationProperties.Name="Back"
                            AutomationProperties.AutomationId="BackButton"
                            AutomationProperties.ItemType="Navigation Button"/>
                <TextBlock x:Name="pageTitle" Text="{StaticResource AppName}" Style="{StaticResource HeaderTextBlockStyle}" Grid.Column="1" 
                            IsHitTestVisible="false" TextWrapping="NoWrap" VerticalAlignment="Bottom" Margin="0,0,30,40"/>
            </Grid>
            <ScrollViewer Grid.Row="1" Margin="25, 0, 0, 0" HorizontalScrollBarVisibility="Auto" >
                <StackPanel>
                    <Grid Margin="0,0,0,10">
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="300" />
                            <ColumnDefinition Width="110" />
                            <ColumnDefinition Width="*" />
                        </Grid.ColumnDefinitions>
                        <Grid.RowDefinitions>
                            <RowDefinition />
                            <RowDefinition />
                        </Grid.RowDefinitions>
                        <StackPanel Grid.ColumnSpan="5">
                            <TextBlock Text="Items" Style="{StaticResource SubheaderTextBlockStyle}" TextAlignment="Center"/>
                        </StackPanel>
                        <Border Grid.Column="0" Grid.Row="1">
                            <TextBlock Text="Room/Category" Style="{StaticResource BodyTextBlockStyle}" />
                        </Border>
                        <Border Grid.Column="1" Grid.Row="1">
                            <TextBlock Text="Square Feet" Style="{StaticResource BodyTextBlockStyle}" />
                        </Border>
                        <Border Grid.Column="2" Grid.Row="1">
                            <TextBlock Text="Note" Style="{StaticResource BodyTextBlockStyle}" />
                        </Border>
                    </Grid>
                    <ItemsControl Name="ItemGrid" Foreground="{ThemeResource ApplicationForegroundThemeBrush}" >
                        <ItemsControl.Resources >
                            <CollectionViewSource x:Name="surveyCategories" />
                        </ItemsControl.Resources>
                        <ItemsControl.ItemTemplate>
                            <DataTemplate>
                                <Grid >
                                    <Grid.ColumnDefinitions>
                                        <ColumnDefinition Width="300" />
                                        <ColumnDefinition Width="110" />
                                        <ColumnDefinition Width="*" />
                                    </Grid.ColumnDefinitions>
                                    <ComboBox x:Name="cboCategory" Grid.Column="0" SelectedValue="{Binding Category, Mode=TwoWay}"  
                                              ItemsSource="{Binding Source={StaticResource surveyCategories}}" SelectedValuePath="Name" DisplayMemberPath="Name" />
                                    <TextBox Grid.Column="1" Text="{Binding Area, Mode=TwoWay}" InputScope="Number" />
                                    <TextBox Grid.Column="3" Text="{Binding Note, Mode=TwoWay}"  />
                                </Grid>
                            </DataTemplate>
                        </ItemsControl.ItemTemplate>
                    </ItemsControl>
                    <AppBarButton Label="Add" Icon="Add" Click="AddItem_Click" />
                </StackPanel>
            </ScrollViewer>
    
        </Grid>
    </Page>
    

    Code Bhind in C#

    using App2.Common;
    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.IO;
    using System.Linq;
    using System.Runtime.InteropServices.WindowsRuntime;
    using Windows.Foundation;
    using Windows.Foundation.Collections;
    using Windows.UI.Xaml;
    using Windows.UI.Xaml.Controls;
    using Windows.UI.Xaml.Controls.Primitives;
    using Windows.UI.Xaml.Data;
    using Windows.UI.Xaml.Input;
    using Windows.UI.Xaml.Media;
    using Windows.UI.Xaml.Navigation;
    
    // The Basic Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234237
    
    namespace App2
    {
    
        class Category
        {
            public Category(string Name, string Area)
            {
                this.Name = Name;
                this.Area = Area ;
            }
    
            public string Name { get; set; }
            public string Area { get; set; }
        }
    
        class Item
        {
            public Item(string Category, string Area, string Note)
            {
                this.Category = Category;
                this.Area = Area;
                this.Note = Note;
            }
            public string Category { get; set; }
            public string Area { get; set; }
            public string Note { get; set; }
        }
    
        /// <summary>
        /// A basic page that provides characteristics common to most applications.
        /// </summary>
        public sealed partial class MainPage : Page
        {
    
            private ObservableCollection<Category> Categories;
            private ObservableCollection<Item> Items;
    
            public MainPage()
            {
                this.InitializeComponent();
                CreateCategories();
                CreateItems();
            }
    
            private void CreateCategories()
            {
                Categories = new ObservableCollection<Category>();
                Categories.Add(new Category("Kitchen", "500"));
                Categories.Add(new Category("Dining Room", "501"));
                Categories.Add(new Category("Living Room", "502"));
                Categories.Add(new Category("Bedroom", "503"));
                Categories.Add(new Category("Garage", "504"));
                surveyCategories.Source = Categories;
            }
    
            private void CreateItems()
            {
                Items = new ObservableCollection<Item>();
                Items.Add(new Item("Kitchen", "500", ""));
                Items.Add(new Item("Dining Room", "501", ""));
                Items.Add(new Item("Living Room", "502", ""));
                Items.Add(new Item("Bedroom", "600", "Master"));
                Items.Add(new Item("Bedroom", "601", "Bedroom 1"));
                Items.Add(new Item("Bedroom", "602", "Bedroom 2"));
                Items.Add(new Item("Bedroom", "603", "Bedroom 3"));
                Items.Add(new Item("Garage", "504", ""));
    
                ItemGrid.ItemsSource = Items;
                
            }
    
            private void AddItem_Click(object sender, RoutedEventArgs e)
            {
                Items.Add(new Item("", "", ""));
            }
        }
    }
    

    I really need to be able to configure the ItemsSource for the ComboBox.  It is obvious that the method I have chosen is using the same instance of Categories for the ItemsSource for the ComboBox for each record in the ItemsControl.  This type of approach works great for C# DataGridView when you define the columns.  Is there a way for it to work for XAML and C# as well.  Any help would be greatly appreciated.

    Jim


    Jim Wilcox

    Tuesday, April 29, 2014 2:27 PM

Answers

  • Hi,

    You cannot do it above. Because the combobox binding to a same source, so if you change one Item on a Record, it changes all of them. You can edit your code like below:

    <Page
        x:Class="comboboxtest.MainPage"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:comboboxtest"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d">
        <Page.Resources>
            <!-- TODO: Delete this line if the key AppName is declared in App.xaml -->
            <x:String x:Key="AppName">My Application</x:String>
            
             
           
        </Page.Resources>
    
        <!--
            This grid acts as a root panel for the page that defines two rows:
            * Row 0 contains the back button and page title
            * Row 1 contains the rest of the page layout
        -->
        <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
            <Grid.ChildrenTransitions>
                <TransitionCollection>
                    <EntranceThemeTransition/>
                </TransitionCollection>
            </Grid.ChildrenTransitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="140"/>
                <RowDefinition Height="*"/>
            </Grid.RowDefinitions>
    
            <!-- Back button and page title -->
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="120"/>
                    <ColumnDefinition Width="*"/>
                </Grid.ColumnDefinitions>
                <Button x:Name="backButton" Margin="39,59,39,0" Command="{Binding NavigationHelper.GoBackCommand, ElementName=pageRoot}"
                            Style="{StaticResource NavigationBackButtonNormalStyle}"
                            VerticalAlignment="Top"
                            AutomationProperties.Name="Back"
                            AutomationProperties.AutomationId="BackButton"
                            AutomationProperties.ItemType="Navigation Button"/>
                <TextBlock x:Name="pageTitle" Text="{StaticResource AppName}" Style="{StaticResource HeaderTextBlockStyle}" Grid.Column="1" 
                            IsHitTestVisible="false" TextWrapping="NoWrap" VerticalAlignment="Bottom" Margin="0,0,30,40"/>
            </Grid>
            <ScrollViewer Grid.Row="1" Margin="25, 0, 0, 0" HorizontalScrollBarVisibility="Auto" >
                <StackPanel>
                    <Grid Margin="0,0,0,10">
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="300" />
                            <ColumnDefinition Width="110" />
                            <ColumnDefinition Width="*" />
                        </Grid.ColumnDefinitions>
                        <Grid.RowDefinitions>
                            <RowDefinition />
                            <RowDefinition />
                        </Grid.RowDefinitions>
                        <StackPanel Grid.ColumnSpan="5">
                            <TextBlock Text="Items" Style="{StaticResource SubheaderTextBlockStyle}" TextAlignment="Center"/>
                        </StackPanel>
                        <Border Grid.Column="0" Grid.Row="1">
                            <TextBlock Text="Room/Category" Style="{StaticResource BodyTextBlockStyle}" />
                        </Border>
                        <Border Grid.Column="1" Grid.Row="1">
                            <TextBlock Text="Square Feet" Style="{StaticResource BodyTextBlockStyle}" />
                        </Border>
                        <Border Grid.Column="2" Grid.Row="1">
                            <TextBlock Text="Note" Style="{StaticResource BodyTextBlockStyle}" />
                        </Border>
                    </Grid>
                    <ItemsControl Name="ItemGrid" Foreground="{ThemeResource ApplicationForegroundThemeBrush}"   >
                        
                        <ItemsControl.ItemTemplate>
                            <DataTemplate>
                                <Grid >
                                    <Grid.ColumnDefinitions>
                                        <ColumnDefinition Width="300" />
                                        <ColumnDefinition Width="110" />
                                        <ColumnDefinition Width="*" />
                                    </Grid.ColumnDefinitions>
                                    <ComboBox x:Name="cboCategory" Grid.Column="0"  
                                              ItemsSource="{Binding Categories}" SelectedIndex="{Binding selectIndex}"  >
                                        <ComboBox.ItemTemplate>
                                            <DataTemplate>
                                                <TextBlock Text="{Binding Path=Name }"/>
                                            </DataTemplate>
                                        </ComboBox.ItemTemplate>
                                    </ComboBox>
                                    <TextBox Grid.Column="1" Text="{Binding Area, Mode=TwoWay}" InputScope="Number" />
                                    <TextBox Grid.Column="3" Text="{Binding Note, Mode=TwoWay}"  />
                                </Grid>
                            </DataTemplate>
                        </ItemsControl.ItemTemplate>
                    </ItemsControl>
                    <AppBarButton Label="Add" Icon="Add" Click="AddItem_Click" />
                </StackPanel>
            </ScrollViewer>
    
        </Grid>
    
    </Page>
    
    namespace comboboxtest
    {
        /// <summary>
        /// An empty page that can be used on its own or navigated to within a Frame.
        /// </summary>
        ///
        class Category
        {
            public Category(string Name, string Area)
            {
                this.Name = Name;
                this.Area = Area;
            }
    
            public string Name { get; set; }
            public string Area { get; set; }
        }
    
        class Item
        {
            public Item(int selectIndex, ObservableCollection<Category> Categories, string Area, string Note)
            {
                this.Categories = Categories;
                this.Area = Area;
                this.Note = Note;
                this.selectIndex = selectIndex;
            }
            public ObservableCollection<Category> Categories { get; set; }
            public int selectIndex{get;set;}
            public string Area { get; set; }
            public string Note { get; set; }
        }
        
       public sealed partial class MainPage : Page
        {
           
           private ObservableCollection<Category> Categories;
           
            private ObservableCollection<Item> Items;
    
    
            public MainPage()
            {
                this.InitializeComponent();
                CreateCategories();
                CreateItems();
    
            }
            private void CreateCategories()
            {   
                Categories = new ObservableCollection<Category>();
                Categories.Add(new Category("Kitchen", "500"));
                Categories.Add(new Category("Dining Room", "501"));
                Categories.Add(new Category("Living Room", "502"));
                Categories.Add(new Category("Bedroom", "503"));
                Categories.Add(new Category("Garage", "504"));
               
                
            }
    
            private void CreateItems()
            {
                Items = new ObservableCollection<Item>();
                Items.Add(new Item(0,Categories ,"500", ""));
                Items.Add(new Item(1,Categories, "501", ""));
                Items.Add(new Item(2, Categories,"502", ""));
                Items.Add(new Item(3,Categories, "600", "Master"));
                Items.Add(new Item(3,Categories, "601", "Bedroom 1"));
                Items.Add(new Item(3,Categories, "602", "Bedroom 2"));
                Items.Add(new Item(3, Categories,"603", "Bedroom 3"));
                Items.Add(new Item(4, Categories,"504", ""));
    
                ItemGrid.ItemsSource = Items;
                
    
            }
    
            private void AddItem_Click(object sender, RoutedEventArgs e)
            {
               
            }
    
        }
    }
    

    For more information you can refer to the links below:

    http://msdn.microsoft.com/en-us/library/windows/apps/xaml/hh758318.aspx

    Best Wishes!


    We are trying to better understand customer views on social support experience, so your participation in this interview project would be greatly appreciated if you have time. Thanks for helping make community forums a great place.
    Click HERE to participate the survey. Thanks<br/> MSDN Community Support<br/> <br/> Please remember to &quot;Mark as Answer&quot; the responses that resolved your issue. It is a common way to recognize those who have helped you, and makes it easier for other visitors to find the resolution later.

    • Marked as answer by Jim Wilcox AIM Wednesday, April 30, 2014 2:03 PM
    Wednesday, April 30, 2014 5:41 AM

All replies

  • Hi,

    You cannot do it above. Because the combobox binding to a same source, so if you change one Item on a Record, it changes all of them. You can edit your code like below:

    <Page
        x:Class="comboboxtest.MainPage"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:comboboxtest"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d">
        <Page.Resources>
            <!-- TODO: Delete this line if the key AppName is declared in App.xaml -->
            <x:String x:Key="AppName">My Application</x:String>
            
             
           
        </Page.Resources>
    
        <!--
            This grid acts as a root panel for the page that defines two rows:
            * Row 0 contains the back button and page title
            * Row 1 contains the rest of the page layout
        -->
        <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
            <Grid.ChildrenTransitions>
                <TransitionCollection>
                    <EntranceThemeTransition/>
                </TransitionCollection>
            </Grid.ChildrenTransitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="140"/>
                <RowDefinition Height="*"/>
            </Grid.RowDefinitions>
    
            <!-- Back button and page title -->
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="120"/>
                    <ColumnDefinition Width="*"/>
                </Grid.ColumnDefinitions>
                <Button x:Name="backButton" Margin="39,59,39,0" Command="{Binding NavigationHelper.GoBackCommand, ElementName=pageRoot}"
                            Style="{StaticResource NavigationBackButtonNormalStyle}"
                            VerticalAlignment="Top"
                            AutomationProperties.Name="Back"
                            AutomationProperties.AutomationId="BackButton"
                            AutomationProperties.ItemType="Navigation Button"/>
                <TextBlock x:Name="pageTitle" Text="{StaticResource AppName}" Style="{StaticResource HeaderTextBlockStyle}" Grid.Column="1" 
                            IsHitTestVisible="false" TextWrapping="NoWrap" VerticalAlignment="Bottom" Margin="0,0,30,40"/>
            </Grid>
            <ScrollViewer Grid.Row="1" Margin="25, 0, 0, 0" HorizontalScrollBarVisibility="Auto" >
                <StackPanel>
                    <Grid Margin="0,0,0,10">
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="300" />
                            <ColumnDefinition Width="110" />
                            <ColumnDefinition Width="*" />
                        </Grid.ColumnDefinitions>
                        <Grid.RowDefinitions>
                            <RowDefinition />
                            <RowDefinition />
                        </Grid.RowDefinitions>
                        <StackPanel Grid.ColumnSpan="5">
                            <TextBlock Text="Items" Style="{StaticResource SubheaderTextBlockStyle}" TextAlignment="Center"/>
                        </StackPanel>
                        <Border Grid.Column="0" Grid.Row="1">
                            <TextBlock Text="Room/Category" Style="{StaticResource BodyTextBlockStyle}" />
                        </Border>
                        <Border Grid.Column="1" Grid.Row="1">
                            <TextBlock Text="Square Feet" Style="{StaticResource BodyTextBlockStyle}" />
                        </Border>
                        <Border Grid.Column="2" Grid.Row="1">
                            <TextBlock Text="Note" Style="{StaticResource BodyTextBlockStyle}" />
                        </Border>
                    </Grid>
                    <ItemsControl Name="ItemGrid" Foreground="{ThemeResource ApplicationForegroundThemeBrush}"   >
                        
                        <ItemsControl.ItemTemplate>
                            <DataTemplate>
                                <Grid >
                                    <Grid.ColumnDefinitions>
                                        <ColumnDefinition Width="300" />
                                        <ColumnDefinition Width="110" />
                                        <ColumnDefinition Width="*" />
                                    </Grid.ColumnDefinitions>
                                    <ComboBox x:Name="cboCategory" Grid.Column="0"  
                                              ItemsSource="{Binding Categories}" SelectedIndex="{Binding selectIndex}"  >
                                        <ComboBox.ItemTemplate>
                                            <DataTemplate>
                                                <TextBlock Text="{Binding Path=Name }"/>
                                            </DataTemplate>
                                        </ComboBox.ItemTemplate>
                                    </ComboBox>
                                    <TextBox Grid.Column="1" Text="{Binding Area, Mode=TwoWay}" InputScope="Number" />
                                    <TextBox Grid.Column="3" Text="{Binding Note, Mode=TwoWay}"  />
                                </Grid>
                            </DataTemplate>
                        </ItemsControl.ItemTemplate>
                    </ItemsControl>
                    <AppBarButton Label="Add" Icon="Add" Click="AddItem_Click" />
                </StackPanel>
            </ScrollViewer>
    
        </Grid>
    
    </Page>
    
    namespace comboboxtest
    {
        /// <summary>
        /// An empty page that can be used on its own or navigated to within a Frame.
        /// </summary>
        ///
        class Category
        {
            public Category(string Name, string Area)
            {
                this.Name = Name;
                this.Area = Area;
            }
    
            public string Name { get; set; }
            public string Area { get; set; }
        }
    
        class Item
        {
            public Item(int selectIndex, ObservableCollection<Category> Categories, string Area, string Note)
            {
                this.Categories = Categories;
                this.Area = Area;
                this.Note = Note;
                this.selectIndex = selectIndex;
            }
            public ObservableCollection<Category> Categories { get; set; }
            public int selectIndex{get;set;}
            public string Area { get; set; }
            public string Note { get; set; }
        }
        
       public sealed partial class MainPage : Page
        {
           
           private ObservableCollection<Category> Categories;
           
            private ObservableCollection<Item> Items;
    
    
            public MainPage()
            {
                this.InitializeComponent();
                CreateCategories();
                CreateItems();
    
            }
            private void CreateCategories()
            {   
                Categories = new ObservableCollection<Category>();
                Categories.Add(new Category("Kitchen", "500"));
                Categories.Add(new Category("Dining Room", "501"));
                Categories.Add(new Category("Living Room", "502"));
                Categories.Add(new Category("Bedroom", "503"));
                Categories.Add(new Category("Garage", "504"));
               
                
            }
    
            private void CreateItems()
            {
                Items = new ObservableCollection<Item>();
                Items.Add(new Item(0,Categories ,"500", ""));
                Items.Add(new Item(1,Categories, "501", ""));
                Items.Add(new Item(2, Categories,"502", ""));
                Items.Add(new Item(3,Categories, "600", "Master"));
                Items.Add(new Item(3,Categories, "601", "Bedroom 1"));
                Items.Add(new Item(3,Categories, "602", "Bedroom 2"));
                Items.Add(new Item(3, Categories,"603", "Bedroom 3"));
                Items.Add(new Item(4, Categories,"504", ""));
    
                ItemGrid.ItemsSource = Items;
                
    
            }
    
            private void AddItem_Click(object sender, RoutedEventArgs e)
            {
               
            }
    
        }
    }
    

    For more information you can refer to the links below:

    http://msdn.microsoft.com/en-us/library/windows/apps/xaml/hh758318.aspx

    Best Wishes!


    We are trying to better understand customer views on social support experience, so your participation in this interview project would be greatly appreciated if you have time. Thanks for helping make community forums a great place.
    Click HERE to participate the survey. Thanks<br/> MSDN Community Support<br/> <br/> Please remember to &quot;Mark as Answer&quot; the responses that resolved your issue. It is a common way to recognize those who have helped you, and makes it easier for other visitors to find the resolution later.

    • Marked as answer by Jim Wilcox AIM Wednesday, April 30, 2014 2:03 PM
    Wednesday, April 30, 2014 5:41 AM
  • Hey Anne,

    I would like to give you a big cyber hug for showing me this.  From what I can tell, the trick is to create a separate list of categories for each instance of Item.  This seems like it could be a memory hog but it is the only explanation anyone has offered.  Thank you so much for your response.

    Jim


    Jim Wilcox

    Wednesday, April 30, 2014 2:03 PM