转自豆丁
最近在C#的windows form编程中,涉及到了控件的z-order(Z轴次序)的属性。它来源于三维空间的概念,x和y分别代表屏幕所在平面上的水平坐标和垂直坐标,而z轴则是垂直于屏幕的。在此根据<<Programming Microsoft Window with c#.net>>书籍中的相关内容及自己的理解做一个总结,并实现了一个应用实例,即由按钮和列表控件一起构成的分栏框。
对于属于同一父控件的一组控件,z-order的作用表现在两方面:一是控件组中每个控件在同一位置时的叠加顺序;二是当几个控件停靠在父控件的同一边缘时的堆积顺序。z-order的初始值是根据你设置每个控件的parent属性值的顺序或者将一个控件加入到一个控件集合的顺序来决定的。具体如下:
若一个控件是在z轴最上层,则它具有以下属性:
l 它第一个被设置parent属性或第一个被加入到控件集合中;
l 它在控件集合中的index即索引值为0;
l 它在其它所有控件的最上层,可以接收到鼠标消息;
l 当多个控件停靠到父控件的同一边缘时,它最靠近客户区的中间;
若一个控件是在z轴最下层,则它具有以下属性:
l 它最后一个被设置parent属性或最后一个被加入到控件集合中;
l 它在控件集合中的index即索引值为(Controls.Count-1,即控件组中的控件数目减1);
l 它在其它所有控件的最下层;
l 当多个控件停靠到父控件的同一边缘时,它停靠在此边缘上;
可以查看相应的Windows 窗体设计器生成的代码,可以看到最靠近客户区中间的控件是最早被加入到控件集合中的,而停靠在边缘的控件则是最后被加入到控件集合中的。明白了以上基础原理后,即可调用控件类提供的相关方法例如SetChildIndex或者BringToFront和SendToBack等等来修改控件的z-order,从面起到按需调整控件位置的作用。
以下将实现一个利用z-order进行编程的实例。
新建一个窗体Form1,添加一个Panel,再在Panel中从上到下依次画出三个Button控件及对应的三个ListBox控件(大小不必太严格,后面调整了Dock属性后就会对齐)button1,listBox1, button2,listBox2, button3,listBox3。设想程序运行的最早的初始情况是button1, button2, button3均停靠在Panel的上边缘,然后接着是listBox3。因些将三个Button控件的Dock属性设为Top,而将listBox1和ListBox2的Visible属性设置为false,ListBox2设置为true.
到此界面设计完毕,接着主要的代码编写步骤如下:
a.在Form类中添加如下变量用于存放panel中的所有子控件的初始位置信息。
private const int numOfcontrols=6;//子控件数量
private Control []controlsOriginalPosition;
b.在Form类的构造函数中初始化该控件数组变量
controlsOriginalPosition=new Control[numOfcontrols];
//数组中依次存放的是panel中从下到上的子控件
controlsOriginalPosition[0]=this.listBox3;
controlsOriginalPosition[1]=this.button3;
controlsOriginalPosition[2]=this.listBox2;
controlsOriginalPosition[3]=this.button2;
controlsOriginalPosition[4]=this.listBox1;
controlsOriginalPosition[5]=this.button1;
c.在Form类中添加如下函数
//对于一个容器控件中的控件集合,将集合中的所有控件的索引进行修改,
//以改变控件停靠在容器同一边时的排列位置。
//例如所有控件的索引从屏幕的下面到上面依次为:0 1 2 3.
//对应的controlsOriginalPosition中的控件索引依次为:0 1 2 3
//若conToChangePosi的索引为2,则将controlsOriginalPosition中的控件索引依次修改为:1 0 2 3
//其中索引为2 3的将放到容器上面,索引为1 0的将放到容器下面
private bool changeConPosi(Control parentOfCon,Control conToChangePosi)
{
int index=0;
//
//在存放了控件集合的数组中找到该控件
//
for(index=0;index<numOfcontrols;index++)
{
if(conToChangePosi==controlsOriginalPosition[index])
break;
}
if(index==numOfcontrols)
return false;
//
//依次修改控件索引值
//
if(index>0)
for(int i=0;i<index;i++) //修改排在index前面的索引部分 parentOfCon.Controls.SetChildIndex(controlsOriginalPosition[i],index-1-i);
for(int i=index;i<numOfcontrols;i++) //修改排在index后面的索引部分
parentOfCon.Controls.SetChildIndex(controlsOriginalPosition[i],i);
//
//修改索引值完毕后,重新排列控件顺序
//
for(int i=0;i<index;i++)
controlsOriginalPosition[i].Dock=DockStyle.Bottom;
for(int i=index;i<numOfcontrols;i++)
controlsOriginalPosition[i].Dock=DockStyle.Top;
return true;
}
d.建立各button的单击事件处理函数,仅举button1的例子如下:
private void button1_Click(object sender, System.EventArgs e)
{
changeConPosi(this.panel1,this.button1);
this.listBox1.Visible=true;
this.listBox2.Visible=false;
this.listBox3.Visible=false;
this.listBox1.Dock=DockStyle.Fill;
}
最终程序运行效果如下:
单击button1
单击button2
单击button3
当然,将窗体中的Panel换成GroupBox,或者将ListBox换成ListView,TreeView等其它控件也是可以的,或者控件总数目有所变化挺方便的,不需要修改changeConPosi函数,而只需要修改controlsOriginalPosition数组的初始化部分。