I'm trying to update a property of an element in the XAML of a view:
this.WhenAnyValue(x => x.ViewModel.IsEnabled).BindTo(this, x => x.MyButton.IsEnabled);
This works as expected, however, it generates a warning at runtime:
POCOObservableForProperty: rx_bindto_test.MainWindow is a POCO type and won't send change notifications, WhenAny will only return a single value!
I can get rid of the warning by changing the expression to:
this.WhenAnyValue(x => x.ViewModel.IsEnabled).Subscribe(b => MyButton.IsEnabled = b);
but I'm still wondering why it doesn't work properly with BindTo().
Edit: it appears even the regular Bind
and OneWayBind
generate this warning.
ViewModel
as a dependency property on the View to be able to observe it? (when I declare it as a regular property on the View, ReactiveUI generates the same POCO warning) I can't simply make it inherit from ReactiveObject because C# doesn't support multiple inheritance.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); }
}
}
I think the confusion comes from the fact that you get a warning on "MyButton" resolving, and not on the ViewModel.
MyButton is a "constant" object, w/o any lifecycle (neither INPC nor DependencyObject), hence you can safely ignore this warning.
Alternatively you can register the below extra property resolver, which will behaves like the POCO one (minus the warning) for every internal field of a FrameworkElement, which is pretty close to every control from the XAML (I believe):
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>>());
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With