Execute command after view is loaded WPF MVVM - wpf

I have a project based WPF and MVVM.
My project is based on a wizard containing a content control which shows my views (User Controls)
I want to execute a command after the view is loaded completely, I would like the user to see the view UI immediately after the command will be executed.
I tried using :
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<i:InvokeCommandAction Command="{Binding StartProgressCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
But the command is executed before I see the view UI and it's not what I'm looking for.
Does anyone have an idea how should I need to implement it?

You could use the Dispatcher for this and set the priority to ApplicationIdle so that it will on execute when everything has finished
Application.Current.Dispatcher.Invoke(
DispatcherPriority.ApplicationIdle,
new Action(() =>
{
StartProgressCommand.Invoke(args);
}));
more information on the dispatcher http://msdn.microsoft.com/en-us/library/system.windows.threading.dispatcherpriority.aspx
cheers.
ste.

That's because even though technically the view is loaded (i.e: all the components are ready in memory), your app is not idle yet, and thus the UI isn't refreshed yet.
Setting a command using interaction triggers on the Loaded event is already good, as there is no better event to attach to.
Now to really wait until the UI is shown, do this in your StartProgress() (I'm assuming here that this is the name of the method that StartProgressCommand point to):
public void StartProgress()
{
new DispatcherTimer(//It will not wait after the application is idle.
TimeSpan.Zero,
//It will wait until the application is idle
DispatcherPriority.ApplicationIdle,
//It will call this when the app is idle
dispatcherTimer_Tick,
//On the UI thread
Application.Current.Dispatcher);
}
private static void dispatcherTimer_Tick(object sender, EventArgs e)
{
//Now the UI is really shown, do your computations
}

another way to do it:
define this xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" and xmlns:mi="http://schemas.microsoft.com/expression/2010/interactions" on your usercontrol XAML and add Microsoft.Expression.Interactions assembly on your project. use CallMethodAction on your trigger, just as bellow:
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<mi:CallMethodAction TargetObject="{Binding}" MethodName="StartProgressCommand"/>
</i:EventTrigger>
</i:Interaction.Triggers>
Put the triger inside the root element of your usercontrol, e.g: grid. And change your StartProgressCommand, in your ViewModel class, from command to plain old regular Method, e.g:
public void StartProgressCommand()
{
/* put your program logic here*/
}
It'll run the method exactly one time every time your user control rendered.

We use a the timer solution - i too was very dubious about this but it does seem to work fine.
public static class DispatcherExtensions
{
private static Dictionary<string, DispatcherTimer> timers =
new Dictionary<string, DispatcherTimer>();
private static readonly object syncRoot = new object();
public static void DelayInvoke(this Dispatcher dispatcher, string namedInvocation,
Action action, TimeSpan delay,
DispatcherPriority priority = DispatcherPriority.Normal)
{
lock (syncRoot)
{
RemoveTimer(namedInvocation);
var timer = new DispatcherTimer(delay, priority, (s, e) =>
{
RemoveTimer(namedInvocation);
action();
}, dispatcher);
timer.Start();
timers.Add(namedInvocation, timer);
}
}
public static void CancelNamedInvocation(this Dispatcher dispatcher, string namedInvocation)
{
lock (syncRoot)
{
RemoveTimer(namedInvocation);
}
}
private static void RemoveTimer(string namedInvocation)
{
if (!timers.ContainsKey(namedInvocation)) return;
timers[namedInvocation].Stop();
timers.Remove(namedInvocation);
}
}
Then we invoke using
Dispatcher.CurrentDispatcher.DelayInvoke("InitSomething",()=> {
DoSomething();
},TimeSpan.FromSeconds(1));

You can check IsLoaded property for view, when view is in loaded form it returns false, when view is fully loaded, this property become true.
Thanks,
Rajnikant

Have you tried binding to the ContentRendered event? It will occur after the loaded event, yet I´m not sure whether or not this is a gurantee that the UI thread has finished painting the window then.

You can Write a "Thread.Sleep(10000)" in the first line of "CommandExecute" method. Use the same loaded trigger.
if you don't want to use Thread.Sleep then you can go for "DispatcherTimer". Start a timer in your command execute method and shift all your code to timer tick event.
set your timer interval to 2 seconds, so that user will sen the UI.

I came with this solution for this one.
I wanted to use a boolean property set as true at start of work and to false at the end to allow to notify user of background work.
Basically, it uses
a DispatcherTimer to launch a method after UI render according to this
An async method wich will execute the Action passed as a parameter
Call :
this.LaunchThisWhenUiLoaded(() => { /*Stuff to do after Ui loaded here*/ });
Method :
private DispatcherTimer dispatchTimer;
private Action ActionToExecuteWhenUiLoaded;
/// <summary>
/// Handy method to launch an Action after full UI rendering
/// </summary>
/// <param name="toExec"></param>
protected void LaunchThisWhenUiLoaded(Action toExec)
{
ActionToExecuteWhenUiLoaded = toExec;
// Call UiLoaded method when UI is loaded and rendered
dispatchTimer = new DispatcherTimer(TimeSpan.Zero, DispatcherPriority.ContextIdle, UiLoaded, Application.Current.Dispatcher);
}
/// <summary>
/// Method called after UI rendered
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected async void UiLoaded(object sender, EventArgs e)
{
this.IsBusy = true;
if (ActionToExecuteWhenUiLoaded != null)
await Task.Run(ActionToExecuteWhenUiLoaded);
dispatchTimer.Stop();
this.IsBusy = false;
}
Maybe not the clean but it works as expected.

Related

RadBusyIndicator not showing PRISM/MEF/WPF from ViewModel

I am using MVVM/PRISM/MEF for my WPF application. It has one DataGrid with multiple records, and when one row is double clicked a separate view is added to region with multiple controls on it, the initialization of controls takes about 10 seconds for new screen, so thats why I want to show RadBusyIndicator during that time.
Following in the XAML
<!-- This is Main View -->
<!-- Module: MainModule, ViewModel: MainViewViewModel -->
<telerik:RadBusyIndicator IsBusy="{Binding IsBusy}" BusyContent="{Binding BusyContent}">
<!-- All PRISM regions are here -->
</telerik:RadBusyIndicator>
Its view model is
class MainViewViewModel : ViewModelBase
{
ImportingConstructor]
public MainViewViewModel(IEventAggregator eventAggregator, IRegionManager regionManager, IServiceLocator serviceLocator)
:base(eventAggregator, regionManager, serviceLocator)
{
eventAggregator.GetEvent<BusyStateChangedEvent>().Subscribe(OnBusyStateChanged,ThreadOption.BackgroundThread);
}
#region BusyStateChanged
private void OnBusyStateChanged(bool newState)
{
IsBusy = newState;
}
#endregion
}
And in other view when DataGrid row is double clicked ViewModelBase function is called, as follows
public class ViewModelBase
{
private NavigationItem global_navItem = null;
public virtual void OnNavigationItemChanged(NavigationItem item)
{
changeNav = true;
global_navItem = item;
//Firing event to change the state
EventAggregator.GetEvent<BusyStateChangedEvent>().Publish(true);
//Using BackgroundWorker, but its not showing any Busy Indicator as well
var bw = new BackgroundWorker();
bw.DoWork += bw_DoWork;
bw.RunWorkerCompleted += bw_RunWorkerCompleted;
bw.RunWorkerAsync();
}
void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
//Setting busy indicator to false
EventAggregator.GetEvent<BusyStateChangedEvent>().Publish(false);
}
void bw_DoWork(object sender, DoWorkEventArgs e)
{
//DisplayView function is taking too long
if (global_navItem != null) this.DisplayView(global_navItem);
}
}
public void DisplayView(NavigationItem item)
{
try
{
//This call is taking long as it initializes the View
MyCustomeUserControl view = this.ServiceLocator.GetInstance<MyCustomeUserControl>(item.viewName);
view.Region = this.Region;
}catch(Exception e)
{
}
}
Events are being fired correctly and view is displayed correctly, but my problem is that Busy indicator is not shown at all, when I double click on DataGrid row the GUI become unresponsive, and after some time the new view appears. I am in doubt that this is problem of GUI thread being busy, but what can I do to avoid this, I have used BackgroudWorker already?
EDIT
1- I am raising PropertyChanged event for IsBusy Property. and I have already tried all options for Thread in event subscription. i.e. Thread.BackgroundThread, Thread.UIThread and Thread.PublisherThread. but no change.
2- I have tested Thread.Sleep rather that DisplayView in bw_DoWork, and its showing RadBusyIndicator properly, so it means that GUI controls are being initialized in GUI thread, no matter I have created a BackgroundWorker for it.
Would the indicator appear if you use Thread.Sleep(5000) instead of this.DisplayView(global_navItem)?
I assume showing the view will use the UI thread and this will block the UI no matter you use a BackgroundWorker or not.
Edit:
As it seems like your UI loading operation blocks the UI thread and so your BusyIndicator, you can try to host one of them in a different thread. An approach is explained in this article.
Finally I have found a solution. For reference following post can be seen. I have implemented a child chrome-less window with RadBusyIndicator using the approach discussed in this post.
Creating multiple UI Threads in WPF

