Search code examples
c#wpfmvvmdatagridcomboboxcolumn

ComboBoxColumn Issue with WPF-MVVM datagrid


I would like to add a ComboBox column in my dataGrid "Product" with the list of Suppliers. But nothing I did works. What is the good way to bind this column?

The Models:

 public partial class foodSupplier
    {
        public foodSupplier()
        {
            this.products = new HashSet<product>();
        }

        public int idfoodSupplier { get; set; }
        public string supplier { get; set; }

        public virtual ICollection<product> products { get; set; }
    }

public partial class product
    {
        public int idproduct { get; set; }
        public string @ref { get; set; }
        public int supplier { get; set; }
        public string refsup { get; set; }
        public string description { get; set; }
        public int MOQ { get; set; }
        public int unit { get; set; }
        public decimal priceMOQ { get; set; }

        public virtual foodSupplier foodSupplier { get; set; }
        public virtual unit unit1 { get; set; }
    }

Here my command base (ViewModel):

public class CommandBase<T> :INotifyPropertyChanged
        {
            #region "INotifyPropertyChanged members"

            public event PropertyChangedEventHandler PropertyChanged;
            //This routine is called each time a property value has been set. 
            //This will //cause an event to notify WPF via data-binding that a change has occurred. 
            protected void OnPropertyChanged(string propertyName)
            {
                var handler = PropertyChanged;
                if (handler != null)
                {
                    handler(this, new PropertyChangedEventArgs(propertyName));
                }
            }

            #endregion

            private ObservableCollection<T> collection;
            public ObservableCollection<T> Collection
            {
                get
                {
                    if (collection == null)
                    {
                        collection = new ObservableCollection<T>();
                    }
                    return collection;
                }
                set { collection = value; OnPropertyChanged("Collection");}
            }


            private ICommand getCommand;
            private ICommand saveCommand;
            private ICommand removeCommand;

            public ICommand GetCommand
            {
                get
                {
                    return getCommand ?? (getCommand = new RelayCommand(param => Get(),param=>CanGet()));
                }
            }
            protected virtual bool CanGet()
            {
                return true;
            }

            public ICommand SaveCommand
            {
                get
                {
                    return saveCommand ?? (saveCommand = new RelayCommand(param => Save()));
                }
            }
            protected virtual bool CanSave()
            {
                return true;
            }

            public ICommand DeleteCommand
            {
                get
                {
                    return removeCommand ?? (removeCommand = new RelayCommand(param=>Delete()));
                }
            }
            protected virtual bool CanDelete()
            {
                return true;
            }
    }

Here the ProductViewModel:

    public class ProductViewModel : CommandBase<product>
        {
            public Context ctx = new Context();

            protected override void Get()
            {
                ctx.products.ToList().ForEach(supplier => ctx.products.Local.Add(supplier));
                Collection = ctx.products.Local;
            }
            protected override bool CanGet()
            {
                return true;
            }
            protected override void Save()
            {
                foreach (product item in Collection)
                {
                    if (ctx.Entry(item).State == System.Data.Entity.EntityState.Added)
                    {
                        ctx.products.Add(item);
                    }
                }
                ctx.SaveChanges();
            }
        }
}

Here the SupplierViewModel:

public class SupplierViewModel : CommandBase<foodSupplier>
    {
        public Context ctx = new Context();

        protected override void Get()
        {
            ctx.foodSuppliers.ToList().ForEach(supplier => ctx.foodSuppliers.Local.Add(supplier));
            Collection = ctx.foodSuppliers.Local;
        }
        protected override bool CanGet()
        {
            return true;
        }
        protected override void Save()
        {
            foreach (foodSupplier item in Collection)
            {
                if (ctx.Entry(item).State == System.Data.Entity.EntityState.Added)
                {
                    ctx.foodSuppliers.Add(item);
                }
            }
            ctx.SaveChanges();
        }
}

Here the view:

<Page.Resources>
        <vm:ProductViewModel x:Key="product"/>
        <vm:SupplierViewModel x:Key="supplier"/>
    </Page.Resources>

    <Grid DataContext="{Binding Source={StaticResource product}}">
        <StackPanel Grid.ColumnSpan="2" Margin="0,0,58,0">
            <Button  x:Name="BtnDelete"
                Content="Delete" 
                Command="{Binding DeleteCommand}"/>
            <Button  x:Name="BtnAdd" 
                Content="Save" 
                Command="{Binding SaveCommand}"/>
            <DataGrid x:Name="dataGrid" Margin="5"  ItemsSource="{Binding Collection}" AutoGenerateColumns="False">
                <DataGrid.Columns>
                    <DataGridTextColumn Header="id" Binding="{Binding idproduct, UpdateSourceTrigger=PropertyChanged}" Visibility="Hidden"/>
                    <DataGridTextColumn Header="Ref FTHK" Binding="{Binding ref, UpdateSourceTrigger=PropertyChanged}" />
          <DataGridComboBoxColumn Header="Supplier" ItemsSource="{Binding Collection, Source={StaticResource supplier}}????" DisplayMemberPath="supplier" SelectedValueBinding="{Binding ????}" SelectedValuePath="idSupplier"/>
                    <DataGridTextColumn Header="Ref Supplier" Binding="{Binding refsup, UpdateSourceTrigger=PropertyChanged}"/>
                    <DataGridTextColumn Header="Description" Binding="{Binding description, UpdateSourceTrigger=PropertyChanged}"/>
                    <DataGridTextColumn Header="MOQ" Binding="{Binding MOQ, UpdateSourceTrigger=PropertyChanged}"/>
                    <DataGridTextColumn Header="Unit" Binding="{Binding unit, UpdateSourceTrigger=PropertyChanged}"/>
                    <DataGridTextColumn Header="Prix/MOQ" Binding="{Binding priceMOQ, UpdateSourceTrigger=PropertyChanged}"/>
                </DataGrid.Columns>
                <i:Interaction.Triggers>
                    <i:EventTrigger EventName="Loaded">
                        <i:InvokeCommandAction Command="{Binding GetCommand}" />
                    </i:EventTrigger>
                </i:Interaction.Triggers>
            </DataGrid>
        </StackPanel>
    </Grid>

