-5

Im using System.Windows.Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Input, new Action(() => ... for a wpf graphic refresh.

It works in my other function greatfully, but in my SQL delete function it wount be triggered/executed.

I tried it with System.Windows.Forms.Application.DoEvents(); but it wount do anything.

Set_Loading_Changed()
{
    System.Windows.Application.Current.Dispatcher.BeginInvoke(
        DispatcherPriority.Input, 
        new Action(() =>
        {
            if (BLoading)
            {
                DataGrid_Anzeige.IsEnabled = false;

                Mouse.OverrideCursor = System.Windows.Input.Cursors.Wait;
            }
            else
            {
                DataGrid_Anzeige.IsEnabled = true;
        
                Mouse.OverrideCursor = null;
            }
        }));
}
Btn_Remove()
{
    ...
    Set_Loading_Changed();

    using (OleDbConnection ODC = new OleDbConnection("..."))
    {
        foreach (var selectedRow in DataGrid_Anzeige.SelectedItems.OfType<DataRowView>())
        {
            sSQL_Statement = "...";

            ODC.Open();
            OleDbCommand ODCmd = new OleDbCommand(sSQL_Statement, ODC);

            ODCmd.ExecuteNonQuery();
            ODC.Close();

EDIT:

I insert the complete part of my Set_Load_Changed() function, hope you can get a clue with this informations.

Im using it primarly in my search Thread (Task.Factory.StartNew(() => { ... }));) so it must be the DispatcherPriority.Input.

Hille
  • 1,763
  • 18
  • 29
  • 3
    What are you doing inside BeginInvoke? And how do you know that it doesn't run? Try to change the priority to Normal. – mm8 Oct 25 '17 at 13:08
  • @mm8 becouse its running fine in 2 other functions where i Need the Input priority. Gimme a sec i edit the post. – Hille Oct 25 '17 at 13:09
  • Do you want the Set_Loading_Changed() to be executed *before* your SQL or what is your issue? – mm8 Oct 25 '17 at 13:10
  • Yea thats what i Need to do, i deactivates the Buttons and textboxes. @mm8 – Hille Oct 25 '17 at 13:18
  • 2
    Try to call Invoke instead of BeginInvoke. – mm8 Oct 25 '17 at 13:19
  • Now the `Mouse.OverrideCursor = System.Windows.Input.Cursors.Wait;` works. Ist enough for my purpose, thx :) – Hille Oct 25 '17 at 13:23
  • Great. I posted an answer. Please accept it if your issue has been solved. – mm8 Oct 25 '17 at 14:25
  • None of this mess would be happening if you had an actual ICommand & used MVVM instead of mutilating WPF like this. – Maverik Jan 05 '18 at 13:31
  • 2
    This _should_ work as written. My guess would be that you are resetting the cursor somewhere else in your code, or proceeding to block the UI thread before the cursor update is applied. Try calling `Mouse.UpdateCursor()` immediately after you set `OverrideCursor`. If that doesn't help, find every place in your code where you override the cursor, set a breakpoint, and see if you're clearing your cursor somewhere unexpectedly. – Mike Strobel Jan 05 '18 at 16:06
  • @Hille - This question seems to target the BeginInvoke issues in WPF. But if you're just looking for a solution to update your mouse cursor specifically, just let me know and perhaps update your question. I remember having a lot of trouble with changing the cursor in WPF and came up with a pretty good solution. – Oceans Jan 11 '18 at 12:02
  • @Oceans the problem is: the mouse cursor does not update. I can only update it with an invoke and it doesn't work – Hille Jan 11 '18 at 12:28
  • [How can I improve my question? ](https://meta.stackoverflow.com/questions/361801/what-can-i-improve-in-my-question) – Hille Jan 11 '18 at 12:36

3 Answers3

25

You're running into a common issue with misunderstanding the WPF threading system. The way WPF is structured is with one thread for the program to run and modify the UI in, usually called the UI thread, and a second thread which you have no normal way of using, which automatically renders the UI, commonly called the rendering or compositing thread.

The key point you need to know here is that if you stall the UI thread with a large operation (like a database read or a large calculation) immediately after BeginInvoke(), then you're preventing the UI thread from running those commands until you allow it to invoke the next action. BeginInvoke() simply queues the action to be performed the next time the dispatcher is allowed - the dispatcher will not interrupt what is currently being done. Setting the priority to Input ensures that it will be handled ahead of other lower priority work, but still will not cause it to interrupt your current method.
If you instead call Invoke(), you are interrupting your work to ask the dispatcher to perform the action and then return to what you're doing when it's finished.

While this is preferable to the behavior you're currently getting, this isn't how you're intended to use the dispatcher, and will still cause your app to appear 'frozen' while it completes the long operation. To avoid this, the easiest thing to do is run the long operation in a Task, using the async/await keywords and the Task Parallel Library.

Stephen Cleary has an excellent blog where he covers a lot of topics related to this. His introductory post (dating back to the keywords' introduction) is here. I would encourage poking around his blog if you have more issues in this area - he's one of the leading experts in explaining this area, and has covered most of the problems you run into.


Further reading:
What's the difference between Invoke() and BeginInvoke()?
WPF Threading Model

Martin Backasch
  • 1,485
  • 1
  • 18
  • 24
Zarenor
  • 1,101
  • 1
  • 11
  • 22
2

To change the cursor in WPF is unfortunately not as straightforward as in WinForms. I remember struggling with it myself until I stumbled upon the following solution. I didn't come up with this myself, I'll try and find the source to give credit where it is due.

using System;
using System.Collections.Generic;
using System.Windows.Input;

namespace MyNamespace
{
    public class OverrideCursor : IDisposable
    {
        static Stack<Cursor> s_Stack = new Stack<Cursor>();

        public OverrideCursor(Cursor changeToCursor = null)
        {
            if (changeToCursor == null)
                changeToCursor = Cursors.Wait;

            s_Stack.Push(changeToCursor);

            if (Mouse.OverrideCursor != changeToCursor)
                Mouse.OverrideCursor = changeToCursor;
        }

        public void Dispose()
        {
            s_Stack.Pop();
            var cursor = _stack.Count > 0 ? _stack.Peek() : null;
            if (Mouse.OverrideCursor != cursor)
                Mouse.OverrideCursor = cursor;

        }
    }
}

Now this disposable class can be used anywhere in your project to change the cursor temporarily.

using (new OverrideCursor())
{
    //your code 
}

This will change the cursor to anything you want by passing the cursor as parameter of the constructor, or nothing to use Cursors.Wait by default. For the time needed to execute any code placed inside the using-block the cursor will be changed turning back to normal afterwards.
You can also initiate an object of the class without the using-block to set it indefinitely but you shouldn't forget to call Dispose() when done.

Edit: source: https://stackoverflow.com/a/675686/4579864

Oceans
  • 3,340
  • 2
  • 16
  • 36
0

If want to do whatever you are doing in Set_Loading_Changed() before you connect to the database, you should call Invoke instead of BeginInvoke:

Set_Loading_Changed()
{
    System.Windows.Application.Current.Dispatcher.Invoke(...);
}

What's the difference between Invoke() and BeginInvoke()

mm8
  • 135,298
  • 10
  • 37
  • 59