Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Updating/using a variable from another ViewModel

Tags:

c#

mvvm

wpf

In order to practice WPF+MVVM, I've decided to write a school program.
So far I have the class and student classes.
There is also the basic view model, ViewModelBase.cs, which derives from INPC, and instances class - "StudentClass".

All other viewmodels derive from viewmodelbase.

The thing is I have a page/window for each "feature", (for example; viewing all of the students, adding a student, deleting a student etc...) and I want to be able to access that class from anywhere in the application since all of the info is stored there basically.

In order to stay organized, each "feature" has its own viewmodel (StudentListViewModel.cs, AddStudentViewModel.cs...).

I tried to access the class from the viewmodels, which just caused a case where the class updates in one window and not in the other.

When I set the viewmodels of the "student list" window, and "add a student" window, the list are sync-ed, obviously. So I guess the thing is the class instance gets duplicated or something like that.

I've uploaded the project for reference: http://www.mediafire.com/?n70c7caqex6be1g

Hopefully someone can help me.

I tried looking on google for answers, but all of the answers mentions "Messengers" and "events" which are related to frameworks. And since I didn't use a framework for this project, those solutions doesn't apply for me.

Another solution was to pass an instance of the viewmodel to another, but none of my viewmodels call or instantiate another viewmodel.

Update:

XAML in StudentList.xaml: (This is a usercontrol because I'm using a template called ModernUI)

<UserControl x:Class="ClassStudent.StudentList"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         mc:Ignorable="d" 
         d:DesignHeight="300" d:DesignWidth="300" DataContext="{StaticResource StudentListViewModel}">
<Grid Style="{StaticResource ContentRoot}">
    <ListView ItemsSource="{Binding StudentClass.StudentList}" >
        <ListView.View>
            <GridView>
                <GridViewColumn Header="Name" DisplayMemberBinding="{Binding Name}" Width="60"/>
                <GridViewColumn  Header="Age" DisplayMemberBinding="{Binding LastName}" Width="60"/>
            </GridView>
        </ListView.View>
    </ListView>
</Grid>

XAML in AddStudent.xaml:

<UserControl x:Class="ClassStudent.AddStudent"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         mc:Ignorable="d" 
         d:DesignHeight="300" d:DesignWidth="300">
<Grid Style="{StaticResource ContentRoot}"  DataContext="{StaticResource AddStudentViewModel}">
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto" />
        <RowDefinition Height="Auto" />
        <RowDefinition Height="Auto" />
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*" />
        <ColumnDefinition Width="*" />
    </Grid.ColumnDefinitions>
    <Label Content="Name"/>
    <TextBox Text="{Binding Student.Name, Mode=TwoWay}" Grid.Column="1"/>
    <Label Content="Last Name" Grid.Row="1"/>
    <TextBox Text="{Binding Student.LastName, Mode=TwoWay}" Grid.Row="1" Grid.Column="1"/>

    <Button Command="{Binding AddStudent}" Content="Add Student!" Grid.Row="2" />
</Grid>

AddStudentViewModel.cs:

public class AddStudentViewModel : ViewModelBase
{
    private Student _student;
    private ICommand _addStudent;
    private ViewModelBase newIns;

    public ICommand AddStudent
    {
        get
        {
            if (_addStudent == null)
            {
                _addStudent = new RelayCommand(param => this.Add(), null);
            }

            return _addStudent;
        }
    }

    public Student Student
    {
        get
        {
            return _student;
        }
        set
        {
            _student = value;
            NotifyPropertyChanged("Student");
        }
    }

    private void Add()
    {
        StudentClass.StudentList.Add(Student);
        Student = new Student();
    }

    public AddStudentViewModel()
    {
        Student = new Student();
    }
}

ViewModelBase.cs:

public class ViewModelBase : INotifyPropertyChanged
{
    private Class _studclass;

    public Class StudentClass
    {
        get { return _studclass; }
        set
        {
            _studclass = value;
            NotifyPropertyChanged("StudentClass");
        }
    }

    public ViewModelBase()
    {
        StudentClass = new Class();
        Student asaf = new Student();
        asaf.Name = "Asaf";
        asaf.LastName = "biton";
        StudentClass.StudentList.Add(asaf);
    }

    public event PropertyChangedEventHandler PropertyChanged;
    protected void NotifyPropertyChanged(string PropertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(PropertyName));
        }
    }
}
like image 912
Asaf Avatar asked Oct 27 '25 05:10

Asaf


1 Answers

When I set the viewmodels of the "student list" window, and "add a student" window, the list are sync-ed, obviously. So I guess the thing is the class instance gets duplicated or something like that.

I guest you want to inform or communicate between two view models. So I know two ways to implement a similar mechanism but don't use any framework at here.

  1. Build a controller which will keep list of instance view models and then define rule when view model A raise event then call event to B. It will take your efforts a lot.

  2. You can look at "observer pattern" to build a publish/ subscribe event. When A raise a publish event the view model B which already register subscribe event to execute the function. I suggest you should apply an EventAggregator pattern will get more generic and can use ubiquitous.

    Follow Martin Fowler:

    An Event Aggregator acts as a single source of events for many objects. It registers for all the events of the many objects allowing clients to register with just the aggregator.

    So you can take look at EventAggregator from Martin Fowler to implement an EventAggregator by your self or you can use Prism framework with EventAggregator pattern already built-in. You can look at The Caliburn micro framework as well. It is simple, lightweight which have EventAggreagator pattern and a tons best practices in WPF is one of a best of WPF framework will help you save a lot of efforts when deal with MVVM in WPF.

like image 179
Toan Vo Avatar answered Oct 29 '25 21:10

Toan Vo