简体   繁体   English

调用异步方法后,进程似乎没有 go 回到 WPF 应用程序中的主线程

[英]After calling an async method the process don't seem to go back to main thread in WPF app

I'm new to both WPF and threading, and this is partly based on code I have not made, so I'm not even able to ask the right questions or know where to look.我对 WPF 和线程都是新手,这部分是基于我没有编写的代码,所以我什至无法提出正确的问题或知道去哪里找。

I have a application with a toggle button that can stop and start some services.我有一个带有可以停止和启动某些服务的切换按钮的应用程序。

When you start up the app, the services are running.当您启动应用程序时,服务正在运行。 You can then click the button to stop them.然后,您可以单击按钮停止它们。 While waiting for the services to stop (or start) the button is disabled.在等待服务停止(或启动)时,该按钮被禁用。 When all services have transitioned from one state to another, the text on the button changes, and the button is supposed to be enabled, except it does not, the text is being changed, but the button does not become enabled.当所有服务都从一个 state 转换到另一个时,按钮上的文本会更改,并且应该启用该按钮,但它没有,文本正在更改,但该按钮未启用。 However, if I click anywhere on the GUI, the button becomes enabled.但是,如果我单击 GUI 上的任意位置,按钮就会启用。 And this leads me to think that the clicking somehow (re)acivates a thread.这让我认为点击以某种方式(重新)激活了一个线程。

Comments to the code代码注释

In the constructor, I refer to the method CanServicesExecute() whether the button is enabled or not.在构造函数中,无论按钮是否启用,我都会参考 CanServicesExecute() 方法。 So while the program is running this method is being hit all the time by the system.因此,当程序运行时,此方法一直被系统击中。

The part that checks if the services are running is done by a separate thread in order not the block the rest of the GUI, but only disabling the one button.检查服务是否正在运行的部分由单独的线程完成,以便不阻止 GUI 的 rest,而仅禁用一个按钮。 It checks every 5 sec.它每 5 秒检查一次。

In this code I have substituted the actual checking if services have terminated or stopped with a simple wait, but my problem remains: After the Thread.Sleep, the Servicesstate is being set, and the button IS getting another text, but the CanServicesExecute() is no longer being hit.在这段代码中,我用简单的等待代替了实际检查服务是否已终止或停止,但我的问题仍然存在:在 Thread.Sleep 之后,正在设置 Servicesstate,并且按钮正在获取另一个文本,但 CanServicesExecute()不再被击中。 Until I click somewhere in the GUI, and then it is being hit constantly again.直到我单击 GUI 中的某个位置,然后它又被不断地击中。

using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
using Contracts.Annotations;
using Contracts.BusinessObjects;
using eDeployment.Properties;
using eTray.Common;
using eTray.Server;
using eTray.Types.Server;
using Infrastructure.Commands;
using Settings = eDeployment.Properties.Settings;

namespace eDeployment.ViewModel
{
    public class DeploymentViewModel : INotifyPropertyChanged
    { 

        enum ServicesState
        {
            Running = 0,
            Terminated = 1,
            Transitioning = 2,
            Unfinished = 3
        }

        
        private ServicesState IsServicesRunning { get; set; }


        public DeploymentViewModel()
        {
            StopStartServicesCommand = new RelayCommand<object>(_ => StopStartServices(), _ => CanServicesExecute());
        }

        private void StopStartServices()
        {
            if (IsServicesRunning == ServicesState.Running)
                StopServicesCommand();
            else
            {
                StartServicesCommand();
            }
        }

        private async void StopServicesCommand()
        {
            SetServiceState(ServicesState.Transitioning, Resources.TryStopServices);
            //CheckServicesX(false);
            CheckServicesProcessX(true);
        }

        private async void StartServicesCommand()
        { 
            SetServiceState(ServicesState.Transitioning, Resources.TryStartServices);
            LogWrite(Resources.TryStartServices, false);
            //CheckServicesX(true);
            CheckServicesProcessX(true);
        }

        private async void CheckServicesProcessX(bool starting)
        {
           await Task.Run(() => CheckServicesX(starting))
               .ContinueWith(t => { }, TaskScheduler.FromCurrentSynchronizationContext());

        }


        private  void CheckServicesX(bool starting)
        {
            Thread.Sleep(3000); 
            
                if (!starting)
                {
                    SetServiceState(ServicesState.Terminated, Resources.ServicesTerminated);
                    LogWrite(Resources.ServicesTerminated, false);
                    
                }
                else
                {
                     SetServiceState(ServicesState.Running, Resources.ServicesRunning);
                    LogWrite(Resources.ServicesRunning, false);
                   
                }
        }
        
        private async void CheckServicesY(bool starting)
        {
            
             await Task.Delay(3000); 
            
                if (!starting)
                {

                    SetServiceState(ServicesState.Terminated, Resources.ServicesTerminated);
                    LogWrite(Resources.ServicesTerminated, false);
                    
                }
                else
                {
                     SetServiceState(ServicesState.Running, Resources.ServicesRunning);
                    LogWrite(Resources.ServicesRunning, false);
                   
                }
        }

        private void SetServiceState(ServicesState servicestate, string message)
        {
            switch (servicestate)
            {
                case ServicesState.Running:
                    IsServicesRunning = ServicesState.Running;
                    ColorIndicator = "Green";
                    ServicesLabel = Resources.LabeL_StopServices;

                    break;
                case ServicesState.Transitioning:
                    IsServicesRunning = ServicesState.Transitioning;
                    ColorIndicator = "Orange";

                    break;
                case ServicesState.Terminated:
                    IsServicesRunning = ServicesState.Terminated;
                    ServicesLabel = Resources.LabeL_StartServices;
                    ColorIndicator = "Red";

                    break;
                case ServicesState.Unfinished:
                    IsServicesRunning = ServicesState.Terminated;
                    ColorIndicator = "Orange";

                    break;
            }

            ServiceStatusText = message;
        }


        private bool CanServicesExecute()
        {
            if (IsServicesRunning == ServicesState.Transitioning)
                return false;
            return true;
        }
    }
}

You need to make sure that CanServicesExecute is called whenever you want to refresh the status of the Button that is bound to the StopStartServicesCommand .您需要确保在您想要刷新绑定到CanServicesExecuteButton的状态时StopStartServicesCommand You do this by raising the command's CanExecuteChanged event.您可以通过引发命令的CanExecuteChanged事件来执行此操作。

How to raise the event depends on the implementation of the RelayCommand<T> class that you are using.如何引发事件取决于您正在使用的RelayCommand<T> class 的实现。 It should have a public method that raises the event:它应该有一个引发事件的公共方法:

StopStartServicesCommand.RaiseCanExecuteChanged();

Call this one in CheckServicesX or whenever you want to enable or disable the Button based on the current status.CheckServicesX中调用此选项,或者在您想要根据当前状态启用或禁用Button时调用此选项。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM