Adaptive of C ා WinForm form and its control

Posted by twigletmac on Sat, 07 Mar 2020 09:38:12 +0100

In order to improve the user's experience, the form can no longer be fixed in size (the user can't change the size of the form at will), so to make the form adapt to the resolution of the computer screen, the controls in the form should change with the change proportion of the form.

Through online search and learning, it is found that there are several good methods for bigwigs, so their code is added to their own projects for testing. Although they can realize the function of proportion change, they sometimes have problems such as control dislocation (method 1 below), slow page loading, etc. Without finding a better way, first sum up these two methods.

1, Class AutoSizeFormClass

Refer to the original link of big brother: Improving the adaption of C ා WinForm form and its controls,Controls in c-winform interface change with window size

1. Function of class:

(1) use it to record the initial position and size of the form and its controls;

(2) according to the changed size of the window, the horizontal and vertical direction of the control can be changed proportionally, that is, adaptive;

2. AutoSizeFormClass code

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace FB100_RS485
{
    class AutoSizeFormClass
    {
        //(1) . declares a structure that records only the initial position and size of the form and its controls.
        public struct controlRect
        {
            public int Left;
            public int Top;
            public int Width;
            public int Height;
        }
        //(2) . declare 1 object
        //Note that you cannot use the list of controls to record List nCtrl; because of the association of controls, the current size is always recorded.
        //     public List oldCtrl= new List(); / / the greater than and less than signs in western languages are filtered out here. They can only be changed to Chinese ones. You need to change back to western languages in use
        //public List<controlRect> oldCtrl = new List<controlRect>();//20200305
        
        //Stores the control name and its location
        public Dictionary<String, controlRect> oldCtrl = new Dictionary<String, controlRect>();//20200305

        int ctrlNo = 0;//1;
        //(3) . create two functions
        //(3.1) record the initial position and size of the form and its controls,
        public void controllInitializeSize(Control mForm)
        {
            controlRect cR;
            cR.Left = mForm.Left; cR.Top = mForm.Top; cR.Width = mForm.Width; cR.Height = mForm.Height;
            //oldCtrl.Add(cR); / / the first one is "form itself", which can only be added once / / 20200305
            oldCtrl.Add(mForm.Name, cR);//20200305
            insertDictionary(mForm.Name, cR);//20200305

            AddControl(mForm);//The rest of the controls in the form may also be nested (such as panel), which should be extracted separately because of recursive call

            //this.WindowState = (System.Windows.Forms.FormWindowState)(2); / / after recording the initial position and size of the control, maximize
            //0 - Normalize , 1 - Minimize,2- Maximize
        }

        private void AddControl(Control ctl)
        {
            foreach (Control c in ctl.Controls)
            {  //**In this case, the sub control of the control is recorded first, and then the control itself is recorded
               //if (c.Controls.Count > 0)
               //   AddControl(c); / / other controls in the form may also be nested (such as panel), which should be extracted separately because they need to be called recursively
                controlRect objCtrl;
                objCtrl.Left = c.Left; objCtrl.Top = c.Top; objCtrl.Width = c.Width; objCtrl.Height = c.Height;

                //oldCtrl.Add(c.Name, objCtrl);//20200305
                if (oldCtrl.ContainsKey(c.Name))//If the key already exists, change the corresponding value
                {
                    //MessageBox.Show("successfully changed value");
                    oldCtrl[c.Name] = objCtrl;
                }
                else//If the key does not exist, add
                {
                    oldCtrl.Add(c.Name, objCtrl);//The first one is "form itself". You can only add it once / / 20200305
                }

                insertDictionary(c.Name, objCtrl);

                //**Put here, first record the control itself, then record the child controls of the control
                if (c.Controls.Count > 0)
                    AddControl(c);//The rest of the controls in the form may also be nested (such as panel), which should be extracted separately because of recursive call

            }
        }

        //(3.2) control adaptive size,
        public void controlAutoSize(Control mForm)
        {

            if (ctrlNo == 0)
            { //*If you record the original size and position of the control in form1 \ u load of the form, there is no problem normally, but there will be a problem if you want to add skin, because some controls, such as dataGridView's child controls, have not been completed, and the number is small
              //*To record the original size and position of the control when changing the size for the first time in form1 ﹣ sizechanged of the form, the child controls of all controls have been formed here
              
                controlRect cR;
                //  cR.Left = mForm.Left; cR.Top = mForm.Top; cR.Width = mForm.Width; cR.Height = mForm.Height;
                cR.Left = 0; cR.Top = 0; cR.Width = mForm.PreferredSize.Width; cR.Height = mForm.PreferredSize.Height;

                //oldCtrl.Add(mForm.Name, cR); / / the first one is "form itself". You can only add it once / / 20200305

                if (oldCtrl.ContainsKey(mForm.Name))//If the key already exists, change the corresponding value
                {
                    //MessageBox.Show("successfully changed value");
                    oldCtrl[mForm.Name] = cR;
                }
                else//If the key does not exist, add
                {
                    oldCtrl.Add(mForm.Name, cR);//The first one is "form itself". You can only add it once / / 20200305
                }

                AddControl(mForm);//Other controls in the form may be nested with other controls (such as panel), so they can be extracted separately to call recursively
            }
            //float wScale = (float)mForm.Width / (float)oldCtrl[0].Width; / / the ratio between the old and new forms, and the oldest form
            //float hScale = (float)mForm.Height / (float)oldCtrl[0].Height;//.Height;
            float wScale = (float)mForm.Width / oldCtrl[mForm.Name].Width; ;//The ratio between the old and new forms to the oldest
            float hScale = (float)mForm.Height / oldCtrl[mForm.Name].Height; ;//.Height;

            ctrlNo = 1;//Enter = 1, the 0 is the form itself, the control in the form, starting from the No. 1

            AutoScaleControl(mForm, wScale, hScale);//The rest of the controls in the form may also be nested (such as panel), which should be extracted separately because of recursive call
        }

        private void AutoScaleControl(Control ctl, float wScale, float hScale)
        {
            int ctrLeft0, ctrTop0, ctrWidth0, ctrHeight0;
            //Int Ctrl no = 1; / / the first is the Left,Top,Width,Height of the form itself, so the form control starts from Ctrl no = 1
            foreach (Control c in ctl.Controls)
            { //**In this case, the child control of the control is scaled first, and then the control itself is scaled
              //if (c.Controls.Count > 0)
              //   Autoscale control (C, wscale, hscale); / / other controls in the form may also nest controls (such as panel), which need to be extracted separately because they need to be called recursively
              /*
                ctrLeft0 = oldCtrl[ctrlNo].Left;
                ctrTop0 = oldCtrl[ctrlNo].Top;
                ctrWidth0 = oldCtrl[ctrlNo].Width;
                ctrHeight0 = oldCtrl[ctrlNo].Height;
              */

                ctrLeft0 = oldCtrl[c.Name].Left;
                ctrTop0 = oldCtrl[c.Name].Top;
                ctrWidth0 = oldCtrl[c.Name].Width;
                ctrHeight0 = oldCtrl[c.Name].Height;

                //c.Left = (int)((ctrLeft0 - wLeft0) * wScale) + wLeft1; / / linear ratio between new and old controls
                //c.Top = (int)((ctrTop0 - wTop0) * h) + wTop1;
                c.Left = (int)((ctrLeft0) * wScale);//Linear scale between old and new controls. Control position is only relative to the form, so + wLeft1 cannot be added
                c.Top = (int)((ctrTop0) * hScale);//
                c.Width = (int)(ctrWidth0 * wScale);//It is only related to the original size, so it cannot be multiplied by the current width (int)(c.Width * w);
                c.Height = (int)(ctrHeight0 * hScale);//

                AutoScaleFont(c);

                ctrlNo++;//Accumulating ordinal number
                         //**In this case, first scale the control itself, and then scale the sub control of the control
                if (c.Controls.Count > 0)
                    AutoScaleControl(c, wScale, hScale);//The rest of the controls in the form may also be nested (such as panel), which should be extracted separately because of recursive call


                if (ctl is DataGridView)
                {
                    DataGridView dgv = ctl as DataGridView;
                    Cursor.Current = Cursors.WaitCursor;

                    int widths = 0;
                    for (int i = 0; i < dgv.Columns.Count; i++)
                    {
                        dgv.AutoResizeColumn(i, DataGridViewAutoSizeColumnMode.AllCells);  // Auto adjust column width  
                        widths += dgv.Columns[i].Width;   // Calculate the width and                       
                    }
                    if (widths >= ctl.Size.Width)  // If the width of the adjustment column is greater than the set column width  
                        dgv.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.DisplayedCells;  // Adjust the mode of the column automatically  
                    else
                        dgv.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill;  // Fill if less than  

                    Cursor.Current = Cursors.Default;
                }
            }
        }


        private void AutoScaleFont(Control c)
        {
            string[] type = c.GetType().ToString().Split('.');
            string controlType = type[type.Length - 1];

            switch (controlType)
            {
                //case "Button":
                //    c.Font = new System.Drawing.Font("Tahoma", c.Height * 0.4f);
                //    break;

                //case "GroupBox":
                //    c.Font = new System.Drawing.Font("Tahoma", c.Height * 0.06f);
                //    break;
            }
        }


        private void insertDictionary(String name, controlRect cr)   //Add control name and location, update if name is duplicate
        {
            Dictionary<String, controlRect> temp = new Dictionary<String, controlRect>();
            bool flag = false;
            foreach (var pair in oldCtrl)
            {
                if (pair.Key.ToString() == name)
                {
                    temp.Add(name, cr);
                    flag = true;
                }
            }
            if (flag == false)
            {
                oldCtrl.Add(name, cr);
            }
            foreach (var value in temp)
            {
                oldCtrl.Remove(value.Key.ToString());
                oldCtrl.Add(value.Key, value.Value);
            }
            temp.Clear();
        }
    }
}           

