因为在.NET里WinForm中提供了ScrollableControl,因而我们可以通过设置AutoScroll属性来自动的显示滚动条并通过它来滚动画面,但是在实际的应用中,我们有时可能会觉得这个被系统提供的控件“并不好用”。这种情况往往表现在对滚动条位置不满意的时候,或是不想使用从ScrollableControl继承的控件的时候,或是其它的原因。这个时候我们就会考虑在程序中直接使用VScrollBar及HScrollBar控件。可是用过这两个控件的程序员都知道,对这两个控件的使用其实并不容易,除非你是个很厉害的家伙。
我并不是个很厉害的家伙,但我也确有个自己做法,效果,我认为是不错的,当然你可能有更好的做法。
为了程序的需要我定义了几个变量:
private int m_HBarHeight;
private VScrollBar m_VBar;
private HScrollBar m_HBar;
接下来,给变量们做了初始值或初始设置,代码放到构造函数里了:
this .m_HBarHeight = SystemInformation.HorizontalScrollBarHeight;
this .m_VBar = new VScrollBar();
this .m_HBar = new HScrollBar();
this .setScrllBars();
this .m_VBar.Scroll += new ScrollEventHandler( this .VBar_Scroll);
this .m_HBar.Scroll += new ScrollEventHandler( this .HBar_Scroll);
this .Controls.Add( this .m_VBar);
this .Controls.Add( this .m_HBar);
this .ResumeLayout( true );
里面有一个函数setScrllBars();接下来会说到,只说这些代码的意图是向当前窗体或控件里添加了两个滚动条并对其有些设置而已。
实际上所有的代码我是写到一个从Control继承下来的类,这里提到的所有代码几乎都在这个类里完成。
我们不防设置一个虚拟的显示大小,比如300×200的大小,我们在控件里通过滚动条就滚动这一个大小的区域,这里我们会想,如果控件的可见区域大于这个300×200的大小,滚动条是没有必要显示出来的,如果控件可见区域要是小于这个区域了,滚动条要可见了,有一个问题要说了,有一个可能的情况就是比如本来水平的滚动条不可见,但是因为垂直的可见了使得控件的水平可见区减小了,这个情况可能引起水平滚动条的可见,若水平滚动条可见又使得垂直方向的可见区域减小又会反向影响到了垂直滚动条的值。同样,水平滚动条也可能把一个本不不可见的垂直的滚动条“挤”的显示出来,垂直滚动条的显示使水平可见区域减小而要反向影响到水平滚动条的最大值。我曾试着用代码来协调这个情况,但是直到现在我才找到一个较“合理”的做法,这个将在我说代码里有处理。
下一个问题就是我们知道在两个滚动条都显示的时候在控件的右下角有一个空白区域,这也是个协调的难点。我曾试着对滚动条设置了两次来达到这个效果,但也是直到现在我才否定了这个做法,采用一次设置了。
再下一个问题就是滚动条的本身的特有属性设置,比如LargeChange(最大滚动大小),Maximum(滚动的最大取值),当然还有Minimum,这个一般来说就是0了。从表面上我们看的清楚,不就是这三个值吗?有什么?其实这里是有一个公式的。就是要设置到Maximum的值必需进行与LargeChange-1相加运算,否则是不对的。
下面的方法体就试着解决了上面提到的三个问题,且是我的控件中最主要的部分,假设控件中的两个滚动条就像一个多行的文本编辑框一样的情况出现两个滚动条,因为我只是提到了对滚动条的设置,所以这个文里也就只有这一个方法,目的当然就是记录下,方便以后查找了:
{
// 设置垂直最大滚动值
int vLarge = this .Height;
// 设置水平最大滚动值
int hLarge = this .Width;
// 显示区域的高
int vValue = size.Height;
// 显示区域的宽
int hValue = size.Width;
// 滚动条的最大值
int vMaxValue = 0 ;
int hMaxValue = 0 ;
// 垂直滚动条的可见标志
bool vVisible = false ;
// 水平滚动条的可见标志
bool hVisible = false ;
if (vValue > this .Height - 2 * this .m_BorderWidth)
{
// 垂直方向上显示区域高大于窗口高时,垂直滚动条直接可见
vVisible = true ;
// 垂直滚动条宽影响水平滚动条可见性
hVisible = hValue > ( this .Width - this .m_VBarWidth - 2 * this .m_BorderWidth);
// 水平滚动条的最大值因垂直滚动的显示而受影响
hMaxValue = hValue - ( this .Width - this .m_VBarWidth - 2 * this .m_BorderWidth);
// 水平滚动条可见性又会反向影响到垂直滚动条的最大值
if ( ! hVisible)
{
vMaxValue = vValue - ( this .Height - 2 * this .m_BorderWidth);
}
else
{
vMaxValue = vValue - ( this .Height - this .m_HBarHeight - 2 * this .m_BorderWidth);
}
}
else if (hValue > this .Width - 2 * this .m_BorderWidth)
{
// 水平方向上显示区域宽大于窗口宽时,水平滚动条直接可见
hVisible = true ;
// 水平方向滚动条高影响垂直滚动条可见性
vVisible = vValue > ( this .Height - this .m_HBarHeight - 2 * this .m_BorderWidth);
// 垂直滚动条的最大值因水平滚动条的显示而受影响
vMaxValue = vValue - ( this .Height - this .m_HBarHeight - 2 * this .m_BorderWidth);
// 垂直滚动条的可见性又反向影响到了水平滚动条的最大值
if ( ! vVisible)
{
hMaxValue = hValue - ( this .Width - 2 * this .m_BorderWidth);
}
else
{
hMaxValue = hValue - ( this .Width - this .m_VBarWidth - 2 * this .m_BorderWidth);
}
}
int vOffset = this .m_VBarWidth; // 垂直滚动条高的差值
int hOffset = this .m_HBarHeight; // 水平滚动条宽的差值
if (vVisible != hVisible)
{
// 当垂直滚动条和水平滚动条可见性不同时,要显示的滚动条占满位置
if (vVisible)
{
vOffset = 0 ;
}
if (hVisible)
{
hOffset = 0 ;
}
}
// 计算垂直滚动条靠近右边所需的矩形区域
Rectangle vRect = new Rectangle( this .Width - this .m_BorderWidth - this .m_VBarWidth, this .m_BorderWidth, this .m_VBarWidth, this .Height - 2 * this .m_BorderWidth - vOffset);
// 计算水平滚动条靠近下边所需的矩形区域
Rectangle hRect = new Rectangle( this .m_BorderWidth, this .Height - this .m_HBarHeight - this .m_BorderWidth, this .Width - 2 * this .m_BorderWidth - hOffset, this .m_HBarHeight);
// 当窗口的宽不足容纳垂直滚动条时,需隐藏垂直滚动条
bool tmpVHide = this .Width < ( this .m_VBarWidth + 2 * this .m_BorderWidth);
// 当窗口的高不足容纳水平滚动条时,需隐藏水平滚动条
bool tmpHHide = this .Height < ( this .m_HBarHeight + 2 * this .m_BorderWidth);
// 在需要显示每个滚动条的时候先设置其各个值
this .m_VBar.Bounds = vRect;
this .m_VBar.LargeChange = vLarge;
this .m_VBar.Maximum = vMaxValue + vLarge - 1 ;
this .m_VBar.Minimum = 0 ;
this .m_HBar.Bounds = hRect;
this .m_HBar.LargeChange = hLarge;
this .m_HBar.Maximum = hMaxValue + hLarge - 1 ;
this .m_HBar.Minimum = 0 ;
// 如果窗口高不够,则直接不显示垂直滚动条
if (tmpVHide)
{
vVisible = false ;
}
// 如果窗口宽不够,则直接不显示水平滚动条
if (tmpHHide)
{
hVisible = false ;
}
// 显示标志决定滚动条的可见性
this .m_VBar.Visible = vVisible;
this .m_HBar.Visible = hVisible;
}
程序里用到了一个变量:m_BorderWidth,这只不过是一个控件边的参数。可以是0也可以是1或任何一个认为合适的数而已。还有一个关键性的变量:size,其实这个不过是定义了一个要显示的大小而已,如果说控件的显示区域是可见区域,那么这个大小表示了控件中滚动条所表示的最大的可滚动区域,在Windows中,这个大小叫DisplayRectangle或许更合适,而控件的显示区域叫ClientRectangle。