在后台操作任务并使用来自另一个 Class 的控制元素
Operate Task in Background AND use Control Elements from another Class
基本上我想实现一个比较简单的事情。我有一个 C#
wpf
-应用程序,其中一个 class(这里是 main
)我有几个控制元素。 (按钮、文本框……)
现在我执行一个方法(这里是 main
的构造函数)。在这个方法中,我想从不同的 class 调用另一个方法(这里是方法 SomeFunction()
)。现在这个方法有两个条件:
- 它应该在后台运行,因此 GUI 仍然可用(四处移动、单击按钮等)
- 它实际上需要来自 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>
基本上我想实现一个比较简单的事情。我有一个 C#
wpf
-应用程序,其中一个 class(这里是 main
)我有几个控制元素。 (按钮、文本框……)
现在我执行一个方法(这里是 main
的构造函数)。在这个方法中,我想从不同的 class 调用另一个方法(这里是方法 SomeFunction()
)。现在这个方法有两个条件:
- 它应该在后台运行,因此 GUI 仍然可用(四处移动、单击按钮等)
- 它实际上需要来自 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>