WPF 跨线程 Dispatcher 线程调度
MVVMLight 中的线程调度
引入命名空间:using GalaSoft.MvvmLight.Threading;
,其在程序集 GalaSoft.MvvmLight.Platform
里面。
通常,在 MVVM Light 应用程序中,DispatcherHelper
在 App.xaml.cs 中进行初始化,App.xaml.cs 是定义应用程序启动类的文件。
public partial class App : Application
{
public App()
{
GalaSoft.MvvmLight.Threading.DispatcherHelper.Initialize();
}
}
这里模拟添加数据:
XAML:
<Window.DataContext>
<local:MainVM/>
</Window.DataContext>
<Window.Resources>
<local:BoolToVisibility x:Key="boolToVisibility" />
</Window.Resources>
<Grid>
<Grid.Resources>
<Style TargetType="{x:Type Border}" x:Key="ProcessBarBorder">
<Setter Property="BorderBrush" Value="LightGray" ></Setter>
<Setter Property="BorderThickness" Value="1" ></Setter>
<Setter Property="Background" Value="White" ></Setter>
</Style>
</Grid.Resources>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition />
</Grid.RowDefinitions>
<StackPanel Orientation="Vertical" IsEnabled="{Binding IsEnableForm}" >
<StackPanel>
<DataGrid ItemsSource="{Binding UserList}" AutoGenerateColumns="False" CanUserAddRows="False"
CanUserSortColumns="False" Margin="10" AllowDrop="True" IsReadOnly="True" >
<DataGrid.Columns>
<DataGridTextColumn Header="学生姓名" Binding="{Binding UserName}" Width="80" />
<DataGridTextColumn Header="学生家庭地址" Binding="{Binding UserAdd}" Width="200" >
<DataGridTextColumn.ElementStyle>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="TextWrapping" Value="Wrap"/>
<Setter Property="Height" Value="auto"/>
</Style>
</DataGridTextColumn.ElementStyle>
</DataGridTextColumn>
<DataGridTextColumn Header="电话" Binding="{Binding UserPhone}" Width="100" />
<DataGridTextColumn Header="性别" Binding="{Binding UserSex}" Width="*" />
</DataGrid.Columns>
</DataGrid>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="10,10,10,10">
<StackPanel Orientation="Vertical" Margin="0,0,10,0" >
<StackPanel Orientation="Horizontal" Margin="0,0,0,5" >
<TextBlock Text="学生姓名" Width="80" ></TextBlock>
<TextBox Text="{Binding User.UserName}" Width="200" />
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,0,0,5">
<TextBlock Text="学生电话" Width="80" ></TextBlock>
<TextBox Text="{Binding User.UserPhone}" Width="200" />
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,0,0,5">
<TextBlock Text="学生家庭地址" Width="80"></TextBlock>
<TextBox Text="{Binding User.UserAdd}" Width="200"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,0,0,5" >
<TextBlock Text="学生性别" Width="80" ></TextBlock>
<TextBox Text="{Binding User.UserSex}" Width="200" />
</StackPanel>
<StackPanel Margin="80 10 0 0">
<Button Content="提交" Width="200" HorizontalAlignment="Left" Command="{Binding AddRecordCmd}" ></Button>
</StackPanel>
</StackPanel>
</StackPanel>
</StackPanel>
<!-- 进度条,延迟框 -->
<Border Grid.Row="1" Style="{StaticResource ProcessBarBorder}" Padding="5" Visibility="{Binding IsWaitingDisplay,Converter={StaticResource boolToVisibility}}" Panel.ZIndex="999" HorizontalAlignment="Center" VerticalAlignment="Center" Height="50">
<StackPanel Orientation="Vertical" HorizontalAlignment="Left">
<ProgressBar Value="{Binding ProcessRange}" Maximum="100" Width="400" Height="5" ></ProgressBar>
<TextBlock Text="{Binding ProcessRange,StringFormat='执行进度:\{0\}/100'}" Margin="0,10,0,0" ></TextBlock>
</StackPanel>
</Border>
</Grid>
转换器:
/// <summary>
/// 转换器
/// </summary>
public class BoolToVisibility : IValueConverter
{
#region "IValueConverter Members"
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value == null)
{
return false;
}
if (value is bool)
{
Boolean valueB = (Boolean)value;
switch (valueB)
{
case true:
return Visibility.Visible;
case false:
return Visibility.Collapsed;
}
}
else if (value is String)
{
switch (value.ToString().ToLower())
{
case "true":
return Visibility.Visible;
case "false":
return Visibility.Collapsed;
}
}
return false;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
#endregion
}
Models:
public class UserParam
{
public String UserName { get; set; }
public String UserPhone { get; set; }
public String UserAdd { get; set; }
public String UserSex { get; set; }
}
public class CreateUserInfoHelper
{
//执行进度事件(响应注册的事件)
public event EventHandler<CreateArgs> CreateProcess;
//待创建信息
public UserParam up { get; set; }
public CreateUserInfoHelper(UserParam _up)
{
up = _up;
}
public void Create()
{
Thread t = new Thread(Start);//抛出一个行线程
t.IsBackground = true;
t.Start();
}
private void Start()
{
try
{
//ToDo:编写创建用户的DataAccess代码
for (Int32 idx = 1; idx <= 10; idx++)
{
CreateProcess(this, new CreateArgs()
{
isFinish = ((idx == 10) ? true : false),
process = idx * 10,
userInfo = null
});
Thread.Sleep(500);
}
CreateProcess(this, new CreateArgs()
{
isFinish = true,
process = 100,
userInfo = up
});
}
catch (Exception ex)
{
CreateProcess(this, new CreateArgs()
{
isFinish = true,
process = 100,
userInfo = null
});
}
}
/// <summary>
/// 创建步骤反馈参数
/// </summary>
public class CreateArgs : EventArgs
{
/// <summary>
/// 是否创建结束
/// </summary>
public Boolean isFinish { get; set; }
/// <summary>
/// 进度
/// </summary>
public Int32 process { get; set; }
/// <summary>
/// 处理后的用户信息
/// </summary>
public UserParam userInfo { get; set; }
}
}
ViewModel:
public class MainVM : ViewModelBase
{
/// <summary>
/// 构造函数
/// </summary>
public MainVM()
{
InitData();
//DispatcherHelper.Initialize();
}
#region 全局属性
private ObservableCollection<UserParam> userList;
/// <summary>
/// 数据列表
/// </summary>
public ObservableCollection<UserParam> UserList
{
get { return userList; }
set { userList = value; RaisePropertyChanged(() => UserList); }
}
private UserParam user;
/// <summary>
/// 当前用户信息
/// </summary>
public UserParam User
{
get { return user; }
set { user = value; RaisePropertyChanged(() => User); }
}
private Boolean isEnableForm;
/// <summary>
/// 是否表单可用 添加数据的时候表单不可用状态
/// </summary>
public bool IsEnableForm
{
get { return isEnableForm; }
set { isEnableForm = value; RaisePropertyChanged(() => IsEnableForm); }
}
private Boolean isWaitingDisplay;
/// <summary>
/// 是否显示延迟旋转框 在添加数据的时候可见,否则不可见
/// </summary>
public bool IsWaitingDisplay
{
get { return isWaitingDisplay; }
set { isWaitingDisplay = value; RaisePropertyChanged(() => IsWaitingDisplay); }
}
private Int32 processRange;
/// <summary>
/// 进度比例
/// </summary>
public int ProcessRange
{
get { return processRange; }
set { processRange = value; RaisePropertyChanged(() => ProcessRange); }
}
#endregion
#region 全局命令
private RelayCommand addRecordCmd;
/// <summary>
/// 添加资源
/// </summary>
public RelayCommand AddRecordCmd
{
get
{
if (addRecordCmd == null) addRecordCmd = new RelayCommand(() => ExcuteAddRecordCmd());
return addRecordCmd;
}
set
{
addRecordCmd = value;
}
}
#endregion
#region 辅助方法
/// <summary>
/// 初始化数据
/// </summary>
private void InitData()
{
UserList = new ObservableCollection<UserParam>()
{
new UserParam(){ UserName="孙悟空", UserAdd="花果山", UserPhone ="88888888", UserSex="男" },
new UserParam(){ UserName="猪八戒", UserAdd="高老庄", UserPhone ="88888888", UserSex="男" },
new UserParam(){ UserName="沙和尚", UserAdd="流沙河", UserPhone ="88888888", UserSex="男" }
};
User = new UserParam();
IsEnableForm = true;
IsWaitingDisplay = false;
}
/// <summary>
/// 执行命令
/// </summary>
private void ExcuteAddRecordCmd()
{
UserParam up = new UserParam { UserAdd = User.UserAdd, UserName = User.UserName, UserPhone = User.UserPhone, UserSex = User.UserSex };
CreateUserInfoHelper creatUser = new CreateUserInfoHelper(up);
creatUser.CreateProcess += new EventHandler<CreateUserInfoHelper.CreateArgs>(CreateProcess);
creatUser.Create();
IsEnableForm = false;
IsWaitingDisplay = true;
}
/// <summary>
/// 创建进度
/// </summary>
/// <param name="sender"></param>
/// <param name="args"></param>
private void CreateProcess(object sender, CreateUserInfoHelper.CreateArgs args)
{
DispatcherHelper.CheckBeginInvokeOnUI(() =>
{
if (args.isFinish)
{
if (args.userInfo != null)
{
UserList.Add(args.userInfo);
}
IsEnableForm = true;
IsWaitingDisplay = false;
}
else
{
ProcessRange = args.process;
}
});
}
#endregion
}
一般情况下线程调度
解决办法就是去通知主线程来处理UI, 通过向主线程的 Dispatcher 队列注册工作项,来通知UI线程更新结果。
Dispatcher 提供两个注册工作项的方法:Invoke 和 BeginInvoke。
这两个方法均调度一个委托来执行。Invoke 是同步调用,也就是说,直到 UI 线程实际执行完该委托它才返回。BeginInvoke是异步的,将立即返回。
可以在主线程获取到 Dispatcher 并传出去。下面做一些更改。
Model:
public class CreateUserInfoHelper
{
//执行进度事件(响应注册的事件)
public event EventHandler<CreateArgs> CreateProcess;
//待创建信息
public UserParam up { get; set; }
public CreateUserInfoHelper(UserParam _up)
{
up = _up;
}
public void Create()
{
// 获取主线程的 Dispatcher
Dispatcher disp = System.Windows.Application.Current.MainWindow.Dispatcher;
Thread t = new Thread(() => Start(disp)); // 抛出一个行线程 将主线程 Dispatcher 传进去
t.IsBackground = true;
t.Start();
}
private void Start(Dispatcher dispatcher)
{
try
{
//ToDo:编写创建用户的DataAccess代码
for (Int32 idx = 1; idx <= 10; idx++)
{
CreateProcess(this, new CreateArgs()
{
isFinish = ((idx == 10) ? true : false),
process = idx * 10,
userInfo = null,
disap = dispatcher // 将主线程 Dispatcher 传入
});
Thread.Sleep(500);
}
CreateProcess(this, new CreateArgs()
{
isFinish = true,
process = 100,
userInfo = up,
disap = dispatcher
});
}
catch (Exception ex)
{
CreateProcess(this, new CreateArgs()
{
isFinish = true,
process = 100,
userInfo = null,
disap = dispatcher
});
}
}
/// <summary>
/// 创建步骤反馈参数
/// </summary>
public class CreateArgs : EventArgs
{
/// <summary>
/// 增加 主线程 Dispatcher
/// </summary>
public Dispatcher disap { get; set; }
/// <summary>
/// 是否创建结束
/// </summary>
public Boolean isFinish { get; set; }
/// <summary>
/// 进度
/// </summary>
public Int32 process { get; set; }
/// <summary>
/// 处理后的用户信息
/// </summary>
public UserParam userInfo { get; set; }
}
}
viewModel 将和跨线程空间有关的函数 CreateProcess 做些更改:
/// <summary>
/// 创建进度
/// </summary>
/// <param name="sender"></param>
/// <param name="args"></param>
private void CreateProcess(object sender, CreateUserInfoHelper.CreateArgs args)
{
args.disap.BeginInvoke(new Action(() =>
{
if (args.isFinish)
{
if (args.userInfo != null)
{
UserList.Add(args.userInfo);
}
IsEnableForm = true;
IsWaitingDisplay = false;
}
else
{
ProcessRange = args.process;
}
}));
}
这样 UI 线程也可以实现调度了。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了