0

I'm using MVVM pattern, developing my WPF application. I also use Entity Framework as ORM. Here're my models (EF):

public class User : BaseEntity
{
    public string Name { get; set; }

    public int OfficeId { get; set; }
    public Office Office { get; set; }
}

public class Office : BaseEntity
{
    public string Name { get; set; }

    public int DepartmentId { get; set; }
    public Department Department { get; set; }

    public virtual ICollection<User> Users { get; set; }
}

public class Department : BaseEntity
{
    public string Name { get; set; }

    public virtual ICollection<Office> Offices { get; set; }
}

Let's assume, that I've got an instance of User class from my context:

   var userInstance = context.Get<User>().Single(user => user.ID == 1);

Now I'd like to pass this instance to my View to make some changes for concrete user (called, for example, UserEditView), so I have to create a UserModel class to deal with User data according to MVVM. So, here's what I think I have to write in my code:

public class UserModel : ObservableObject
{
    private User user;

    public string Office Office
    {
        get
        {
            return this.user.Office.Name;
        }
        set
        {
            //what shoud I write Here??
            if(value != user.Office)
            {
                 user.Office=value;
                 OnPropertyChanged("Office");
            }
        }
    }
}

I'm really frustrated! How should I deal with that? There're thousands of examples, but they are so simple. I'm wondering what should I do to have a ComboBox in my EditView with a list of all Offices, existing in my DB. And list of Offices should depend on another one Combobox, which contains a list of Departments.

  • But where should I get this lists from?
  • Should I pass a collection from my UserModel? Or what?
  • Can anybody give me a simple example about how to do this correctly?

PS: Of course, I know couple ways to implement such behaviour, but in that case my code seems to be ugly and not maintainable. Please, help. Thanks a lot!

klutch1991
  • 229
  • 2
  • 16
  • One thing you are missing is implementing INotifyPropertyChanged for each object you want to bind the XAML too. Start here http://stackoverflow.com/questions/1315621/implementing-inotifypropertychanged-does-a-better-way-exist – kenny Feb 04 '16 at 09:06
  • @Kenny, I know about INPC, of course. But that's not the point.. – klutch1991 Feb 04 '16 at 09:08
  • 1
    Typically for me, I would use IOC to inject in your data store(s) into the constructor. http://stackoverflow.com/a/25524753/3225 – kenny Feb 04 '16 at 09:18

1 Answers1

1

this is depends on your DB architecture. Here is some common suggestion (but there can be a lot of others).

  1. Don't panic - you have a correct question.
  2. Create the view model set it to be a main view model of your window.
  3. In your view model create two collections Users (containing UserModels) and Departments (containing DepartmentMode), since you want to change offices each time you re-select department, you don't need the Offices collection in main view model.
  4. Pull each collection data from your data base.
  5. Implement each model with INPC.
  6. Take in account the WPF MVVM best practices.
  7. Apply a correct bindings.
  8. And be happy - you are a programmer.

Updates - 1

XAML code

    <Grid x:Name="LayoutRoot">
    <Grid.DataContext>
        <someBindingExampleSoHelpAttempt:MainViewModel/>
    </Grid.DataContext>
    <ListView ItemsSource="{Binding Users}">
        <ListView.ItemContainerStyle>
            <Style TargetType="ListViewItem">
                <Setter Property="ContentTemplate">
                    <Setter.Value>
                        <DataTemplate DataType="someBindingExampleSoHelpAttempt:UserModel">
                            <Grid>
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition Width="100"></ColumnDefinition>
                                    <ColumnDefinition Width="200"></ColumnDefinition>
                                    <ColumnDefinition Width="50"></ColumnDefinition>
                                    <ColumnDefinition Width="50"></ColumnDefinition>
                                </Grid.ColumnDefinitions>
                                <TextBox Grid.Column="0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"  Text="{Binding Name, UpdateSourceTrigger=LostFocus, Mode=TwoWay}"/>
                                <TextBox Grid.Column="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"  Text="{Binding LastName, UpdateSourceTrigger=LostFocus, Mode=TwoWay}"/>
                                <ComboBox Grid.Column="2"
                                          IsTextSearchEnabled="True"     
                                          IsTextSearchCaseSensitive="False"     
                                          StaysOpenOnEdit="True"    
                                          TextSearch.TextPath="DepartmentName"
                                          ItemsSource="{Binding RelativeSource={RelativeSource Mode=FindAncestor, 
                                    AncestorType={x:Type ListView}}, Path=DataContext.Departments}"    
                                          SelectedValue="{Binding Department}"     
                                          DisplayMemberPath="DepartmentName" 
                                          IsEditable="True"
                                          HorizontalAlignment="Stretch"     
                                          VerticalAlignment="Stretch"/>
                                <ComboBox Grid.Column="3"
                                          IsTextSearchEnabled="True"     
                                          IsTextSearchCaseSensitive="False"     
                                          StaysOpenOnEdit="True"
                                          IsEditable="True"
                                          TextSearch.TextPath="OfficeName"
                                          ItemsSource="{Binding OfficesCollection}"    
                                          SelectedValue="{Binding Office}"     
                                          DisplayMemberPath="OfficeName"      
                                          HorizontalAlignment="Stretch"     
                                          VerticalAlignment="Stretch"/>
                            </Grid>

                        </DataTemplate>
                    </Setter.Value>
                </Setter>
            </Style>
        </ListView.ItemContainerStyle>
    </ListView></Grid>

VM and models

    public class MainViewModel:BaseObservableObject
{
    private DepartmentModel _selectedDepartment;
    private OfficeModel _selectedOffice;

    public MainViewModel()
    {
        Dal = new DataLayer();
        Users = new ObservableCollection<UserModel>();
        Departments = new ObservableCollection<DepartmentModel>(Dal.GetAllDepartments());
        InitUsersCollection();
    }

