0
votes

I am new to WPF, and currently I am building an master-details app using Entity framework 6 (code first) based on MVVM.

Here is the UI.

enter image description here

Master datagrid ---Category

Details datagrid --- Product

Functions I need:

  1. Modify , Add, Remove "Category" item or "Product" item

  2. Through "save" button to save to database.

  3. Category filter to control displayed items in master datagrid.

I am binding master datagrid to icollectionview "Categrories" in my view model.

<Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:WPFwithEFSampleCodeFirst" mc:Ignorable="d" x:Class="WPFwithEFSampleCodeFirst.MainWindow"
    Title="MainWindow" Height="352.134" Width="585.53" Loaded="Window_Loaded">

<Grid  Margin="0,0,0,-3">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="0*"/>
        <ColumnDefinition Width="77*"/>
        <ColumnDefinition Width="25*"/>
    </Grid.ColumnDefinitions>
    <Button Content="Save"  Command="{Binding SaveCmd}" Grid.Column="1" HorizontalAlignment="Left" Margin="425,137,0,0" VerticalAlignment="Top" Width="75" Click="Button_Click" Grid.ColumnSpan="2"/>
    <DataGrid x:Name="MasterGrid" SelectedItem="{Binding SelectedCategory}" ItemsSource="{Binding Categories}" Grid.ColumnSpan="2"   AutoGenerateColumns="False" HorizontalAlignment="Left" Margin="32,10,0,0" VerticalAlignment="Top" Height="124" Width="330" >
        <DataGrid.Columns>
            <DataGridTextColumn  Width="SizeToHeader" Header="Category Id" Binding="{Binding Path = CategoryId}"/>
            <DataGridTextColumn  Width="SizeToHeader" Header="Category Name" Binding="{Binding Path = Name}"/>
        </DataGrid.Columns>
    </DataGrid>

    <DataGrid ItemsSource="{Binding SelectedCategoryProducts}" Grid.ColumnSpan="2" AutoGenerateColumns="False" HorizontalAlignment="Left" Margin="32,153,0,0" VerticalAlignment="Top" Height="146" Width="330">
        <DataGrid.Columns>
            <DataGridTextColumn  Binding="{Binding CategoryId}" Header="Category Id" Width="SizeToHeader"/>
            <DataGridTextColumn Binding="{Binding Name}" Header="Product Name" Width="SizeToHeader"/>
            <DataGridTextColumn  Binding="{Binding ProductId}" Header="Product Id" Width="SizeToHeader"/>
        </DataGrid.Columns>
    </DataGrid>
    <ComboBox ItemsSource ="{Binding DistinctCategoryName }" SelectedItem="{Binding SelectedCategoryName , Mode = TwoWay }"  IsSynchronizedWithCurrentItem="True" Grid.Column="1" HorizontalAlignment="Left" Margin="398,37,0,0" VerticalAlignment="Top" Width="120" Grid.ColumnSpan="2"/>
    <Label Content="Category Name filter" Grid.Column="1" HorizontalAlignment="Left" Margin="398,6,0,0" VerticalAlignment="Top" Grid.ColumnSpan="2"/>
</Grid>

ViewModel:

    class MainWindowViewModel:INotifyPropertyChanged
{
    ProductContext context = new ProductContext();

    public MainWindowViewModel()
    {
        IList<Category> categories = GetCategories();
        _categoryView = CollectionViewSource.GetDefaultView(categories);

        DistinctCategoryName = GetDistinctCategoryName();
        _saveCmd = new RelayCommand(Save, CanSave);

    }

    public IList<Category> GetCategories()
    {
        return context.Categories.ToList();
    }

    private ICollectionView _categoryView;
    public ICollectionView Categories
    {
        get { return _categoryView; }
    }

    private string _selectedCategoryName;
    public string SelectedCategoryName
    {
        get { return _selectedCategoryName; }
        set
        {
            _selectedCategoryName = value;

            _categoryView.Filter = new Predicate<object>(GetFilteredView);
            _categoryView.Refresh();

            OnPropertyChanged("SelectedCategoryName");
        }
    }



    private IList<string> _distinctCategoryName;

    public IList<string> DistinctCategoryName
    {
        get { return _distinctCategoryName; }
        set 
        { 
            _distinctCategoryName = value;
            OnPropertyChanged("DistinctCategoryName");
        }

    }

    private List<string> GetDistinctCategoryName()
    {
        List<string> distCateName;
        List<Category> catelist = context.Categories.ToList();
        distCateName = catelist.Select(e => e.Name).Distinct().ToList();

        return distCateName;
    }


    private ICommand _saveCmd;
    public ICommand SaveCmd { get { return _saveCmd; } }
    public void Save(object obj)
    {   
        foreach (var product in context.Products.Local.ToList())
        {
            if (product.Category == null)
            {
                context.Products.Remove(product);
            }
        }


        context.SaveChanges();// What can I do Here to save the category change???

        //DistinctCategoryName = GetDistinctCategoryName();
    }

    public bool CanSave(object obj)
    {

        return true;
    }

    public bool GetFilteredView(object sourceObject)
    {
        Category cate = sourceObject as Category;
        if (cate.Name == _selectedCategoryName)
        {
            return true;
        }
        return false;
    }

    private Category _selectedCategory;
    public Category SelectedCategory
    {
        get { return _selectedCategory; }
        set
        {
            _selectedCategory = value;
            OnPropertyChanged("SelectedCategory");
            OnPropertyChanged("SelectedCategoryProducts");
        }
    }

    public ObservableCollection<Product> SelectedCategoryProducts
    {
        get
        {
            if (_selectedCategory == null) return null;

            return _selectedCategory.Products;
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    public void OnPropertyChanged(string propertyName)
    {
        //Fire the PropertyChanged event in case somebody subscribed to it
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}

Issues:

I am binding a ICollectionView to Master Datagrid because of the filter feature. The add(add a new row) or remove(hit Delete key) category action cannot be saved to database. (I think it is because I am working on the VIEW instead of actual source.) So what is the proper way to add/remove item through a icollectionview and reflect to database(source)? Or I shouldn't use ICollectionView?

1

1 Answers

0
votes

Inherit your viewmodel class from the BindableBase class.

Create a property and a private member for categories.

private ObservableCollection<DataClass> categories = new ObservableCollection<DataClass>();

public ObservableCollection<DataClass> Categories
{
get { return categories; }
set { SetProperty(ref categories, value); }
}

Now bind the source to Categories. Each time you add a new item, the categories is updated. On save command you can save the entire list or the current selected item to db. The thing is you can get the property changed event of the class or the property in any other view/view model and use that event to save your data to database too.