.net 让Winforms UI在出现非用户驱动的事件之前通过对话框阻塞的正确方法?

.net 让Winforms UI在出现非用户驱动的事件之前通过对话框阻塞的正确方法?,.net,winforms,multithreading,architecture,timer,.net,Winforms,Multithreading,Architecture,Timer,今天早些时候,我曾询问过这种方法的具体细节,但困惑的回答让我怀疑我是否在这方面完全错了 这就是问题所在。我需要构建一个WinForms应用程序,定期检查来自某个外部服务的值。如果它处于某个状态,应用程序应该弹出一个模式表单,阻止用户与应用程序交互,直到状态改变为止。被阻止的UI的行为应与使用ShowDialog()显示表单时的行为相同。也就是说,在对话框关闭之前,用户不应该以任何方式与ui交互(在这种情况下,这只能由应用程序完成) 在我的特定应用程序中,我在几个实例中突然出现了这个需求。例如,在

今天早些时候,我曾询问过这种方法的具体细节,但困惑的回答让我怀疑我是否在这方面完全错了

这就是问题所在。我需要构建一个WinForms应用程序,定期检查来自某个外部服务的值。如果它处于某个状态,应用程序应该弹出一个模式表单,阻止用户与应用程序交互,直到状态改变为止。被阻止的UI的行为应与使用ShowDialog()显示表单时的行为相同。也就是说,在对话框关闭之前,用户不应该以任何方式与ui交互(在这种情况下,这只能由应用程序完成)

在我的特定应用程序中,我在几个实例中突然出现了这个需求。例如,在某一点上,应用程序运行一个计时器,该计时器反复检查由称重设备不断更新的db表中单元格的值。如果该值低于某个阈值,则会提示用户将项目放置在秤上-在用户这样做之前,他或她可能无法继续或与UI交互

我尝试过的每个解决方案都充满了线程问题。对话框不会阻止UI,计时器不会继续检查外部服务,或者应用程序无法在时间到来时关闭表单


这似乎是一个相当标准的winforms要求,必须有一个相关的设计模式,它不引入Windows.Forms.Timer(我喜欢保留它)。如何执行此操作?

您可能希望阻止当前线程,直到另一个线程在后台执行检查。这可以通过加入(
.Join()
)在后台执行检查的线程来完成。此打开将阻止调用线程,直到结束。如果我有更多的时间,我会为您制作一段代码,但这可能会给您一个想法

下面是代码(Form1和add 2按钮:Button1和Button2)

