-3

SOLUTION

I found the solution by myself. Thanks for all the responses, it helped me on the right way.

There was actually a pretty simple way of doing it.

I just changed my Command to a Async Method (public async void "MethodName") and if I wanted to do the ExecuteAbortCommand in the back, I just did this:

    await Task.Run(() => {
    ExecuteAbortSessionCommand();
    });

It did the job for me. I hope it will help other people

Tobias
  • 15
  • 5
  • There's no point in delegating all action from BackgroundWorker back to the UI thread by Invoke. Only access to UI elements should be Invoke'd. OTOH, you shouldn't mix business logic and UI logic like this. – JeffRSon Oct 04 '16 at 10:35
  • Okay, you will prefer to make an invoke in the specific Command in example ExecuteAbortStructureCommand? But how do I get the Command to run in the background? – Tobias Oct 04 '16 at 10:40
  • 1
    Use `BackgroundWorker`. You will find lots of documentation and examples. If you "can't get any of these things to work" post "these things". – JeffRSon Oct 04 '16 at 10:46
  • Example, I tried to use this example in the link (The solution answer): http://stackoverflow.com/questions/14526356/ui-still-freezes-using-backgroundworker I did like the above example, I declared a Backgroundworker and putted a "DoWork" in my constructor that was refered to my code. In my AbortSessionCommand I putted "RunWorkerAsync" in so it executes the DoWork with my code – Tobias Oct 04 '16 at 10:52
  • Another example is this link: http://stackoverflow.com/questions/5483565/how-to-use-wpf-background-worker I delcared the background worker, I made the events in the constructor. In the DoWork command, I putted my code from the AbortSessionCommand in as I wanted to execute in the background. The RunWorkerAsync did i put in my AbortSessionCommand to execute the DoWork – Tobias Oct 04 '16 at 10:55
  • I am not seeing anything about that command that should take a lot of time. – paparazzo Oct 04 '16 at 11:02
  • In this example, it refreshes the Datagrid, so it takes like 25 seconds – Tobias Oct 04 '16 at 11:04
  • You should put *your* code and the actual problem inside the question itself. – JeffRSon Oct 04 '16 at 11:07
  • I have written my code in my question and I have told you my problem? I need to run this command as you see above in the background – Tobias Oct 04 '16 at 11:08
  • If it take 25 seconds to refresh a Datagrid doing it from background is not going to help that. VTC – paparazzo Oct 04 '16 at 11:14
  • Why not? If the process can be doing in the background while the UI is free to use to other functions, it would be useful? Another example is when I start the application, it loads a lot of data from servers, it maybe takes 30 seconds, there it would be useful as well. – Tobias Oct 04 '16 at 11:16
  • I would love to know how to execute a command in the background, then I will could use it in many differences ways – Tobias Oct 04 '16 at 11:21
  • BackgroundWorker is obsolete and shouldn't be used. Anything it can do can be done in a better way using Task and IProgress. In this case, it looks like the OP is trying to invoke WPF commands asynchronously, something BGW was never meant to handle. – Panagiotis Kanavos Oct 04 '16 at 11:21
  • @Tobias what are you actually trying to do? What is the *real* problem? Eg, execute a command in the background in response to a UI action? How are the commands implemented? Do you use an MVVM framework? You should be able to use `async/await` and `Task.Run` without trouble, to run long-running tasks in the background, then resume on the UI thread – Panagiotis Kanavos Oct 04 '16 at 11:22
  • @PanagiotisKanavos, my command is refreshing the datagrid which takes some time, but I would like to just make that execution in the background. So if I could let the process runs in the background, it will do my work – Tobias Oct 04 '16 at 11:23
  • Loading a grid is a UI operation. It can't be done in the background *nor should it*. No-one is able to view 1000 rows at once. If it takes too long to load a grid, you should use virtualization and/or paging to load only the data that is currently visible. Loading the *data* though can easily be done in the background. EF and ADO.NET have asynchronous execution methods – Panagiotis Kanavos Oct 04 '16 at 11:24
  • The time of loading the Datagrid is because it loads the data from a server. The command is asking for some values in the server and then load it in to my Datagrid. This command may run in the background – Tobias Oct 04 '16 at 11:29
  • Every time you write `catch (Exception e)` in your code you should slap yourself on the hand and promise never to do it again. This is how to create bugs. https://blogs.msdn.microsoft.com/ericlippert/2008/09/10/vexing-exceptions/ – Enigmativity Oct 04 '16 at 13:55
  • Okay, haha. I will remember that, thanks for the notice. When I run the code right now, I get the error with "The calling thread must be STA, because many UI components require this" – Tobias Oct 04 '16 at 14:01
  • `await Task.Delay(5);` means wait 5 milliseconds to slow the process down and simulate a slow task, MultifuntionsAccess can be deleted, your button click would call `.ExecuteAbortCommand();` – MikeT Oct 04 '16 at 14:11
  • I have removed the MultifunctionAccess Method and the CancelMethod. Do I still need to use Status Property then? – Tobias Oct 04 '16 at 14:14
  • How do I fix the error: "The calling thread must be STA, because many UI components require this" ?? – Tobias Oct 04 '16 at 14:16
  • I have added the code who gives the error – Tobias Oct 04 '16 at 14:22

