Update viewmodel based on MainWindow event

I have a UdpClient, firing off a DataRecevied event on my MainWindow:

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

My ClientData and User classes look as follow:

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

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

On my MainWindow, I have a UserControl called UserListView which has a ViewModel called UserListViewModel

The ViewModel looks as follow:

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

The difficulty I have here, is when the ClientConnectionOnDataReceived event on the MainWindow gets fired, I would like to update my ClientData class, My Viewmodel should then somehow be notified that the list changed, and subsequently update my UI.

Can anyone give me a solid example of how to achieve this using MVVM (Prism) in WPF?

I am new to MVVM, so i am still trying to figure this out.


First of all, there's no obvious reason why the main window should do the subscription.

I'd go for something like this:

  • create a service that encapsulates the subscription (and subscribes in its constructor)
  • register that as a singleton
  • have it implement INotifyPropertyChanged (to notify consumers of a change to Users )
  • inject the service into UserListViewModel and observe the Users property (see PropertyObserver)
  • when Users in the service changes, update Users in the user list view model
  • and best of all, no need for ObservableCollection here :-)

    EDIT: example:

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

    and then register the service

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

    in your bootstrapper or your module's initialization.

    链接地址: http://www.djcxy.com/p/39546.html

    上一篇: WPF鼠标事件更新性能

    下一篇: 基于MainWindow事件更新viewmodel