Click here to Skip to main content
15,897,704 members
Articles / Programming Languages / C#

Relative Design Components on WinForm

Rate me:
Please Sign up or sign in to vote.
4.83/5 (4 votes)
14 Nov 2012CPOL2 min read 17.1K   339   12  
Simple code to make your components always fit exact size of Form, during and after resize.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;


/*
 * copyright :: Gerard Sławiński
 * 
 * Class created by Gerard Sławiński || turbosqel
 * 
 * version :: 0.9 (test round)
 * 
 */


namespace TForm.classes.display {

    public class RelativeLayout {

        ////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////

        // <------------------------------ MAIN DATA


        protected static Dictionary<Control, List<Position>> items = new Dictionary<Control, List<Position>>();

        protected static List<Position> noAssign = new List<Position>();

        ////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////

        // <------------------------------------------ MANAGE ITEMS

        ////////////////////////////////////////////////////////////

        // <------------------------------ ADD / REMOVE

        public static Position Add(Control component) {
            Control parent = component.Parent;
            component.ParentChanged += NewParent;
            Position Pos = new Position(component, parent);
            Register(Pos);
            return Pos;
        }


        public static void Remove(Position pos) {
            RemovePosition(pos);
        }

        public static bool Remove(Control child) {
            if (child.Parent == null) {
                foreach (Position pos in noAssign) {
                    if (pos.Target == child) {
                        noAssign.Remove(pos);
                        pos.internalRemove();
                        return true;
                    }
                }
            } else {
                foreach (KeyValuePair<Control, List<Position>> pair in items) {
                    if (pair.Key == child.Parent) {
                        Position position = null;
                        foreach (Position pos in pair.Value) {
                            if (pos.Target == child) {
                                position = pos;
                                break;
                            }
                        };
                        if (position != null) {
                            RemovePosition(position);
                            return true;
                        }
                    };
                }
            }
            return false;
        }

        // <------------------------------ INVALIDATE

        public static bool ReplaceContainer(Control parent) {
            if(items.ContainsKey(parent)){
                List<Position> list = items[parent];
                foreach (Position pos in list) {
                    pos.Resize();
                }
                return true;
            }
            return false;
        }




        ////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////
				

        ///<summary>
        ///   remove component and position instance from lists
        ///</summary>


        static void RemovePosition(Position pos) {
            if (pos.Parent == null) {
                noAssign.Remove(pos);
            } else {
                items[pos.Parent].Remove(pos);
                VerifyParent(pos.Parent);
            };
            pos.internalRemove();
        }

        ///<summary>
        ///  remove child Control from parent and remove from list if there is no more children  
        ///</summary>
				
        static void RemoveParent(Control child ,Control parent) {
            if (items.ContainsKey(parent)) {
                List<Position> parentList = items[parent];
                foreach (Position pos in parentList) {
                    if(pos.Target == child){
                        parentList.Remove(pos);
                        break;
                    }
                }
                VerifyParent(parent);
            }
        }

        static void VerifyParent(Control parent) {
            List<Position> list = items[parent];
            if (list != null && list.Count == 0) {
                parent.Resize -= ParentResize;
                items.Remove(parent);
            }
        }

        static void Register(Position pos) {
            if (pos.Parent == null) {
                noAssign.Add(pos);
            } else {
                List<Position> itemsList;
                if (items.ContainsKey(pos.Parent)) {
                    itemsList = items[pos.Parent];
                } else {
                    itemsList = new List<Position>();
                    items[pos.Parent] = itemsList;
                    pos.Parent.Resize += ParentResize;
                }
                itemsList.Add(pos);
            }
        }

        ////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////

        // <------------------------------ EVENT HANDLERS
				

        ///<summary>
        ///   change item parent
        ///</summary>

        static void NewParent(object sender, EventArgs e) {
            Control Current = (Control)sender;
            Control newParent = Current.Parent;
            Position position = null;
            // check if last parent was null:
            foreach (Position pos in noAssign) {
                if (pos.Target == Current) {
                    position = pos;
                    noAssign.Remove(position);
                    goto AddItem;
                }
            }
            // loop and find sender list
            foreach (KeyValuePair<Control, List<Position>> pair in items) {
                // focus on list
                List<Position> list = pair.Value;
                // check for sender Position
                foreach (Position pos in list) {
                    // compare item with target Control
                    if (pos.Target == Current) {
                        position = pos;
                        // remove old reference if exist
                        //   1. check if parent was null
                        List<Position> OldList = items.ContainsKey(pos.Parent) ? items[pos.Parent] : null;
                        if (OldList != null && OldList.Contains(pos)) {
                            OldList.Remove(pos);
                            // remove parent if there are no more children
                            VerifyParent(pos.Parent);
                        }
                        // target position instance found
                        goto AddItem;
                    }
                    
                }
            }
            return;
        AddItem:
            // rest::
            // set Position new parent
            position.Parent = newParent;
            // add into index
            Register(position);
        }

