在后台操作任务并使用来自另一个 Class 的控制元素

Operate Task in Background AND use Control Elements from another Class

基本上我想实现一个比较简单的事情。我有一个 C# wpf-应用程序,其中一个 class(这里是 main)我有几个控制元素。 (按钮、文本框……)
现在我执行一个方法(这里是 main 的构造函数)。在这个方法中,我想从不同的 class 调用另一个方法(这里是方法 SomeFunction())。现在这个方法有两个条件:

  1. 它应该在后台运行,因此 GUI 仍然可用(四处移动、单击按钮等)
  2. 它实际上需要来自 GUI(在 main 中)的控制元素的输入

上网查了一下,我的第一个需求是想用Task.
实际上这很好用,但是我还必须使用 Dispatcher,以免 运行 进入错误:

System.InvalidOperationException: The calling thread cannot access this object because a different thread owns it

但是,通过使用 Dispatcher,GUI 再次被阻止并且无法再使用。

有什么办法可以同时满足top的两个要求吗?

主要 class 看起来像这样:

public class Main
{
    public Main()
    {
        // This class has buttons, textboxes, labels, etc.
        InitializeComponent();

        Task.Run(() => // Task is needed to do work in background (GUI is not blocked)
        {
            Dispatcher.Invoke(() => // Dispatcher is needed to avoid "System.InvalidOperationException"
            {
                // Initialize a new object with this class as parameter
                OtherClass otherObject = new OtherClass(this);

                // Now call a method from another class
                otherObject = new OtherClass(this);
            });
        });
    }
}

另一个 class 是这样的:

public class OtherClass 
{
    public SomeFunction(Main main)
    {
        // Pretty hard work here to do ...
        string test = main.textbox.Text;
    }
}

尝试这样的事情

public void DoSomething()
    {
        Task.Run(() =>
        {
            BackgroundProcess();
        });
    }

    private void BackgroundProcess()
    {
        string ControlValue;
        Application.Current.Dispatcher.Invoke(() =>
        {
            ControlValue = textBox.Text;
        });
        ControlValue += ControlValue;
        Application.Current.Dispatcher.Invoke(() =>
        {
            textBox.Text = ControlValue;
        });
    }

我所做的只是在绝对必要时访问控件,并在调度程序中执行此操作。其他一切都在后台线程中完成。只要您为此只做一些输入,它就不会阻塞调度程序。

我有一个地方可以使用 TextBox.AppendText 和 TextBox.ScrollToEnd 将进程的进度记录到文本框中,这似乎不会影响 UI.所以关键是使用控件做最少的必要工作。如果您正在等待输入,那么您应该使用 MVVM 和 DataBinding,以便在值更改时启动流程的相关部分。

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Security;
using System.Text;
using System.Threading.Tasks;
using Microsoft.VisualBasic;

public class MVVMExampleViewModel : System.ComponentModel.INotifyPropertyChanged
{
    private string _MajorChange;
    public string MajorChange
    {
        get
        {
            return _MajorChange;
        }
        set
        {
            _MajorChange = value;
            DoPropertyChanged("MajorChange");
-- Start process using value here
        }
    }

    private void DoPropertyChanged(string propertyname)
    {
        PropertyChanged(me, New PropertyChangedEventArgs(propertyname));
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

WPF 用法

<Grid DataContext="{Binding CurrentMVVMExample}" 
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="Auto" />
                            <ColumnDefinition Width="Auto" />
                        </Grid.ColumnDefinitions>
                        <Grid.RowDefinitions>
                            <RowDefinition />

                        </Grid.RowDefinitions>
                        <Label Content="{DynamicResource NewItemMessage}" Visibility="{Binding IsDetached, Converter={StaticResource BooleanToVisibilityConverter}, Mode=OneWay}" Foreground="#FFBD0000" RenderTransform="{DynamicResource NewItemMessageLocation}"/>

                        <Label Content="Status" Grid.Column="0" Grid.Row="1" />
                        <TextBox  Text="{Binding MajorChange, Delay=500, UpdateSourceTrigger=PropertyChanged}" 
                            Grid.Column="1" Grid.Row="1" Width="{DynamicResource SectionFieldWidth}" />
                    </Grid>