2 Answers2

0

a couple of points you need to bare in mind when creatnig multithreaded tasks in wpf

firstly unless your background process are directly view related you shouldn't have the thread in your view, leave it in the view model where it belongs, this will remove most of the issues your are having with updates as these will be handled in the binding having no effect on your gui

second if you do need to do something view related, only use the Dispatcher.Invoke for the smallest possible transaction i.e. Textbox.Dispatcher.Invoke(()=>Textbox.Text = "something") this is because the dispatcher will lock both your thread and the element the dispatcher is attached to while it gets the dispatcher to execute the code in the primary GUI thread of the control

as you said your task is server related then their is not view element to it so yoif you keep it in the view model like it should be then your code should look something like this

public class LoaderViewModel 
{
    public LoaderViewModel()
    {
        loader.DataLoaded += Loader_DataLoaded;
    }
    public Dispatcher Dispatcher {get;  set; } 
    public ObservableCollection<DataViewModel> Values { get; } = new ObservableCollection<DataViewModel>();

    ModelLoader loader = new ModelLoader();

    public void LoadData()
    {
        loader.LoadData();
    }

    private void Loader_DataLoaded(object sender, IEnumerable<DataModel> e)
    {
        Dispatcher.Invoke(() => Values.Clear());
        foreach (var item in e)
        {
            Dispatcher.Invoke(()=>Values.Add(new DataViewModel( item)));
        }
    }

}

public class DataViewModel : INotifyPropertyChanged
{
    public DataViewModel(DataModel model)
    {
        Model = model;
        Model.ValueLoaded += Model_ValueLoaded;
    }

    private void Model_ValueLoaded(object sender, EventArgs e)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Text"));
    }

    public event PropertyChangedEventHandler PropertyChanged;

    public DataModel Model { get;private set; }
    public string Text
    {
        get { return Model.Value; }
    }



}
public class DataModel
{
    public DataModel(int id)
    {
        ID = id;
    }

    public int ID { get; set; }

    private string _Value = "Loading...";
    Task backgroundLoad;
    public string Value
    {
        get {

            if (backgroundLoad == null && _Value == "Loading...")//background Load on Demand
            {
                backgroundLoad = Task.Run(async () => 
                {
                    Value = await Server.GetDataValues(ID);
                });
            }

            return _Value;

        }
        set
        {

            if (_Value != value)
            {
                _Value = value;
                ValueLoaded?.Invoke(this, EventArgs.Empty);
            }
        }
    }

    public event EventHandler ValueLoaded;
}
public class ModelLoader
{
    public void LoadData()
    {
        loading = Task.Run(async()=>
        {
            var results = (await Server.GetDataIDs()).Select(i => new DataModel(i));

            DataLoaded?.Invoke(this, results);
        });
    }

    private Task loading;

