locked
How to bind UI which are present inside ObservableCollection inside ObservableCollection using Mvvm RRS feed

  • Question

  • I am under a situation where i have ObservableCollection of "tablegenerateModel" class which further contains ObservableCollection of "column_Data" class,and this "column_Data" class contains 3 UI elements which have to be binded to DataGrid containing three columns.  **I have written all my classes and xaml, You can even copy paste and verify my problem**.

    So my Model.cs is as follows : So my Model.cs is as follows :

        public class tablegenerateModel 
        {      
            public ObservableCollection<column_Data> Column_data_List { get; set; }
           
            public ICommand CreatColumnCommand
            {
                get
                {
                    if (_CreatColumnCommand == null)
                    {
                        _CreatColumnCommand = new RelayCommand(
                            param => vm = new ViewModel(this)
                        );
                    }
                    return _CreatColumnCommand;
                }
            }
        }
        public class column_Data
        {
            public string Column_Name { get; set; }        
        }
    }

    My View Model is:

        public class ViewModel : INotifyPropertyChanged
        {
            private ObservableCollection<tablegenerateModel> _lb_GlobalList;
            private ObservableCollection<column_Data> _Column_data_List;
            private ICommand _clickCommand;       
            public ObservableCollection<tablegenerateModel> lb_GlobalList
            {
                get
                {
                    return _lb_GlobalList;
                }
                set
                {
                    if (value != _lb_GlobalList)
                    {
                        _lb_GlobalList = value;
                        RaisePropertyChanged("lb_GlobalList");
                    }
                }
            }
    
            public ObservableCollection<column_Data> Column_data_List
            {
                get
                {
                    return _Column_data_List;
                }
                set
                {
                    if (value != _Column_data_List)
                    {
                        _Column_data_List = value;
                        RaisePropertyChanged("Column_data_List");
                    }
                }
            }
    
            public ICommand ClickCommand
            {
                get
                {
                    return _clickCommand ?? (_clickCommand = new CommandHandler(() => MyAction(), _canExecute));
                }
            }
            private bool _canExecute;
            public ViewModel()
            {
                _canExecute = true;
                lb_GlobalList= new ObservableCollection<tablegenerateModel>();
                for (int i = 1; i < 6; i++)
                {
                   //add data to _lb_GlobalList
                }        
            }
    
            public ViewModel(tablegenerateModel tablegenerateModels) //IT WILL BE CALLED WHEN BUTTON WILL BE CLICKED (see tablegenerateModel class ICommand for it)
            {
                tablegenerateModels.Column_data_List = new ObservableCollection<column_Data>();
                for (int i = 1; i < tablegenerateModels.NumberOfColumns; i++)
                {
                   add data to Column_data_List
                }           
                MessageBox.Show("The NumberOfColumns value is:" + tablegenerateModels.NumberOfColumns);
            }
        }
    }

    And View is :

    <Window x:Class="PyXgen.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:vM="clr-namespace:PyXgen"
            Title="MainWindow" Height="350" Width="525">
        <Window.DataContext>
            <vM:ViewModel></vM:ViewModel>
        </Window.DataContext>    
            <ListBox  Grid.Row="1" ItemsSource="{Binding lb_GlobalList}" ScrollViewer.VerticalScrollBarVisibility="Auto" BorderBrush="Gray" Margin="0,30,0,0" Grid.RowSpan="2" >
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <Grid>
                            <StackPanel>
                                <Grid Margin="0,2">                               
                                    <!--Starting label-->                                    
                                    <Grid  Margin="0,2" Grid.Row="5">                                                                   
                                        <DataGrid x:Name="gvSelectedCourses"  Grid.Column="1" HorizontalAlignment="Center" ItemsSource="{Binding Column_data_List , Mode=TwoWay}" AutoGenerateColumns="False" Width="450">
                                            <DataGrid.Columns>
                                                <DataGridTextColumn Header="Column Name" Binding="{Binding  ,Mode=TwoWay}" Width="150"/>
                                                <DataGridTextColumn Header="Data type" Binding="{Binding column_Datas.Data_type,Mode=TwoWay}"  Width="150"/>
                                                <DataGridTextColumn Header="Data Size" Binding="{Binding column_Datas.Size,Mode=TwoWay}"  Width="150"/>
                                            </DataGrid.Columns>
                                        </DataGrid>
                                    </Grid>
                                </Grid>
                            </StackPanel>
                        </Grid>
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>      
        </Grid>
    </Window>

    The button click to "_CreatColumnCommand" (in tableGenerateModel class) will invoke the ViewModel contructor passing "this" object to get the entered integer value to repeat DataGrid rows number of times the entered integer value (this integer value is entered in "NumberOfColumns" textbox of same class)

    Now problem is how to bind the "_Column_data_List" such that it will Add and Update the DataGrid's row (by the integer value which user have entered in textbox.

    Currently when i enter the integer value and create button then ViewModel contructor is called with MessageBox popup

     MessageBox.Show("The NumberOfColumns value is:" + tablegenerateModels.NumberOfColumns);

    How to bind the "_Column_data_List" to such that it will add the DataGrid's row (by the intger value which user have entered in textbox, in the snapshot above).

    Could some one help me please ?




    • Moved by Emile Supiot Tuesday, September 22, 2015 11:11 AM Written in English
    • Edited by iamxavxav Wednesday, September 23, 2015 4:19 AM
    Tuesday, September 22, 2015 10:08 AM

