Events generated through ReactiveCommand run in unexpected order - wpf

I have a problem where I see events running after I expect all events to be completed. But that happens only when the method called is called by a ReactiveCommand.
I have a viewmodel that has one property and a calculated property:
public class ViewModel : ReactiveObject
{
private int _property;
private readonly ObservableAsPropertyHelper<int> _calculatedProperty;
public ViewModel()
{
Command = new DelegateCommand(x => Test("Command"));
ReactiveCommand = ReactiveCommand.Create(() => Test("ReactiveCommand"));
this.WhenAnyValue(x => x.Property)
.Do(x => Console.WriteLine($"CalculatedProperty will be set to {x}"))
.ToProperty(this, x => x.CalculatedProperty, out _calculatedProperty);
this.WhenAnyValue(x => x.CalculatedProperty)
.Subscribe(v => Console.WriteLine($"CalculatedProperty was set to {v}"));
}
public void Test(string method)
{
Console.WriteLine($"Begin {method}");
Property++;
Console.WriteLine($"End {method}");
}
public int Property
{
get => _property;
set => this.RaiseAndSetIfChanged(ref _property, value);
}
public int CalculatedProperty => _calculatedProperty.Value;
public ICommand Command { get; }
public ReactiveCommand ReactiveCommand { get; }
}
I have a window with 2 buttons, one bound to the Command and one to the ReactiveCommand (and one button with an oldfashioned clickhandler which behaves identical to the Command).
When I click the Command button, the outputted text is as I expect:
Begin Command
CalculatedProperty will be set to 1
CalculatedProperty was set to 1
End Command
However when I click the ReactiveCommand-button I get this:
Begin ReactiveCommand
CalculatedProperty will be set to 2
End ReactiveCommand
CalculatedProperty was set to 2
The calculated property event firing after the End-message is being run. This causes problems in my code and I do not understand why this happens. I've been investigating and playing with sending in schedulers in the ReactiveCommand.Create method, looking at SynchronizationContexts but all my experiments haven't worked out.
Why is this happening and what can I do to stop it?

Be due to the fact that by default ReactiveCommand will issue the commands onto the Main Thread dispatcher for ReactiveCommand.Create(() => action)
Your Delegate command is just running it straight away without going through the dispatcher which is similar functionality to what ImmediateScheduler would do.
You could pass the ImmediateScheduler.Default to the second parameter of the ReactiveCommand.Create()

I found out that the ToProperty method also takes in a scheduler. When I changed the:
.ToProperty(this, x => x.CalculatedProperty, out _calculatedProperty);
to:
.ToProperty(this, x => x.CalculatedProperty, out _calculatedProperty,
scheduler: ImmediateScheduler.Instance);
it worked as I intended. Not sure if this is intended behaviour or some kind of bug. It certainly was unexpected for me.

Related

AutoRefreshOnObservable only works once. Why?

