【WPF】开启一个线程、xaml绑定属性、绑定命令、取消线程、线程触发UI更新

8 篇文章 0 订阅

主界面

XAML:按钮绑定命令和属性

    <Grid>
		<StackPanel>
			<ProgressBar x:Name="ProgressBar" Height="20" VerticalAlignment="Top" Margin="10 10 10 10" Value="{Binding Progress}"/>
			<Grid>
				<Grid.ColumnDefinitions>
					<ColumnDefinition/>
					<ColumnDefinition/>
				</Grid.ColumnDefinitions>
				<Button x:Name="StartButton" Grid.Column="0" Content="start!" VerticalAlignment="Top" Margin="10 0 10 10" Command="{Binding ClickCommandStart}" IsEnabled="{Binding StartIsEnabled}" />
				<Button x:Name="CancelButton" Grid.Column="1" Content="cancel!" VerticalAlignment="Top" Margin="10 0 10 10" Command="{Binding ClickCommandCancel}" />
			</Grid>
		</StackPanel>
	</Grid>

主界面交互逻辑:

定义类ViewModel,与主界面上下文绑定。

public class ViewModel : DependencyObject
 DataContext = new ViewModel();//主界面绑定数据上下文模型

定义可以被xaml绑定的命令:

public class CommandHandler : ICommand //可以被xaml绑定的命令
        //UI的start按钮绑定的命令 click from XAML
        public ICommand ClickCommandStart
        {
            get { return new CommandHandler(() => ActionStart(), canExecute); }            
        }
        //UI的取消按钮绑定的命令
        public ICommand ClickCommandCancel
        {
            get { return new CommandHandler(() => ActionCancel(), canExecute); }
        }

定义可以被xaml绑定的属性:

        //DependencyProperty  表示可以通过样式、数据绑定、动画和继承等方法设置的属性
        public DependencyProperty StartIsEnabledProperty;//开始按钮可用属性
        public DependencyProperty ProgressProperty;//进度属性
            //使用指定的属性名称、属性类型和所有者类型注册依赖项属性。
            StartIsEnabledProperty = DependencyProperty.Register("StartIsEnabled", typeof(bool), typeof(ViewModel));
            ProgressProperty = DependencyProperty.Register("Progress", typeof(int), typeof(ViewModel));
        //UI中start按钮绑定的 启用属性
        public bool StartIsEnabled
        {
            get { return (bool)GetValue(StartIsEnabledProperty); }
            set { SetValue(StartIsEnabledProperty, value); }
        }
        //进度条绑定的 进度属性
        public int Progress
        {
            get { return (int)GetValue(ProgressProperty); }
            set { SetValue(ProgressProperty, value * 10); }
        }

在代码里设置属性:

StartIsEnabled = true;
 Progress = 0;

UI更新线程进度:

在线程中定义两个事件:

        public event Action<int> ProcessAdvanced;//汇报进度事件

        public event Action<bool> WorkComplete; //工作完成事件:  bool==true 表示线程被取消。  bool==false 表示线程执行完毕

开启线程时将UI的同步上下文传递给线程:

            Thread thread = new Thread(worker.Work);
            thread.Start(context);//将当前线程同步上下文传递给 worker线程

开启线程前绑定线程事件的处理方法:

            worker.ProcessAdvanced += Worker_ProcessAdvanced;//工作线程进度事件
            worker.WorkComplete += Worker_WorkComplete;//工作线程完成事件

UI的同步上下文获取:

 context = SynchronizationContext.Current;//获取当前线程的同步上下文。传递给工作线程,在工作线程里传递给UI进度信号

工作线程通过UI的同步上下文触发事件:

        //发送事件进度 send event abount progress 在派生类中重写时,将同步消息分派到同步上下文。
        context.Send(OnProgressAdvanced, i);
        //发送关于已完成工作的事件 send event about work done
        context.Send(OnWorkComplete, cancelled);

        //通知UI线程 工作线程进度
        public void OnProgressAdvanced(object progress)
        {
            ProcessAdvanced((int)progress);
        }
        //通知UI线程 工作线程完成
        public void OnWorkComplete(object cancelled)
        {
            WorkComplete((bool) cancelled);
        }

全部代码:

