很多小伙伴对“跨线程或跨类”使用WPF或WinFrom页面的控件不太明白,奉上一段小案例供参考(下面为WPF代码,WinFrom同理):

  注:在WPF或WinFrom中,UI元素只能由其主线程来操作,其他任何线程都不可以直接操作UI,可以使用Dispatcher.Invoke(同步操作)或Dispatcher.BeginInvoke (异步操作)来操作。

1、xaml页面代码:
1 <Window
 2         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 3         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 4         xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
 5         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
 6         xmlns:local="clr-namespace:WpfPaging"
 7         xmlns:dxlc="http://schemas.devexpress.com/winfx/2008/xaml/layoutcontrol" x:Class="WpfPaging.测试跨线程跨类调用控件"
 8         mc:Ignorable="d"
 9         Title="MainWindow" Height="700" Width="1000" Loaded="Window_Loaded">
10     <Grid>
11         <DataGrid HorizontalAlignment="Left" Height="623" Margin="10,10,0,0" VerticalAlignment="Top" Width="972" Name="dgtext"  CanUserAddRows="True" AutoGenerateColumns="True">
12             <DataGrid.Columns>
13                 <DataGridTextColumn Header="选中" Width="35" />
14                 <DataGridTextColumn Header="公司名称" Width="80" Binding="{Binding 公司名称, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
15                 <DataGridTextColumn Header="姓名" Width="80" Binding="{Binding 姓名, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
16                 <DataGridTextColumn Header="年龄" Width="80" Binding="{Binding 年龄, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
17                 <DataGridTextColumn Header="职务" Width="80" Binding="{Binding 职务, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
18             </DataGrid.Columns>
19         </DataGrid>
20         <Button Content="添加" HorizontalAlignment="Left" Margin="10,638,0,0" VerticalAlignment="Top" Width="75" Name="btnAdd" Click="BtnAdd_Click" RenderTransformOrigin="1.68,0.545"/>
21         <Button Content="保存" HorizontalAlignment="Left" Margin="121,638,0,0" VerticalAlignment="Top" Width="75" Name="btnSave" Click="btnSave_Click" RenderTransformOrigin="1.68,0.545"/>
22         <Button Content="删除" HorizontalAlignment="Left" Margin="236,638,0,0" VerticalAlignment="Top" Width="75" x:Name="btnDele" Click="btnDele_Click" RenderTransformOrigin="1.68,0.545"/>
23         <Button Content="跨线程调用控件" HorizontalAlignment="Left" Margin="334,638,0,0" VerticalAlignment="Top" Width="113" x:Name="btn1" Click="btn1_Click" RenderTransformOrigin="1.68,0.545"/>
24         <Button Content="跨类调用控件" HorizontalAlignment="Left" Margin="458,638,0,0" VerticalAlignment="Top" Width="97" x:Name="btn2" Click="btn2_Click" RenderTransformOrigin="1.68,0.545"/>
25         <Button Content="跨线程跨类调用控件" HorizontalAlignment="Left" Margin="566,638,0,0" VerticalAlignment="Top" Width="140" x:Name="btn3" Click="btn3_Click" RenderTransformOrigin="1.68,0.545"/>
26     </Grid>
27 </Window>
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
2、xaml.cs代码
1 using System.Collections.Generic;
 2 using System.Threading;
 3 using System.Windows;
 4 using System.Windows.Threading;
 5 using WpfPaging.NewFolder1;
 6 
 7 namespace WpfPaging
 8 {
 9     // 声明一个委托
10     //public delegate void ChangeForm1TextBoxValue(string txt);
11     /// <summary>
12     /// MainWindow.xaml 的交互逻辑
13     /// </summary>
14     public partial class 测试跨线程跨类调用控件 : Window
15     {
16         #region 变量
17         // 委托事件
18         //public static event ChangeForm1TextBoxValue ChangeTextBoxValue;
19         #endregion
20 
21         public 测试跨线程跨类调用控件()
22         {
23             InitializeComponent();
24         }
25 
26         private void Window_Loaded(object sender, RoutedEventArgs e)
27         {
28             // 获取这些新添加的记录
29             List<Information> lstInformati = new List<Information>();
30             Information information = new Information();
31             information.公司名称 = "公司名称0";
32             information.姓名 = "姓名0";
33             information.年龄 = "年龄0";
34             information.职务 = "职务0";
35             lstInformati.Add(information);
36             dgtext.ItemsSource = lstInformati;
37             dgtext.Items.Refresh();
38         }
39         // 跨线程调用控件
40         private void btn1_Click(object sender, RoutedEventArgs e)
41         {
42             Thread t1 = new Thread(() =>
43             {
44                 // 获取这些新添加的记录
45                 List<Information> lstInformati = new List<Information>();
46                 Information information = new Information();
47                 information.公司名称 = "公司名称跨线程";
48                 information.姓名 = "姓名跨线程";
49                 information.年龄 = "年龄跨线程";
50                 information.职务 = "职务跨线程";
51                 lstInformati.Add(information);
52                 Dispatcher.Invoke(DispatcherPriority.Normal, (ThreadStart)delegate
53                 {
54                     dgtext.ItemsSource = lstInformati;
55                     dgtext.Items.Refresh();
56                 });
57             })
58             {
59                 IsBackground = true
60             };
61             t1.Start();
62     }
63         // 跨类调用控件
64         private void btn2_Click(object sender, RoutedEventArgs e)
65         {
66             TreadTest1 treadTest1 = new TreadTest1();
67             treadTest1.TestC(this.dgtext);
68         }
69         // 跨线程跨类调用控件
70         private void btn3_Click(object sender, RoutedEventArgs e)
71         {
72             Thread t1 = new Thread(TestITC);
73             t1.Start();
74         }
75 
76         private void TestITC()
77         {
78             TreadTest1 treadTest1 = new TreadTest1();
79             treadTest1.TestITC( this.dgtext);
80         }
81     }
82 }
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
3、调用类代码:
1 using System.Collections.Generic;
 2 using System.Windows.Threading;
 3 using System.Windows.Controls;
 4 using System.Threading;
 5 
 6 namespace WpfPaging.NewFolder1
 7 {
 8      声明一个委托
 9     //public delegate void ChangeForm1TextBoxValue(string txt);
10     public class TreadTest1
11     {
12          委托事件
13         //public event ChangeForm1TextBoxValue ChangeTextBoxValue;
14         /// <summary>
15         /// 跨类调用控件
16         /// </summary>
17         internal void TestC(System.Windows.Controls.DataGrid dgtext)
18         {
19             // 获取这些新添加的记录
20             List<Information> lstInformati = new List<Information>();
21             Information information = new Information();
22             information.公司名称 = "公司名称 跨类";
23             information.姓名 = "姓名 跨类";
24             information.年龄 = "年龄 跨类";
25             information.职务 = "职务 跨类";
26             lstInformati.Add(information);
27             dgtext.ItemsSource = lstInformati;
28             dgtext.Items.Refresh();
29         }
30         /// <summary>
31         /// 跨线程跨类调用控件
32         /// </summary>
33         /// <param name="dgtext"></param>
34         internal void TestITC(DataGrid dgtext)
35         {
36             // 获取这些新添加的记录
37             List<Information> lstInformati = new List<Information>();
38             Information information = new Information();
39             information.公司名称 = "跨线程跨类调用控件";
40             information.姓名 = "跨线程跨类调用控件";
41             information.年龄 = "跨线程跨类调用控件";
42             information.职务 = "跨线程跨类调用控件";
43             lstInformati.Add(information);
44             dgtext.Dispatcher.Invoke(DispatcherPriority.Normal, (ThreadStart)delegate
45             {
46                 dgtext.ItemsSource = lstInformati;
47                 dgtext.Items.Refresh();
48             });
49         }
50     }
51 }
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
4、补充-Invoke推荐写法

  Invoke方法将委托添加到UI线程的消息队列中,等待UI线程调用D执行。

  在UI线程中应避免使用Invoke方法,防止出现额外的开销,针对这种情况我们可以使用控件.InvokeRequired进行判断。

#region WinFrom写法
private void UpdateUIControl(string text) 
{ 
    if (this.InvokeRequired)   // 当前线程不是控件UI线程时为true
    { 
        this.Invoke(new Action<string>(UpdateUIControl), text); 
        return; 
    } 
    this.label1.Text = text; 
}
#endregion WinFrom写法

#region WPF写法
private void UpdateUIControl(string text) 
{ 
    if (this.Dispatcher.CheckAccess())   // 当前线程不是控件UI线程时为true
    { 
        this.Dispatcher.Invoke(new Action<string>(UpdateUIControl), text); 
        return; 
    } 
    this.label1.Text = text; 
}
#endregion WPF写法
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
5、关于Dispatcher.Invoke的理解请见下面的链接
(1)smallerpig的博客-Dispatcher.Invoke 方法
(2) 微软官方文档-Dispatcher 类
(3) 微软官方文档-Dispatcher.Invoke 方法
(4) 微软官方文档-Dispatcher.BeginInvoke 方法
6、线程与委托学习链接:
(1)线程学习笔记
(2)C#委托
(3)Thread类接收ThreadStart委托或ParameterizedThreadStart委托的构造函数的模板

作者:꧁执笔小白꧂