tags:

views:

1881

answers:

7

Forum; I am a newbie working out a bit of code. I would like to know the best way to use separate .cs files containing other classes and functions. As an example of a basic function would would like to click on a clear button and have it clear all fields on a form. The code is split up into three .cs files. Main.cs, MainForm.Designer.cs and btClear.cs.

MainForm.Designer.cs contains the designers automatically created code including the Clear Button, and the text boxes I would like to clear.

I would like to have the code to clear the text boxes contained in btClear.cs. I understand it would be easy to simply to place this code in MainForm.Designer.cs however I would like to use this as a template in order to use separate .cs files as a standard practice.

Can someone give me an example of how to do this please?

+1  A: 

One class per .cs file is a good rule to follow.

I would not separate page specific functionality into separate .cs files unless you plan to encapsulate into a reusable class.

Edit:

Put the clear function in the button's OnClick event. No reason to separate this out.

.aspx file:

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="SimpleWebTest._Default" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&gt;

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Untitled Page</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <asp:Button ID="ClearButton" runat=server onclick="ClearButton_Click" Text="Button" />
    </div>
    </form>
</body>
</html>

.cs file

using System;

namespace SimpleWebTest
{
    public partial class _Default : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e) { }

        protected void ClearButton_Click(object sender, EventArgs e)
        {
            using (ComplexOperationThingy cot = new ComplexOperationThingy())
            {
                cot.DoSomethingComplex();
            }
        }
    }
}

ComplexOperationThingy.cs:

using System;

namespace SimpleWebTest
{
    public class ComplexOperationThingy
    {
        ComplexOperationThingy() { }

        public void DoSomethingComplex()
        {
            //Do your complex operation.
        }
    }
}
Chris Ballance
I agree with that, but I am asking more about the how.
tejas_grande
I was thinking of using this as a template for more complex operations
tejas_grande
I see no reason for a negative vote on this one! +1
Cerebrus
+3  A: 

You can use a partial class for your MainForm class, since that's already being done in MainForm.cs and MainForm.Designer.cs.

btnClear.cs

public partial class MainForm
{
   private void clearForm(object sender, EventArgs e)
   {
     // ...
   }
}

And register for the event in MainForm.cs or MainForm.Designer.cs

this.btnClear.click += clearForm;

Edit:

If you want a generic way of doing it, you could set the Tag property of your controls to be the default value. And with an extension method, you could do something like formGroup.Reset();

using System.Windows.Forms;

public class ControlExtensions
{
  public void Reset(this Control control)
  {
    if (control.Tag != null)
    {
      if (control is TextBoxBase && control.Tag is string)
      {
        control.Text = control.Tag as string;
      }
      else if (control is CheckBox && control.Tag is bool)
      {
        control.Checked = control.Tag as bool;
      }
      // etc
    }

    foreach (Control child in control.Children)
      child.Reset();
  }
}
Samuel
I don't think this really addresses what he is looking for.
Chris Ballance
ClearForm does not show up in the current context in the MainForm. How do I fix this?
tejas_grande
+1  A: 

It is fairly simple to do. In your btClear.cs file simply generate a class definition like:

public class MyUtility
{
    public static void ClearContents(IEnumerable<Control> controls)
    {
        //TODO: Code that clears contents of controls passed
    }
}

Then you can call it from wherever the namespace the generated class is contained in is referenced via:

MyUtility.ClearContents(SomeControlCollection);

I hope I didn't completely miss the mark on your intention.

Quintin Robinson
A: 

I find that it's best to not directly edit the .Designer.cs file, especially from a Source Control standpoint. You can use partial classes to contain you related code; in this case, you can use MainForm.Designer.cs as an example of how to accomplish partial classes (eg. partial class MainForm{}).

If you're adding event handlers through the Windows Forms Designer, it should automatically place them in MainForm.cs, not MainForm.Designer.cs. If you project grows to any significant size, I often find it useful to create multiple partial class files, one for each major group of functionality. This makes it a whole lot easier to find a particular function (and related functions).

CodeSavvyGeek
+1  A: 

In general you shouldn't put your code in the MainForm.designer.cs file. The *.designer.cs files are used by Visual Studio to wire up all of the tedious plumbing of actually put controls on the form.

