C# - 将控件从一个线程传输到另一个线程
本文关键字:线程 一个 传输 另一个 控件 | 更新日期: 2023-09-27 17:56:28
越多,完全加载所需的时间就越多,并且用户只能在完成后使用任何控件。
所以我想象了以下内容:
使窗口不包含控件,并在另一个线程(如 BackgroundWorker)上创建它们。然后,控件将逐渐添加到主网格中,因此用户可以使用第一个控件,而不必等待创建其余控件。
为了尝试将其变为现实,我已经尝试使用一些技术。最后一个是尝试使用Invoke,实际上,我不知道它是如何工作的,也不知道如何使用。
public MainWindow()
{
InitializeComponent();
Thread t = new Thread(new ThreadStart(CreateControls));
t.SetApartmentState(ApartmentState.STA);
t.Start();
}
private void CreateControls()
{
Button btn = new Button();
btn.Width = 90;
btn.Height = 30;
btn.Background = Brushes.Black;
btn.Foreground = Brushes.White;
btn.Content = "Click me";
this.Dispatcher.BeginInvoke((Action)delegate
{
Dispatcher.Invoke((Action)(() =>
{
MainGD.Children.Add(btn);
}));
});
}
在MainGD.Children.Add(btn);
,我收到:调用线程无法访问此对象,因为其他线程拥有它。
在我的研究中,我发现了将按钮创建放在 Invoke 中的方法,如下所示:
this.Dispatcher.BeginInvoke((Action)delegate
{
Dispatcher.Invoke((Action)(() =>
{
Button btn = new Button();
btn.Width = 90;
btn.Height = 30;
btn.Background = Brushes.Black;
btn.Foreground = Brushes.White;
btn.Content = "Click me";
MainGD.Children.Add(btn);
}));
});
但我不想在主线程中创建控件。
谁能帮我解决这个问题?
这可能吗?
关键是:是否可以在另一个线程内创建一个 GUI 控件并将其传输到 GUI 线程?这就是我想要做的。
好主意,但恐怕行不通。用户界面代码必须位于 GUI 线程上。
BackgroundWorker 和线程用于执行工作,GUI 线程用于执行用户界面。"工作"是类似于数字处理或数据库查找的事情:最终将其结果发送到GUI线程进行显示的事情。
如果您的窗体需要很长时间才能纯粹通过向其添加控件来加载,则可能会有太多控件 - 看看是否可以重新设计它。
听起来 async/await 在这里可能有用。 我参与了一个项目,其中我们的窗口 UI 由顶部的一些菜单项、中间的自定义控件区域和底部的一些操作按钮(如确定、取消)组成。 菜单项和操作按钮是在创建窗口时创建的。 但是,中间区域中的控件是使用异步调用生成的。
我们使用 ObservableCollection 作为中间区域中 ItemsControl 的数据源。 然后,在 Window.ContentRender 事件中(因此在绘制窗口之后),我们"等待"生成自定义控件的方法。
下面是 MyWindow 代码隐藏的片段:
public partial class MyWindow : Window
{
/* Window c'tor, other methods/props ommited */
// Load our custom controls asynchronously after the window is rendered
private async void MyWindow_ContentRendered(object sender, EventArgs e)
{
// kick off the asynchronous call to create a custom controls
await CreateCustomControls();
}
// Asynchronously adds MyCustomControls to the ObservableCollection
// (we actually used a separate static class that contained our definitions
// and created the controls)
private async Task CreateCustomControls()
{
// the def describes what you want to build and is not detailed here
foreach (var controlDef in MyCustomControlDefinitions)
{
// create a control based on the definition
var newControl = await CreateMyCustomControl(controlDef);
// Don't add the control until it is completely built
MyCustomControls.Add(newControl);
}
}
//The (bindable) collection of custom controls
public ObservableCollection<MyCustomControl> MyCustomControls
{
get { return (ObservableCollection<MyCustomControl>)GetValue(MyCustomControlsProperty); }
set { SetValue(MyCustomControlsProperty, value); }
}
public static readonly DependencyProperty MyCustomControlsProperty =
DependencyProperty.Register("MyCustomControls", typeof(ObservableCollection<MyCustomControl>),
typeof(MyWindow), new PropertyMetadata(new ObservableCollection<MyCustomControl>()));
}
下面是 MyWindow XAML 的一个片段:
<Window>
<Grid>
<!-- Scrollable Panel for MyCustomControls -->
<ScrollViewer>
<ItemsControl ItemsSource="{Binding MyCustomControls}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</ScrollViewer>
</Grid>
</Window>
重要的是要注意,它仅提供"感知到"的性能提升。 我们所做的只是在创建其余控件时允许窗口有点用处。 因此,用户可以立即与顶部和底部控件交互(当窗口显示时),一旦将自定义控件添加到 ObservableCollection,用户也可以与它们进行交互。
希望这有帮助。