    public event EventHandler<IEnumerable<DataModel>> DataLoaded;

}
//simulates a slow responding server
public static class Server
{
    private static Random rnd = new Random();
    public static async Task<IEnumerable<int>> GetDataIDs()
    {
        await Task.Delay(5000);
        return Enumerable.Range(1, 15);
    }
    public static async Task<string> GetDataValues(int id)
    {
        await Task.Delay(rnd.Next(100,6000));
        return $"Values {id}";
    }
}

the View for the quick demo i knocked up was

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApplication1"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Window.DataContext>
        <local:LoaderViewModel x:Name="viewModel" />
    </Window.DataContext>
    <DockPanel>
        <Button Click="Clear_Click" DockPanel.Dock="Top" >Clear</Button>
        <Button  Click="Load_Click" DockPanel.Dock="Top">Load Data</Button>
        <ListView ItemsSource="{Binding Values}" ScrollViewer.VerticalScrollBarVisibility="Visible" >
            <ListView.ItemTemplate>
                <DataTemplate>
                    <Label Content="{Binding Text}"/>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </DockPanel>
</Window>


    public partial class MainWindow : Window
{
    public MainWindow()
    {
        this.InitializeComponent();
        viewModel.Dispatcher = Dispatcher;
    }

    private void Clear_Click(object sender, RoutedEventArgs e)
    {
        viewModel.Values.Clear();
    }
    private void Load_Click(object sender, RoutedEventArgs e)
    {
        viewModel.LoadData();
    }
}

Note: the view is just a quick and dirty example to demo the ViewModel