Any code that you want to use to interact with controls on the form should go MainForm.cs file. So if you want a function to clear controls on the form then you should place the code there.

If you the code that you are writing clear the controls on the form is reusable in other projects then you should put it in another .cs file that can be linked into other projects.

As Chris said, generally placing one class per .cs file is also a good idea. When you do, the file should have the name of the class it contains. So if you have a class named MyButtonHelpers then the source file should be named MyButtonHelpers.cs.

Mike Chess
A: 

The convention in C# (and many other languages) is one file per class (and usually one class per file). There are certainly exceptions to this rule, but splitting a single class across multiple files should be a rare occurrence, not a standard practice.

You mentioned in a comment that you foresee more "complex operations" than the example you gave. What sort of operations are you envisioning? As long as you're just talking about dealing with form controls, the logic belongs in the .cs file for the form (in this case, MainForm.cs, and not MainForm.designer.cs - as others have said, you shouldn't ever modify the .designer file).

If your form is getting too complicated, then it may make sense to break it up into separate custom controls. This would allow you to keep your source files smaller, but (since each Control maps to a class) you'd be adhering to the one class <-> one file convention.

John Price
+2  A: 

The way most .net programmers would do it is:

  • Leave all your code inside MainForm.cs, but declare a new method for separating the code that does the clearing.

I always leave in the event handler method (the one VS generates when you double-click the button) code to change the mouse cursor (in case the code I'm calling takes a couple of seconds or more to run) and catch all unhandled exceptions, and put my logic in a separate private method:

partial class MainForm : Form // this is MainForm.cs
{
    // This is the method VS generates when you double-click the button
    private void clearButton_Click(object sender, EventArgs e)
    {
        this.Cursor = Cursors.WaitCursor;
        try
        {
            ClearForm();
        }
        catch(Exception ex)
        {
            // ... have here code to log the exception to a file 
            // and/or showing a message box to the user
        }
        finally
        {
            this.Cursor = Cursors.Default;
        }
    }

    private void ClearForm()
    {
        // clear all your controls here
        myTextBox.Clear();
        myComboBox.SelectedIndex = 0;
        // etc...
    }
}

In my opinion, if you want to follow the conventional way of doing things in .net, you should stick with this first example.

Moving code out of the form .cs files is recommended when the code is not part of the form logic, for example, data access code or business logic. It this case I don't think that clearing the form controls qualifies as business logic. It is just part of the form code and should stay in the MainForm.cs.

But if you really want to put the ClearForm method in another .cs file, you could create a new class and move the ClearForm method there. You would also need to change the Modifiers property of each control to Public so your new class can have access to them from outside MainForm. Something like this:

public class FormCleaner // this is FormCleaner.cs
{
    public void ClearForm(MainForm form)
    {
        // clear all your controls here
        form.myTextBox.Clear();
        form.myComboBox.SelectedIndex = 0;
        // etc...
    }
}

You would have to change the main form code to:

partial class MainForm : Form // this is MainForm.cs
{
    // This is the method VS generates when you double-click the button
    private void clearButton_Click(object sender, EventArgs e)
    {
        this.Cursor = Cursors.WaitCursor;
        try
        {
            FormCleaner cleaner = new FormCleaner();
            cleaner.ClearForm(this);
        }
        catch(Exception ex)
        {
            // ... have here code to log the exception to a file 
            // and/or showing a message box to the user
        }
        finally
        {
            this.Cursor = Cursors.Default;
        }
    }
}

But note that your FormCleaner class would have to know about your MainForm class in order to know how to clear each of the controls of the form, or you would need to come up with a generic algorithm that is able to loop through the Controls collection of any form to clean them:

public class FormCleaner // this is FormCleaner.cs
{
    // 'generic' form cleaner
    public void ClearForm(Form form)
    {
        foreach(Control control on form.Controls)
        {
            // this will probably not work ok, because also
            // static controls like Labels will have their text removed.
            control.Text = "";
        }
    }
}

And as others have said, MainForm.Designer.cs is machine-generated and you should never put your own code there.

Sergio Acosta
I marked this as the answer because it seemed the most complete. However, it seems to be clear that separate .cs files do not normally work how I thought they would. Thanks for the education!
tejas_grande