使用 ReactiveUI 的 BindTo() 更新 XAML 属性 会生成警告
Using ReactiveUI's BindTo() to update a XAML property generates a warning
我正在尝试更新视图 XAML 中元素的 属性:
this.WhenAnyValue(x => x.ViewModel.IsEnabled).BindTo(this, x => x.MyButton.IsEnabled);
这按预期工作,但是,它会在运行时生成警告:
POCOObservableForProperty: rx_bindto_test.MainWindow is a POCO type and won't send change notifications, WhenAny will only return a single value!
我可以通过将表达式更改为来消除警告:
this.WhenAnyValue(x => x.ViewModel.IsEnabled).Subscribe(b => MyButton.IsEnabled = b);
但我仍然想知道为什么它不能与 BindTo() 一起正常工作。
编辑:即使是常规 Bind
和 OneWayBind
也会生成此警告。
- 我做错了什么?
- 并且真的有必要将
ViewModel
定义为视图的依赖项 属性 才能观察到它吗? (当我在视图上将其声明为常规 属性 时,ReactiveUI 会生成相同的 POCO 警告)我不能简单地使其从 ReactiveObject 继承,因为 C# 不支持多重继承。
MainWindow.xaml.cs
public partial class MainWindow : Window, IViewFor<MyViewModel>, IEnableLogger {
public static readonly DependencyProperty ViewModelProperty = DependencyProperty.Register("ViewModel",
typeof(MyViewModel), typeof(MainWindow));
public MyViewModel ViewModel {
get { return (MyViewModel)GetValue(ViewModelProperty); }
set { SetValue(ViewModelProperty, value); }
}
object IViewFor.ViewModel {
get { return ViewModel; }
set { ViewModel = (MyViewModel)value; }
}
public MainWindow() {
InitializeComponent();
this.WhenAnyValue(x => x.ViewModel).BindTo(this, x => x.DataContext);
this.WhenAnyValue(x => x.ViewModel.IsEnabled).BindTo(this, x => x.MyButton.IsEnabled);
ViewModel = new MyViewModel();
ViewModel.IsEnabled = true;
}
}
MainWindow.xaml
<Window x:Class="rx_bindto_test.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Button x:Name="MyButton">My Button</Button>
</Grid>
</Window>
MyViewModel.cs
public class MyViewModel : ReactiveObject, IEnableLogger {
private bool isEnabled;
public bool IsEnabled {
get { return isEnabled; }
set { this.RaiseAndSetIfChanged(ref isEnabled, value); }
}
}
我认为这里的主要问题是你只希望它朝一个方向发展——所以你想要单向绑定。如果您尝试这样做会怎么样:
this.OneWayBind(ViewModel x => x.IsEnabled, x => y.MyButton.IsEnabled);
我认为这里的问题是 IsEnabled 没有与之关联的 属性 更改通知(但我仍在学习 RxUI,它是一个复杂的代码库!)。
(尝试)回答您的问题:
- 见上文。
- 是的。我也希望但是 VM 不是由 window class 定义的,因为不是每个人都使用 MVVM。为了使管道正常工作,您需要将其作为依赖项 属性(否则在更改时不会发送通知)。我刚刚(痛苦地)发现,您的视图在其生命周期内可能有多个 VM - 如果您有一个大型 ListView 并且视图缓存打开。
我认为混淆是因为您在 "MyButton" 解析时收到警告,而不是在 ViewModel 上收到警告。
MyButton 是一个 "constant" 对象,w/o 任何生命周期(既不是 INPC 也不是 DependencyObject),因此您可以安全地忽略此警告。
或者,您可以注册下面的额外 属性 解析器,对于 FrameworkElement 的每个内部字段,它的行为类似于 POCO 解析器(减去警告),它非常接近 XAML(我相信):
Locator.CurrentMutable.Register(() => new CustomPropertyResolver(), typeof(ICreatesObservableForProperty));
public class CustomPropertyResolver : ICreatesObservableForProperty
{
public int GetAffinityForObject(Type type, string propertyName, bool beforeChanged = false)
{
if (!typeof(FrameworkElement).IsAssignableFrom(type))
return 0;
var fi = type.GetTypeInfo().GetFields(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly)
.FirstOrDefault(x => x.Name == propertyName);
return fi != null ? 2 /* POCO affinity+1 */ : 0;
}
public IObservable<IObservedChange<object, object>> GetNotificationForProperty(object sender, System.Linq.Expressions.Expression expression, bool beforeChanged = false)
{
var foo = (FrameworkElement)sender;
return Observable.Return(new ObservedChange<object, object>(sender, expression), new DispatcherScheduler(foo.Dispatcher))
.Concat(Observable.Never<IObservedChange<object, object>>());
}
}
我正在尝试更新视图 XAML 中元素的 属性:
this.WhenAnyValue(x => x.ViewModel.IsEnabled).BindTo(this, x => x.MyButton.IsEnabled);
这按预期工作,但是,它会在运行时生成警告:
POCOObservableForProperty: rx_bindto_test.MainWindow is a POCO type and won't send change notifications, WhenAny will only return a single value!
我可以通过将表达式更改为来消除警告:
this.WhenAnyValue(x => x.ViewModel.IsEnabled).Subscribe(b => MyButton.IsEnabled = b);
但我仍然想知道为什么它不能与 BindTo() 一起正常工作。
编辑:即使是常规 Bind
和 OneWayBind
也会生成此警告。
- 我做错了什么?
- 并且真的有必要将
ViewModel
定义为视图的依赖项 属性 才能观察到它吗? (当我在视图上将其声明为常规 属性 时,ReactiveUI 会生成相同的 POCO 警告)我不能简单地使其从 ReactiveObject 继承,因为 C# 不支持多重继承。
MainWindow.xaml.cs
public partial class MainWindow : Window, IViewFor<MyViewModel>, IEnableLogger {
public static readonly DependencyProperty ViewModelProperty = DependencyProperty.Register("ViewModel",
typeof(MyViewModel), typeof(MainWindow));
public MyViewModel ViewModel {
get { return (MyViewModel)GetValue(ViewModelProperty); }
set { SetValue(ViewModelProperty, value); }
}
object IViewFor.ViewModel {
get { return ViewModel; }
set { ViewModel = (MyViewModel)value; }
}
public MainWindow() {
InitializeComponent();
this.WhenAnyValue(x => x.ViewModel).BindTo(this, x => x.DataContext);
this.WhenAnyValue(x => x.ViewModel.IsEnabled).BindTo(this, x => x.MyButton.IsEnabled);
ViewModel = new MyViewModel();
ViewModel.IsEnabled = true;
}
}
MainWindow.xaml
<Window x:Class="rx_bindto_test.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Button x:Name="MyButton">My Button</Button>
</Grid>
</Window>
MyViewModel.cs
public class MyViewModel : ReactiveObject, IEnableLogger {
private bool isEnabled;
public bool IsEnabled {
get { return isEnabled; }
set { this.RaiseAndSetIfChanged(ref isEnabled, value); }
}
}
我认为这里的主要问题是你只希望它朝一个方向发展——所以你想要单向绑定。如果您尝试这样做会怎么样:
this.OneWayBind(ViewModel x => x.IsEnabled, x => y.MyButton.IsEnabled);
我认为这里的问题是 IsEnabled 没有与之关联的 属性 更改通知(但我仍在学习 RxUI,它是一个复杂的代码库!)。
(尝试)回答您的问题:
- 见上文。
- 是的。我也希望但是 VM 不是由 window class 定义的,因为不是每个人都使用 MVVM。为了使管道正常工作,您需要将其作为依赖项 属性(否则在更改时不会发送通知)。我刚刚(痛苦地)发现,您的视图在其生命周期内可能有多个 VM - 如果您有一个大型 ListView 并且视图缓存打开。
我认为混淆是因为您在 "MyButton" 解析时收到警告,而不是在 ViewModel 上收到警告。
MyButton 是一个 "constant" 对象,w/o 任何生命周期(既不是 INPC 也不是 DependencyObject),因此您可以安全地忽略此警告。
或者,您可以注册下面的额外 属性 解析器,对于 FrameworkElement 的每个内部字段,它的行为类似于 POCO 解析器(减去警告),它非常接近 XAML(我相信):
Locator.CurrentMutable.Register(() => new CustomPropertyResolver(), typeof(ICreatesObservableForProperty));
public class CustomPropertyResolver : ICreatesObservableForProperty
{
public int GetAffinityForObject(Type type, string propertyName, bool beforeChanged = false)
{
if (!typeof(FrameworkElement).IsAssignableFrom(type))
return 0;
var fi = type.GetTypeInfo().GetFields(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly)
.FirstOrDefault(x => x.Name == propertyName);
return fi != null ? 2 /* POCO affinity+1 */ : 0;
}
public IObservable<IObservedChange<object, object>> GetNotificationForProperty(object sender, System.Linq.Expressions.Expression expression, bool beforeChanged = false)
{
var foo = (FrameworkElement)sender;
return Observable.Return(new ObservedChange<object, object>(sender, expression), new DispatcherScheduler(foo.Dispatcher))
.Concat(Observable.Never<IObservedChange<object, object>>());
}
}