namespace MultithreadingTest
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            DataContext = new ViewModel();//主界面绑定数据上下文模型
        }
    }

    /// <summary>
    /// Button's actions from XAML handler
    /// 按钮的操作 从XAML处理程序
    /// </summary>
    public class CommandHandler : ICommand //可以被xaml绑定的命令
    {
        public event EventHandler CanExecuteChanged;
        private Action action;//命令的操作
        private bool canExecute;//命令是否可执行

        public CommandHandler(Action action, bool canExecute)
        {
            this.action = action;
            this.canExecute = canExecute;
        }

        public bool CanExecute(object parameter)
        {
            return canExecute;
        }

        public void Execute(object parameter)
        {
            action();
        }
    }

    /// <summary>
    /// Button's actions and multithreading work event handlers
    /// </summary>
    public class ViewModel : DependencyObject
    {
        // private ICommand click;
        private bool canExecute;
        private Worker worker;
        private SynchronizationContext context;//提供在各种同步模型中传播同步上下文的基本功能。
        //DependencyProperty  表示可以通过样式、数据绑定、动画和继承等方法设置的属性
        public DependencyProperty StartIsEnabledProperty;//开始按钮可用属性
        public DependencyProperty ProgressProperty;//进度属性

        public ViewModel()
        {
            canExecute = true;
            //使用指定的属性名称、属性类型和所有者类型注册依赖项属性。
            StartIsEnabledProperty = DependencyProperty.Register("StartIsEnabled", typeof(bool), typeof(ViewModel));
            ProgressProperty = DependencyProperty.Register("Progress", typeof(int), typeof(ViewModel));

            context = SynchronizationContext.Current;//获取当前线程的同步上下文。传递给工作线程,在工作线程里传递给UI进度信号

            StartIsEnabled = true;
        }

        //UI的start按钮绑定的命令 click from XAML
        public ICommand ClickCommandStart
        {
            get { return new CommandHandler(() => ActionStart(), canExecute); }            
        }
        //UI的取消按钮绑定的命令
        public ICommand ClickCommandCancel
        {
            get { return new CommandHandler(() => ActionCancel(), canExecute); }
        }

        //单击开始操作(第一个线程)  click start action (first thread)
        public void ActionStart()
        {
            Console.WriteLine("Action Start");

            // TODO: how to interact with UI elements?
            StartIsEnabled = false;//禁用开始按钮

            worker = new Worker();
            worker.ProcessAdvanced += Worker_ProcessAdvanced;//工作线程进度事件
            worker.WorkComplete += Worker_WorkComplete;//工作线程完成事件

            Thread thread = new Thread(worker.Work);
            thread.Start(context);//将当前线程同步上下文传递给 worker线程
        }

        //单击取消工作线程 click cancel action (first thread)
        public void ActionCancel()
        {
            StartIsEnabled = true;//开始按钮可用
            worker.Cancel();//取消工作线程
            Console.WriteLine("Action Cancel");
        }

        //与XAML交互的属性 properties to interact with XAML
        //UI中start按钮绑定的 启用属性
        public bool StartIsEnabled
        {
            get { return (bool)GetValue(StartIsEnabledProperty); }
            set { SetValue(StartIsEnabledProperty, value); }
        }
        //进度条绑定的 进度属性
        public int Progress
        {
            get { return (int)GetValue(ProgressProperty); }
            set { SetValue(ProgressProperty, value * 10); }
        }
        
        //工作线程完成 处理函数 event handler (executes in second thread)
        private void Worker_WorkComplete(bool cancelled)//UI线程对于工作线程的完成事件处理方法
        {
            StartIsEnabled = true;

            if (cancelled)
                Progress = 0;

            if (cancelled)
                Console.WriteLine("Work cancelled!");
            else
                Console.WriteLine("Work done.");
        }

        // event handler (starts from second thread)
        private void Worker_ProcessAdvanced(int progress)//UI线程对于工作线程进度更新事件的处理方法
        {
            Progress = progress + 1;    // dirty progressbar delay hack
            Progress = progress;
            Console.WriteLine("Progress handled " + progress.ToString());            
        }
    }


    /// <summary>
    /// Slow methods that shoud be started asynchronously
    /// 应该异步启动的慢方法
    /// </summary>
    public class Worker
    {
        private bool cancelled = false;

        public void Work(object param)
        {
            SynchronizationContext context = (SynchronizationContext) param;

            for (int i = 1; i <= 10; i++)
            {
                if (cancelled)
                    break;

                Console.WriteLine("Working...");
                Thread.Sleep(200);

                //发送事件进度 send event abount progress 在派生类中重写时,将同步消息分派到同步上下文。
                context.Send(OnProgressAdvanced, i);
            }

            //发送关于已完成工作的事件 send event about work done
            context.Send(OnWorkComplete, cancelled);
        }
        //工作线程的取消方法
        public void Cancel()
        {
            cancelled = true;
            Console.WriteLine("Work cancelled!");
        }

        public event Action<int> ProcessAdvanced;//汇报进度事件

        public event Action<bool> WorkComplete; //工作完成事件:  bool==true 表示线程被取消。  bool==false 表示线程执行完毕

        //工作线程操作封装 用于同步上下文的调用 worker actions wrapper voids for sync.context's call
        //通知UI线程 工作线程进度
        public void OnProgressAdvanced(object progress)
        {
            ProcessAdvanced((int)progress);
        }
        //通知UI线程 工作线程完成
        public void OnWorkComplete(object cancelled)
        {
            WorkComplete((bool) cancelled);
        }

    }
}

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 在WPF中,界面元素是通过主线程更新的,因此在使用线程更新UI时必须将操作转移到主线程。这是因为WPF是基于Windows消息循环的,任何对UI更新必须在主线程上进行。 为了在后台线程更新UI,可以使用Dispatcher类的Invoke或BeginInvoke方法。这两个方法允许我们将任务调度到UI线程上执行。 Invoke方法会阻塞当前线程,直到UI线程执行完毕,而BeginInvoke方法则是异步执行,不会阻塞当前线程。使用这两个方法,可以将UI更新代码包装在委托中,然后将其传递给Dispatcher对象,以便在UI线程上执行。 例如,以下是一个在后台线程更新UI的示例: ```csharp using System; using System.Threading; using System.Windows; using System.Windows.Controls; namespace WPFThreadUpdateUI { public partial class MainWindow : Window { private Thread workerThread; private TextBox textBox; public MainWindow() { InitializeComponent(); } private void StartButton_Click(object sender, RoutedEventArgs e) { workerThread = new Thread(WorkerThreadMethod); workerThread.Start(); } private void WorkerThreadMethod() { // 后台线程更新UI Dispatcher.Invoke(() => { textBox = new TextBox(); textBox.Text = "UI更新成功"; mainGrid.Children.Add(textBox); }); } } } ``` 在上述示例中,当点击"Start"按钮时,会创建一个后台线程并执行WorkerThreadMethod方法。在方法中,通过Dispatcher.Invoke方法将UI更新的代码封装在委托中,然后传递给Dispatcher对象以在UI线程上执行。这样就可以在后台线程更新UI。 需要注意的是,当使用Dispatcher.Invoke或BeginInvoke方法更新UI时,一定要确保在访问UI元素之前检查调用线程UI线程之间的关系,以避免线程冲突和UI更新问题。 ### 回答2: 在WPF(Windows Presentation Foundation)中,UI更新通常需要在UI线程上执行。这是因为UI元素和控件只能在其创建线程上进行更新,如果在其他线程上尝试更新UI,将会引发异常。 要在线程更新UI,可以使用Dispatcher对象的Invoke或BeginInvoke方法。Dispatcher对象提供了对UI线程的访问,这样就可以在其他线程中向UI线程发送消息并更新UI。 示例代码如下: ```csharp // 在其他线程更新UI private void UpdateUI() { Dispatcher.Invoke(() => { // 在UI线程上执行更新操作 // 这里可以更新UI元素或控件的属性、内容等 }); } // 启动一个线程,并在其中更新UI private void StartUpdateThread() { Thread updateThread = new Thread(() => { // 执行其他任务... // 更新UI UpdateUI(); // 执行其他任务... }); updateThread.Start(); } ``` 在上述示例中,我们首先创建了一个新的线程`updateThread`,然后在其中调用`UpdateUI`方法,它通过`Dispatcher.Invoke()`方法将UI更新操作发送到UI线程。由于采用了Invoke方法,因此更新操作会等待UI线程完成操作后才会继续执行。 通过调用`StartUpdateThread`方法,我们可以在其他线程中启动一个新的线程更新UI。 总结而言,通过使用Dispatcher对象的Invoke或BeginInvoke方法,可以在WPF中的其他线程更新UI。这确保了UI更新操作的线程安全性,并避免了潜在的异常情况。 ### 回答3: 在WPF中,要更新UI,我们必须确保所有对UI元素的更改都发生在主线程上,这是因为WPF中的UI元素是线程的单元,只能由创建它的线程进行访问和修改。如果我们试图在非主线程更新UI,可能会导致异常或者无法正确更新UI。 为了在线程之间更新UI,我们可以使用Dispatcher对象。Dispatcher是WPF应用程序中的一个功能强大的工具,它允许我们在主线程上执行操作,包括更新UI元素。 我们可以通过调用Dispatcher对象的Invoke或BeginInvoke方法来跨线程更新UI。Invoke方法是同步的,会阻塞非主线程直到UI更新完成,而BeginInvoke方法是异步的,允许非主线程继续执行而无需等待UI更新完成。 例如,我们可以使用以下代码在非主线程更新UI元素的内容: Dispatcher.Invoke(() => { // 在主线程更新UI的代码 label1.Content = "更新后的内容"; }); 通过这种方式,我们可以安全地在WPF中的任何线程更新UI,而无需担心线程冲突或异常。然而,需要注意的是,频繁地在非主线程更新UI可能会导致性能下降,因此我们应该尽量减少跨线程更新UI的需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值