Materialdesigninxamltoolkit: [BUTTON EFFECT DELAY] Ripple effect starts only AFTRE its corresponding function is completed.

Created on 15 Jan 2020  Â·  6Comments  Â·  Source: MaterialDesignInXAML/MaterialDesignInXamlToolkit

Overview

Hello,

We are now implementing the library into a relatively big/complicated system (with about 200 screens).

Keeping developing the system, we have found the problem; The button's ripple effect can only get fired AFTRE the corresponding function is all finished. So, there is some gap (especially when the processing is heavy(3-4 seconds seriously)).

Our understanding of the main purpose or concept of the Material Design's micro interaction(ripple effect) is letting users know that their action is successfully started so they can avoid confusion. Therefore accordingly, we do not think the current move is appropriate leading our users interviewed to the answers " When we click the buttons, we don't know what's happening" And we believe that this issue should be fixed on the library layer.

Image

rippleissue gif

Code

XAML file

<!-- EmpButton derives from Button -->
<cc:EmpButton
    Style="{StaticResource EmpButtonStyle.Plain}"
    Command="{Binding RefreshCommand}"

Content="一覧を更新"/>

ViewModel file

RefreshCommand.Subscribe(() =>
{
    //heavy method… like getting data from servers
    RefreshUketsukePatientList(false);

});

What we tried (and failed)

Firstly, we tried to asyncronize the method. However, UI processing methods(like ones opening a dialog) didn’t let us do so. Plus we already have got synchronous codes that we cannot touch. Then we gave up.

Secondly, we tried to overcome it by creating and using a wrapper letting us execute methods aftre ripple effect has been completed. But it had shown several side effects; Not opening dialogs and not showing messages etc. So we decided not to use the way as well.

So please help us

It is our pleasure if you could change the timing of the effect to start BEFORE the function runs. Please ask questions if you do not see what I meant above (Because of my English and so forth)

Thank you so much.

question

Most helpful comment

If you have some synchronous code in the main thread/UI and doesn't want freeze the UI while doing it, a possible workaround would be to use the dispatcher and the Task.Run() like this:

```c#
await this.Dispatcher.Invoke(new Func(() =>
{
return Task.Run(() => SynchronousVoidMethod());
}));

//If your method returns something like a T value
var TValue = await this.Dispatcher.Invoke(new Func>(() =>
{
return Task.Run(() => SynchronousTMethod());
}));
```

All 6 comments

Your understanding of MDIX is probably fine, your understanding of C#'s async/await and cross thread is probably what needs some training. And it sounds like you need... Async commands!
Article here: https://johnthiriet.com/mvvm-going-async-with-async-command/#
Repository here: https://github.com/johnthiriet/AsyncVoid/tree/master/AsyncVoid/AsyncVoid

Here's an simplified example for you:
```C#
public AsyncCommand SetTandStatusCommand { get; }
...
SetTandStatusCommand = new AsyncCommand(async (object obj) =>
{
if (obj is TandStatus tandStatus)
{
await Task.Delay(5000);
}
});


If you need to update main thread/UI of example showing a dialog
```C# 
Dispatcher.CurrentDispatcher.Invoke(async () => 
{
    ConfirmSaveDialog dialog = new ConfirmSaveDialog();
    object result = await _dialogHostService.Show(dialog, "RootDialog");

    if (result is DialogHostCancellation || result is bool answer && !answer)
    {
        return;
    }
    // answer true
});

The animation is able to run to its completion since I am not blocking main thread.
If you block main thread = No UI interaction or animation.

@jespersh is exactly correct. It is also worth mentioning that if your applications UI is not responsive for several seconds Windows will flag it as not responsive and give the user an opportunity to terminate it. You want to make sure that you are moving any long running processes to async, and not synchronous.

@jespersh @Keboo Thank you very much. We are now trying to change to Async command according to jesperch's example. The only remaining (and serious..) problem we have got is there are tons of synchronous codes that have been already implemented in the system. And we do not have the way to fix them AT ONCE, Realistically speaking, We cannot manually change all of the methods. So, if you can come up with other ideas, please give us clues.. But again, thanks mates!

.

What is your synchronous code doing?

If you have some synchronous code in the main thread/UI and doesn't want freeze the UI while doing it, a possible workaround would be to use the dispatcher and the Task.Run() like this:

```c#
await this.Dispatcher.Invoke(new Func(() =>
{
return Task.Run(() => SynchronousVoidMethod());
}));

//If your method returns something like a T value
var TValue = await this.Dispatcher.Invoke(new Func>(() =>
{
return Task.Run(() => SynchronousTMethod());
}));
```

Thanks I will try it!!

Was this page helpful?
0 / 5 - 0 ratings