MVVM Light Messenger Action Executing Multiple Times

Thanks for the first response. I tried and it worked. I did not use attached behavior. I used EventTrigger.
xmlns:interactivity="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
<interactivity:Interaction.Triggers>
<!-- In order to Call Cleanup in ViewModel to unregister Messenger. -->
<interactivity:EventTrigger EventName="Unloaded">
<interactivity:InvokeCommandAction Command="{Binding ViewUnloadCommand}" />
</interactivity:EventTrigger>
</interactivity:Interaction.Triggers>
Then my view will call the command in ViewModel to unregister the Messenger when this view is unloaded.
Thanks again.
Thanks Laurent for your fantastic work on MVVM light.
I've been working on a WPF project using this framework. Then I encountered this issue. I tried to search it on Google, MSDN and StackOverFlow. I found this solution when Messgener is used between ViewModel and View. I would do something like this in CodeBehind file, to call Unregister in Unloaded event handler.
public FinishedTodoItemTreeViewUserControl()
{
InitializeComponent();
Messenger.Default.Register<DialogMessage>(this, FinishedTodoItemTreeViewModel.DeleteAllDoneItemsConfirmMessageToken, dialog =>
{
var confirmResult = MessageBox.Show(dialog.Content, dialog.Caption, dialog.Button, dialog.Icon);
dialog.ProcessCallback(confirmResult);
});
}
private void currentControl_Unloaded(object sender, RoutedEventArgs e)
{
Messenger.Default.Unregister(this);
}
But when I am doing this in ViewModel, when I should call Unregister or Cleanup? Because I still need to receive this message again when it happens. But I don't want to receive this message multiple times with just one shot.
Thanks in advance.
/// <summary>
/// Register to be observer of TodoItems change notification receiver.
/// </summary>
private void RegisterTodoItemsChangedNotification()
{
Messenger.Default.Register<UnfinishedTodoItemTreeViewModel>(this, UnfinishedTodoItemTreeViewModel.RelatedTodoItemsChangedMessageToken, itemTreeViewModel =>
{
if (itemTreeViewModel.ActionCategory == UnfinishedTodoItemTreeViewModel.TodoItemActionCategory.Done)
{
DecreaseRelatedFilterTodoItemCount(itemTreeViewModel.FilterType);
AllTodoItemCount -= 1;
}
else if (itemTreeViewModel.ActionCategory == UnfinishedTodoItemTreeViewModel.TodoItemActionCategory.Undone)
{
IncreaseRelatedFilterTodoItemCount(itemTreeViewModel.FilterType);
AllTodoItemCount += 1;
}
});
}
In the view model, you should unregister whenever it makes sense. I'm guessing you will want to do this when the control it is bound to is unloaded?
You can write an attached behavior for this- just be aware of the other reasons why unloaded may fire. See this answer for one example.

