一.C#关于控件随窗体的改变而自动调整大小并且能重新定位的一些简单介绍
在设计可供用户调整大小的窗体时,如何实现该窗体上的控件也应能正确地随窗体的改变而自动调整大小并且能重新定位?此时就要借助控件的.Anchor属性。Anchor属性定义控件的定位点位置。当控件锚定到某个窗体时,如果该窗体的大小被调整,那么该控件维持它与定位点位置之间的距离不变。例如,如果“:Button”控件锚定到窗体的左、右和底边缘,那么当调整该窗体的大小时,Button控件水平调整大小,维持到该窗体左边和右边的距离不变,另外控件垂直定位其自身,以便其到窗体底边的距离始终不变,如果控件未锚定而窗体的大小被调整,则该控件相对于窗体边缘的位置将发生变化。下面介绍如何将控件锚定到窗体上。
首先,选择要锚定的控件。然后,在属性窗口中,单击Anchor属性右边的箭头,将显示一个编辑器,该编辑器显示一个十字线。若要设置定位点,单击该十字线的上、下、左或右部分。在默认情况下,控件锚定左边和上边,若要清除已锚定控件的边,请单击该十字线的相应臂。再次单击Anchor属性名称关闭Anchor属性编辑器。当窗体在运行显示时,该控件调整大小保持与该窗体边缘的距离不变,到锚定边缘的距离始终保持在“Windows窗体设计器”中定位该控件时所定义的距离。需要注意的是,某些控件(如ComboBox控件)有高度限制,将控件锚定到其窗体或容器的底部,无法强制该控件超过其高度限制。
.NET框架允许你对子控件设置属性,命令在调整父窗体大小时,它们应该如何运作。用来命令控件在调整大小时动作的两个属性就是“Dock”和
“Anchor”。Dock和Anchor通过将控件连接到它们父窗体的某个位置,而免除了使应用程序具有不可预知界面的麻烦。最好的一点就是设立这些属性不需要任何手
写代码。所有事情都可以通过Visual Studio IDE中的点和单击来完成。
Anchor属性
这个属性迫使控件将其自身定位在父窗体或父控件中的某个相对或绝对位置。这个属性有四个可以开启或关闭的值:
- Top——表示控件中与父窗体(或父控件)相关的顶部应该保持固定。
- Bottom——表示控件中与父窗体(或父控件)相关的底边应该保持固定。
- Left——表示控件中与父窗体(或父控件)相关的左边缘应该保持固定。
- Right——表示控件中与父窗体(或父控件)相关的右边缘应该保持固定。
要对一个控件设置Anchor属性,只需在Visual Studio设计器中选择控件,然后转到属性窗口。你会看到一个标注为“Anchor”的属性。
点击这个属性值的部分,会出现一个小窗口让你选择想要赋予控件的锚点。图表A所示是选择了“顶边、左边”的anchor设置窗口。
图表B所示是选择了“底边、右边”的窗口。
在Visual Studio中,当控件放置于窗体时,默认的anchor设置是“顶部、左边”,这使得控件和窗体的顶边和左边缘固定相关。
到真正发现不同的anchor设置对控件的影响时,你才能体会到锚定的意义。下面的图像会有所帮助。
图表C所示是一个有十个子控件的窗体。每一个子控件都有不同的Anchor属性值,并用它的anchor设置标注。灰白色控件后面的深红色框是另一个子
控件——它的Anchor属性被设为顶部、底部、左边和右边。图表D所示是区域被调大以后的同一个窗体。
正如你所看到的那样,每一个控件在父窗体中都自动地保持它的位置。我们没有编写代码来完成这一点;只是简单地设置了控件的Anchor属性。
有几个重要的地方不得不提。一个就是如果你没有指定一个控件有左或右锚定,它将在父窗体中保留一个相对左/右位置。如果你没有指定一个控件
是否有顶部或底部锚定,也是一样的。对于这一点,一个很好的例子就是标注为“无Anchor”的控件。这样的控件没有锚定值,所以它只是漂浮在窗体
*。另一个极端就是选择了所有anchor值的控件(顶部、底部、左边、右边)。对这一点,图表C和图表D中其它控件后面的深红色方形可见物就
是一个例子。当选择了所有的anchor值时,控件只是在调整父窗体大小时,随着增大和收缩——与窗体的边缘比较起来它的所有边缘保持静止不变。
Dock属性
Dock属性迫使控件紧贴父窗体(或控件)的某个边缘。虽然Anchor属性也可以实现这一点,但是dock属性使得你能够在父窗体中让子窗体可以在上方
(或旁边)互相“堆叠”。如果某个子窗体改变了大小,其它停驻在它旁边的子窗体也会随之改变。和Anchor属性不同的是,你可以将Dock属性设置为
一个单值。有效值如下所示:
- Top——迫使控件位于父窗体(或控件)的顶部。如果有同一个父窗体的其它子控件也被设置为停驻在顶部的话,那么控件将在彼此上方相互堆叠。
- Bottom——迫使控件位于父窗体(或控件)的底部。如果有同一个父窗体的其它子控件也被设置为停驻在底部的话,那么控件将在彼此上方相互堆叠。
- Left——迫使控件位于父窗体(或控件)的左边。如果有同一个父窗体的其它子控件也被设置为停驻在左边的话,那么控件将在彼此旁边相互堆叠。
- Right——迫使控件位于父窗体(或控件)的右边。如果有同一个父窗体的其它子控件也被设置为停驻在右边的话,那么控件将在彼此旁边相互堆叠。
- Fill——迫使控件位于父窗体(或控件)的上方。如果有同一个父窗体的其它子控件也被设置为停驻在上方的话,那么控件将在彼此上方相互堆叠。
- None——表示控件将会正常运转。
个小窗口让你指定该控件将如何停驻。被赋予各种值的该窗体将显示在以下图像中(图表E、图表F和图表G):
值。
有不同dock值的5个子控件
图表I所示的是和图表H一样的窗口,除了一点,就是现在窗口已经被调整为更大的轨迹。
需要记住的是,对于Dock属性,添加控件的顺序会影响它们停驻的方式。例如,如果你对窗体添加控件A,指示其停驻填充,然后你对窗体添加控件B
并指示其停驻顶部,控件B将覆盖控件A的上部。原因就是控件B被认为是在控件A的“前方”,因为它是在控件A之后添加的。
要解决这种情况,你必须在Visual Studio中右击控件A,并在上下文菜单中选择“放到前面(Bring To Front)”。这样就能使控件A出现在控件B的前方,
控件也就能像预期的那样运作了。
二.C#控件随窗体改变自适应类
一.很多情况下,需要窗体填满各种尺寸的显示器,同时需要同步缩放控件的大小。这种时候就需要有个统一的类进行调用实现。
下面的类(ControlResizer)是可以同步缩放控件的位置,宽度高度,字体大小。
/// <summary>
/// 同步缩放窗体上控件的大小和字体
/// </summary>
public class ControlResizer
{
class ControlPosAndSize
{
public float FrmWidth { get; set; }
public float FrmHeight { get; set; }
public int Left { get; set; }
public int Top { get; set; }
public int Width { get; set; }
public int Height { get; set; }
public float FontSize { get; set; }
}
private Form _form;
//句柄,大小信息
private Dictionary<int, ControlPosAndSize> _dic = new Dictionary<int, ControlPosAndSize>();
public ControlResizer(Form form)
{
_form = form;
//绑定窗体大小改变事件
_form.Resize += _form_Resize;
//窗体上新增控件的处理
_form.ControlAdded += form_ControlAdded;
_form.ControlRemoved += form_ControlRemoved;
//记录控件和窗体大小
SnapControlSize(_form);
}
void form_ControlRemoved(object sender, ControlEventArgs e)
{
var key = e.Control.Handle.ToInt32();
_dic.Remove(key);
}
/// <summary>
///绑定控件添加事件
/// </summary>
private void form_ControlAdded(object sender, ControlEventArgs e)
{
var ctl = e.Control;
var ps = new ControlPosAndSize
{
FrmHeight = _form.Height,
FrmWidth = _form.Width,
Width = ctl.Width,
Height = ctl.Height,
Left = ctl.Left,
Top = ctl.Top,
FontSize = ctl.Font.Size
};
var key = ctl.Handle.ToInt32();
_dic[key] = ps;
}
void _form_Resize(object sender, EventArgs e)
{
ResizeControl(_form);
}
/// <summary>
///重新设置窗体控件的大小
/// </summary>
private void ResizeControl(Control control)
{
foreach (Control ctl in control.Controls)
{
var key = ctl.Handle.ToInt32();
if (_dic.ContainsKey(key))
{
var ps = _dic[key];
var newx = _form.Width / ps.FrmWidth;
var newy = _form.Height / ps.FrmHeight;
ctl.Top = (int)(ps.Top * newy);
ctl.Height = (int)(ps.Height * newy);
ctl.Left = (int)(ps.Left * newx);
ctl.Width = (int)(ps.Width * newx);
ctl.Font = new Font(ctl.Font.Name, ps.FontSize * newy, ctl.Font.Style, ctl.Font.Unit);
if (ctl.Controls.Count > 0)
{
ResizeControl(ctl);
}
}
}
}
/// <summary>
/// 创建控件的大小快照,参数为需要记录大小控件的 容器
/// </summary>
private void SnapControlSize(Control control)
{
foreach (Control ctl in control.Controls)
{
var ps = new ControlPosAndSize
{
FrmHeight = _form.Height,
FrmWidth = _form.Width,
Width = ctl.Width,
Height = ctl.Height,
Left = ctl.Left,
Top = ctl.Top,
FontSize = ctl.Font.Size
};
var key = ctl.Handle.ToInt32();
_dic[key] = ps;
//绑定添加事件
ctl.ControlAdded += form_ControlAdded;
ctl.ControlRemoved += form_ControlRemoved;
if (ctl.Controls.Count > 0)
{
SnapControlSize(ctl);
}
}
}
}
二.关于类(ControlResizer)的调用方式,使用的时候在FormLoad里面绑定一下即可:
{
private ControlResizer Resizer; //定义缩放类
public Form2()
{
InitializeComponent();
}
private void Form2_Load(object sender, EventArgs e)
{
//绑定
Resizer=new ControlResizer(this);
}
}
下过如下所示: