Надлежащий способ заставить пользовательский интерфейс Winforms блокировать с помощью диалогового окна, пока не произойдет какое-либо неуправляемое событие? - PullRequest
2 голосов
/ 17 декабря 2008

Я спрашивал об особенностях этого подхода ранее сегодня здесь , но запутанные ответы заставляют меня задуматься, правильно ли я все это делаю.

Так вот в чем проблема. Мне нужно спроектировать приложение WinForms, которое периодически проверяет значение какого-либо внешнего сервиса. Если оно находится в определенном состоянии, предполагается, что приложение вызывает модальную форму, блокирующую взаимодействие пользователя с приложением до тех пор, пока состояние не изменится. Заблокированный пользовательский интерфейс должен действовать так же, как при отображении формы с помощью ShowDialog (). То есть пользователь не должен каким-либо образом взаимодействовать с пользовательским интерфейсом, пока диалоговое окно не будет закрыто (в этом случае это может сделать только приложение).

В моем конкретном приложении у меня есть требование в нескольких случаях. Например, в какой-то момент приложение запускает таймер, который многократно проверяет значение ячейки в таблице базы данных, которая постоянно обновляется взвешивающим устройством. Если значение ниже определенного порогового значения, пользователю предлагается разместить элементы на шкале - он или она не может продолжать или взаимодействовать с пользовательским интерфейсом, пока они не сделают это.

Каждое решение, которое я пробовал, было полно проблем с потоками. Либо диалоговое окно не заблокирует пользовательский интерфейс, либо таймер не продолжит проверку внешней службы, либо приложение не сможет закрыть форму, когда придет время.

Это похоже на довольно стандартное требование winforms, и должен существовать соответствующий шаблон проектирования, который не вносит в Windows.Forms.Timer (мне нравится держать его мои представления пассивными ). Как бы вы это сделали?

Ответы [ 3 ]

1 голос
/ 17 декабря 2008

Предыдущий пост (с Join ()) блокирует пользовательский интерфейс, но также предотвращает его рендеринг, показывая пустое (белое) окно, как в случае сбоя приложения. Лучше было бы создать собственное модальное окно (которое пользователь не может ни перемещать, ни закрывать, ни делать что-либо) с прослушивателем событий, который закроет окно в своем обработчике. Затем, как только ваше взвешивающее устройство обнаружит вес, оно может инициировать событие (или каким-либо образом связаться с вашим приложением, веб-сервисами, ipc и т. Д.), Чтобы уведомить его.

0 голосов
/ 21 декабря 2008

Наконец-то нашли решение, которое кажется естественным и прекрасно работает. Наконец-то уловка состояла в том, чтобы прочитать книгу Threading * Альбахари и узнать о 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;
        }       
    }
0 голосов
/ 17 декабря 2008

Возможно, вы захотите заблокировать текущий поток, пока другой поток не выполнит проверку в фоновом режиме. Это можно сделать, присоединив (.Join()) к потоку, выполняющему проверку в фоновом режиме. Это будет блокировать вызывающий поток, пока он не закончится. Если у меня будет больше времени, я сделаю для вас фрагмент кода, но это может дать вам представление.

Вот код (Form1 и добавьте 2 кнопки: Button1 и 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
    }
}

Нажмите кнопку 2 и поток заблокирует все, пока метод с проверкой внешнего значения (это сделано для этого примера, будет цикл).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...