Reputation: 2210
I'm writing an application in WPF using Caliburn Micro. Following some tutorials on their website I wanted to implement a BusyIndicator control from the Xceed.Wpf.Toolkit. I'm using coroutines and return IEnumerable from my method to do 3 things: show the busy indicator, switch screens, hide the busy indicator. Seems simple enough, but whats happening is, the BusyIndicator never shows up. I think there's something I don't understand about the way WPF renders its controls. Here's some code.
This is my Loader class for displaying my BusyIndicator control on the ShellView.xaml
public class Loader : IResult
{
private readonly String _message;
private readonly bool _hide;
private readonly IShell _shell;
public Loader(IShell shell, String message)
{
_message = message;
_shell = shell;
}
public Loader(IShell shell, bool hide)
{
_hide = hide;
_shell = shell;
}
public void Execute(CoroutineExecutionContext context)
{
var view = _shell.View as ShellView;
if (view == null)
return;
if (_hide)
{
view.BusyIndicator.IsBusy = false;
}
else
{
view.BusyIndicator.BusyContent = _message;
view.BusyIndicator.IsBusy = true;
// I WOULD ASSUME THIS WOULD IMMEDIATELY UPDATE THE BusyIndicator CONTROL TO SHOW BUT IT DOESNT
}
Completed(this, new ResultCompletionEventArgs());
}
public event EventHandler<ResultCompletionEventArgs> Completed = delegate { };
public static IResult Show(String message = null)
{
return new Loader(IoC.Get<IShell>(), message);
}
public static IResult Hide()
{
return new Loader(IoC.Get<IShell>(), true);
}
}
This is my ShowScreen class that navigates to the next screen by getting the IShell and calling ActivateItem. Nothing fancy here.
public class ShowScreen : IResult
{
private readonly Type _screenType;
public ShowScreen(Type screenType)
{
_screenType = screenType;
}
public void Execute(CoroutineExecutionContext context)
{
var screen = IoC.GetInstance(_screenType, null);
shell.ActivateItem(screen);
Completed(this, new ResultCompletionEventArgs());
}
public event EventHandler<ResultCompletionEventArgs> Completed;
public static ShowScreen Of<T>()
{
return new ShowScreen(typeof(T));
}
}
Both of these on their own work with no problems, its when I chain them together in a coroutine like this is when it doesnt work the way I'd expect:
public class HomeViewModel : Screen
{
public IEnumerable<IResult> OpenFirstPage()
{
yield return Loader.Show("Please Wait");
yield return ShowScreen.Of<FirstPageViewModel>();
yield return Loader.Hide();
}
}
I almost feel like I need to tell WPF to explicitly show my BusyIndicator somehow. Like it doesn't instantly show the BusyIndicator when I tell it to. When I take out the last Loader.Hide() command, it navigates to the next screen THEN shoes the BusyIndicator. This is driving me insane.
Upvotes: 0
Views: 532
Reputation: 2210
After messing with this stupid thing all night I've finally found a solution. In my ShowScreen class I needed to wrap the showing of the screen in a Task.Factory.StartNew() like this
public void Execute(CoroutineExecutionContext context)
{
Task.Factory.StartNew(() =>
{
object screen = null;
var shell = IoC.Get<IShell>();
if (_viewModel != null)
{
screen = _viewModel;
}
else
{
screen = !String.IsNullOrEmpty(_name)
? IoC.Get<object>(_name)
: IoC.GetInstance(_screenType, null);
}
shell.ActivateItem(screen);
Completed(this, new ResultCompletionEventArgs());
});
}
Now everything executes in the order I want it to execute. Thanks @pushpraj for the ideas.
Upvotes: 1