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 线程也可以实现调度了。




posted @   double64  阅读(471)  评论(0编辑  收藏  举报
编辑推荐:
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
点击右上角即可分享
微信分享提示