我正在使用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这样做?
答案 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);
});
}