Is Dispatcher not required in MVVM patern with WPF?

I am starting a new thread and trying to update UI elements through properties defined in my View Model and I am able to do it without any error, but if I try to update UI elements through code-behind, it throws the known UI access error("The calling thread cannot access this object because a different thread owns it."). First question would be ..Whats the difference between the two approaches ? Second question would be when I would use Disptacher in ViewModel ideally ?
Code Behind
private void Button_Click(object sender, RoutedEventArgs e)
{
Thread th = new Thread(new ThreadStart(delegate()
{
textbox.Text = "Rajib";
}
));
th.Start();
}
//inside XAML
<TextBox x:Name="textbox" Text="{Binding UserInput, Mode=TwoWay}" />
MVVM
public string UserInput
{
get { return _UserInput; }
set { _UserInput = value; OnPropertyChanged("UserInput"); }
}
//Called through a ICommand property on a button click
public void ExecuteCommand(object obj)
{
InvokeCallThroughAnonymousDelegateThread();
}
private void InvokeCallThroughAnonymousDelegateThread()
{
ThreadStart start = delegate()
{
UserInput = "Calling from diff thread";
};
new Thread(start).Start();
}
Any attempt to update the UI must be done within the dispatcher thread. However, for property change events, WPF automatically dispatches for you when the event is raised from a background thread. You can read more about this on Bea Costa's (former WPF data binding PM) blog:
http://bea.stollnitz.com/blog/?p=34
They were going to do the same for INotifyCollectionChanged events but never got around to it in prior releases. For 4.5 they will now be synchronizing collection changed events automatically in addition to INotifyPropertyChanged.
The NotifyPropertyChanged has its thread context changed by WPF through the event, but your code behind doesn't change the thread context to the UI Thread. In your codebehind, use this instead:
Task.Factory.StartNew(() =>
{
// Background work
}).ContinueWith((t) => {
// Update UI thread
}, TaskScheduler.FromCurrentSynchronizationContext());
Regarding when to use the Dispatcher directly, I have a mid-sized project where I haven't used the Dispatcher in any ViewModel. I have used it to deal with Xaml resources, weak event handling, and it is used inside of MefedMVVM and Prism, which I also use.