公共部分类表单1:表单
{
随机r=新随机();
公共表格1()
{
初始化组件();
}
螺纹接头;
私有void Form1\u加载(对象发送方、事件参数e)
{
t检查=新线程(新线程开始(检查方法));
}
私有无效按钮1\u单击(对象发送者,事件参数e)
{
this.Text=r.Next(0,10).ToString();
如果(tCheck.ThreadState==ThreadState.Stopped)
{
this.button1.Text=“已停止”;
}
}
//在此处检查来自某个外部服务的值
私有无效检查方法()
{
int i=0;

虽然(i上一篇文章(使用Join())会阻止UI,但也会阻止它呈现,显示一个空白(白色)窗口,就像应用程序崩溃一样。更好的选择是创建一个自定义模式窗口(用户不能移动、关闭或做任何事情)事件监听器将在其处理程序中关闭窗口。然后,一旦称重设备检测到重量,它就可以触发事件(或以某种方式与应用程序、Web服务、ipc等通信)通知它。

终于想出了一个感觉自然且效果很好的解决方案。诀窍是最终阅读并了解ManualResetEvent

下面是它的工作原理。您在类上声明ManualResetEvent的实例,并在调用resetEvent.WaitOne(超时)时达到应用程序应该阻止的点。这将阻止线程,直到另一个线程(正在检查某些条件)调用resetEvent.Set()。这是我的完整类

/// <summary>
/// If necessary, resolve the tare for a given transaction by querying the measurement 
/// device and comparing with a threshold.  Will block until an appropriate tare is provided 
/// or a timeout condition is met.
/// </summary>
    public class TareResolver {
    private IDevice _device;
        private IDialogFactory _dialogs;
        private ManualResetEvent _resolvedEvent = new ManualResetEvent(false);
        ICurrentDateTimeFactory _nowFactory;
    ICheckInSettings _settings;
    ITimer _timer;
    private UnitOfMeasureQuantityPair _threshold;
        public TareResolver(IDevice device, IDialogFactory dialogs, ICheckInSettings settings, ICurrentDateTimeFactory nowFactory) {
            _device = device;
            _dialogs = dialogs;
        _settings = settings;
            _nowFactory = nowFactory;
        _timer = new DriverInterface2.Domain.Utilities.Timer(_settings.TarePollFreqeuency);
        _timer.Tick += new Action(_timer_Tick);
    }

    void _timer_Tick() {
        if (ThresholdMet(_device.CurrentValue, _threshold)) 
            _resolvedEvent.Set();
    }

        public virtual UnitOfMeasureQuantityPair Resolve(Transaction transaction) {
        _threshold = _settings.TareThreshold;
            if (!ThresholdMet(_device.CurrentValue, _threshold)) {
                var dialog = _dialogs.GetProvideTareDialog();
                dialog.Show();
            _timer.Start();
            if (!_resolvedEvent.WaitOne(_settings.TakeTareTimeout, false)) {
                _timer.Stop();
                dialog.Hide();
                throw new TimeoutException("Tare provider operation has either been cancelled or has timed out");
            }
            _timer.Stop();
                dialog.Hide();
            }
            return _device.CurrentValue;
        }

        private bool ThresholdMet(UnitOfMeasureQuantityPair val, UnitOfMeasureQuantityPair thresh) {
            if ((thresh == null) || (val >= thresh))
                return true;
            return false;
        }       
    }
//
///如有必要,通过查询测量值来解决给定事务的皮重
///设备并与阈值进行比较。将阻止,直到提供适当的皮重
///或者满足超时条件。
/// 
公营货仓{
专用设备;
私人IDialogFactory对话;
private ManualResetEvent _ResolveEvent=新的ManualResetEvent(错误);
ICurrentDateTimeFactory\u Now工厂;
ICheckInSettings\u设置;
定时器;
专用测量单位QuantityPair _阈值;
公共TareResolver(IDevice设备、IDialogFactory对话框、ICheckins设置、ICurrentDateTimeFactory nowFactory){
_装置=装置;
_对话=对话;
_设置=设置;
_nowFactory=nowFactory;
_timer=newdriverinterface2.Domain.Utilities.timer(\u settings.tarepollFrequency);
_timer.Tick+=新操作(_timer_Tick);
}
void _timer_Tick(){
if(ThresholdMet(_device.CurrentValue,_threshold))
_resolvedEvent.Set();
}
公共虚拟计量单位QuantityPair解析(事务){
_阈值=_settings.TareThreshold;
如果(!ThresholdMet(_device.CurrentValue,_threshold)){
var dialog=_dialogs.getProviderAreDialog();
dialog.Show();
_timer.Start();
if(!\u resolvedvent.WaitOne(\u settings.TakeTareTimeout,false)){
_timer.Stop();
dialog.Hide();
抛出新的TimeoutException(“皮重供应器操作已被取消或超时”);
}
_timer.Stop();
dialog.Hide();
}
返回_device.CurrentValue;
}
专用布尔阈值(测量单位QuantityPair val,测量单位QuantityPair Threshold){
如果((thresh==null)| |(val>=thresh))
返回true;
返回false;
}       
}

ok…但是如何阻止用户只是在窗口周围单击并在窗口下方键入文本?这与通过单击按钮触发阻止活动不同,当前正在加入的线程是UI线程。但是在示例中,如果您有一个监视器
/// <summary>
/// If necessary, resolve the tare for a given transaction by querying the measurement 
/// device and comparing with a threshold.  Will block until an appropriate tare is provided 
/// or a timeout condition is met.
/// </summary>
    public class TareResolver {
    private IDevice _device;
        private IDialogFactory _dialogs;
        private ManualResetEvent _resolvedEvent = new ManualResetEvent(false);
        ICurrentDateTimeFactory _nowFactory;
    ICheckInSettings _settings;
    ITimer _timer;
    private UnitOfMeasureQuantityPair _threshold;
        public TareResolver(IDevice device, IDialogFactory dialogs, ICheckInSettings settings, ICurrentDateTimeFactory nowFactory) {
            _device = device;
            _dialogs = dialogs;
        _settings = settings;
            _nowFactory = nowFactory;
        _timer = new DriverInterface2.Domain.Utilities.Timer(_settings.TarePollFreqeuency);
        _timer.Tick += new Action(_timer_Tick);
    }

    void _timer_Tick() {
        if (ThresholdMet(_device.CurrentValue, _threshold)) 
            _resolvedEvent.Set();
    }

        public virtual UnitOfMeasureQuantityPair Resolve(Transaction transaction) {
        _threshold = _settings.TareThreshold;
            if (!ThresholdMet(_device.CurrentValue, _threshold)) {
                var dialog = _dialogs.GetProvideTareDialog();
                dialog.Show();
            _timer.Start();
            if (!_resolvedEvent.WaitOne(_settings.TakeTareTimeout, false)) {
                _timer.Stop();
                dialog.Hide();
                throw new TimeoutException("Tare provider operation has either been cancelled or has timed out");
            }
            _timer.Stop();
                dialog.Hide();
            }
            return _device.CurrentValue;
        }

        private bool ThresholdMet(UnitOfMeasureQuantityPair val, UnitOfMeasureQuantityPair thresh) {
            if ((thresh == null) || (val >= thresh))
                return true;
            return false;
        }       
    }