0

In my app I have some functions that loop over all the components in a form to establish some properties like the following ones:

Function used to set all the textbox in a form to readonly when we are just viewing data:

public void SetReadOnly(Control.ControlCollection controls, bool readOnly)
{
    foreach(var panel in controls.OfType<Panel>())
    {
        SetReadOnly(panel.Controls, readOnly);
    }

    foreach(var tabpageview in controls.OfType<TabPageView>())
    {
        foreach(var tabpageviewpage in tabpage.Controls)
        {
            SetReadOnly(tagpageviewpage.Controls, readOnly);
        }
    }

    foreach(var textbox in controls.OfType<TextBox>())
    {
        textbox.ReadOnly = readOnly;
    }
}

Function used to clean the content of all the textboxes after data has been inserted in the database:

public void CleanTextBox(Control.ControlCollection controls)
{
    foreach(var panel in controls.OfType<Panel>())
    {
        CleanTextBox(panel.Controls);
    }

    foreach(var tabpageview in controls.OfType<TabPageView>())
    {
        foreach(var tabpageviewpage in tabpage.Controls)
        {
            CleanTextBox(tagpageviewpage.Controls);
        }
    }

    foreach(var textbox in controls.OfType<TextBox>())
    {
        textbox.Text = "";
    }
}

Function that receives a list of fields and paint the labels with that name to red to remark that is required:

public void SetRequired(Control.ControlCollection controls, List<string> requiredFields)
{
    foreach(var panel in controls.OfType<Panel>())
    {
        SetRequired(panel.Controls, requiredFields);
    }

    foreach(var tabpageview in controls.OfType<TabPageView>())
    {
        foreach(var tabpageviewpage in tabpage.Controls)
        {
            SetRequired(tagpageviewpage.Controls, requiredFields);
        }
    }

    foreach(var field in requiredFields)
    {
        FindLabelByName(field).Foreground(Color.Red);
    }
}    

As you can see all those function has something in common where they receive a collection of controls and loop over them. If it is a panel, a tabpageview, or another component with children, then it loops again until it find its textboxes and do some action.

Is it possible to make one function that receives another function as a parameter so if I start using groupboxes I can just put it in one function instead of having to put it in all the functions? Something like this.

public void LoopComponents(Control.ControlCollection controls, xxx function)
{
    foreach(var panel in controls.OfType<Panel>())
    {
        LoopComponents(panel.controls, function);
    }

    foreach(var tabpageview in controls.OfType<TabPageView>())
    {
        foreach(var tabpageviewpage in tabpageview.Controls)
        {
            LoopComponents(tabpageviewpage.Controls, function);
        }
    }

    function();
}

public void SetReadOnly(Control.ControlCollection controls, bool readOnly)
{
    foreach(var textbox in controls.OfType<TextBox>())
    {
        textbox.ReadOnly = readOnly;
    }
}

public void CleanTextBox(Control.ControlCollection controls)
{
    foreach(var textbox in controls.OfType<TextBox>())
    {
        textbox.Text = "";
    }
}

public void SetRequired(Control.ControlCollection controls, List<string> requiredFields)
{
    foreach(var field in requiredFields)
    {
        FindLabelByName(field).Foreground(Color.Red);
    }
}   

public void Test()
{
    LoopComponents(Controls, SetReadOnly(Controls, true));
    LoopComponents(Controls, CleanTextBox(Controls));
    LoopComponents(Controls, SetRequired(Controls, requiredFieldsList));
}

Currently I'm using C# 4.0, but if it's possible in a later version it doesn't matter, just curious to know if it's possible. Thanks

aenon83
  • 59
  • 10
  • 1
    Please check; [Pass Method as parameter](https://stackoverflow.com/questions/2082615/pass-method-as-parameter-using-c-sharp) this might be helpful for you. – Ishtiaq Oct 31 '18 at 15:36
  • I saw that thread. The difference is that in that example the functions always receive a string and return something, while in my case the functions receive different parameters. Thanks anyways. – aenon83 Oct 31 '18 at 15:41
  • You might want to look into the Visitor Pattern. – Damien_The_Unbeliever Oct 31 '18 at 15:49

1 Answers1

0

You must define a delegate or use Predefined ones, Like Action<> and Func<>. Delegates will defines a method signature:

 delegate void Operator(Control.ControlCollection controls);

and you can modify LoadComponent in this way:

 LoadComponent(Control.ControlCollection controls, Operator Op)
 {
     foreach(var control in controls.OfType<Panel>())
     {
        Op(panel.controls);
     }
     //....
 }

and you can write some function as Operator, Like:

 public void CleanTextBox(Control.ControlCollection controls)
 {
     foreach(var textbox in controls.OfType<TextBox>())
     {
        textbox.Text = "";
     }
 }

Note: in Above function CleanTextBox inputs and outputs are the same in Operator delegate. so it will matches to Operator delegate.

Use Predefined delegates:

you can use either predefined delegates. As your functions are void, you can use one of the Action's overloads:

 LoadComponent(Action<Control.ControlCollection controls, Action<Control.ControlCollection> Op)
 {
     foreach(var control in controls.OfType<Panel>())
     {
        Op(panel.controls);
     }
     //....
 }

this new version just removed the delegate deceleration. in both fashion of the work you can use this code to do your operation on the data's:

LoopComponents(Controls, CleanTextBox(Controls));

** Func delegates are useful when you want to have a return value**

Edit: If you want to Call different method with a single delegate you must create new method to unify these methods:

rewrite LoadComponents:

  LoadComponents(Control.ControlCollection controls, Action Op)
  {
       Op.Invoke();
       //....
  }

(This is Lambda Expression to unify your methods)

 LoadComponents(Controls, new Action(()=>{ SetRequired(Controls,true);}));
 LoopComponents(Controls, new Action(()=>{ CleanTextBox(Controls);})); 
 LoopComponents(Controls, new Action(()=>{ SetRequired(Controls, requiredFieldsList);});
RezaNoei
  • 1,266
  • 1
  • 8
  • 24
  • Yes, I read about that, but my functions have different signatures, so I would need three delegates and one `LoadComponent` function for every delegate, having the same problem if I want to loop over a new type of control. – aenon83 Oct 31 '18 at 16:14
  • I have added some way to do what you wanted. – RezaNoei Oct 31 '18 at 19:10