I'm working on a small programm where i'm evaluating if reactive ui is the right framework for another project. So far so good... At the moment i'm a little bit lost in a DynamicData related function. I'm trying to execute a command in the MainViewWindow every time a combo box in a ReactiveUserControl is changed. All my Models are extending ReactiveObject and the Properties are set up with the RaiseAndSetIfChanged setter.
In my ReactiveUserControl ViewModel I invoke my Command SaveImage from the ReactiveUserControl ViewModel as it is described here:
https://reactiveui.net/docs/handbook/message-bus/#ways-to-avoid-using-messagebus
Defining the ObservableCollection
public ObservableCollection<FileViewModel> VisibleFiles { get; protected set; }
Initialize the Collection, Files is a SourceList
WatchFiles = ReactiveCommand.Create(() =>
{
VisibleFiles = new ObservableCollection<FilesViewModel>(Files.Items);
VisibleFiles.ToObservableChangeSet().AutoRefreshOnObservable(doc => doc.SaveImage).Select(_ => WhenAnyFileChanged()).Switch().Subscribe<FilesViewModel>(x => {
Console.WriteLine("HOORAY");
});
});
private IObservable<FilesViewModel> WhenAnyFileChanged()
{
return VisibleFiles.Select(x => x.SaveFile.Select(_ => x )).Merge();
}
The First time a combo box changed it gets evaluated correct. I get the "Hooray". But every time after that there is no output. If I invoke the Watch Files Command again it is again working once.
Why is this happening, and how can i solve it to print every time a file changed the "Hooray" ? I can see, that the ObservableCollection detects the change, and also the Command in the ReactiveUserControl is invoked on the change. But the WhenAnyFileChanged Method doesn't return the changed element after the first invokation.
Hopefully it is understandable what I'm trying to achieve, an what's the problem.
Update: I don't know why, but if i check the ChangeSet in the Select() i get TotalChanges 10 at initialisation, which is right. Then with my first working change TotalChanges is 0 but is evaluated right. On my next attempt on changing i still get 0 TotalChanges but also no correct evaluation in WhenAnyFileChanged().
Refreshes() is 1 on every change.
Update 2: Changing AutoRefreshOnObservable() to AutoRefresh() brings the desired functionality.
I copied the original message bus example and wrote a unit test to see whether the code behaves as expected. I can confirm the issue you are seeing is present in the example. The following code only fires once.
public MainViewModel()
{
OpenDocuments = new ObservableCollection<DocumentViewModel>();
OpenDocuments
.ToObservableChangeSet()
.AutoRefreshOnObservable(document => document.Close)
.Select(_ => WhenAnyDocumentClosed())
.Switch()
.Subscribe(x => OpenDocuments.Remove(x), ex=>{},()=>{});
}
IObservable<DocumentViewModel> WhenAnyDocumentClosed()
{
return OpenDocuments
.Select(x => x.Close.Select(_ => x))
.Merge();
}
And here's the test to prove it. It fails on the second attempt to remove.
[Fact]
public void MyTest()
{
//I added an id field to help with diagnostics / testing
_mainViewModel.OpenDocuments.Count.Should().Be(4);
_mainViewModel.OpenDocuments.Any(dvm => dvm.Id == "1").Should().BeTrue();
_mainViewModel.OpenDocuments[0].Close.Execute().Subscribe();
_mainViewModel.OpenDocuments.Count.Should().Be(3);
_mainViewModel.OpenDocuments.Any(dvm => dvm.Id == "1").Should().BeFalse();
_mainViewModel.OpenDocuments[0].Close.Execute().Subscribe();
_mainViewModel.OpenDocuments.Count.Should().Be(2);
_mainViewModel.OpenDocuments.Any(dvm => dvm.Id == "2").Should().BeFalse();
}
I am unsure why this fails, but the most optimal fix is to make use of Dynamic Data's MergeMany operator which is similar to Rx's Merge but automatically wires observables when items are added to the underlying list and unwires them when items are removed. The fix is:
public class MainViewModel : ReactiveObject
{
public ObservableCollection<DocumentViewModel> OpenDocuments { get;}
public MainViewModel()
{
OpenDocuments = new ObservableCollection<DocumentViewModel>();
OpenDocuments
.ToObservableChangeSet()
.MergeMany(x => x.Close.Select(_ => x))
.Subscribe(x => OpenDocuments.Remove(x));
}
}
Running the same unit tests pass.
The code with unit test is available in this gist

Why isn't OnActivate being called?

