NickG
NickG

Reputation: 9830

Can I have two Web User Controls (.ascx) share the same codebehind class?

I have a situation where I need to have two totally different layouts for a Web User Control but it's quite important that the codebehind for the controls remains identical.

Is there any way I can use the same codebehind for two different Web User Controls? I've tried changing the Page directive to use the same class, but occasionally when you build, a .designer.cs file gets generated with duplicate controls so you get a compilation error.

I also tried inheriting from a common base class but you cannot see any of the controls from this class, which makes it very difficult to take this approach.

Or is there a way to prevent the designer.cs class from being regenerated for the "clone" control?

Upvotes: 2

Views: 2105

Answers (3)

Stilgar
Stilgar

Reputation: 23591

In addition to @Ondrej Svejdar 's correct answer I suggest that you take a look at the MVP design pattern and maybe use it with your Web Forms code. It works, I have tried it in a big project and the result is no worse than the separation of concerns in an MVC project. There are frameworks to help you implement it but for some reason I prefer implementing it myself (after all it is a couple of base classes and a base interface)

Upvotes: 1

Bharadwaj
Bharadwaj

Reputation: 2553

I have just now tested this in VS 2012.

  1. I have added 2 user controls
  2. Deleted .ascx.cs and .ascx.Designer.cs file from second control
  3. Modified Page directive of second control for CodeBehind and Inherits attribute. No need to generate Designer file for second control.

And it works. I don't know this is valid approach or not. I have tested it by adding a Label in each control and setting its value in code behind. The value reflects in both controls.

You may face issue when you have a asp.net control in one user control and it doesn't exists in other.

Upvotes: 2

Ondrej Svejdar
Ondrej Svejdar

Reputation: 22094

Create base class, and implement common logic there. Then inherit controls from this base class. For accessing controls from base class level make abstract get properties on base class level for every single control you'll need. Implement those properties in child control classes.

public abstract class BaseControl : UserControl
{
  public abstract TextBox FirstName { get; }

  public void SomeLogicExample() 
  {
    FirstName.Text = "Something"; 
  }
}

public class ControlA : BaseControl
{
  public override TextBox FirstName
  {
    // txtFirstNameA is ID of TextBox, so it is defined in ControlA.designer.cs
    get { return txtFirstNameA; }
  }
}

public class ControlB : BaseControl
{
  public override TextBox FirstName
  {
    // txtFirstNameB is ID of TextBox, so it is defined in ControlB.designer.cs
    get { return txtFirstNameB; }
  }
}

Alternatively and a lot less elegant is locating controls at runtime; because you pay for searching the whole control tree and you have to handle control not found scenarios:

public abstract class BaseControl : UserControl
{
  public T GetControlByType<T>(Func<T, bool> predicate = null) where T : Control
  {
    var stack = new Stack<Control>(new Control[] { this });
    while (stack.Count > 0)
    {
      var control = stack.Pop();
      T match = control as T;
      if (match != null)
      {
        if (predicate == null || predicate(match))
        {
          return match;
        }
      }

      foreach (Control childControl in control.Controls)
      {
        stack.Push(childControl);
      }
    }

    return default(T);
  }    

  public TextBox FirstName
  {
    get { return GetControlByType<TextBox>(t => t.ID == "txtFirstName"); }
  }

  public void SomeLogicExample() 
  {
    FirstName.Text = "Something"; 
  }
}

Upvotes: 3

Related Questions