0

I want to create custom property in ViewModel that implements INotifyPropertyChanged and bind that property to datagrid.

So the idea is (for example) i have FirstName and LastName string property in Model, and i want to make property FullName = FirstName + " " + LastName in ViewModel ( i dont want to do it in Model class!), and i want to bind this property in datagrid where i have FirstName, LastName and FullName columns

Here is my model class:

    class User
    {
        public int Id { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        //....
    }

ViewModel:

class ViewModelUsers : ViewModelBase
    {
        public ObservableCollection<Models.User> Users { get; private set; }

        string _FlllName;
        public string FullName
        {
            //...
        }



        public ViewModelUsers()
        {
            //....
        }

And my xaml:

        <DataGrid ItemsSource="{Binding Users}"
                  AutoGenerateColumns="False"
                  >
            <DataGrid.Columns>
                <DataGridTextColumn Header="First name" Binding="{Binding FirstName, Mode=OneWay}" Width="Auto"></DataGridTextColumn>
                <DataGridTextColumn Header="Last name" Binding="{Binding LastName, Mode=OneWay}" Width="Auto"></DataGridTextColumn>
                <DataGridTextColumn Header="Full name" Binding="{Binding Path=FullName, Mode=OneWay}" Width="Auto"></DataGridTextColumn>

This code is not complete, there is way more lines, but i hope you understand what i want to achieve. I am new to this c#-WPF (mvvm) so as i understood Model.cs should be "clean", and all logic should be in viewModel. ViewModel inherits ViewModelBase which implements INotifyPropertyChanged.

As i said i am new to this, i am looking for best practice implementing MVVM and learning as i go.

edis1409
  • 149
  • 1
  • 7
  • why would you wan't it in the UsersViewModel when it is something that refers to a specific User And sounds like it should be in the model. ? – eran otzap Jul 28 '19 at 13:26
  • well it was in Model, but i needed to implement propertychange there so it would work, and i read [here](https://stackoverflow.com/questions/772214/in-mvvm-should-the-viewmodel-or-model-implement-inotifypropertychanged) that implementing that in Model is not good practice. – edis1409 Jul 28 '19 at 13:30
  • It's fine. There is not reasoning to remove property changed events from models that are in use in Bindings. I believe that the models probably described as Poco elements received from the server should be agnostic to their consumers. If that becomes an issue some applications might have a UIModel class which wraps these pocos. – eran otzap Jul 28 '19 at 13:52
  • The question "should the ViewModel or Model implement INotifyPropertyChanged" that you are referring to should be closed as primarily opinion based. There is nothing in MVVM that forbids that a model class implements INotifyPropertyChanged. It's all just personal preferences. – Clemens Jul 28 '19 at 14:21
  • Okay guys, so it's totally fine that i implement INotifyPropertyChanged in model, i know that. In the future i will use EF to create database from models, and i have to exclude come properties ( i don't want fullname in db). So i thought Model class should be bare minimum of what is in database, as i said, i am beginner, and i read multiple tutorials, each one says different story, can't wrap my head of what is the best approach here. Thanks for your help, i will go with your advice. – edis1409 Jul 28 '19 at 14:33

1 Answers1

0

When you only need the fullname for display you can use a MultiBinding together with a IMultiValueConverter to concatenate the values.

<DataGrid ItemsSource="{Binding Users}"
          AutoGenerateColumns="False">
  <DataGrid.Columns>
    <DataGridTextColumn Header="First name"
                        Binding="{Binding FirstName}" />
    <DataGridTextColumn Header="Last name"
                        Binding="{Binding LastName}" />
    <DataGridTextColumn Header="Full name">
      <DataGridTextColumn.Binding>
        <MultiBinding Converter="{StaticResource NameMultiValueConverter}">
          <Binding Path="FirstName" />
          <Binding Path="LastName" />
        </MultiBinding>
      </DataGridTextColumn.Binding>
    </DataGridTextColumn>
  </DataGrid.Columns>
</DataGrid>

The IMultiValueConverter that concatenates the firstname and lastname
of each data item:

public class NameMultiValueConverter : IMultiValueConverter
{
  public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
  {
    return String.Format("{0} {1}", values[0], values[1]);
  }

  /// <inheritdoc />
  public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
  {
    throw new NotImplementedException();
  }
}

Regarding MVVM I'd like to say that the model is not supposed to be 'clean'.
By definition the model has no relation or association to the view. The view model decouples the view from the model. The model contains business entities and business logic.

If you like to avoid any dependency from view to model, you should consider to wrap your model Entities into view model data structures that the view can bind to or template. If you are strictly following MVVM and are concerned about the dependencies (on class or assembly level) you wouldn't use data business models in your view.

A model of course can implement the interface INotifyPropertyChanged (it only exposes an event PropertyChanged) but it should not be the source for a binding that targets the view.
Although you can implement InotifyPropertyChanged on a model it might be more convenient for observers to add a dedicated event to the model.

So instead of raising the meaningless PropertyChanged event (e.g. if calculation completes and the value of CalculationResult is set), it would improve event handling and code readability to raise a CalculationCompleted event. The handler of PropertyChanged would always need to filter for the right property by name and gets invoked on every property change. A specialized event is only raised and their handlers only invoked on the specific event.

BionicCode
  • 1
  • 4
  • 28
  • 44