Answers

  • You are binding to one instance of the ViewModel class and adding column_Data objects to the Column_data_List collection of another instance of the ViewModel class. This won't work. Why are you creating a new ViewModel object when the CreatColumnCommand is executed?

    You should add the column_Data objects to the Column_data_List collection of the current tablegenerateModel that is displayed in the ListBox:

     public class tablegenerateModel {
        // public event PropertyChangedEventHandler PropertyChanged;
        public tablegenerateModel()
        {
          Column_data_List = new ObservableCollection<column_Data>();
        }
        public string Name { get; set; }
        public string Description { get; set; }
        public string Order { get; set; }
        public ObservableCollection<column_Data> Column_data_List { get; set; }
        public int NumberOfColumns { get; set; }
    
        private ICommand _CreatColumnCommand;   //THIS IS THE BUTTON EVENT GENERATED WHEN USER PRESS AN INTEGER VALUE IN "NumberOfColumns" AND PRESS "Create" button. So the "Column_data_List" will repeat that many times the value user entered
        ViewModel vm;
    
        public ICommand CreatColumnCommand
        {
          get
          {
            if (_CreatColumnCommand == null) {
              _CreatColumnCommand = new RelayCommand(
                  param => 
                  {
                    for (int i = 1; i < NumberOfColumns; i++) {
                      Column_data_List.Add(new column_Data()
                      {
                        Column_Name = "ColumnName" + i,
                        Data_Size = i,
                        Data_types = "Double" + i
                      });
                    }
                    MessageBox.Show("The NumberOfColumns value is:" + NumberOfColumns);
                  }
              );
            }
            return _CreatColumnCommand;
          }
        }
      }
    


    ...and bind directly to the properties of the  column_Data object:

    <DataGrid x:Name="gvSelectedCourses"  Grid.Column="1" HorizontalAlignment="Center" ItemsSource="{Binding Column_data_List , Mode=TwoWay}" AutoGenerateColumns="False" Width="450"
                                                  CanUserAddRows="False">
                                            <DataGrid.Columns>
                                                <DataGridTextColumn Header="Column Name" Binding="{Binding Path=Column_Name,Mode=TwoWay}" Width="150"/>
                                                <DataGridTextColumn Header="Data type" Binding="{Binding Data_type,Mode=TwoWay}"  Width="150"/>
                                                <DataGridTextColumn Header="Data Size" Binding="{Binding Size,Mode=TwoWay}"  Width="150"/>
                                            </DataGrid.Columns>
                                        </DataGrid>

    Hope that helps.


    Please remember to close your threads by marking helpful posts as answer and then start a new thread if you have a new question. Please don't ask several questions in the same thread.

    • Marked as answer by iamxavxav Wednesday, September 23, 2015 4:16 AM
    Tuesday, September 22, 2015 3:53 PM

