【问题标题】:Update viewmodel based on MainWindow event基于 MainWindow 事件更新视图模型
【发布时间】:2016-08-12 10:08:11
【问题描述】:

我有一个 UdpClient,在我的 MainWindow 上触发了一个 DataRecevied 事件:

public partial class MainWindow : Window
{
    public static YakUdpClient ClientConnection = new YakUdpClient();
    public ClientData;

    public MainWindow()
    {
        InitializeComponent();
        Loaded += OnLoaded;
    }

    private void OnLoaded(object sender, RoutedEventArgs routedEventArgs)
    {
        ClientData = new ClientData();
        ClientConnection.OnDataReceived += ClientConnectionOnDataReceived;
    }

    private void ClientConnectionOnDataReceived(object sender, MessageEventArgs messageEventArgs)
    {
        ClientData.Users = messageEvenArgs.ConnectedUsers;
    }
}

我的 ClientData 和 User 类如下所示:

public class ClientData
{
    public List<User> Users {get;set;)
}

public class User
{
    public string Name {get;set;}
}

在我的 MainWindow 上,我有一个名为 UserListView 的 UserControl,它有一个名为 UserListViewModel 的 ViewModel

ViewModel 如下所示:

public class UserListViewModel: BindableBase
{
    public UserListViewModel()
    {
        //I am sure there are better ways of doing this :(
        Users = new ObservableCollection<User>((MainWindow)Application.Current.MainWindow).ClientData.Users
    });

    private ObservableCollection<User> _users;
    public ObservableCollection<User> Users
    {
        get{ return _users;}
        set { this.SetProperty(ref this._users, value); }
    }
}

我在这里遇到的困难是,当 MainWindow 上的 ClientConnectionOnDataReceived 事件被触发时,我想更新我的 ClientData 类,然后应该以某种方式通知我的 Viewmodel 列表已更改,然后更新我的 UI .

谁能给我一个可靠的例子,说明如何在 WPF 中使用 MVVM (Prism) 实现这一目标?

我是 MVVM 的新手,所以我仍在努力解决这个问题。

【问题讨论】:

  • 使用 EventAggregator(有时称为“Messenger”)查看 PubSub 事件;它将与您的 ViewModel 解耦紧密的逻辑,并允许可扩展性/更改。我建议查看 Prism 的 EventAggregator:codeproject.com/Articles/355473/Prism-EventAggregator-Sample
  • 此外,Prism 对于连接 ViewModel 很有用——有一个 BindableBase 类,它很好地实现了 INotifyPropertyChanged,当 ViewModel 发生变化时,它能够更新您的视图。 澄清一下 - 我不隶属于/为 Prism 工作:)
  • @GeoffJames,你会注意到我的UserListViewModel 继承自BindableBase。抱歉,我正在使用棱镜...更新了我的帖子以反映这一点。我对 MVVM 的所有研究都让我困惑了一秒钟
  • 刚刚发现,我的错! 去再喝杯咖啡...... 只是一个失礼:当你 newing 上你的 ClientData 类时,你没有将它实例化为一个变量......?此外,可能值得将此变量的引用传递给您的UserListViewModel 的ctor - 然后,您可以设置传入的ClientData 实例的私有字段,并以这种方式监听OnDataReceived 事件?而不是从MainWindow
  • @GeoffJames,ClientData 类在多个 UserControl 中被引用,所以通常我希望我的 MainWindow 更新 ClientData 类,并希望我的不同 ViewModels 从 CLientData 类中检索数据

标签: wpf prism viewmodel


【解决方案1】:

首先,主窗口应该进行订阅没有明显的理由。

我会选择这样的:

  • 创建一个封装订阅的服务(并在其构造函数中订阅)
  • 将其注册为单例
  • 让它实现INotifyPropertyChanged(通知消费者Users的更改)
  • 将服务注入UserListViewModel 并观察Users 属性(请参阅PropertyObserver
  • 当服务中的Users发生变化时,更新用户列表视图模型中的Users

最重要的是,这里不需要ObservableCollection :-)

编辑:示例:

interface IUserService : INotifyPropertyChanged
{
    IReadOnlyCollection<User> Users
    {
        get;
    }
}

class YakUdpService : BindableBase, IUserService
{
    private readonly YakUdpClient _yakUdpClient;
    private IReadOnlyCollection<User> _users;

    public YakUdpService()
    {
        _yakUdpClient = new YakUdpClient();
        _yakUdpClient.OnDataReceived += ( s, e ) => Users = e.ConnectedUsers;
    }

    public IReadOnlyCollection<User> Users
    {
        get
        {
            return _users;
        }
        private set
        {
            SetProperty( ref _users, value );
        }
    }
}

class UserListViewModel : BindableBase
{
    private IReadOnlyCollection<UserViewModel> _users;
    private readonly IUserService _userService;
    private readonly PropertyObserver<IUserService> _userServiceObserver;

    public UserListViewModel( IUserService userService )
    {
        _userService = userService;
        _userServiceObserver = new PropertyObserver<IUserService>( userService );
        _userServiceObserver.RegisterHandler( x => x.Users, () => Users = _userService.Users.Select( x => new UserViewModel( x ) ).ToList() );
        //                                                                                                ^^^ should use factory in real code
    }

    public IReadOnlyCollection<UserViewModel> Users
    {
        get
        {
            return _users;
        }
        private set
        {
            SetProperty( ref _users, value );
        }
    }
}

然后注册服务

Container.RegisterType<IUserService, YakUdpService>( new ContainerControlledLifetimeManager() );

在您的引导程序或模块的初始化中。

【讨论】:

  • @GeoffJames 也在他上面的一个 cmets 中提出了这个建议,你有没有机会给我一个如何实现这个的最小例子?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-02-02
  • 1970-01-01
  • 2013-08-26
相关资源
最近更新 更多