My struggles in understanding and learning about Object Oriented design, and the tools and knowledge I've taken from them.

Monday, October 25, 2010

Resizable Controls on a .Net desktop form

I've never really found a great way to have controls on a form grow and shrink with a form when a C# Desktop app form gets resized.  I'm sure that there are other solutions out there besides the one I created, but sometimes sometimes I feel the need to just write code.  So, a little background on what I did:

1.  I created a form object called ResizeableForm, which inherits from System.Windows.Forms.
2.  I created an AbstractControl object, which Has-A System.Windows.Forms.Control on it
3.  I created an AbstractControlCollection object, which inherits from List<AbstractControl>.  This simply provides a quicker means to do bulk operations on my collection of AbstractControls.

When the ResizeableForm gets painted, I initialize my AbstractControlCollection (if it is null):


private
void onFormPaint(object sender, PaintEventArgs e)
{
  if (_controls == null)
    _controls =
new AbstractControlCollection(this);
}

I also added a Resize event handler to make a call to my AbstractControlCollection.Resize() method:


private
void onFormResize(object sender, EventArgs e)
{
  if (_controls == null)
    return;

  _controls.Resize();
}

Obviously, the above code requires that you link up these delegates to the form's event handlers with the following lines:




this
.Paint += new PaintEventHandler(onFormPaint);
this.Resize += new EventHandler(onFormResize);
The abstractcontrol object keeps track of ratios that the original control had, in terms of width and height, as well as distance from the top and left of the form.  Below is the class:


///
<summary>
/// Stores information about a form, and provides a quick means
/// to resize a control when a form is resized
/// </summary>
public class AbstractControl
{
  /// <summary>
  /// The real control associated with this control
  /// </summary>
  Control _associatedControl; 

  /// <summary>
  /// The form associated with the control
  /// </summary>
  ResizableForm _parentForm; 

  /// <summary>
  /// Original Height ratio
  /// </summary>
  double _originalHeightRatio; 

  /// <summary>
  /// Original width ratio
  /// </summary>
  double _originalWidthRatio; 

  /// <summary>
  /// Original X Location of the control
  /// </summary>
  double _originalXRatio; 

  /// <summary>
  /// Original Y Location of the form
  /// </summary>
  double _originalYRatio; 

  /// <summary>
  /// The handle associated with the associated control
  /// </summary>
  IntPtr _handle;

  public AbstractControl(Control c, ResizableForm originalForm)
  {
     _parentForm = originalForm;
     _associatedControl = c;
     _handle = c.Handle;
     _originalHeightRatio = (
double)c.Height / (double)originalForm.Height;
     _originalWidthRatio = (
double)c.Width / (double)originalForm.Width;
     _originalXRatio = (
double)c.Location.X / (double)originalForm.Width;
     _originalYRatio = (
double)c.Location.Y / (double)originalForm.Height;
  } 

  /// <summary>
  /// Public access to this control's handle
  /// </summary>
  public IntPtr Handle
  {
    get { return _handle; }
  } 

  /// <summary>
  /// Provides a means to resize the control based on the new form size
  /// </summary>
  public void Resize()
  {
    int newWidth = (int)((double)(_parentForm.Width * _originalWidthRatio));
    int newHeight = (int)((double)(_parentForm.Height * _originalHeightRatio));
    int newX = (int)((double)(_parentForm.Width * _originalXRatio));
    int newY = (int)((double)(_parentForm.Height * _originalYRatio));
    _associatedControl.Width = newWidth;
    _associatedControl.Height = newHeight;
    _associatedControl.Location =
new System.Drawing.Point(newX, newY);
  }

So, the remaining piece of the code is the Abstract Control Collection:


///
<summary>
/// Represents a collection of abstract controls
/// </summary>
public class AbstractControlCollection : List<AbstractControl>
{
  /// <summary>
  /// Form associated with the control collection
  /// </summary>
  ResizableForm _originalForm; 

  /// <summary>
  /// Default constructor - adds controls to the collection based on the controls on the form
 
///  </summary>
  /// <param name="originalForm">Form associated with the collection</param>
  public AbstractControlCollection(ResizableForm originalForm)
  {
    _originalForm = originalForm;
    addControls(originalForm);
  } 

  /// <summary>
  /// Method to add all controls and children controls to the collection
  /// </summary>
  /// <param name="baseControl">Control to add child controls from</param>
  private void addControls(Control baseControl)
  {
    foreach (Control c in baseControl.Controls)
    {
       AbstractControl abstractControl = new AbstractControl(c, _originalForm);

       if
(!this.Contains(abstractControl, true))
          t
his.Add(abstractControl);
       addControls(c);
     }
  } 

  /// <summary>
  /// Quick way to resize all the controls on the form at the same time
  /// </summary>
  public void Resize()
  {
     foreach (AbstractControl c in this)
       c.Resize();
  } 

  /// <summary>
  /// Separate contains function to check to see if the collection contains a control
  /// </summary>
  /// <param name="control">Control to search for</param>
  /// <param name="overrideIndicator">Overload var</param>
  /// <returns>True if the collection contains the searched control</returns>
  public bool Contains(AbstractControl control, bool overrideIndicator)
  {
    foreach (AbstractControl c in this)
    {
       if (control.Handle == c.Handle)
          return true;
    }
    return false;
  }
}

Conclusion
To use this code, simply add the files to your solution, and when creating a form, simply have it inhert from ResizeableForm, instead of System.Windows.Forms.Form.  Everything else takes care of itself

Followers

Search This Blog

Powered by Blogger.