All replies

  • You are binding to one instance of the ViewModel class and adding column_Data objects to the Column_data_List collection of another instance of the ViewModel class. This won't work. Why are you creating a new ViewModel object when the CreatColumnCommand is executed?

    You should add the column_Data objects to the Column_data_List collection of the current tablegenerateModel that is displayed in the ListBox:

     public class tablegenerateModel {
        // public event PropertyChangedEventHandler PropertyChanged;
        public tablegenerateModel()
        {
          Column_data_List = new ObservableCollection<column_Data>();
        }
        public string Name { get; set; }
        public string Description { get; set; }
        public string Order { get; set; }
        public ObservableCollection<column_Data> Column_data_List { get; set; }
        public int NumberOfColumns { get; set; }
    
        private ICommand _CreatColumnCommand;   //THIS IS THE BUTTON EVENT GENERATED WHEN USER PRESS AN INTEGER VALUE IN "NumberOfColumns" AND PRESS "Create" button. So the "Column_data_List" will repeat that many times the value user entered
        ViewModel vm;
    
        public ICommand CreatColumnCommand
        {
          get
          {
            if (_CreatColumnCommand == null) {
              _CreatColumnCommand = new RelayCommand(
                  param => 
                  {
                    for (int i = 1; i < NumberOfColumns; i++) {
                      Column_data_List.Add(new column_Data()
                      {
                        Column_Name = "ColumnName" + i,
                        Data_Size = i,
                        Data_types = "Double" + i
                      });
                    }
                    MessageBox.Show("The NumberOfColumns value is:" + NumberOfColumns);
                  }
              );
            }
            return _CreatColumnCommand;
          }
        }
      }
    


    ...and bind directly to the properties of the  column_Data object:

    <DataGrid x:Name="gvSelectedCourses"  Grid.Column="1" HorizontalAlignment="Center" ItemsSource="{Binding Column_data_List , Mode=TwoWay}" AutoGenerateColumns="False" Width="450"
                                                  CanUserAddRows="False">
                                            <DataGrid.Columns>
                                                <DataGridTextColumn Header="Column Name" Binding="{Binding Path=Column_Name,Mode=TwoWay}" Width="150"/>
                                                <DataGridTextColumn Header="Data type" Binding="{Binding Data_type,Mode=TwoWay}"  Width="150"/>
                                                <DataGridTextColumn Header="Data Size" Binding="{Binding Size,Mode=TwoWay}"  Width="150"/>
                                            </DataGrid.Columns>
                                        </DataGrid>

    Hope that helps.


    Please remember to close your threads by marking helpful posts as answer and then start a new thread if you have a new question. Please don't ask several questions in the same thread.

    • Marked as answer by iamxavxav Wednesday, September 23, 2015 4:16 AM
    Tuesday, September 22, 2015 3:53 PM
  • Thankyou so much.. But it always shows one grid by default . How it is generated. And how to remove that ?

    For example see : http://prntscr.com/8j9hd9 ? and Why  it do not show data in column 2 and column3 see this please http://prntscr.com/8j9i6y

    Tuesday, September 22, 2015 4:55 PM
  • Set the CanUserAddRows property of the DataGrid to False in the XAML markup as per my last reply.

    Also, you have misspelled some of the case-sensitive property names. Try this:

                                        <DataGrid x:Name="gvSelectedCourses"  Grid.Column="1" HorizontalAlignment="Center" ItemsSource="{Binding Column_data_List , Mode=TwoWay}" AutoGenerateColumns="False" Width="450"
                                                  CanUserAddRows="False">
                                            <DataGrid.Columns>
                                                <DataGridTextColumn Header="Column Name" Binding="{Binding Path=Column_Name,Mode=TwoWay}" Width="150"/>
                                                <DataGridTextColumn Header="Data type" Binding="{Binding Data_types,Mode=TwoWay}"  Width="150"/>
                                                <DataGridTextColumn Header="Data Size" Binding="{Binding Data_Size,Mode=TwoWay}"  Width="150"/>
                                            </DataGrid.Columns>
                                        </DataGrid>


    Hope that helps.

    Please remember to mark all helpful posts as answer when you close the thread. And please start a new thread if you have a new question.


    Tuesday, September 22, 2015 4:58 PM