MikeT
  • 5,398
  • 3
  • 27
  • 43
  • All the models and etc are in the ViewModel as it has to be. But I invoke a command, when I click on the CancelButton which execute a method that check if there have been any amends or any added instruments in the datagrid etc.. – Tobias Oct 04 '16 at 12:11
  • I am going to try your way of doing it - thanks for the answer! The "AultifuntionsAccess" method, where are u including it? – Tobias Oct 04 '16 at 12:18
  • The View should just call the start or cancel function in the ViewModel which won't do anything other than start the task off or mark the cancellation, their should be minimal delay and no communication to the server at all, that would all happen inside the thread – MikeT Oct 04 '16 at 12:26
  • The View? Sorry, what do you mean? – Tobias Oct 04 '16 at 12:28
  • The View is the User Interface, in MVVM it should do nothing but translate the ViewModel into a user understandable form and convert user actions into computer understandable actions that the ViewModel executes, likewise the ViewModel shouldn't know or care where the data comes from DB,File, Web service, that's the responsibility of the Model see https://msdn.microsoft.com/en-gb/library/hh848246.aspx – MikeT Oct 04 '16 at 12:34
  • Okay, I see. Is it possible to make a chat, where we can communicate? If you wanna help me ofc, it would be great, because I probably need some more help about this. I have tried to added your code into mine, but it seems to effect other code. – Tobias Oct 04 '16 at 12:42
  • unfortunately i can't provide realtime support, but if you once get your MVVM sorted into the correct layers i think most of your issues will correct themselves, read the MVVM article attached its one of the best explanations around https://msdn.microsoft.com/en-gb/library/hh848246.aspx – MikeT Oct 04 '16 at 12:48
  • I will try to play around with your code. Maybe I have some questions once. But when I firstly implementing your code, I get this debug error, when I run it. The calling thread must be STA, because many UI components require this – Tobias Oct 04 '16 at 12:52
  • That should only happen if your have a background thread trying to directly update the GUI, if you notice in the example i have a Status property on the View Model that is updated by the background task, it never touches the Label that displays the Status, the only place you might have any difficulty is if you are using an observable collection as they don't support multithreaded access – MikeT Oct 04 '16 at 13:03
  • Okay, I see - Thanks. I have updated my question a little bit with another code that results in the delay. Do you have another easier way of fixing that? – Tobias Oct 04 '16 at 13:05
  • When you Start your View up, it will connect to a ViewModel, no threading required at all at this point, the view model can then request the data from what ever source it uses to get the data (hopefully a Model) once the task has completed it changes itself with the returned data which will fire the Property/Collection Changed events which will notify the view to update its bindings ASAT no need to access the dispatcher at all – MikeT Oct 04 '16 at 13:18
  • If data loading is really such a laborious process i would instigate a Lazy Loader, your primary get will return a List of ID's no data, you will then create ViewModels using the ID's then each ViewModel will begin a background call to load the data from the source, the first time it is access, you could even combine this into a tiered approach so quick it loads minimal data (ie Description/Icon), then only if required does it then collected the full object – MikeT Oct 04 '16 at 13:28
  • Okay, In your example, you are using CancellationToken. I only have to implement it, if I want to cancel a process, right? Because I don't need to cancel a process at anytime – Tobias Oct 04 '16 at 13:32
  • And if I don't need to cancel the event, I dont need the CancelMethod either right? The StartMethod is the action who gets invoke, when I click on the CancelButton right? The LongAction is the method with all the code who has to run in the background, right? The MultiFunctionAccess, do I call in the same action with StartMethod right? (The action who gets invoke when I click the CancelButton) – Tobias Oct 04 '16 at 13:43
  • Thats correct, though having a cancel option is a good idea even if you only use it when you shut down the program – MikeT Oct 04 '16 at 13:48
  • Please, see my "Answer" to my own post. Maybe it is wrong to post it, but it gives a better overview over it. – Tobias Oct 04 '16 at 13:51
  • the first thing that jumped out was that your `Start` called `MultiFunctionAccess` which then also called `Start`, thats a textbook way to end up in a infinite recursive loop. `MultiFunctionAccess`is just a way to perform the default action that is relevant to the current state of the task. if you don't intend to cancel then all you need is `Start` – MikeT Oct 04 '16 at 14:03
  • Okay, so in my MultifunctionsAccess. It should just look like this: ` public void MultifuntionsAccess()//access points for your view { switch (Status) { case "Running": break; case "Not Started": case "Completed": case "Cancelled": ExecuteAbortCommand(); break; default: break; } }` – Tobias Oct 04 '16 at 14:05
  • if you don't want to perform a default action lookup you can dump it entirely – MikeT Oct 04 '16 at 14:06
  • Okay, I will try to remove the Method. It should still work? – Tobias Oct 04 '16 at 14:07
  • I am getting the error: "The calling thread must be STA, because many UI components require this" How do I fix that issue? – Tobias Oct 04 '16 at 14:07
  • And do I need to use the status property then? If I remove the MultiFunctionAccess Method? – Tobias Oct 04 '16 at 14:09
  • The status was just there to demonstrate making changes to the view model and having them reflected in the view, if you don't want to show the status of the task there is no need for it – MikeT Oct 04 '16 at 14:14
  • Okay, so I can delete them right? I just really want to be safe, because I want to this work! Therefore I ask dumb questions :) – Tobias Oct 04 '16 at 14:15
  • And how do I fix the error: "The calling thread must be STA, because many UI components require this" ?? – Tobias Oct 04 '16 at 14:17
  • Try to look at my question code now. Should that work too? – Tobias Oct 04 '16 at 14:20
  • you need to show more of your code if you want an answer to that – MikeT Oct 04 '16 at 14:20
  • I have done it. – Tobias Oct 04 '16 at 14:32
  • thats good, i've altered the example to show what it would look like with lazy loading and a full MVVM design – MikeT Oct 04 '16 at 15:28
  • ohh, I thought I was close to have a solution and now you update the code and I am completely on scratch again :( You really need to explain a bit deeper what to do step by step, else I have no clue how to implement these things :) – Tobias Oct 05 '16 at 07:06
0

SOLUTION

I found the solution by myself. Thanks for all the responses, it helped me on the right way.

There was actually a pretty simple way of doing it.

I just changed my Command to a Async Method (public async void "MethodName") and if I wanted to do the ExecuteAbortCommand in the back, I just did this:

    await Task.Run(() => {
    ExecuteAbortSessionCommand();
    });

It did the job for me. I hope it will help other people

Tobias
  • 15
  • 5