UI不会在具有Caliburn Micro和BusyIndi​​cator控件的WPF MVVM应用程序中按预期更新

时间:2022-11-27 19:37:01

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.

我正在使用Caliburn Micro在WPF中编写应用程序。在他们网站上的一些教程之后,我想从Xceed.Wpf.Toolkit实现一个BusyIndi​​cator控件。我正在使用协同程序并从我的方法返回IEnumerable来做3件事:显示忙碌指示器,切换屏幕,隐藏忙碌指示器。看起来很简单,但最新发生的是,BusyIndi​​cator永远不会出现。我认为有一些我不了解WPF呈现其控件的方式。这是一些代码。

This is my Loader class for displaying my BusyIndicator control on the ShellView.xaml

这是我的Loader类,用于在ShellView.xaml上显示我的BusyIndi​​cator控件

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.

这是我的ShowScreen类,它通过获取IShell并调用ActivateItem导航到下一个屏幕。这里没什么好看的。

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.

我几乎觉得我需要告诉WPF以某种方式明确地显示我的BusyIndi​​cator。就像我告诉它时不会立即显示BusyIndi​​cator。当我取出最后一个Loader.Hide()命令时,它导航到下一个屏幕,然后穿上BusyIndi​​cator。这让我疯了。

1 个解决方案

#1


1  

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

整晚搞砸了这个蠢事之后我终于找到了解决办法。在我的ShowScreen类中,我需要在这样的Task.Factory.StartNew()中显示屏幕的显示

    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.

现在一切按我希望它执行的顺序执行。感谢@pushpraj的想法。

#1


1  

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

整晚搞砸了这个蠢事之后我终于找到了解决办法。在我的ShowScreen类中,我需要在这样的Task.Factory.StartNew()中显示屏幕的显示

    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.

现在一切按我希望它执行的顺序执行。感谢@pushpraj的想法。