首页 > 解决方案 > 在后台操作任务并使用另一个类的控制元素

问题描述

基本上我想实现一个相当简单的事情。我有一个C# wpf-Application,在一个类中(在这里main)我有几个控制元素。(按钮,文本框,...)
现在我执行一个方法(这里是 的构造函数main)。在这个方法中,我想从不同的类中调用另一个方法(这里是 method SomeFunction())。现在这个方法有两个条件:

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

在搜索了互联网之后,我对第一个要求的想法是使用Task.
实际上这工作得很好,但我还必须使用Dispatcher, 才不会遇到错误:

System.InvalidOperationException:调用线程无法访问此对象,因为不同的线程拥有它

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

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

主类看起来像这样:

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);
            });
        });
    }
}

另一个类是这样的:

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

标签: c#wpftaskwpf-controlsdispatcher

解决方案


尝试这样的事情

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>

推荐阅读