        static void ParentResize(object sender, EventArgs e) {
            List<Position> list = items[(Control)sender];
            foreach (Position pos in list) {
                pos.Resize();
            }
        }

        


        ////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////

        // <------------------------------ DEBUG

        /*
        public static void DisplayElements() {
            DebugConsole.text = "================= < start > Display elements ::";
            foreach (KeyValuePair<Control, List<Position>> pair in items) {
                DebugConsole.text = "Parent control ::" + pair.Key.Name + " , elements :" + pair.Value.Count;
                foreach(Position pos in pair.Value){
                    DebugConsole.text = pos.ToString();
                }
            };
            DebugConsole.text = "Unassign elements:";
            foreach (Position pos in noAssign) {
                DebugConsole.text = pos.ToString();
            }
            DebugConsole.text = "================= < end >";
        }
        */

    }


    ////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////
				

    public class Position {

        ////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////

        // <------------------------------ STATIC INITIALIZERS

        public static bool DefaultAutoResize = true;

        ////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////

        // <------------------------------ MAIN PARAMS

        public Control Parent;
        public Control Target;
        public Rectangle Rect = new Rectangle();

        public bool AllowResize = DefaultAutoResize;

        ////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////

        // <------------------------------ POSITION PARAMS

        // <------------- up center down

        public float Top = float.NaN;

        public float Vertical = float.NaN;

        public float Bottom = float.NaN;

        ///<summary>
        /// between 0-1; 0-100% of parent size  
        ///</summary>

        public float RelativeVertical = float.NaN;

        // <------------- left center right

        public float Left = float.NaN;

        public float Horizontal = float.NaN;

        public float Right = float.NaN;

        ///<summary>
        /// between 0-1; 0-100% of parent size  
        ///</summary>

        public float RelativeHorizontal = float.NaN;

        // <------------------------------ SIZE PARAMS

        // <------------- regular and relative

        ///<summary>
        ///   set width or relative width . To set regular width , value must be positive . 
        ///   Relative value should be negative and between 0 and -1 ( 0 - 100% ) , scale of parent size
        ///</summary>

        public float Width = float.NaN;

        ///<summary>
        ///   set height or relative height . To set regular height , value must be positive . 
        ///   Relative value should be negative and between 0 and -1 ( 0 - 100% ) , scale of parent size
        ///</summary>

        public float Height = float.NaN;

        ////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////

        // <------------------------------ EVENTS

        public delegate void ResizeRequestHandler(Position target);
        public event ResizeRequestHandler ResizeRequest;

        public delegate void ResizedHandler(Position target);
        public event ResizedHandler Resized;

        ////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////
        
        // <------------------------------ CTORS

        public Position(Control target , Control parent = null) {
            Parent = parent;
            Target = target;

            Rect.x = Target.Left;
            Rect.y = Target.Top;
            Rect.width = Target.Width;
            Rect.height = Target.Height;
        }

        ////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////

        // <------------------------------ RESIZE

        /*
         * 
        private function get horizontalMode():int {
			return ((isNaN(_left) ? 0:1) + (isNaN(_horizontal) ? 0:2) + (isNaN(_right) ? 0:4));
		};
         * 
         * 
         */

        /*
         * 
         * 1 top
         * 2 vertical
         * 3 top + vertical
         * 4 bottom
         * 5 top + bottom
         * 6 vertical + bottom
         * 7 top + vertical + bottom
         * 
         */

        protected int horizontalMode() {
            return (float.IsNaN(Left) ? 0 : 1) + ((float.IsNaN(Horizontal) && float.IsNaN(RelativeHorizontal)) ? 0 : 2) + (float.IsNaN(Right) ? 0 : 4); 
        }

        protected int verticalMode() {
            return (float.IsNaN(Top) ? 0 : 1) + ((float.IsNaN(Vertical) && float.IsNaN(RelativeVertical)) ? 0 : 2) + (float.IsNaN(Bottom) ? 0 : 4);
        }

