Winforms:ViewModel更改时重新绑定

时间:2016-07-27 01:45:13

标签: winforms mvvm reactiveui

我正在使用Winforms ReactiveUI应用程序,我有一个实现IViewFor的UserControl:

public partial class CustomView : UserControl, IViewFor<CustomViewModel>
{
    public CustomViewModel ViewModel { get; set; }
    object IViewFor.ViewModel {
        get { return ViewModel; }
        set { ViewModel = value as CustomViewModel; }
    }

    public CustomView()
    {
        InitializeComponent();

        this.Bind(ViewModel, x => x.SomeBindingList, x => x.DataGridBindingSource.DataSource);
    }
}

在调用控件中,我将ViewModel设置为:

customView.ViewModel = new CustomViewModel(model)

但是,当数据发生变化时,会重新分配customView.ViewModel(使用上面相同的代码),但它不会自动重新绑定。我假设那是因为ViewModel没有PropertyChanged事件。

我可以在INotifyPropertyChanged上实现CustomView,但我想知道 - 有没有方便的方法/ ReactiveUI这样做?

2 个答案:

答案 0 :(得分:1)

我认为你在传递新模型而不是替换ViewModel方面走在正确的轨道上。我不确定您的确切要求,但这是一个可能有帮助的示例。选择新用户会更改CustomView的DataGridView中的联系人列表。

<强>物品

public class Item
    {
        public string Name { get; set; }
        public int Value { get; set; }
        public Item(string name, int value)
        {
            Name = name; Value = value;
        }            
    }

<强> MainViewModel

public class MainViewModel : ReactiveObject
{

    int _userId;
    public int UserId
    {
        get { return _userId; }
        set { this.RaiseAndSetIfChanged(ref _userId, value); }
    }

    ObservableAsPropertyHelper<User> _user;
    public User User => _user.Value;

    ObservableAsPropertyHelper<List<Item>> _userList;
    public List<Item> UserList => _userList.Value;

    public ReactiveCommand<User> LoadUser { get; protected set; }
    public ReactiveCommand<List<Item>> LoadUserList { get; protected set; }

    public MainViewModel()
    {

        LoadUser = ReactiveCommand.CreateAsyncObservable(_ => LoadUserImp(UserId));
        _user = LoadUser.ToProperty(this, x => x.User, null);

        LoadUserList = ReactiveCommand.CreateAsyncObservable(_ => LoadUserListImp());
        _userList = LoadUserList.ToProperty(this, x => x.UserList, new List<Item>());

        // Listens for change to UserId and loads new User.
        this.WhenAnyValue(x => x.UserId).Where(id => id > 0).InvokeCommand(LoadUser);

    }

    private IObservable<User> LoadUserImp(int userId)
    {
        User user;

        if (userId == 1)
        {
            user = new User { Id = 1, Name = "Bob" };
        }
        else
        {
            user = new User { Id = 2, Name = "Jane" };
        }

        return Observable.Return(user);

    }

    private IObservable<List<Item>> LoadUserListImp()
    {

        Item item1 = new Item("Bob", 1);
        Item item2 = new Item("Jane", 2);
        List<Item> items = new List<Item> { item1, item2 };

        return Observable.Return(items);

    }

}

<强>的MainView

public partial class MainView : Form, IViewFor<MainViewModel>
{        
    public MainViewModel ViewModel { get; set; }

    object IViewFor.ViewModel
    {
        get { return ViewModel; }

        set { ViewModel = value as MainViewModel; }
    }

    public MainView()
    {
        InitializeComponent();

        List<Item> items = new List<Item>();

        UserComboBox.DataSource = items;
        UserComboBox.DisplayMember = "Name";
        UserComboBox.ValueMember = "Value";

        // Two way binding.
        this.Bind(ViewModel, vm => vm.UserId, v => v.UserComboBox.SelectedValue);

        // One way binding.
        this.OneWayBind(ViewModel, vm => vm.UserList, v => v.UserComboBox.DataSource);

        // Per Paul Betts: Invoking this in the VM constructor means that your VM class becomes more difficult to test, 
        // because you always have to mock out the effects of calling [LoadUserList], 
        // even if the thing you are testing is unrelated.
        // Instead, I always call these commands in the View.
        this.WhenAnyValue(v => v.ViewModel.LoadUserList)
            .SelectMany(x => x.ExecuteAsync())
            .Subscribe();

        // This is where I would change your model, in this case the user in the CustomView.
        this.WhenAnyObservable(v => v.ViewModel.LoadUser)
            .Subscribe(user => customView1.ViewModel.User = user);

        ViewModel = new MainViewModel();

        customView1.ViewModel = new CustomViewModel();

    }

}

<强> CustomViewModel

public class CustomViewModel : ReactiveObject
{

    ObservableAsPropertyHelper<List<Person>> _contacts;
    public List<Person> Contacts => _contacts.Value;

    User _user;
    public User User
    {
        get { return _user; }
        set { this.RaiseAndSetIfChanged(ref _user, value); }
    }

    public ReactiveCommand<List<Person>> LoadContacts { get; protected set; }

    public CustomViewModel()
    {

        LoadContacts = ReactiveCommand.CreateAsyncObservable(_ => LoadContactsImp(User.Id));

        _contacts = LoadContacts.ToProperty(this, x => x.Contacts, new List<Person>());

        this.WhenAnyValue(vm => vm.User.Id).InvokeCommand(LoadContacts);

    }

    private IObservable<List<Person>> LoadContactsImp(int userId)
    {
        List<Person> contacts;

        if (userId == 1)
        {
            contacts = new List<Person>()
            {
                new Person() { Id = 1, FirstName = "John", LastName = "Jones" },
                new Person() { Id = 2, FirstName = "Beth", LastName = "Johnson" },
            };
        }
        else
        {
            contacts = new List<Person>()
            {
                new Person() { Id = 1, FirstName = "Dave", LastName = "Smith" },
                new Person() { Id = 2, FirstName = "Elizabeth", LastName = "Bretfield" },
            };
        }

        return Observable.Return(contacts);

    }
}

<强> CustomView

public partial class CustomView : UserControl, IViewFor<CustomViewModel>
{

    public CustomViewModel ViewModel { get; set; }


    object IViewFor.ViewModel
    {
        get { return ViewModel; }

        set { ViewModel = value as CustomViewModel; }

    }

    public CustomView()
    {
        InitializeComponent();

        this.OneWayBind(ViewModel, vm => vm.Contacts, v => v.Contacts.DataSource);

    }

}

答案 1 :(得分:0)

为什么CustomView构造函数被称为“BridgeGeometryView”?

您是否在ViewModel中为SomeBindingList使用ReactiveList或ReactiveBindingList?

另外,我建议不要直接设置绑定,在InitializeComponent()方法之后使用WhenActivated:

public BridgeGeometryView()
    {
        InitializeComponent();
        this.WhenActivated(() =>
        {
           this.Bind(ViewModel, x => x.SomeBindingList, x => x.DataGridBindingSource.DataSource);  
        });

    }