I'm asking this here because I'm at a loss for trying to figure this out. I've searched and all that comes up are things that make sense but also don't apply to my situation.
I'm using WPF with MVVM and Caliburn.Micro. I have a shell window with a corresponding view model which is a Conductor<Screen>.Collection.OnceActive and a screen which is inheriting from Screen. I'm calling ActivateItem within the Conductor's constructor to show the subsequent screen, it shows the screen correctly but never calls the Screen's override for OnActivate and the screen's IsActive property is set to False.
This only happens the very first time I call ActivateItem from the Conductor, all additional calls will properly call OnActivate and OnDeactivate.
This makes no sense to me and I have no idea what is going on. I cleaned the solution, rebuilt, and even rebooted but it still doesn't work properly. Below is the code:
Parent Conductor
[Export]
public sealed class ShellViewModel : Conductor<Screen>.Collection.OneActive, IHandle<SimpleMessage>
{
private readonly DashboardViewModel m_Dash;
private readonly LoginViewModel m_Login;
private readonly IEventAggregator m_MsgBus;
[ImportingConstructor]
public ShellViewModel(DashboardViewModel dash, LoginViewModel login, IEventAggregator msgBus)
{
this.m_MsgBus = msgBus;
this.m_Dash = dash;
this.m_Login = login;
this.ActivateItem(this.m_Login);
}
protected override void OnActivate()
{
this.m_MsgBus.Subscribe(this); //called correctly
}
protected override void OnDeactivate(bool close)
{
this.m_MsgBus.Unsubscribe(this); //called correctly
}
public void Handle(SimpleMessage message)
{
switch (message)
{
case SimpleMessage.LoginSuccess:
this.ActivateItem(this.m_Dash);
break;
case SimpleMessage.Logout:
this.ActivateItem(this.m_Login);
break;
}
}
}
Child Screen
[Export]
public sealed class LoginViewModel : Screen
{
private readonly IEventAggregator m_MsgBus;
[ImportingConstructor]
public LoginViewModel(IEventAggregator msgBus)
{
this.m_MsgBus = msgBus;
}
protected override void OnActivate()
{
//NOT called the first time, but is called every other time
MessageBox.Show("ACTIVATE TEST");
}
protected override void OnDeactivate(bool close)
{
//NOT called the first time, but is called every other time
MessageBox.Show("DEACTIVATE TEST");
}
public void CmdLogin(string password)
{
this.m_MsgBus.PublishOnUIThread(SimpleMessage.LoginSuccess);
}
public string Username { get; set; }
public string Password { get; set; }
}
UPDATE
I downloaded the Caliburn Micro source so I could step into the ActivateItem function and see what is going on. For some reason, when I first call ActivateItem from the Conductor the Conductor's IsActive property is set to false which causes Caliburn to skip calling the OnActivate override. I have no idea why the property would be false.
ConductorBaseWithActiveItem.cs
protected virtual void ChangeActiveItem(T newItem, bool closePrevious) {
ScreenExtensions.TryDeactivate(activeItem, closePrevious);
newItem = EnsureItem(newItem);
//Problem is here, IsActive is false the first time around in the conductor
if(IsActive)
ScreenExtensions.TryActivate(newItem);
activeItem = newItem;
NotifyOfPropertyChange("ActiveItem");
OnActivationProcessed(activeItem, true);
}
It looks like the reason IsActive is false in the Conductor is because my Conductor is the root view which is created using DisplayRootViewFor and it looks like that function does not set the IsActive property to true.
So, knowing that, am I simply just implementing this wrong and a Conductor can't/shouldn't be the root view? Do I need to have a 2nd child view which is the conductor (that seems like a bit much)?
I figured it out and it was basically me not thinking. Activating a view in the constructor of the conductor/root view doesn't work properly because it hasn't been activated yet. IsActive isn't set to true until the conductor's/root view's OnActivate is called.
This may be problematic at some point because the conductor isn't active even when OnInitialize is called and that is meant to be the one time init function and OnActivate could be called multiple times. In my case it will be fine because my conductor is the root view so OnActivate will only be called once.
Moral of the story is, don't call ActivateItem in a conductor's constructor when the conductor is a root view.

MVVM RelayCommand CanExecute