3. Usage

1) copy the adaptive class into your project namespace as a whole, and then do three steps in the window that needs to be adaptive;
2) declare adaptive class instances;
3) add the Load event to the form, and call the initialization method of the class in its method form1 Load to record the initial position and size of the form and its controls;
4) add SizeChanged event to the form, and in its method form1 ﹣ SizeChanged, call the class's adaptive method to complete the adaptation;

Add the code in the form code as follows:

namespace FB100_RS485
{

    public partial class mainForm : Form
    {
        //1. Declare an adaptive class instance
        AutoSizeFormClass asc = new AutoSizeFormClass();

        public mainForm()
        {
            InitializeComponent();

            //Eliminate flicker during data update
            dataGridView2.DoubleBufferedDataGirdView(true);

            //If skin is added, the size and location of the control cannot be recorded in form1 \ load, because some controls, such as the child controls of dataGridView, have not been completed
            //In form1 ﹣ sizechanged, record the size and position of the control the first time
            skinEngine1.SkinFile = System.Environment.CurrentDirectory + @"\DiamondBlue.ssk";  //Select skin file

        }

        /// <summary>
        ///Form loading, initializing
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void mainForm_Load(object sender, EventArgs e)
        {
            //2. Add the Load event to the form, and call the initialization method of the class in its method form1 Load to record the initial position and size of the form and its controls
            //asc.controllInitializeSize(this);

            //RS485 communication configuration default parameters
            rs232_init();
            //Public control initialization
            comm_control_init();
        }


