0

Below is the short example for my question. At the beginning of the function I would like to change some visual paramater, such hide window, make it red ("my example") and at the end of the function, I would like to put it back. Somethig like mouse wait cursor for long-term function.

Is there any elegant way to make the button red before function OnButtonClick ends - way to allow window message loop to process request Background=Red in parallel way and immediately redraw window.

private void OnButtonClick(object sender, RoutedEventArgs e)
{            
            System.Windows.Media.Brush br = ((Button)sender).Background;
            ((Button)sender).Background = System.Windows.Media.Brushes.Red;

            Mouse.OverrideCursor = Cursors.Wait;
            Function(); //long-term function 
            Mouse.OverrideCursor = Cursors.Arrow;

            ((Button)sender).Background = br;                  
}
Viliam
  • 636
  • 9
  • 17

2 Answers2

1

You could run it in a new thread, either a background worker or by using Task Parallel library (see below).

private void OnButtonClick(object sender, RoutedEventArgs e)
{            
            System.Windows.Media.Brush br = ((Button)sender).Background;
            ((Button)sender).Background = System.Windows.Media.Brushes.Red;

            Mouse.OverrideCursor = Cursors.Wait;

            // Run function in a new thread.
            Task.Factory.StartNew(() =>
            {
                Function(); // Long running function.
            })
            .ContinueWith((result) =>
                {
                    // Runs when Function is complete...
                    Mouse.OverrideCursor = Cursors.Arrow;
                    ((Button)sender).Background = br;       
                }); 


}

If you are using .NET 4.5 you can also do this with Async / Await keywords too.

BenjaminPaul
  • 2,931
  • 19
  • 18
  • Thank you, however this is not exactly what I am looking for, or perhaps another question: I do not want the user to click on another button and interact with the window. The long running functinon is not that long and originally I use only waiting cursor and now I just want to give the user a better visual information about the busy window. With the background task it is not realy busy for user. – Viliam Sep 26 '14 at 09:15
  • 1
    Your question asks for a "Parallel" way of running Function(). I think this answer perfectly answers the question to be honest. If you have a different question please start a new one instead of changing this question after people have taken the time to answer. – BenjaminPaul Sep 26 '14 at 09:17
  • 2
    @user2136076, then you could try this extension method: [Refresh / Update WPF controls](http://geekswithblogs.net/NewThingsILearned/archive/2008/08/25/refresh--update-wpf-controls.aspx) – Bolu Sep 26 '14 at 14:30
  • Benjamin, I appreciate the idea with Task and especially your efforts to help. But I try it and it did not do exactly what I was looking. The qeestion was parallel way of chanding color, not to run the Function(). And the second question was not the change of the first one, it was just for you, if you have idea how to continue to fix the lack of the Task solution. My fault, the question was not well asked. Bolu guided me to a solution I was looking for. Thank you all. – Viliam Sep 27 '14 at 09:25
0

There is an easy way of doing this.
xaml:

<Button Command="{Binding TestCommand}" Content="Test" Focusable="False" Background="Green">
    <Button.Resources>
        <Style TargetType="Button">
            <Style.Triggers>
               <DataTrigger Binding="{Binding Loading}" Value="true">
                   <Setter Property="Background" Value="Red"/>
               </DataTrigger>
            </Style.Triggers>
        </Style>
    </Button.Resources>
</Button>  

This way it will set the buttons background to the green if the Loading = false

.cs:

public class VM : INotifyPropertyChanged
{
    public bool Loading
    {
        get { return _loading; }
        private set
        {
            if (value.Equals(_loading)) return;
            _loading = value;
            OnPropertyChanged("Loading");
        }
    }
    public RelayCommand TestCommand
    {
        get { return _testCommand; }
    }

    void Test(object parameter)
    {
        Dispatcher.CurrentDispatcher.BeginInvoke( (Action) (() => Loading = true));
        //or if you want to do it with more responsive UI then use
        Dispatcher.CurrentDispatcher.Invoke( (Action) (() => Loading = true));
        doSomething(); //this could be replaced with BackgroundWorker DoWork function  
        //or this code could be the DoWork function.
        Loading = false;
    }
}
XAMlMAX
  • 2,268
  • 1
  • 14
  • 23
  • 1
    Elegant solution but you may run into a timing issue. I think it would be better to use Invoke in place of BeginInvoke to ensure it runs immediately. – Mark Travis Nov 04 '14 at 10:55
  • 1
    @MarkTravis `Invoke` is not guaranteed to start the delegate immediately but it will block the calling thread until the delegate has been serviced by the dispatcher thread - it is run synchronously. http://stackoverflow.com/questions/229554/whats-the-difference-between-invoke-and-begininvoke – Gusdor Nov 05 '14 at 10:38