Управляйте заданием в фоновом режиме и используйте элементы управления из другого класса - PullRequest
0 голосов
/ 31 марта 2020

В основном я хочу добиться довольно простого вопроса. У меня есть C# wpf -приложение, где в одном классе (здесь в main) у меня есть несколько элементов управления. (кнопки, текстовые поля, ...)
Теперь я выполняю метод (здесь конструктор main). В этом методе я хочу вызвать другой метод из другого класса (здесь метод SomeFunction()). Теперь для этого метода есть два условия:

  1. Он должен работать в фоновом режиме, поэтому GUI все еще можно использовать (перемещаться, нажимать кнопки и т. Д. c.)
  2. Фактически требуется ввод от элементов управления из GUI (который находится в main)

После поиска по inte rnet Моей идеей для первого требования было использование 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;
    }
}

1 Ответ

1 голос
/ 31 марта 2020

Попробуйте что-то вроде этого

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, и это, кажется, не влияет на пользовательский интерфейс. Таким образом, ключ заключается в том, чтобы выполнять минимальный объем работы, необходимый с помощью элементов управления. Если вы ожидаете ввода, то вам следует использовать 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>
...