更新/使用另一个ViewModel中的变量

时间:2013-05-11 15:50:23

标签: c# wpf mvvm

为了练习WPF + MVVM,我决定写一个学校课程 到目前为止,我有班级和学生班 还有基本的视图模型,ViewModelBase.cs,它派生自INPC,实例类 - “StudentClass”。

所有其他视图模型都派生自viewmodelbase。

问题是,每个“功能”都有一个页面/窗口,(例如,查看所有学生,添加学生,删除学生等等),我希望能够访问该课程从应用程序的任何地方开始,因为所有信息基本上都存储在那里。

为了保持井井有条,每个“功能”都有自己的视图模型(StudentListViewModel.cs,AddStudentViewModel.cs ...)。

我试图从viewmodels访问该类,这只会导致类在一个窗口而不是在另一个窗口中更新的情况。

当我设置“学生列表”窗口的视图模型和“添加学生”窗口时,显然会同步列表。所以我想问题就是类实例得到重复或类似的东西。

我上传了该项目以供参考:http://www.mediafire.com/?n70c7caqex6be1g

希望有人可以帮助我。

我试着在google上寻找答案,但所有的答案都提到了与框架相关的“信使”和“事件”。由于我没有使用这个项目的框架,这些解决方案不适用于我。

另一个解决方案是将viewmodel的一个实例传递给另一个,但我的viewmodel都没有调用或实例化另一个视图模型。

更新

StudentList.xaml中的XAML :(这是一个用户控件,因为我使用的是名为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>

AddStudent.xaml中的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));
        }
    }
}

2 个答案:

答案 0 :(得分:4)

  

当我设置“学生列表”窗口的视图模型时,“添加一个   学生“窗口,列表是同步的,很明显。所以我想这件事   是类实例得到重复或类似的东西。

我想邀请您在两个视图模型之间通知或通信。所以我知道实现类似机制的两种方法,但不要在这里使用任何框架。

  1. 构建一个控制器,它将保留实例视图模型的列表,然后在视图模型A引发事件时定义规则,然后将事件调用到B.这需要您付出很多努力。

  2. 您可以查看“观察者模式”来构建发布/订阅事件。当A引发发布事件时,已经注册了订阅事件的视图模型B执行该函数。我建议你应该应用一个EventAggregator模式将获得更多通用,并可以使用无处不在。

    关注Martin Fowler:

      

    Event Aggregator充当许多事件的单一事件源   对象。它注册许多对象允许的所有事件   客户只需注册聚合器。

    因此,您可以查看Martin Fowler的EventAggregator来实现您自己的EventAggregator,或者您可以使用已经内置Prism模式的EventAggregator框架。您也可以查看Caliburn micro framework。它简单,轻量级,具有EventAggreagator模式,WPF中的吨最佳实践是最好的WPF框架之一,可以帮助您在WPF中处理MVVM时节省大量精力。

答案 1 :(得分:1)

您可能需要建立某种事件机制。 不要打扰,这是标准的.NET技术。

首先: 有一个模型层。这是ViewModel适应视图的基础。 也许它只是某个数据库和视图模型之间的一个薄层。

有了这个,在你的模型中提供一个事件,告诉你的模型 不知何故改变了。

public event EventHandler<ValueChangedEventArgs> ValuesChanged;

public class ValueChangedEventArgs : System.EventArgs
{
    public readonly string str;

    public ValueChangedEventArgs(string str)
    {
        this.str = str;
    }
}

在模型的某个地方,当值发生变化时,触发该事件:

protected virtual void OnPriceChanged(ValueChangedEventArgs e)
{
    if (ValuesChanged != null)
        ValuesChanged(this, e);
}

也许是模型中值的设定者。

public string StringValue
{
    get
    {
        return stringValue;
    }
    set
    {
        OnPriceChanged(new ValueChangedEventArgs(value));
        stringValue = value;
    }
}

然后只需在ViewModel中订阅该事件:

public void ValuesChangedHandler(object sender, EventArgs e)
{
    // do something with the new value
}

public StudentListViewModel()
{
       yourModel.ValuesChanged += ValuesChangedHandler;
}