【问题标题】:Operate Task in Background AND use Control Elements from another Class在后台操作任务并使用另一个类的控制元素
【发布时间】:2020-03-31 10:12:02
【问题描述】:

基本上我想实现一个相当简单的事情。我有一个C#wpf-Application,在一个类中(这里是main)我有几个控制元素。 (按钮、文本框……)
现在我执行一个方法(这里是main的构造函数)。在这个方法中,我想从不同的类中调用另一个方法(这里是方法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# wpf task wpf-controls dispatcher


    【解决方案1】:

    试试这样的

    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>
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-12-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多