        private void mainForm_SizeChanged(object sender, EventArgs e)
        {
            //3. Add the SizeChanged event to the form, and call the class's adaptive method in its method form1 ﹣ SizeChanged to complete the adaptation
            asc.controlAutoSize(this);
        }
    }
}

4. Summary

(1) page loading is slow, which may cause control dislocation in some cases;

(2) when the interface becomes larger, the text in the control will not follow the change, the spacing between the controls will become large, and the overall view is not very harmonious;

2, Method 2

1. Reference to the original link: Scale the size of C ා WinForm control in proportion to the size of the form

2. Code

        public mainForm()
        {
            InitializeComponent();
            x = this.Width;
            y = this.Height;
            setTag(this);
        }

        #region control size scales equally with form size
        private float x;//Defines the width of the current form
        private float y;//Defines the height of the current form
        private void setTag(Control cons)
        {
            foreach (Control con in cons.Controls)
            {
                con.Tag = con.Width + ";" + con.Height + ";" + con.Left + ";" + con.Top + ";" + con.Font.Size;
                if (con.Controls.Count > 0)
                {
                    setTag(con);
                }
            }
        }
        private void setControls(float newx, float newy, Control cons)
        {
            //Traverse the control in the form and reset the value of the control
            foreach (Control con in cons.Controls)
            {
                //Gets the Tag property value of the control and stores the string array after splitting
                if (con.Tag != null)
                {
                    string[] mytag = con.Tag.ToString().Split(new char[] { ';' });
                    //Determines the value of the control based on the scale of the form scale
                    con.Width = Convert.ToInt32(System.Convert.ToSingle(mytag[0]) * newx);//width
                    con.Height = Convert.ToInt32(System.Convert.ToSingle(mytag[1]) * newy);//height
                    con.Left = Convert.ToInt32(System.Convert.ToSingle(mytag[2]) * newx);//left
                    con.Top = Convert.ToInt32(System.Convert.ToSingle(mytag[3]) * newy);//top
                    Single currentSize = System.Convert.ToSingle(mytag[4]) * newy;//font size
                    con.Font = new Font(con.Font.Name, currentSize, con.Font.Style, con.Font.Unit);
                    if (con.Controls.Count > 0)
                    {
                        setControls(newx, newy, con);
                    }
                }
            }
        }

        private void mainForm_Resize(object sender, EventArgs e)
        {
            //Be sure to set the MinimumSize property of the form and the variable minimum size in the property settings
            //When the form is reduced to a certain extent, the controls in the form will produce a certain degree of dislocation!

            float newx = (this.Width) / x;
            float newy = (this.Height) / y;
            setControls(newx, newy, this);
        }

        #endregion

3. Attention

(1) do not forget the width and height settings in initialization;
(2) the form1 ﹣ Resize method needs to manually set the Resize method in the "properties" of the form (find the Resize method in the figure below, and double-click the blank space behind it);
(3) if you do not need to scale the font, you can comment out the following two lines of code.

Single currentSize = System.Convert.ToSingle(mytag[4]) * newy;//font size
con.Font = new Font(con.Font.Name, currentSize, con.Font.Style, con.Font.Unit);

Conclusion:

The form cannot be shrunk infinitely, because when the form is shrunk to a certain extent, the controls in the form will be misplaced, so it is necessary to set the minimum size of the form (by setting the MinimumSize property of the form).

Published 4 original articles, won praise 1, visited 6276
Private letter follow

Topics: REST Windows less