views:

566

answers:

3

Hello,

I couldn't describe the title of my question the best,I'm sorry. Currently,I use Invoke to access the properties on my form,It works perfect,but I have a function for each property,which is quite not comfortable.

    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;
    }

Each of these functions looks like that

    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;
            }
        }
    }

My question is,can't I do it like that

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

It's definitely not that way,I couldn't think of something more OO,but instead of writing tons of functions with same code,can't I use one by passing the object I'd like to change?

+2  A: 

You could create a method that takes an delegate parameter describing the action to perform. Then you could get rid of the repeated code.

Here is an example: I create a public method called PerformAction on my form. It takes an Action<MainForm> delegate as argument; this delegate describes the action that should be taken.

The instance method should be used when possible, but for completeness I created a static version that gets the Form instance from Form.ActiveForm.

The code looks like this:

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);
        }
    }
}

And can then be used like this from another thread:

    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 could also be made generic so you can use it on any of your forms. Then the signature would be:

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

It would make sense to declare it like this if you have a common base class that is shared amongst your forms. Alternatively, you could create a helper class containing the method.

driis
I'm very interested,please do so.
John
Very nice,but it's not working.It gives me an error on that line "Misc.PerformActionOnMainForm(FormMain => FormMain.groupMain.Enabled = true);" Error "FormMain.groupMain' is inaccessible due to its protection level".I Don't want to change the modified to public,but i tried it - exception,not working
John
Actually it works if everything is "protected internal".Great,thanks!
John
A: 

I asked a similar question but for WPF, the principle is the same though. Modifying the answer that was provided to me by Jon skeet for your question you could use something like this:

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) });    
}

Edit: Changed from using form.Dispatcher.

sipwiz
This also describes the basic principle, the code is wrong however. form.Dispatcher does not exist in WinForms.
driis
Yeah,also you cant write "form.InvokeIfNecessary(form, delegate { form.btnLogin.Enabled = (enabled != 0) });" brackets {} cannot be used.Also I'd also like to see something similiar for the EnableLogin method.Why not EnableProperty method with parameter : the property.
John
Ok I edited the eg. to remove the Dispatcher reference since that's a WPF thing. In regards to the use of the EnableLogin property I cut the setter out as in the eg. it was a lot of code to set a bool and can be done in a single line. If you need to do more in the setter it could still be used.
sipwiz
A: 

You can move the reused functionality into two sperate static methods (one extension) which will greatly simplify what you have in the static helpers and properties.

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);
  }
}

And for your properties:

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

And for your static helpers:

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