Winforms Обновление шаблона пользовательского интерфейса асинхронно - необходимо обобщить - PullRequest
4 голосов
/ 27 августа 2010

Настройка: Основная форма MDI с индикатором выполнения и меткой.

Код в основной форме.

    public delegate void UpdateMainProgressDelegate(string message, bool isProgressBarStopped);

            private void UpdateMainProgress(string message, bool isProgressBarStopped)
            {
                // make sure we are running on the right thread to be
                // updating this form's controls.
                if (InvokeRequired == false)
                {
                    // we are running on the right thread.  Have your way with me!
                    bsStatusMessage.Caption = message + " [ " + System.DateTime.Now.ToShortTimeString() + " ]";
                    progressBarStatus.Stopped = isProgressBarStopped;
                }
                else
                {
                    // we are running on the wrong thread.  
                    // Transfer control to the correct thread!                
                    Invoke(new ApplicationLevelValues.UpdateMainProgressDelegate(UpdateMainProgress), message, isProgressBarStopped);
                }
            }

Дочерняя форма

private readonly ApplicationLevelValues.UpdateMainProgressDelegate _UpdateMainForm;
private void btnX_Click(object sender, EventArgs e)
        {
            _UpdateMainForm.BeginInvoke("StartA", false, null, null);
            try
            {
                if(UpdateOperationA())
                { _UpdateMainForm.BeginInvoke("CompletedA", true, null, null); }
                else
                { _UpdateMainForm.BeginInvoke("CanceledA", true, null, null); }
            }
            catch (System.Exception ex)
            {
                _UpdateMainForm.BeginInvoke("ErrorA", true, null, null);
                throw ex;
            }
        }

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

Ответы [ 3 ]

4 голосов
/ 01 сентября 2010

Это действительно простая проблема рефакторинга, если все ваши операции могут быть представлены как один тип делегата.Например:

private void RunOperationWithMainProgressFeedback(
    Func<bool> operation,
    string startMessage,
    string completionMessage,
    string cancellationMessage,
    string errorMessage)
{
    this._UpdateMainForm.BeginInvoke(startMessage, false, null, null);
    try
    {
        if (operation.Invoke())
        {
            this._UpdateMainForm.BeginInvoke(completionMessage, true, null, null);
        }
        else
        {
            this._UpdateMainForm.BeginInvoke(cancellationMessage, true, null, null);
        }
    }
    catch (Exception)
    {
        this._UpdateMainForm.BeginInvoke(errorMessage, true, null, null);
        throw;
    }
}

private void btnX_Click(object sender, EventArgs e)
{
    this.RunOperationWithMainProgressFeedback(
        this.UpdateOperationA,
        "StartA",
        "CompletedA",
        "CanceledA",
        "ErrorA");
}

Хотя для хранения значений аргументов можно использовать словарь (как это предлагалось в предыдущем ответе VinayC), в этом нет необходимости.Лично я бы избегал этого как из-за читабельности, так и из-за производительности, но мммм ...

2 голосов
/ 27 августа 2010

Вы можете создать словарь / список, содержащий соответствующие детали для каждой кнопки. Эти детали включают имя операции и делегат для вызова операции и т. Д. Теперь вы можете иметь общий обработчик событий для всех кнопок, которые будут искать в словаре, чтобы получить имя операции и выполняемую операцию.

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

1 голос
/ 02 сентября 2010

Я устал от решения одной и той же проблемы снова и снова, поэтому я написал общий набор классов для решения этой проблемы. Основная идея такова:

  • Создайте абстрактный объект Progress с полями, которые вам нужны, например, statusMessage.

  • Создание абстрактного объекта Job. Задание похоже на поток, но в дополнение к основному и начальному элементам у него есть метод UpdateProgress (Progress p). UpdateProgress копирует текущий прогресс в p. Обычно это единственное место, где можно приобрести замки.

  • В вашем конкретном случае используйте подкласс Progress и добавляйте поля по мере необходимости. Затем создайте подкласс Job для хранения того, что вы хотите сделать, и реализуйте UpdateProgress.

  • В пользовательском интерфейсе создайте таймер, чьи основные вызовы выполняются. UpdateProgress затем обновляет пользовательский интерфейс.

Часто таймеры достаточно хороши для обновлений пользовательского интерфейса, и применение модели «тяни-и-синхронизируй» проще, чем использование модели «push-change». Pull-and-sync-ui позволяет избежать многих проблем с многопоточностью, а пользовательский интерфейс переходит в странные состояния.

БОНУС: Сделайте ваш объект Progress объектом INotifyProperyChanged и сделайте привязку данных. (вот почему вы можете не использовать IDictionary).

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