【问题标题】:Updating/using a variable from another ViewModel从另一个 ViewModel 更新/使用变量
【发布时间】:2013-05-11 15:50:22
【问题描述】:

为了练习 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>

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));
        }
    }
}

【问题讨论】:

  • 发布一些相关的 XAML 和代码。我们不会为了回答这个问题而下载您的项目。
  • 完成。认为它会太长,所以我只是上传了整个项目。但是,如果您这么说,那就更好了,然后就可以了。很抱歉没有事先做。

标签: c# wpf mvvm


【解决方案1】:

当我设置“学生列表”窗口的视图模型,并“添加一个 学生”窗口,很明显,列表是同步的。所以我猜是这样的 类实例是否被重复或类似的东西。

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

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

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

    关注马丁·福勒:

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

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

【讨论】:

  • 认为是观察者模式
  • EventAggregator 是一条单向街道。一旦开始使用它,您将永远不会再使用其他任何东西 ;) 我喜欢 caliburn.micro 附带的那个!
  • 你说得对。我也喜欢 Caliburn micro 和 Caliburn。它们是非常好的框架。顺便说一句,感谢您的 cmets。
  • 谢谢。我听说过 Caliburn,但我希望至少能够在使用框架之前自己完成它。您发布的链接我不太明白,但我会尝试在 google 上查找。
  • @xTCx 在获得帮助之前先尝试理解这个概念是非常好的。如果您想了解更多关于 caliburn.micro 的信息,请查看 mindscapehq.com/blog/index.php/2012/01/12/…
【解决方案2】:

您可能必须建立某种事件机制。 不要打扰,这是标准的 .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;
}

【讨论】:

  • 听起来可以。不过我的情况有点不同。在我的“班级”模型中,我只有一个 ObserableCollection 学生和一个构造函数。我所做的只是添加到集合中,而不是设置它。那我该怎么做呢?
  • 模型-视图-视图模型模式需要一个模型。您显示的数据来自该模型,在视图模型中进行调整并显示在视图中。通过事件连接模型和视图模型。然后通过Binding连接ViewModel和View。 ObservableCollection 绝对是 ViewModel 的一部分。在您的模型中,您有一个数据库连接、一个列表或类似的东西。
  • 我有 2 个模型。班级,学生。班级是学生名单,学生基本上就是姓名,姓氏等......
猜你喜欢
  • 1970-01-01
  • 2020-11-25
  • 1970-01-01
  • 1970-01-01
  • 2014-02-18
  • 1970-01-01
  • 1970-01-01
  • 2019-05-02
  • 1970-01-01
相关资源
最近更新 更多