Bound Button Not Enabling After Background Worker Process Completes

I have a background worker process that starts provisioning a new client for our system. Here is what the DoWork method looks like:
ProvisioningManager manager = new ProvisioningManager(false)
{
};
System.Windows.Application.Current.Dispatcher.Invoke((Action)(() =>
{
this.MaxSteps = manager.MaxProgress;
}));
manager.StatusUpdated += new ProvisioningManager.StatusUpdatedHandler(manager_StatusUpdated);
manager.TaskCompleted += new ProvisioningManager.TaskCompleteHandler(manager_TaskCompleted);
manager.ProvisionClient();
while (!manager.Completed)
{
System.Threading.Thread.Sleep(100 * 60);
}
Basically it creates the manager that handles talking to the different sub-systems which provision the client.
Now I have a status update event and completed event for the provisioning manager. When the TaskCompleted event fires I want to be able to set a property on my display object so that the finish button in the wizard is enabled:
void manager_TaskCompleted(object sender, ProvisioningManager.Task taskType)
{
System.Windows.Application.Current.Dispatcher.Invoke((Action)(() =>
{
this.ProvisioningComplete = true;
}));
}
The XAML for the button looks like this:
<wizard:WizardPage Header="Provisioning Client..."
ShowBack="False"
AllowBack="False"
AllowFinish="{Binding Source={StaticResource ResourceKey=dataObject}, Path=ProvisioningComplete}"
Loaded="Provisioning_Loaded">
</wizard:WizardPage>
This isn't working. Even though I make sure to hit the dispatcher thread to set the property of the display object it doesn't actually change the button to enabled until I click on the window. Is this a bug in AvalonWizard or am I not on the correct thread to set an INotifyPropertyChanged? Is there a way to hack this; basically can I programmatically focus the window without the mouse click?
I tired placing that while loop in the DoWork method so that I could use the BackgroundWorker's completed method:
void provisioningWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
System.Windows.Application.Current.Dispatcher.Invoke((Action)(() =>
{
this.ProvisioningComplete = true;
}));
}
That doesn't work either. What gives?!
Update
Here is the requested static resource instantiation for the display object:
<Window.Resources>
<ObjectDataProvider x:Key="dataObject" ObjectType="{x:Type winDO:NewClientWizardDO}" />
</Window.Resources>
Update II
Here is the property and property change firer:
public bool ProvisioningComplete
{
get { return this._ProvisioningComplete; }
set
{
this._ProvisioningComplete = value;
this.NotifyPropertyChanged("ProvisioningComplete");
}
}
protected void NotifyPropertyChanged(params string[] propertyNames)
{
if (this.PropertyChanged != null)
{
foreach (string propertyName in propertyNames)
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
sorry if I don't understand something, but is the ProvisioningComplete property marked as "volatile"? If not then this might be the problem.
So I couldn't find out exactly why I was having this issue. I tried setting focus to the window, the button, etc. I tried multiple ways of letting the view know the viewmodel had updated. Basically every suggestion I could find on the web didn't work. It almost seems like a bug.
A smarty on my team suggested faking a mouse click on the window. His idea was that since all it took to activate the button was a simple mouse click on the screen then faking one should have the same effect. I thought (and think) that this hack was ridiculous. I did try it out just to see if I could call it a "solution".
Well, it worked. We had this same problem in another one of our wizards (not AvalonWizard but a homegrown one). I think there has to be some underlying issue with the way the window redraws after a background thread updates objects that are bound to the UI.
Anyhow, the way I found to solve this issue is with the following hack-tastic code.
//import user32.dll and setup the use of the mouse_event method
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
/// <summary>
/// Watches for properties to change on the data object, mainly the ProvisioningComplete method
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void DataObject_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
switch (e.PropertyName)
{
case "ProvisioningComplete":
//if the provisioning is completed then we need to make the finish button selectable.
if (this.DataObject.ProvisioningComplete)
{
System.Windows.Application.Current.Dispatcher.Invoke((Action)(() =>
{
//give the window focus
this.Focus();
//update the layout
WizardPageProvisioningClient.UpdateLayout();
//fake mouse click 50 pixels into the window
mouse_event(MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP, (uint)(this.Left + 50), (uint)(this.Top + 50), 0, 0);
}));
}
break;
}
}
I've tested this when the window is not the active window and when the user leaves the window as selected. The focus method seems to take care of this issue when the window isn't active. Our QA team hasn't run a complete test against the UI so I can't say if there is any situations where it doesn't work, but it seems to be the best solution that I've come up with to date.
I'm open to any other suggestions if anyone out there has a better idea of what could be causing the button to not update.

Update UI from ViewModel class (MVVM pattern) in WPF

I'm using the MVVM pattern in my first WPF app and have a problem with something quite basic I assume.
When the user hits the "save" button on my view, a command gets executed that calls the private void Save() in my ViewModel.
The problem is that the code in "Save()" takes some time to execute, so I'd like to hide the "Save" button in the UI view before executing the large chunk of code.
The problem is that the view doesn't update untill all code is executed in the viewmodel.
How can I force the view to redraw and process the PropertyChanged events before executing the Save() code?
Additionally, I would like a reuseable way, so that I can easily do the same thing in other pages as well.. Anyone else made something like this already? A "Loading..." message?
If it takes a long time, consider using a separate thread, for example by using a BackgroundWorker, so that the UI thread can stay responsive (i.e. update the UI) while the operation is performed.
In your Save method, you would
change the UI (i.e. modify some INotifyPropertyChanged or DependencyProperty IsBusySaving boolean which is bound to your UI, hides the Save button and maybe shows some progress bar with IsIndeterminate = True) and
start a BackgroundWorker.
In the DoWork event handler of your BackgroundWorker, you do the lengthy saving operation.
In the RunWorkerCompleted event handler, which is executed in the UI thread, you set IsBusySaving to false and maybe change other stuff in the UI to show that you are finished.
Code example (untested):
BackgroundWorker bwSave;
DependencyProperty IsBusySavingProperty = ...;
private MyViewModel() {
bwSave = new BackgroundWorker();
bwSave.DoWork += (sender, args) => {
// do your lengthy save stuff here -- this happens in a separate thread
}
bwSave.RunWorkerCompleted += (sender, args) => {
IsBusySaving = false;
if (args.Error != null) // if an exception occurred during DoWork,
MessageBox.Show(args.Error.ToString()); // do your error handling here
}
}
private void Save() {
if (IsBusySaving) {
throw new Exception("Save in progress -- this should be prevented by the UI");
}
IsBusySaving = true;
bwSave.RunWorkerAsync();
}
You're using MVVM pattern, so your Save Button's Command is set to an instance of the RoutedCommand object which is added to the Window's CommandBindings collection either declaratively or imperatively.
Assuming that you do it declaratively. Something like
<Window.CommandBindings>
<CommandBinding
Command="{x:Static namespace:ClassName.StaticRoutedCommandObj}"
CanExecute="Save_CanExecute"
Executed="Save"
/>
</Window.CommandBindings>
For the handler of Executed routed event, your Save() method, on entry, you set a variable to false, on return you set it back to true. Something like.
void Save(object sender, ExecutedRoutedEventArgs e)
{
_canExecute = false;
// do work
_canExecute = true;
}
For the handler of the CanExecute routed event, the Save_CanExecute() method, you use the variable as one of the condition.
void ShowSelectedXray_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = _canExecute && _others;
}
I hope I am clear. :)
You could always do something like this:
public class SaveDemo : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private bool _canSave;
public bool CanSave
{
get { return _canSave; }
set
{
if (_canSave != value)
{
_canSave = value;
OnChange("CanSave");
}
}
}
public void Save()
{
_canSave = false;
// Do the lengthy operation
_canSave = true;
}
private void OnChange(string p)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(p));
}
}
}
Then you could bind the IsEnabled property of the button to the CanSave property, and it will automatically be enabled/disabled. An alternative method, and one I would go with would be to use the Command CanExecute to sort this, but the idea is similar enough for you to work with.
You can accomplish this by the following code..
Thread workerThread = null;
void Save(object sender, ExecutedRoutedEventArgs e)
{
workerThread = new Thread(new ThreadStart(doWork));
SaveButton.isEnable = false;
workerThread.start();
}
do all your lengthy process in dowork() method
in some other method...
workerThread.join();
SaveButtton.isEnable = true;
This will cause to run save lengthy process in another thread and will not block your UI, if you want to show an animation while user click on save button then show some progress bar like iPhone etc... give me feedback i'll try to help you even more.
Late answer, but I figured it'd be good to input a bit as well.
Instead of creating your own new thread, it would probably be better to leave it up to the threadpool to run the save. It doesn't force it to run instantly like creating your own thread, but it does allow you to save threading resources.
The way to do that is:
ThreadPool.QueueUserWorkItem(Save);
The problem with using this approach, as well, is that you're required to have your "Save()" method take in an object that will act as a state. I was having a similar problem to yours and decided to go this route because the place that I'm working is very Resource-Needy.

Resources