        public void Resize() {
            if (AllowResize) {
                // horizontal :: 
                // x and width
                switch (horizontalMode()) {
                    case 0 :
                        Rect.x = Target.Left;
                        Rect.width = Target.Width;
                        break;
                    case 1 :
                        Rect.x = (int)Left;
                        Rect.width = (float.IsNaN(Width) ? Target.Width : (int)(Width < 0 ? -Parent.Width * Width : Width));
                        break;
                    case 2 :
                        Rect.width = (float.IsNaN(Width) ? Target.Width : (int)(Width < 0 ? - Parent.Width * Width : Width));
                        Rect.x = (int)((float.IsNaN(RelativeHorizontal) ? Parent.Width / 2 + Horizontal : Parent.Width * RelativeHorizontal) - Rect.width / 2);
                        break;
                    case 4 :
                        Rect.width = (float.IsNaN(Width) ? Target.Width : (int)(Width < 0 ? - Parent.Width * Width : Width));
                        Rect.x = (int)(Parent.Width - Right - Rect.width);
                        break;
                    case 3 :
                        Rect.x = (int)Left;
                        Rect.width = (int)( (float.IsNaN(RelativeHorizontal) ? Parent.Width / 2 + Horizontal : Parent.Width * RelativeHorizontal) - Left);
                        break;
                    case 5 :
                        Rect.x = (int)Left;
                        Rect.width = (int)(Parent.Width - Left - Right);
                        break;
                    case 6 :
                        Rect.x = (int)((float.IsNaN(RelativeHorizontal) ? Parent.Width / 2 + Horizontal : Parent.Width * RelativeHorizontal));
                        Rect.width = (int)(Parent.Width - Rect.x - Right );
                        break;
                    case 7 :
                        Rect.width = (float.IsNaN(Width) ? Target.Width : (int)(Width < 0 ? -Parent.Width * Width : Width));
                        float space = Parent.Width - Right - Left;
                        Rect.x = (int)((float.IsNaN(RelativeHorizontal) ? space/2 + RelativeHorizontal : space * RelativeHorizontal) + Left);
                        break;
                }

                // vertical :: 
                // y and height

                switch (verticalMode()) {
                    case 0:
                        Rect.y = Target.Top;
                        Rect.height = Target.Height;
                        break;
                    case 1:
                        Rect.y = (int)Top;
                        Rect.height = (float.IsNaN(Height) ? Target.Height : (int)(Height < 0 ? Parent.Height * Height : Height));
                        break;
                    case 2:
                        Rect.height = (float.IsNaN(Height) ? Target.Height : (int)(Height < 0 ? Parent.Height * Height : Height));
                        Rect.y = (int)((float.IsNaN(RelativeVertical) ? Parent.Height / 2 + Vertical : Parent.Height * RelativeVertical) - Rect.height / 2);
                        break;
                    case 4:
                        Rect.height = (float.IsNaN(Height) ? Target.Height : (int)(Height < 0 ? Parent.Height * Height : Height));
                        Rect.y = (int)(Parent.Height - Bottom - Rect.height);
                        break;
                    case 3:
                        Rect.y = (int)Top;
                        Rect.height = (int)((float.IsNaN(RelativeVertical) ? Parent.Height / 2 + Vertical : Parent.Height * RelativeVertical) - Top);
                        break;
                    case 5:
                        Rect.y = (int)Top;
                        Rect.height = (int)(Parent.Height - Top - Bottom);
                        break;
                    case 6:
                        Rect.y = (int)((float.IsNaN(RelativeVertical) ? Parent.Height / 2 + Vertical : Parent.Height * RelativeVertical));
                        Rect.height = (int)(Parent.Height - Rect.y - Bottom);
                        break;
                    case 7:
                        Rect.height = (float.IsNaN(Height) ? Target.Height : (int)(Height < 0 ? Parent.Height * Height : Height));
                        float space = Parent.Height - Bottom - Top;
                        Rect.y = (int)((float.IsNaN(RelativeVertical) ? space / 2 + Vertical : space * RelativeVertical) + Top);
                        break;
                }


                Target.Left = Rect.x;
                Target.Top = Rect.y;
                Target.Width = Rect.width <= 0 ? Target.Width : Rect.width;
                Target.Height = Rect.height <= 0 ? Target.Height : Rect.height;


                if (Resized != null) {
                    Resized(this);
                } 
            } else {
                if (ResizeRequest != null) {
                    ResizeRequest(this);
                }
            }
        }

        public Rectangle GetPosition() {

            return new Rectangle();
        }


        ////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////

        // <------------------------------ UTIL

        public override string ToString() {
            return "[ Position ::  target:"+ (Target == null ? "null" :Target.Name)+"]";
        }

        internal void internalRemove() {
            Target = null;
            Parent = null;
        }


        ~Position() {
            internalRemove();
        }

    }

    ////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////


    ////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////

    // <------------------------------
				


    public struct Rectangle {

        public int x;
        public int y;
        public int width;
        public int height;

        public override string ToString() {
            return "[ :Rectangle:  x:" + x +", y:"+y+", width:"+width + ", height:"+height+"]";
        }

    }


}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
Poland Poland
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions