Как получить доступ к свойствам формы с помощью Invoke, но с параметром объекта в С#?

Я не мог описать название моего вопроса лучше всего, извините. В настоящее время я использую Invoke для доступа к свойствам в моей форме. Он работает отлично, но у меня есть функция для каждого свойства, что довольно неудобно.

    public static void EnableLogin(int enabled)
    {
        var form = Form.ActiveForm as FormMain;
        if (form != null)
            form.EnableLogin = enabled;
    }

    public static void EnableConfirm(int enabled)
    {
        var form = Form.ActiveForm as FormMain;
        if (form != null)
            form.EnableConfirm = enabled;
    }

    public static void EnableRetry(int enabled)
    {
        var form = Form.ActiveForm as FormMain;
        if (form != null)
            form.EnableRetry = enabled;
    }

    public static void EnableTABLogin(int enabled)
    {
        var form = Form.ActiveForm as FormMain;
        if (form != null)
            form.EnableTABLogin = enabled;
    }

Каждая из этих функций выглядит так

    public int EnableLogin
    {
        set
        {
            if (this.InvokeRequired)
            {
                this.Invoke((MethodInvoker)delegate
                {
                    if (value == 0)
                        this.btnLogin.Enabled = false;
                    else
                        this.btnLogin.Enabled = true;
                });
            }
            else
            {
                if (value == 0)
                    this.btnLogin.Enabled = false;
                else
                    this.btnLogin.Enabled = true;
            }
        }
    }

Мой вопрос, я не могу сделать это так

    public static void EnableObject(object name)
    {
        var form = Form.ActiveForm as FormMain;
        if (form != null)
            form.Enable + name = enabled;
    }

Это определенно не так, я не мог придумать что-то большее ООП, но вместо того, чтобы писать тонны функций с одним и тем же кодом, нельзя ли использовать одну, передав объект, который я хотел бы изменить?


person Ivan Prodanov    schedule 11.04.2009    source источник


Ответы (3)


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

Вот пример: я создаю общедоступный метод с именем PerformAction в своей форме. Он принимает делегат Action‹MainForm› в качестве аргумента; этот делегат описывает действие, которое следует предпринять.

По возможности следует использовать метод экземпляра, но для полноты картины я создал статическую версию, которая получает экземпляр формы из Form.ActiveForm.

Код выглядит следующим образом:

using System;
using System.Windows.Forms;

namespace WinFormTest
{
    public partial class MainForm : Form
    {
        public void PerformAction(Action<MainForm> action)
        {
            if (InvokeRequired)
                Invoke(action,this);
            else
                action(this);
        }

        public static void PerformActionOnMainForm(Action<MainForm> action)
        {
            var form = ActiveForm as MainForm;
            if ( form!= null)
                form.PerformAction(action);
        }
    }
}

И затем можно использовать так из другого потока:

    MainForm.PerformActionOnMainForm(form => form.Text = "My form");
    // The action can also be a code block or a method:
    MainForm.PerformActionOnMainForm(form =>
                                         {
                                             form.Width = 200;
                                             form.Height = 300;
                                             form.Left = 100;
                                             form.Top = 200;
                                         });

PerformAction также можно сделать универсальным, чтобы вы могли использовать его в любой из ваших форм. Тогда подпись будет такой:

public void PerformAction<T>(Action<T> action) 
    where T : Form 
    { 
        ... 
    }

Было бы разумно объявить его таким образом, если у вас есть общий базовый класс, который используется всеми вашими формами. В качестве альтернативы вы можете создать вспомогательный класс, содержащий метод.

person driis    schedule 11.04.2009
comment
Мне очень интересно, пожалуйста. - person Ivan Prodanov; 11.04.2009
comment
Очень красиво, но не работает. Выдает ошибку в этой строке Misc.PerformActionOnMainForm(FormMain => FormMain.groupMain.Enabled = true); Ошибка FormMain.groupMain' недоступна из-за его уровня защиты. Я не хочу менять измененный на общедоступный, но я пробовал - исключение, не работает - person Ivan Prodanov; 11.04.2009
comment
На самом деле это работает, если все защищено внутри. Большое спасибо! - person Ivan Prodanov; 11.04.2009

Я задал похожий вопрос, но для WPF принцип тот же. Изменив ответ, который дал мне Джон Скит на ваш вопрос, вы можете использовать что-то вроде этого:

public static void InvokeIfNecessary(Form form, MethodInvoker action)
{    
     if (form.InvokeRequired)    
     {        
          form.Invoke(DispatcherPriority.Normal, action);    
     }    
     else    
     {        
          action();    
     }
}

public static void EnableLogin(int enabled)    
{        
    var form = Form.ActiveForm as FormMain;        
    if (form != null)            
        InvokeIfNecessary(form, delegate { form.btnLogin.Enabled = (enabled != 0) });    
}

Изменить: изменено использование form.Dispatcher.

person sipsorcery    schedule 11.04.2009
comment
Это также описывает основной принцип, однако код неверен. form.Dispatcher не существует в WinForms. - person driis; 11.04.2009
comment
Да, также вы не можете написать form.InvokeIfNecessary(form, delegate { form.btnLogin.Enabled = (enabled != 0) }); нельзя использовать скобки {}. Также я хотел бы увидеть что-то похожее для метода EnableLogin. Почему бы не использовать метод EnableProperty с параметром: свойство. - person Ivan Prodanov; 11.04.2009
comment
Хорошо, я отредактировал, например. чтобы удалить ссылку Dispatcher, поскольку это вещь WPF. Что касается использования свойства EnableLogin, я вырезал установщик, как, например, в примере. было много кода для установки логического значения, и это можно было сделать в одной строке. Если вам нужно сделать больше в сеттере, его все равно можно использовать. - person sipsorcery; 11.04.2009

Вы можете переместить повторно используемые функции в два отдельных статических метода (одно расширение), что значительно упростит то, что у вас есть в статических помощниках и свойствах.

public static class FormHelpers
{
  public static void InvokeOnActive<TForm>(Action<TForm> action)
    where TForm : Form
  {
    TForm activeForm = Form.ActiveForm as TForm;
    if (activeForm != null)
      activeForm.InvokeEx(f => { action(f); return f; });
  }
}

public static class ControlExtensions
{
  public static TResult InvokeEx<TControl, TResult>(this TControl control,
                                              Func<TControl, TResult> func)
    where TControl : Control
  {
    if (control.InvokeRequired)
      return (TResult)control.Invoke(func, control);
    else
      return func(control);
  }
}

И для ваших свойств:

public int EnableLogin
{
  set { this.InvokeEx(f => f.btnLogin.Enabled = value == 0); }
}

И для ваших статических помощников:

public static void EnableLogin(int enabled)
{
  FormHelpers.InvokeOnActive<FormMain>(f => f.EnableLogin = enabled);
}
person Samuel    schedule 11.04.2009