3

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

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

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

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

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

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

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

希望有人可以帮助我。

我尝试在谷歌上寻找答案,但所有答案都提到了与框架相关的“信使”和“事件”。而且由于我没有为该项目使用框架,因此这些解决方案不适用于我。

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

更新:

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>

添加StudentViewModel.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));
        }
    }
}
4

2 回答 2

4

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

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

  1. 构建一个控制器,该控制器将保留实例视图模型列表,然后在视图模型 A 引发事件然后向 B 调用事件时定义规则。这将花费你很多努力。

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

    关注马丁·福勒:

    事件聚合器充当许多对象的单一事件源。它注册了许多对象的所有事件,允许客户端仅向聚合器注册。

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

于 2013-05-11T16:09:02.913 回答
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;
}
于 2013-05-11T16:23:55.527 回答