Solution

  • you have to load the SupplierCollection same as you did in loading products. I will prefer the on demand concept. Somthing like the code below

    public class CommandBase<T> : INotifyPropertyChanged
    {
        #region "INotifyPropertyChanged members"
    
        public event PropertyChangedEventHandler PropertyChanged;
        //This routine is called each time a property value has been set. 
        //This will //cause an event to notify WPF via data-binding that a change has occurred. 
        protected void OnPropertyChanged(string propertyName)
        {
            var handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    
        #endregion
    
        private ObservableCollection<T> collection;
        public ObservableCollection<T> Collection
        {
            get
            {
                if (collection == null)
                {
                    Get();
                }
                return collection;
            }
            set { collection = value; OnPropertyChanged("Collection"); }
        }
    
    
        private ICommand getCommand;
        private ICommand saveCommand;
        private ICommand removeCommand;
    
        public ICommand GetCommand
        {
            get
            {
                return getCommand ?? (getCommand = new RelayCommand(Get, CanGet));
            }
        }
        protected virtual bool CanGet()
        {
            return true;
        }
    
        protected virtual void Get()
        {
            //return true;
        }
    
        //public ICommand SaveCommand
        //{
        //    get
        //    {
        //        return saveCommand ?? (saveCommand = new RelayCommand(param => Save()));
        //    }
        //}
        protected virtual bool CanSave()
        {
            return true;
        }
    
        //public ICommand DeleteCommand
        //{
        //    get
        //    {
        //        return removeCommand ?? (removeCommand = new RelayCommand(param => Delete()));
        //    }
        //}
        protected virtual bool CanDelete()
        {
            return true;
        }
    }
    

    Now you view will look like.

    <Page.Resources>
        <vm:ProductViewModel x:Key="product"/>
        <vm:SupplierViewModel x:Key="supplier"/>
        <DataTemplate x:Key="SupplierDataTemplate">
            <TextBlock Text="{Binding supplier}"/>
        </DataTemplate>
    </Page.Resources>
    <Grid>
        <StackPanel>
            <Grid DataContext="{Binding Source={StaticResource product}}" x:Name="ProductGrid">
                <StackPanel Grid.ColumnSpan="2" Margin="0,0,58,0">
                    <Button  x:Name="BtnDelete"
                Content="Delete" 
                Command="{Binding DeleteCommand}"/>
                    <Button  x:Name="BtnAdd" 
                Content="Save" 
                Command="{Binding SaveCommand}"/>
                    <DataGrid x:Name="dataGrid" Margin="5"  ItemsSource="{Binding Collection}" AutoGenerateColumns="False">
                        <DataGrid.Columns>
                            <DataGridTextColumn Header="id" Binding="{Binding idproduct, UpdateSourceTrigger=PropertyChanged}" Visibility="Hidden"/>
                            <DataGridTextColumn Header="Ref FTHK" Binding="{Binding ref, UpdateSourceTrigger=PropertyChanged}" />
                            <DataGridComboBoxColumn Header="Supplier" ItemsSource="{Binding Collection, Source={StaticResource supplier}}"
                                                    DisplayMemberPath="supplier" 
                                                     SelectedValueBinding="{Binding supplier}"/>
                            <DataGridTextColumn Header="Ref Supplier" Binding="{Binding refsup, UpdateSourceTrigger=PropertyChanged}"/>
                            <DataGridTextColumn Header="Description" Binding="{Binding description, UpdateSourceTrigger=PropertyChanged}"/>
                            <DataGridTextColumn Header="MOQ" Binding="{Binding MOQ, UpdateSourceTrigger=PropertyChanged}"/>
                            <DataGridTextColumn Header="Unit" Binding="{Binding unit, UpdateSourceTrigger=PropertyChanged}"/>
                            <DataGridTextColumn Header="Prix/MOQ" Binding="{Binding priceMOQ, UpdateSourceTrigger=PropertyChanged}"/>
                        </DataGrid.Columns>
                        <!--<i:Interaction.Triggers>
                            <i:EventTrigger EventName="Loaded">
                                <i:InvokeCommandAction Command="{Binding GetCommand}" />
                            </i:EventTrigger>
                        </i:Interaction.Triggers>-->
                    </DataGrid>
                </StackPanel>
            </Grid>
    
        </StackPanel>
    </Grid>
    

    Hope it helps