Mare Infinitus
Mare Infinitus

Reputation: 8182

UI not updating from EventAggregator

For a project I use a very simple progress overlay.

It just displays a small marquee progressbar and covers the screen.

So in my ShellView I have

<Border Grid.Row="0"
        Grid.RowSpan="11"
        Grid.Column="0"
        Grid.ColumnSpan="11"
        HorizontalAlignment="Stretch"
        VerticalAlignment="Stretch"
        Panel.ZIndex="3"
        Background="#9E000000"
        BorderBrush="Black"
        BorderThickness="3"
        Visibility="{Binding IsProgressing,
                                 Converter={StaticResource BoolToVisibility}}">

        <!-- omitted progressbar, text etc -->
</Border>

And I have a very simple event, which just sets the Visibility (IsProgressing) binding and some text to show.

Whenever I want to have a progressbar, I just publish that event, like

_eventAggregator.Publish(new ProgressingChange(true, "Loading ..."));

This works very well so far, besides one case:

For the application I use events for navigation of my screens.

So there is another event which I publish, like:

_eventAggregator.Publish(new NavigationEvent(typeof(TargetViewModel)));

which just sets the target screen:

public void Handle(NavigationEvent navigate)
{
    var target = _screenFactory.FromType(navigate.TargetScreen);
    this.ActivateItem(target);
}

One of my Screens has lots of items and takes about 3 seconds to load.

So I wanted to show my Progress overlay while the screen is loading.

This is what does not work. Both the new Screen and the Overlay are showing simultaneously when those events are combined.

This is:

_eventAggregator.Publish(new ProgressingChange(true, "Loading ..."));
_eventAggregator.Publish(new NavigationEvent(typeof(LongLoadingViewModel)));

For debugging reasons I did not deactivate the Progressing overlay to the what is happening.

So the screen is loaded, nothing is shown on the screen for about 3 seconds, and then both the Progress overlay and the new screen are shown.

I have tried

What am I missing?

What is happening here?

How can I get that to work?

-edit-

Here is the code of my Handler method:

public void Handle(ProgressingChange progressing)
{
    this.IsProgressing= progressing.IsProgressing;
    this.ProgressingText= progressing.ProgressingText;
    NotifyOfPropertyChange(() => IsProgressing);
    NotifyOfPropertyChange(() => ProgressingText);  
    // of course, there is Notify in the setters themselves, too
}

And I used this code from the source linked above to force UI updates, but that did not work

void AllowUIToUpdate() {

DispatcherFrame frame = new DispatcherFrame();

Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Render, new DispatcherOperationCallback(delegate(object parameter)
{
    frame.Continue = false;

    return null;
}), null);

Dispatcher.PushFrame(frame);

}

Also, I tried publishing in a critical section to force the first publish to be executed before the second one, but that did not work either.

-edit2- Code that at least shows the Progress overlay

_eventAggregator.Publish(new ProgressingChange(true, "Activating ..."));
Dispatcher.CurrentDispatcher.BeginInvoke(new Action(() => _eventAggregator.Publish(new NavigationEvent(typeof(LongLoadingViewModel)))), DispatcherPriority.ContextIdle, null);

Upvotes: 0

Views: 836

Answers (1)

Wim.van.Gool
Wim.van.Gool

Reputation: 1330

It might be a hack, but you could try to first wait for the Border (porgress bar) to be rendered before the navigation-event is published. To do this, you might be able to adapt the solution that is given here to execute some code when the UI-thread is no longer busy (see link for full explanation):

Dispatcher.BeginInvoke(new Action(() => Trace.WriteLine("DONE!", "Rendering")), DispatcherPriority.ContextIdle, null);

If this works, then at least you have something to start off making the code a bit cleaner.

Upvotes: 1

Related Questions