    private void InitUsersCollection()
    {
        if(Departments == null) return;
        Departments.ToList().ForEach(model =>
        {
            model.Offices.ToList().ForEach(officeModel =>
            {
                if (officeModel.Users == null) return;
                officeModel.Users.ToList().ForEach(userModel => Users.Add(userModel));
            });
        });
    }

    public ObservableCollection<UserModel> Users { get; set; }

    public ObservableCollection<DepartmentModel> Departments { get; set; }

    private DataLayer Dal { get; set; }

}

public class DataLayer
{
    public List<DepartmentModel> GetAllDepartments()
    {
        //pull and map your using your DB service
        //For example:
        return new List<DepartmentModel>
        {
            new DepartmentModel
            {
                DepartmentId = 1,
                DepartmentName = "A",
                Offices = new ObservableCollection<OfficeModel>
                {
                    new OfficeModel
                    {
                        DepartmentId = 1,
                        OfficeName = "AA",
                        Users = new ObservableCollection<UserModel>(new List<UserModel>
                        {
                            new UserModel {Name = "Avicenna", LastName = "Abu Ali Abdulloh Ibn-Sino"},
                            new UserModel {Name = "Omar", LastName = "Khayyam"},
                            new UserModel {Name = "RAMBAM", LastName = "Moshe ben Maimon"}
                        })
                    },
                    new OfficeModel 
                    {

                        DepartmentId = 1, 
                        OfficeName = "AB", 
                        Users = new ObservableCollection<UserModel>(new List<UserModel>
                        {
                            new UserModel {Name = "Leo", LastName = "Tolstoi"},
                            new UserModel {Name = "Anton", LastName = "Chekhov"},
                        })},
                }
            },
            new DepartmentModel
            {
                DepartmentId = 2,
                DepartmentName = "B",
                Offices = new ObservableCollection<OfficeModel>
                {
                    new OfficeModel
                    {
                        DepartmentId = 2, OfficeName = "BA",
                        Users = new ObservableCollection<UserModel>(new List<UserModel>
                        {
                            new UserModel {Name = "B", LastName = "O"},
                            new UserModel {Name = "B", LastName = "N"},
                        }),
                    },
                    new OfficeModel
                    {
                        DepartmentId = 2, OfficeName = "BB",
                        Users = new ObservableCollection<UserModel>(new List<UserModel>
                        {
                            new UserModel {Name = "John", LastName = "Walker"},
                            new UserModel {Name = "Gregory", LastName = "Rasputin"},
                        }),
                    },
                }
            },
            new DepartmentModel
            {
                DepartmentId = 3,
                DepartmentName = "C",
                Offices = new ObservableCollection<OfficeModel>
                {
                    new OfficeModel {DepartmentId = 3, OfficeName = "CA"},
                    new OfficeModel {DepartmentId = 3, OfficeName = "CB"},
                    new OfficeModel {DepartmentId = 3, OfficeName = "CC"}
                }
            }
        };
    }
}

public class OfficeModel:BaseObservableObject
{
    private int _departmentModel;
    private string _officeName;
    private DepartmentModel _department;
    private ObservableCollection<UserModel> _users;

    public int DepartmentId
    {
        get { return _departmentModel; }
        set
        {
            _departmentModel = value;
            OnPropertyChanged();
        }
    }

    public DepartmentModel Department
    {
        get { return _department; }
        set
        {
            _department = value;
            OnPropertyChanged();
        }
    }

    public string OfficeName
    {
        get { return _officeName; }
        set
        {
            _officeName = value;
            OnPropertyChanged();
        }
    }

    public ObservableCollection<UserModel> Users
    {
        get { return _users; }
        set
        {
            _users = value;
            OnPropertyChanged(()=>Users);
        }
    }
}

public class DepartmentModel:BaseObservableObject
{

    private string _departmentName;

    public string DepartmentName
    {
        get { return _departmentName; }
        set
        {
            _departmentName = value;
            OnPropertyChanged();
        }
    }

    public int DepartmentId { get; set; }

    public ObservableCollection<OfficeModel> Offices { get; set; }
}

public class UserModel:BaseObservableObject
{
    private string _name;
    private string _lastName;
    private DepartmentModel _department;
    private OfficeModel _office;

    public string Name
    {
        get { return _name; }
        set
        {
            _name = value;
            OnPropertyChanged();
        }
    }

    public string LastName
    {
        get { return _lastName; }
        set
        {
            _lastName = value;
            OnPropertyChanged();
        }
    }

    public DepartmentModel Department
    {
        get { return _department; }
        set
        {
            _department = value;
            OnPropertyChanged();
            OnPropertyChanged(()=>OfficesCollection);
        }
    }

    public ObservableCollection<OfficeModel> OfficesCollection
    {
        get { return Department.Offices; }
    }

    public OfficeModel Office
    {
        get { return _office; }
        set
        {
            _office = value;
            OnPropertyChanged();
        }
    }
}

/// <summary>
/// implements the INotifyPropertyChanged (.net 4.5)
/// </summary>
public class BaseObservableObject : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        var handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }

    protected virtual void OnPropertyChanged<T>(Expression<Func<T>> raiser)
    {
        var propName = ((MemberExpression)raiser.Body).Member.Name;
        OnPropertyChanged(propName);
    }

    protected bool Set<T>(ref T field, T value, [CallerMemberName] string name = null)
    {
        if (!EqualityComparer<T>.Default.Equals(field, value))
        {
            field = value;
            OnPropertyChanged(name);
            return true;
        }
        return false;
    }
}

But please take in account that this is only one of hundreds ways to do that. The SO here if you will need the code example. Regards.

Ilan
  • 2,762
  • 1
  • 13
  • 24