I'm implementing an RelayCommand with an execute and an canExecute part. The RelayCommand works when it is without the canExecute part, however when I add the canExecute part, the command locks the button. The RelayCommand only checks whether or not the button can be executed as long as the CanExecute part is true. Once the canExecute part becomes false, the button can no longer be clicked, even if it is supposed to. How do I make sure that every time I click on the button it controls whether or not it can be executed, and doesn't lock it forever, once it cannot be executed?
RedoCommand = new RelayCommand(undoRedoController.Redo,undoRedoController.CanRedo);
public bool CanRedo()
{
redoStack.Count();
redoStack.Any();
return redoStack.Any();
}
public void Redo()
{
if (redoStack.Count() <= 0) throw new InvalidOperationException();
IUndoRedoCommand command = redoStack.Pop();
undoStack.Push(command);
command.Execute();
}
public class UndoRedoController
{
private static UndoRedoController controller = new UndoRedoController();
private readonly Stack<IUndoRedoCommand> undoStack = new Stack<IUndoRedoCommand>();
private readonly Stack<IUndoRedoCommand> redoStack = new Stack<IUndoRedoCommand>();
private UndoRedoController() { }
public static UndoRedoController GetInstance() { return controller; }
There has been a hiatus with MVVMLight due to the fact that after the .NET 4.5 update the CommandManager no longer fires the can execute check. This has since been solved. Instead of including the GalaSoft.MvvmLight.Command namespace you should use the GalaSoft.MvvmLight.CommandWpf namespace. The RelayCommand defined in that namespace is still checking the CanExecute function that you pass to the command.
Took me about a day to find out what the hell was going wrong in my application. I hope this will help some of you.
Here is a blog post by the developer explanining why this is necessary.
For some reason you have to do the following:
public RelayCommand RedoCommand{
get;
set;
}
you can also put private before set optional, depending on your access level. Then you do
RedoCommand = new RelayCommand(() => undoRedoController.Redo(), () => undoRedoController.CanRedo());
Now your able to call RedoCommand.RaiseCanExecuteChanged();
And everything works.
If you are using an unpatched .net 4.5. Microsoft broke the .CanExecute event.
http://connect.microsoft.com/VisualStudio/feedback/details/753666/net-4-0-application-commands-canexecute-not-updating-in-4-5
If you are using the RelayCommand from http://msdn.microsoft.com/en-us/magazine/dd419663.aspx#id0090030 and are not raising the CanExecuteChanged event when redoStack changes.
(Answering from a Silverlight perspective so assuming this will help you.)
Are you doing a RedoCommand.RaiseCanExecuteChanged() anywhere? Once whatever condition you are monitoring changes, you'll need to raise this command manually.
EDIT
Since you are using MVVM Light.. Heres sample code:
RedoCommand = new RelayCommand(undoRedoController.Redo,undoRedoController.CanRedo);
public bool CanRedo()
{
redoStack.Count();
redoStack.Any();
return redoStack.Any();
}
public void Redo()
{
if (redoStack.Count() <= 0) throw new InvalidOperationException();
IUndoRedoCommand command = redoStack.Pop();
undoStack.Push(command);
command.Execute();
// At this point, your stacks have changed; that is, the stacks
// may or may not contain items. Thus, raise the commands CanExecute part
// which will in turn enable/disable the commands based on the functions
// return value
RedoCommand.RaiseCanExecuteChanged();
// assuming you could possibly have an UndoCommand somewhere
UndoCommand.RaiseCanExecuteChanged();
}

Custom UpdateSourceTrigger with delay?

I'm looking to create a custom version of UpdateSourceTrigger that I can use with my binding. I don't know if this is possible, or if instead, I'd need to just create my own binding class. What I'm looking for is, instead of LostFocus or PropertyChanged, have something where it will update the source after some specified time limit.
I found this, but I don't know if there's a better way (one of the comments mentioned some memory leaks with the implementation).
Any ideas?
I just noticed that WPF 4.5 has a Delay Property, see for more information this link
http://www.shujaat.net/2011/12/wpf-45-developers-preview-delay-binding.html
I wouldn't bother doing this at the binding level, but would instead manifest it in my view model. When the property changes, restart a DispatcherTimer. When the timer expires, kick off your logic. It's that simple.
This can be easily implemented using Reactive Extensions's Throttle() method in conjunction with an observable property.
public class ObservablePropertyBacking<T> : IObservable<T>
{
private readonly Subject<T> _innerObservable = new Subject<T>();
private T _value;
public T Value
{
get { return _value; }
set
{
_value = value;
_innerObservable.OnNext(value);
}
}
#region IObservable<T> Members
public IDisposable Subscribe(IObserver<T> observer)
{
return _innerObservable
.DistinctUntilChanged()
.AsObservable()
.Subscribe(observer);
}
#endregion
}
Used like this:
// wire query observable
var queryActual = new ObservablePropertyBacking<string>();
queryActual.Throttle(TimeSpan.FromMilliseconds(300)).Subscribe(DoSearch);
Implement property:
string query;
public string Query
{
get { return query; }
set
{
queryActual.Value = value;
}
}

Create a new ICommand object in the ViewModel

Both ICommand objects are bound to a ViewModel.
The first approach seems to be used often.
But the second one saves some lines of code but would it not create everytime a new ICommand object when the Binding is refreshed so its a waste of resources?!
private LightCommand _deleteDocumentCommand;
public LightCommand DeleteDocumentCommand
{
get { return _deleteDocumentCommand ?? (_deleteDocumentCommand = new LightCommand(() => DeleteDocument(), () => CanDeleteDocument)); }
}
public LightCommand DeleteDocumentCommand
{
get { return new LightCommand(() => DeleteDocument(), () => CanDeleteDocument); }
}
Yes your 2nd method creates a new command every time the command is referenced, but I also find your 1st method rather hard to read.
My preferred way to make a command in the ViewModel is
private LightCommand _deleteDocumentCommand;
public LightCommand DeleteDocumentCommand
{
get
{
if (_deleteDocumentCommand == null)
{
_deleteDocumentCommand = new LightCommand(
() => DeleteDocument(), () => CanDeleteDocument);
}
return _deleteDocumentCommand;
}
}
It might be more lines of code, but it is easy to read and understand. Besides, usually all my public Properties/Commands are generated by macros and dumped into a #region Properties area that stays collapsed the entire time I work with the ViewModel, so I don't have to scroll through pages of get/set methods.
yes it would. You are better off instantiating it once, so that you have something like this:
LightCommand DeleteCommand { get; set;}
and then in our VM instantiation you assign to it. Or you could use your first example.
I assume you're looking for verification, and that's correct:
Everytime the Binding is refreshed, (or the command is called), a new object is instantiated. It looks like a clear waste of resources.

Resources