diff --git a/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.approved.txt b/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.approved.txt index 29ef2ee690..d4bb95ab0b 100644 --- a/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.approved.txt +++ b/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.approved.txt @@ -1,4 +1,4 @@ -[assembly: System.Runtime.CompilerServices.InternalsVisibleToAttribute("ReactiveUI.AndroidSupport")] +[assembly: System.Runtime.CompilerServices.InternalsVisibleToAttribute("ReactiveUI.AndroidSupport")] [assembly: System.Runtime.CompilerServices.InternalsVisibleToAttribute("ReactiveUI.Tests")] [assembly: System.Runtime.CompilerServices.InternalsVisibleToAttribute("ReactiveUI.Winforms")] [assembly: System.Runtime.CompilerServices.InternalsVisibleToAttribute("ReactiveUI.Wpf")] @@ -171,7 +171,7 @@ namespace ReactiveUI public interface ICreatesObservableForProperty : Splat.IEnableLogger { int GetAffinityForObject(System.Type type, string propertyName, bool beforeChanged = False); - System.IObservable> GetNotificationForProperty(object sender, System.Linq.Expressions.Expression expression, string propertyName, bool beforeChanged = False); + System.IObservable> GetNotificationForProperty(object sender, System.Linq.Expressions.Expression expression, string propertyName, bool beforeChanged = False, bool suppressWarnings = False); } public interface IHandleObservableErrors { @@ -198,7 +198,7 @@ namespace ReactiveUI { public INPCObservableForProperty() { } public int GetAffinityForObject(System.Type type, string propertyName, bool beforeChanged) { } - public System.IObservable> GetNotificationForProperty(object sender, System.Linq.Expressions.Expression expression, string propertyName, bool beforeChanged) { } + public System.IObservable> GetNotificationForProperty(object sender, System.Linq.Expressions.Expression expression, string propertyName, bool beforeChanged, bool suppressWarnings = False) { } } public class Interaction { @@ -293,7 +293,7 @@ namespace ReactiveUI { public IROObservableForProperty() { } public int GetAffinityForObject(System.Type type, string propertyName, bool beforeChanged = False) { } - public System.IObservable> GetNotificationForProperty(object sender, System.Linq.Expressions.Expression expression, string propertyName, bool beforeChanged = False) { } + public System.IObservable> GetNotificationForProperty(object sender, System.Linq.Expressions.Expression expression, string propertyName, bool beforeChanged = False, bool suppressWarnings = False) { } } public interface IRoutableViewModel : ReactiveUI.INotifyPropertyChanging, ReactiveUI.IReactiveObject, Splat.IEnableLogger, System.ComponentModel.INotifyPropertyChanged { @@ -414,7 +414,7 @@ namespace ReactiveUI { public POCOObservableForProperty() { } public int GetAffinityForObject(System.Type type, string propertyName, bool beforeChanged = False) { } - public System.IObservable> GetNotificationForProperty(object sender, System.Linq.Expressions.Expression expression, string propertyName, bool beforeChanged = False) { } + public System.IObservable> GetNotificationForProperty(object sender, System.Linq.Expressions.Expression expression, string propertyName, bool beforeChanged = False, bool suppressWarnings = False) { } } public class PropertyBinderImplementation : ReactiveUI.IPropertyBinderImplementation, Splat.IEnableLogger { @@ -609,6 +609,7 @@ namespace ReactiveUI public static System.IObserver DefaultExceptionHandler { get; set; } public static System.Reactive.Concurrency.IScheduler MainThreadScheduler { get; set; } public static bool SupportsRangeNotifications { get; set; } + public static bool SuppressViewCommandBindingMessage { get; set; } public static ReactiveUI.ISuspensionHost SuspensionHost { get; set; } public static System.Reactive.Concurrency.IScheduler TaskpoolScheduler { get; set; } } diff --git a/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.net461.approved.txt b/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.net461.approved.txt index 8a50e607db..ac55188c65 100644 --- a/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.net461.approved.txt +++ b/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.net461.approved.txt @@ -171,7 +171,7 @@ namespace ReactiveUI public interface ICreatesObservableForProperty : Splat.IEnableLogger { int GetAffinityForObject(System.Type type, string propertyName, bool beforeChanged = False); - System.IObservable> GetNotificationForProperty(object sender, System.Linq.Expressions.Expression expression, string propertyName, bool beforeChanged = False); + System.IObservable> GetNotificationForProperty(object sender, System.Linq.Expressions.Expression expression, string propertyName, bool beforeChanged = False, bool suppressWarnings = False); } public interface IHandleObservableErrors { @@ -198,7 +198,7 @@ namespace ReactiveUI { public INPCObservableForProperty() { } public int GetAffinityForObject(System.Type type, string propertyName, bool beforeChanged) { } - public System.IObservable> GetNotificationForProperty(object sender, System.Linq.Expressions.Expression expression, string propertyName, bool beforeChanged) { } + public System.IObservable> GetNotificationForProperty(object sender, System.Linq.Expressions.Expression expression, string propertyName, bool beforeChanged, bool suppressWarnings = False) { } } public class Interaction { @@ -293,7 +293,7 @@ namespace ReactiveUI { public IROObservableForProperty() { } public int GetAffinityForObject(System.Type type, string propertyName, bool beforeChanged = False) { } - public System.IObservable> GetNotificationForProperty(object sender, System.Linq.Expressions.Expression expression, string propertyName, bool beforeChanged = False) { } + public System.IObservable> GetNotificationForProperty(object sender, System.Linq.Expressions.Expression expression, string propertyName, bool beforeChanged = False, bool suppressWarnings = False) { } } public interface IRoutableViewModel : ReactiveUI.INotifyPropertyChanging, ReactiveUI.IReactiveObject, Splat.IEnableLogger, System.ComponentModel.INotifyPropertyChanged { @@ -419,7 +419,7 @@ namespace ReactiveUI { public POCOObservableForProperty() { } public int GetAffinityForObject(System.Type type, string propertyName, bool beforeChanged = False) { } - public System.IObservable> GetNotificationForProperty(object sender, System.Linq.Expressions.Expression expression, string propertyName, bool beforeChanged = False) { } + public System.IObservable> GetNotificationForProperty(object sender, System.Linq.Expressions.Expression expression, string propertyName, bool beforeChanged = False, bool suppressWarnings = False) { } } public class PropertyBinderImplementation : ReactiveUI.IPropertyBinderImplementation, Splat.IEnableLogger { @@ -525,7 +525,7 @@ namespace ReactiveUI public static System.IObservable> ObservableForProperty(this TSender @this, System.Linq.Expressions.Expression> property, bool beforeChange = False, bool skipInitial = True) { } public static System.IObservable ObservableForProperty(this TSender @this, System.Linq.Expressions.Expression> property, System.Func selector, bool beforeChange = False) where TSender : class { } - public static System.IObservable> SubscribeToExpressionChain(this TSender source, System.Linq.Expressions.Expression expression, bool beforeChange = False, bool skipInitial = True) { } + public static System.IObservable> SubscribeToExpressionChain(this TSender source, System.Linq.Expressions.Expression expression, bool beforeChange = False, bool skipInitial = True, bool suppressWarnings = False) { } } [System.Runtime.Serialization.DataContractAttribute()] public class ReactiveObject : ReactiveUI.IHandleObservableErrors, ReactiveUI.INotifyPropertyChanging, ReactiveUI.IReactiveNotifyPropertyChanged, ReactiveUI.IReactiveObject, Splat.IEnableLogger, System.ComponentModel.INotifyPropertyChanged @@ -613,6 +613,7 @@ namespace ReactiveUI public static System.IObserver DefaultExceptionHandler { get; set; } public static System.Reactive.Concurrency.IScheduler MainThreadScheduler { get; set; } public static bool SupportsRangeNotifications { get; set; } + public static bool SuppressViewCommandBindingMessage { get; set; } public static ReactiveUI.ISuspensionHost SuspensionHost { get; set; } public static System.Reactive.Concurrency.IScheduler TaskpoolScheduler { get; set; } } diff --git a/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.netcoreapp2.0.approved.txt b/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.netcoreapp2.0.approved.txt index a7d48568ee..74db8a926b 100644 --- a/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.netcoreapp2.0.approved.txt +++ b/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.netcoreapp2.0.approved.txt @@ -165,7 +165,7 @@ namespace ReactiveUI public interface ICreatesObservableForProperty : Splat.IEnableLogger { int GetAffinityForObject(System.Type type, string propertyName, bool beforeChanged = False); - System.IObservable> GetNotificationForProperty(object sender, System.Linq.Expressions.Expression expression, string propertyName, bool beforeChanged = False); + System.IObservable> GetNotificationForProperty(object sender, System.Linq.Expressions.Expression expression, string propertyName, bool beforeChanged = False, bool suppressWarnings = False); } public interface IHandleObservableErrors { @@ -192,7 +192,7 @@ namespace ReactiveUI { public INPCObservableForProperty() { } public int GetAffinityForObject(System.Type type, string propertyName, bool beforeChanged) { } - public System.IObservable> GetNotificationForProperty(object sender, System.Linq.Expressions.Expression expression, string propertyName, bool beforeChanged) { } + public System.IObservable> GetNotificationForProperty(object sender, System.Linq.Expressions.Expression expression, string propertyName, bool beforeChanged, bool suppressWarnings = False) { } } public class Interaction { @@ -287,7 +287,7 @@ namespace ReactiveUI { public IROObservableForProperty() { } public int GetAffinityForObject(System.Type type, string propertyName, bool beforeChanged = False) { } - public System.IObservable> GetNotificationForProperty(object sender, System.Linq.Expressions.Expression expression, string propertyName, bool beforeChanged = False) { } + public System.IObservable> GetNotificationForProperty(object sender, System.Linq.Expressions.Expression expression, string propertyName, bool beforeChanged = False, bool suppressWarnings = False) { } } public interface IRoutableViewModel : ReactiveUI.INotifyPropertyChanging, ReactiveUI.IReactiveObject, Splat.IEnableLogger, System.ComponentModel.INotifyPropertyChanged { @@ -413,7 +413,7 @@ namespace ReactiveUI { public POCOObservableForProperty() { } public int GetAffinityForObject(System.Type type, string propertyName, bool beforeChanged = False) { } - public System.IObservable> GetNotificationForProperty(object sender, System.Linq.Expressions.Expression expression, string propertyName, bool beforeChanged = False) { } + public System.IObservable> GetNotificationForProperty(object sender, System.Linq.Expressions.Expression expression, string propertyName, bool beforeChanged = False, bool suppressWarnings = False) { } } public class PropertyBinderImplementation : ReactiveUI.IPropertyBinderImplementation, Splat.IEnableLogger { @@ -519,7 +519,7 @@ namespace ReactiveUI public static System.IObservable> ObservableForProperty(this TSender @this, System.Linq.Expressions.Expression> property, bool beforeChange = False, bool skipInitial = True) { } public static System.IObservable ObservableForProperty(this TSender @this, System.Linq.Expressions.Expression> property, System.Func selector, bool beforeChange = False) where TSender : class { } - public static System.IObservable> SubscribeToExpressionChain(this TSender source, System.Linq.Expressions.Expression expression, bool beforeChange = False, bool skipInitial = True) { } + public static System.IObservable> SubscribeToExpressionChain(this TSender source, System.Linq.Expressions.Expression expression, bool beforeChange = False, bool skipInitial = True, bool suppressWarnings = False) { } } [System.Runtime.Serialization.DataContractAttribute()] public class ReactiveObject : ReactiveUI.IHandleObservableErrors, ReactiveUI.INotifyPropertyChanging, ReactiveUI.IReactiveNotifyPropertyChanged, ReactiveUI.IReactiveObject, Splat.IEnableLogger, System.ComponentModel.INotifyPropertyChanged @@ -607,6 +607,7 @@ namespace ReactiveUI public static System.IObserver DefaultExceptionHandler { get; set; } public static System.Reactive.Concurrency.IScheduler MainThreadScheduler { get; set; } public static bool SupportsRangeNotifications { get; set; } + public static bool SuppressViewCommandBindingMessage { get; set; } public static ReactiveUI.ISuspensionHost SuspensionHost { get; set; } public static System.Reactive.Concurrency.IScheduler TaskpoolScheduler { get; set; } } diff --git a/src/ReactiveUI.Tests/Platforms/winforms/API/ApiApprovalTests.Winforms.net461.approved.txt b/src/ReactiveUI.Tests/Platforms/winforms/API/ApiApprovalTests.Winforms.net461.approved.txt index c9a5aab897..bb01b4e581 100644 --- a/src/ReactiveUI.Tests/Platforms/winforms/API/ApiApprovalTests.Winforms.net461.approved.txt +++ b/src/ReactiveUI.Tests/Platforms/winforms/API/ApiApprovalTests.Winforms.net461.approved.txt @@ -104,7 +104,7 @@ namespace ReactiveUI.Winforms { public WinformsCreatesObservableForProperty() { } public int GetAffinityForObject(System.Type type, string propertyName, bool beforeChanged = False) { } - public System.IObservable> GetNotificationForProperty(object sender, System.Linq.Expressions.Expression expression, string propertyName, bool beforeChanged = False) { } + public System.IObservable> GetNotificationForProperty(object sender, System.Linq.Expressions.Expression expression, string propertyName, bool beforeChanged = False, bool suppressWarnings = False) { } } } namespace ReactiveUI.Winforms.Legacy diff --git a/src/ReactiveUI.Tests/Platforms/wpf/API/ApiApprovalTests.Wpf.net461.approved.txt b/src/ReactiveUI.Tests/Platforms/wpf/API/ApiApprovalTests.Wpf.net461.approved.txt index 61423593e4..fb471e4e7d 100644 --- a/src/ReactiveUI.Tests/Platforms/wpf/API/ApiApprovalTests.Wpf.net461.approved.txt +++ b/src/ReactiveUI.Tests/Platforms/wpf/API/ApiApprovalTests.Wpf.net461.approved.txt @@ -37,7 +37,7 @@ namespace ReactiveUI { public DependencyObjectObservableForProperty() { } public int GetAffinityForObject(System.Type type, string propertyName, bool beforeChanged = False) { } - public System.IObservable> GetNotificationForProperty(object sender, System.Linq.Expressions.Expression expression, string propertyName, bool beforeChanged = False) { } + public System.IObservable> GetNotificationForProperty(object sender, System.Linq.Expressions.Expression expression, string propertyName, bool beforeChanged = False, bool suppressWarnings = False) { } } public class PlatformOperations { diff --git a/src/ReactiveUI.Tests/Platforms/wpf/Mocks/FakeXamlCommandBindingView.cs b/src/ReactiveUI.Tests/Platforms/wpf/Mocks/FakeXamlCommandBindingView.cs new file mode 100644 index 0000000000..32efd7bb80 --- /dev/null +++ b/src/ReactiveUI.Tests/Platforms/wpf/Mocks/FakeXamlCommandBindingView.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Windows.Controls; + +namespace ReactiveUI.Tests.Wpf +{ + public class FakeXamlCommandBindingView : IViewFor + { + private readonly Button _buttonDeclaredInXaml; + + public FakeXamlCommandBindingView() + { + _buttonDeclaredInXaml = new Button(); + + this.BindCommand(ViewModel, vm => vm.Command2, v => v._buttonDeclaredInXaml); + } + + public string NameOfButtonDeclaredInXaml => nameof(_buttonDeclaredInXaml); + + object IViewFor.ViewModel + { + get => ViewModel; + set => ViewModel = (CommandBindingViewModel)value; + } + + public CommandBindingViewModel ViewModel { get; set; } + } +} diff --git a/src/ReactiveUI.Tests/Platforms/wpf/WpfCommandBindingImplementationTests.cs b/src/ReactiveUI.Tests/Platforms/wpf/WpfCommandBindingImplementationTests.cs index 4455eae772..24b90cf8a7 100644 --- a/src/ReactiveUI.Tests/Platforms/wpf/WpfCommandBindingImplementationTests.cs +++ b/src/ReactiveUI.Tests/Platforms/wpf/WpfCommandBindingImplementationTests.cs @@ -11,6 +11,8 @@ using System.Windows; using System.Windows.Input; using ReactiveUI.Tests.Xaml; +using Shouldly; +using Splat; using Xunit; using FactAttribute = Xunit.WpfFactAttribute; @@ -37,5 +39,17 @@ public void CommandBindToExplicitEventWireup() view.Command2.RaiseEvent(new MouseButtonEventArgs(Mouse.PrimaryDevice, 0, MouseButton.Left) { RoutedEvent = UIElement.MouseUpEvent }); Assert.Equal(1, invokeCount); } + + [Fact] + public void BindCommandShouldNotWarnWhenBindingToFieldDeclaredInXaml() + { + var testLogger = new TestLogger(); + Locator.CurrentMutable.RegisterConstant(testLogger); + + var vm = new CommandBindingViewModel(); + var view = new FakeXamlCommandBindingView { ViewModel = vm }; + + testLogger.Messages.ShouldNotContain(t => t.Item1.Contains(nameof(POCOObservableForProperty)) && t.Item1.Contains(view.NameOfButtonDeclaredInXaml) && t.Item3 == LogLevel.Warn); + } } } diff --git a/src/ReactiveUI.Tests/Resolvers/PocoObservableForPropertyTests.cs b/src/ReactiveUI.Tests/Resolvers/PocoObservableForPropertyTests.cs index 12e1335b4c..4476736d1c 100644 --- a/src/ReactiveUI.Tests/Resolvers/PocoObservableForPropertyTests.cs +++ b/src/ReactiveUI.Tests/Resolvers/PocoObservableForPropertyTests.cs @@ -10,6 +10,7 @@ using System.Linq.Expressions; using System.Text; using System.Threading.Tasks; +using Shouldly; using Splat; using Xunit; @@ -45,6 +46,24 @@ public void NotificationPocoErrorOnBind() Assert.Equal(testLogger.LastMessages[0], $"{nameof(POCOObservableForProperty)}: The class {typeof(PocoType).FullName} property {nameof(PocoType.Property1)} is a POCO type and won't send change notifications, WhenAny will only return a single value!"); } + [Fact] + public void NotificationPocoSuppressErrorOnBind() + { + var instance = new POCOObservableForProperty(); + + var testLogger = new TestLogger(); + Locator.CurrentMutable.RegisterConstant(testLogger); + + var testClass = new PocoType(); + + Expression> expr = x => x.Property1; + var exp = Reflection.Rewrite(expr.Body); + + instance.GetNotificationForProperty(testClass, exp, exp.GetMemberInfo().Name, false, true).Subscribe(_ => { }); + + testLogger.LastMessages.ShouldNotContain(m => m.Contains(nameof(POCOObservableForProperty))); + } + private class PocoType { public string Property1 { get; set; } diff --git a/src/ReactiveUI.Winforms/WinformsCreatesObservableForProperty.cs b/src/ReactiveUI.Winforms/WinformsCreatesObservableForProperty.cs index 21e813a44c..2c5ada068d 100644 --- a/src/ReactiveUI.Winforms/WinformsCreatesObservableForProperty.cs +++ b/src/ReactiveUI.Winforms/WinformsCreatesObservableForProperty.cs @@ -43,7 +43,7 @@ public int GetAffinityForObject(Type type, string propertyName, bool beforeChang } /// - public IObservable> GetNotificationForProperty(object sender, Expression expression, string propertyName, bool beforeChanged = false) + public IObservable> GetNotificationForProperty(object sender, Expression expression, string propertyName, bool beforeChanged = false, bool suppressWarnings = false) { var ei = eventInfoCache.Get(Tuple.Create(sender.GetType(), propertyName)); diff --git a/src/ReactiveUI.Wpf/DependencyObjectObservableForProperty.cs b/src/ReactiveUI.Wpf/DependencyObjectObservableForProperty.cs index 15a0999e9c..9de7a6c0de 100644 --- a/src/ReactiveUI.Wpf/DependencyObjectObservableForProperty.cs +++ b/src/ReactiveUI.Wpf/DependencyObjectObservableForProperty.cs @@ -34,14 +34,18 @@ public int GetAffinityForObject(Type type, string propertyName, bool beforeChang } /// - public IObservable> GetNotificationForProperty(object sender, System.Linq.Expressions.Expression expression, string propertyName, bool beforeChanged = false) + public IObservable> GetNotificationForProperty(object sender, System.Linq.Expressions.Expression expression, string propertyName, bool beforeChanged = false, bool suppressWarnings = false) { var type = sender.GetType(); var dpd = DependencyPropertyDescriptor.FromProperty(GetDependencyProperty(type, propertyName), type); if (dpd == null) { - this.Log().Error("Couldn't find dependency property " + propertyName + " on " + type.Name); + if (!suppressWarnings) + { + this.Log().Error("Couldn't find dependency property " + propertyName + " on " + type.Name); + } + throw new NullReferenceException("Couldn't find dependency property " + propertyName + " on " + type.Name); } diff --git a/src/ReactiveUI.Wpf/Registrations.cs b/src/ReactiveUI.Wpf/Registrations.cs index 671bb25233..e9a5b803ae 100644 --- a/src/ReactiveUI.Wpf/Registrations.cs +++ b/src/ReactiveUI.Wpf/Registrations.cs @@ -26,6 +26,8 @@ public void Register(Action, Type> registerFunction) RxApp.TaskpoolScheduler = TaskPoolScheduler.Default; RxApp.MainThreadScheduler = new WaitForDispatcherScheduler(() => DispatcherScheduler.Current); + + RxApp.SuppressViewCommandBindingMessage = true; } } } diff --git a/src/ReactiveUI/Bindings/Command/CommandBinderImplementation.cs b/src/ReactiveUI/Bindings/Command/CommandBinderImplementation.cs index 9e49dfe841..f22198b3b0 100644 --- a/src/ReactiveUI/Bindings/Command/CommandBinderImplementation.cs +++ b/src/ReactiveUI/Bindings/Command/CommandBinderImplementation.cs @@ -138,7 +138,7 @@ private IDisposable BindCommandInternal( var bindInfo = Observable.CombineLatest( @this, - view.WhenAnyDynamic(controlExpression, x => x.Value), + view.SubscribeToExpressionChain(controlExpression, false, false, RxApp.SuppressViewCommandBindingMessage).Select(x => x.Value), (val, host) => new { val, host }); var propSub = bindInfo diff --git a/src/ReactiveUI/Interfaces/ICreatesObservableForProperty.cs b/src/ReactiveUI/Interfaces/ICreatesObservableForProperty.cs index 293448c4ca..693f6010d2 100644 --- a/src/ReactiveUI/Interfaces/ICreatesObservableForProperty.cs +++ b/src/ReactiveUI/Interfaces/ICreatesObservableForProperty.cs @@ -44,9 +44,10 @@ public interface ICreatesObservableForProperty : IEnableLogger /// If true, signal just before the /// property value actually changes. If false, signal after the /// property changes. + /// If true, no warnings should be logged. /// An IObservable which is signalled whenever the specified /// property on the object changes. If this cannot be done for a /// specified value of beforeChanged, return Observable.Never. - IObservable> GetNotificationForProperty(object sender, Expression expression, string propertyName, bool beforeChanged = false); + IObservable> GetNotificationForProperty(object sender, Expression expression, string propertyName, bool beforeChanged = false, bool suppressWarnings = false); } } diff --git a/src/ReactiveUI/Mixins/ReactiveNotifyPropertyChangedMixin.cs b/src/ReactiveUI/Mixins/ReactiveNotifyPropertyChangedMixin.cs index f50d1264b9..a5423dd4bf 100644 --- a/src/ReactiveUI/Mixins/ReactiveNotifyPropertyChangedMixin.cs +++ b/src/ReactiveUI/Mixins/ReactiveNotifyPropertyChangedMixin.cs @@ -125,6 +125,7 @@ public static IObservable ObservableForProperty( /// A expression which will point towards the property. /// If we are interested in notifications before the property value is changed. /// If we don't want to get a notification about the default value of the property. + /// If true, no warnings should be logged. /// The type of the origin of the expression chain. /// The end value we want to subscribe to. /// A observable which notifies about observed changes. @@ -133,14 +134,15 @@ public static IObservable> SubscribeToExpressio this TSender source, Expression expression, bool beforeChange = false, - bool skipInitial = true) + bool skipInitial = true, + bool suppressWarnings = false) { IObservable> notifier = Observable.Return(new ObservedChange(null, null, source)); IEnumerable chain = Reflection.Rewrite(expression).GetExpressionChain(); notifier = chain.Aggregate(notifier, (n, expr) => n - .Select(y => NestedObservedChanges(expr, y, beforeChange)) + .Select(y => NestedObservedChanges(expr, y, beforeChange, suppressWarnings)) .Switch()); if (skipInitial) @@ -179,7 +181,7 @@ private static IObservedChange ObservedChangeFor(Expression expr return new ObservedChange(sourceChange.Value, expression, value); } - private static IObservable> NestedObservedChanges(Expression expression, IObservedChange sourceChange, bool beforeChange) + private static IObservable> NestedObservedChanges(Expression expression, IObservedChange sourceChange, bool beforeChange, bool suppressWarnings) { // Make sure a change at a root node propogates events down var kicker = ObservedChangeFor(expression, sourceChange); @@ -191,12 +193,12 @@ private static IObservable> NestedObservedChange } // Handle non null values in the chain - return NotifyForProperty(sourceChange.Value, expression, beforeChange) + return NotifyForProperty(sourceChange.Value, expression, beforeChange, suppressWarnings) .Select(x => new ObservedChange(x.Sender, expression, x.GetValue())) .StartWith(kicker); } - private static IObservable> NotifyForProperty(object sender, Expression expression, bool beforeChange) + private static IObservable> NotifyForProperty(object sender, Expression expression, bool beforeChange, bool suppressWarnings) { var propertyName = expression.GetMemberInfo().Name; var result = notifyFactoryCache.Get(Tuple.Create(sender.GetType(), propertyName, beforeChange)); @@ -206,7 +208,7 @@ private static IObservable> NotifyForProperty(ob throw new Exception($"Could not find a ICreatesObservableForProperty for {sender.GetType()} property {propertyName}. This should never happen, your service locator is probably broken. Please make sure you have installed the latest version of the ReactiveUI packages for your platform. See https://reactiveui.net/docs/getting-started/installation/nuget-packages for guidance."); } - return result.GetNotificationForProperty(sender, expression, propertyName, beforeChange); + return result.GetNotificationForProperty(sender, expression, propertyName, beforeChange, suppressWarnings); } } } diff --git a/src/ReactiveUI/ObservableForProperty/INPCObservableForProperty.cs b/src/ReactiveUI/ObservableForProperty/INPCObservableForProperty.cs index 0b51b1452a..c6422029ba 100644 --- a/src/ReactiveUI/ObservableForProperty/INPCObservableForProperty.cs +++ b/src/ReactiveUI/ObservableForProperty/INPCObservableForProperty.cs @@ -26,7 +26,7 @@ public int GetAffinityForObject(Type type, string propertyName, bool beforeChang /// [SuppressMessage("Roslynator", "RCS1211", Justification = "Neater with else clause.")] - public IObservable> GetNotificationForProperty(object sender, Expression expression, string propertyName, bool beforeChanged) + public IObservable> GetNotificationForProperty(object sender, Expression expression, string propertyName, bool beforeChanged, bool suppressWarnings = false) { var before = sender as INotifyPropertyChanging; var after = sender as INotifyPropertyChanged; diff --git a/src/ReactiveUI/ObservableForProperty/IROObservableForProperty.cs b/src/ReactiveUI/ObservableForProperty/IROObservableForProperty.cs index caf2bb5b1f..db7ebc634e 100644 --- a/src/ReactiveUI/ObservableForProperty/IROObservableForProperty.cs +++ b/src/ReactiveUI/ObservableForProperty/IROObservableForProperty.cs @@ -25,7 +25,7 @@ public int GetAffinityForObject(Type type, string propertyName, bool beforeChang } /// - public IObservable> GetNotificationForProperty(object sender, Expression expression, string propertyName, bool beforeChanged = false) + public IObservable> GetNotificationForProperty(object sender, Expression expression, string propertyName, bool beforeChanged = false, bool suppressWarnings = false) { var iro = sender as IReactiveObject; if (iro == null) diff --git a/src/ReactiveUI/ObservableForProperty/POCOObservableForProperty.cs b/src/ReactiveUI/ObservableForProperty/POCOObservableForProperty.cs index 55fd028027..2ba461032a 100644 --- a/src/ReactiveUI/ObservableForProperty/POCOObservableForProperty.cs +++ b/src/ReactiveUI/ObservableForProperty/POCOObservableForProperty.cs @@ -28,10 +28,10 @@ public int GetAffinityForObject(Type type, string propertyName, bool beforeChang } /// - public IObservable> GetNotificationForProperty(object sender, Expression expression, string propertyName, bool beforeChanged = false) + public IObservable> GetNotificationForProperty(object sender, Expression expression, string propertyName, bool beforeChanged = false, bool suppressWarnings = false) { var type = sender.GetType(); - if (!hasWarned.ContainsKey((type, propertyName))) + if (!hasWarned.ContainsKey((type, propertyName)) && !suppressWarnings) { this.Log().Warn($"The class {type.FullName} property {propertyName} is a POCO type and won't send change notifications, WhenAny will only return a single value!"); hasWarned[(type, propertyName)] = true; diff --git a/src/ReactiveUI/Platforms/android/AndroidObservableForWidgets.cs b/src/ReactiveUI/Platforms/android/AndroidObservableForWidgets.cs index 68e4eb4c98..14cc0d6167 100644 --- a/src/ReactiveUI/Platforms/android/AndroidObservableForWidgets.cs +++ b/src/ReactiveUI/Platforms/android/AndroidObservableForWidgets.cs @@ -52,7 +52,7 @@ public int GetAffinityForObject(Type type, string propertyName, bool beforeChang } /// - public IObservable> GetNotificationForProperty(object sender, Expression expression, string propertyName, bool beforeChanged = false) + public IObservable> GetNotificationForProperty(object sender, Expression expression, string propertyName, bool beforeChanged = false, bool suppressWarnings = false) { var type = sender.GetType(); var tableItem = dispatchTable.Keys.First(x => x.Item1.IsAssignableFrom(type) && x.Item2 == propertyName); diff --git a/src/ReactiveUI/Platforms/apple-common/KVOObservableForProperty.cs b/src/ReactiveUI/Platforms/apple-common/KVOObservableForProperty.cs index 463ee07781..64a06a3b64 100644 --- a/src/ReactiveUI/Platforms/apple-common/KVOObservableForProperty.cs +++ b/src/ReactiveUI/Platforms/apple-common/KVOObservableForProperty.cs @@ -67,7 +67,7 @@ public int GetAffinityForObject(Type type, string propertyName, bool beforeChang } /// - public IObservable> GetNotificationForProperty(object sender, Expression expression, string propertyName, bool beforeChanged = false) + public IObservable> GetNotificationForProperty(object sender, Expression expression, string propertyName, bool beforeChanged = false, bool suppressWarnings = false) { var obj = sender as NSObject; if (obj == null) diff --git a/src/ReactiveUI/Platforms/apple-common/ObservableForPropertyBase.cs b/src/ReactiveUI/Platforms/apple-common/ObservableForPropertyBase.cs index 1d8ee26fcf..00fdc33be7 100644 --- a/src/ReactiveUI/Platforms/apple-common/ObservableForPropertyBase.cs +++ b/src/ReactiveUI/Platforms/apple-common/ObservableForPropertyBase.cs @@ -57,7 +57,7 @@ public int GetAffinityForObject(Type type, string propertyName, bool beforeChang } /// - public IObservable> GetNotificationForProperty(object sender, Expression expression, string propertyName, bool beforeChanged = false) + public IObservable> GetNotificationForProperty(object sender, Expression expression, string propertyName, bool beforeChanged = false, bool suppressWarnings = false) { if (beforeChanged) { diff --git a/src/ReactiveUI/Platforms/uap/DependencyObjectObservableForProperty.cs b/src/ReactiveUI/Platforms/uap/DependencyObjectObservableForProperty.cs index efe852e3f7..92b1e8513f 100644 --- a/src/ReactiveUI/Platforms/uap/DependencyObjectObservableForProperty.cs +++ b/src/ReactiveUI/Platforms/uap/DependencyObjectObservableForProperty.cs @@ -37,7 +37,7 @@ public int GetAffinityForObject(Type type, string propertyName, bool beforeChang } /// - public IObservable> GetNotificationForProperty(object sender, System.Linq.Expressions.Expression expression, string propertyName, bool beforeChanged = false) + public IObservable> GetNotificationForProperty(object sender, System.Linq.Expressions.Expression expression, string propertyName, bool beforeChanged = false, bool suppressWarnings = false) { Contract.Requires(sender != null && sender is DependencyObject); var type = sender.GetType(); @@ -45,11 +45,14 @@ public IObservable> GetNotificationForProperty(o if (depSender == null) { - this.Log().Warn( - CultureInfo.InvariantCulture, - "Tried to bind DP on a non-DependencyObject. Binding as POCO object", - type.FullName, - propertyName); + if (!suppressWarnings) + { + this.Log().Warn( + CultureInfo.InvariantCulture, + "Tried to bind DP on a non-DependencyObject. Binding as POCO object", + type.FullName, + propertyName); + } var ret = new POCOObservableForProperty(); return ret.GetNotificationForProperty(sender, expression, propertyName, beforeChanged); diff --git a/src/ReactiveUI/RxApp.cs b/src/ReactiveUI/RxApp.cs index 46c5042915..c7e56ee383 100644 --- a/src/ReactiveUI/RxApp.cs +++ b/src/ReactiveUI/RxApp.cs @@ -189,6 +189,11 @@ public static IScheduler TaskpoolScheduler } } + /// + /// Gets or sets a value indicating whether log messages should be suppressed for command bindings in the view. + /// + public static bool SuppressViewCommandBindingMessage { get; set; } + /// /// Gets or sets the Observer which signalled whenever an object that has a /// ThrownExceptions property doesn't Subscribe to that Observable. Use