2

I was asking about specifics with this approach earlier today here but the confused responses has me wondering if I'm going about this all wrong.

So here's the problem. I need to architect a WinForms application that periodically checks a value from some external service. If it is in a certain state the application is supposed to pop up a modal form blocking the user from interacting with the application until such time that the state changes. The blocked UI should act just like when a form is displayed using ShowDialog(). That is to say the user should not be able to interact with the ui in any way until the dialog is closed (in this case this can only be done by the application).

In my particular application I have the requirement crop up in several instances. For example, at one point the application runs a timer that repeatedly checks the value of a cell in a db table that is constantly being updated by a weighing device. If the value is below a certain threshold the user is prompted to place items on the scale - he or she may not proceed nor interact with the UI until they do so.

Every solution I have tried has been chock full of threading problems. Either the dialog won't block the UI, or the timer will not continue checking the external service or the application cannot close the form when the time comes.

This seems like a fairly standard winforms requirement and there must be a relevant design pattern that does not bring in Windows.Forms.Timer (I like keep it my views passive). How would you do this?

4

3 回答 3

1

上一篇文章(使用 Join())阻止了 UI,但也阻止了它的渲染,显示一个空白(白色)窗口,就像应用程序崩溃一样。更好的选择是创建一个自定义模式窗口(用户无法移动或关闭或执行任何操作),其中包含一个事件侦听器,该事件侦听器将在其处理程序中关闭窗口。然后,一旦您的称重设备检测到重量,它就可以触发事件(或以某种方式与您的应用程序、网络服务、ipc 等通信)以通知它。

于 2008-12-17T02:23:27.807 回答
0

You might want to block the current thread until an other thread do the check in the background. This can be done by joining (.Join()) the thread that do the check in the background. This on will block the calling thread until it's over. If I have more time I'll make a snippet code for you, but this might give you an idea.

Here is the code (Form1 and add 2 buttons : Button1 and Button2).

public partial class Form1 : Form
{
    Random r = new Random();
    public Form1()
    {
        InitializeComponent();
    }
    Thread tCheck;
    private void Form1_Load(object sender, EventArgs e)
    {
        tCheck = new Thread(new ThreadStart(checkMethod));

    }

    private void button1_Click(object sender, EventArgs e)
    {
        this.Text = r.Next(0, 10).ToString();
        if (tCheck.ThreadState == ThreadState.Stopped)
        {
            this.button1.Text = "Stopped";
        }
    }

    // checks a value from some external service HERE
    private void checkMethod()
    {
        int i =0;
        while(i<20)
        {
            Thread.Sleep(200);
            i++;
        }

    }

    private void button2_Click(object sender, EventArgs e)
    {
        tCheck.Start();
        Thread.Sleep(500);
        tCheck.Join();//blocking the user from interacting with the application until such time that the state changes
    }
}

Click button 2 and the thread will block all until the method with check for external value (this is done for this example will the loop).

于 2008-12-16T23:58:50.407 回答
0

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

这是它的工作原理。你在你的类上声明一个 ManualResetEvent 的实例,当你到达你的应用程序应该在调用 resetEvent.WaitOne(timeout) 时阻塞的点时。这会阻塞线程,直到另一个线程(正在检查某些条件调用 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;
        }       
    }
于 2008-12-21T02:12:14.173 回答