为偶的NotesManager小程序写了一个带滚动条的Label控件,效果看起来象下面这样:

这个控件的实现思路说起来很简单,就是使用的一般Windows桌面程序中的窗口(Window)和视口(View)的概念。
考虑到Label本身不需要支持编辑的功能,出于效率的考虑,我在ScrollLabel控件中用一个Bitmap对象来保存Window的所有内容。在OnPaint中,如果发现Bitmap对象是空值(通常是第一次Paint),则根据当前Text的内容判断是否需要显示ScrollBar,并创建Bitmap对象,然后根据View的位置来显示应当显示在界面上的内容。以后每次Paint的时候,都只是根据滚动条的位置来计算View的位置,然后显示相应的Bitmap上的内容即可。当Text或者Font改变时,将原有的Bitmap销毁重新计算即可。整个OnPaint方法看起来象下面这样:
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint (e);
if(bmpWindow == null)
{
Rectangle drawRect;
// Calculate the height of the window
SizeF size = e.Graphics.MeasureString(this.Text, this.Font);
rowHeight = size.Height;
float height = (size.Width / ClientSize.Width + 1) * rowHeight;
// Indicate if the scrollbar need to be shown.
if(height > ClientRectangle.Height)
{
// Recalculate the height of window.
height = (size.Width / (ClientSize.Width - scrollWidth)) * rowHeight + ClientSize.Height / 2;
// Show scrollbar
vs.Bounds = new Rectangle(this.ClientSize.Width - scrollWidth, 0,
scrollWidth, ClientSize.Height);
vs.LargeChange = (int)(ClientSize.Height / rowHeight);
vs.Maximum = (int)(height / rowHeight);
vs.Visible = true;
// Create window bitmap.
bmpWindow = new Bitmap(ClientSize.Width - scrollWidth, (int)height + 1);
drawRect = new Rectangle(0, 0, ClientSize.Width - scrollWidth, (int)height + 1);
}
else
{
bmpWindow = new Bitmap(ClientSize.Width, ClientSize.Height);
drawRect = this.ClientRectangle;
}
// Paint on the window bitmap.
Graphics g = Graphics.FromImage(bmpWindow);
g.FillRectangle(new SolidBrush(this.BackColor), drawRect);
g.DrawString(this.Text, this.Font, new SolidBrush(this.ForeColor), drawRect);
g.Dispose();
}
// Draw view.
e.Graphics.DrawImage(bmpWindow, 0, 0 - (int)rowHeight * vs.Value);
}
这个ScrollLabel的代码可以从这里下载。
不过这里使用的方法也有一些小缺陷,主要是源于目前版本的.NET CF不支持的一些功能。比如,为了得到作为Window的Bitmap的宽度和高度,我使用了MeasureString这个方法。但是目前版本的.NET CF中,MeasureString并不支持按给定宽度度量所需要的高度的功能,只能量出将所有的字画在一行上的宽度和高度。我的解决办法是,用MeasureString得到的高度作为行高,用MeasureString得到的宽度和整个ClientSize的宽度比值作为所有的行数,从而得到Window所需的高度。但是这样一来,就无法顾及由于英文的分词所带来的误差。比如MeasureString这个词,如果恰好在行尾,在Paint的时候它不会被截断,而时候整个的被放到下一行来显示。这样不断的叠加,最终在行数上是会产生一定的误差的。我暂时使用的解决方法是,对我用MeasureString得到的Window高度进行了1/2个ClientSize高度的误差补偿,这样你会发现,ScrollBar在滚动时并不是精确地滚动到最后一行,而是还会向下滚动一段空白区域。在大多数情况下,这段误差补偿是可以解决问题的,但是对于包含很多非常长的单词的情况,可能仍然会有问题。
更新后的NotesManager可以在这里下载。
打印 | 张贴于 2004-11-08 13:49:00 | Tag:学习
留言反馈
此言差矣!
请看:
public void MeasureStringSizeFFormat(Graphics g)
{
// Set up string.
string measureString = "Measure String Measure String Measure String Measure StringMeasure String Measure String Measure String Measure StringMeasure String Measure String Measure String Measure StringMeasure String Measure String Measure String Measure String";
Font stringFont = new Font("Arial", 16);
// Set maximum layout size.
SizeF layoutSize = new SizeF(300.0F,100.0F);
// Set string format.
StringFormat newStringFormat = new StringFormat();
newStringFormat.Alignment = StringAlignment.Near;
newStringFormat.LineAlignment = StringAlignment.Near;
// Measure string.
SizeF stringSize = new SizeF();
stringSize = g.MeasureString(measureString, stringFont, (int)layoutSize.Width, newStringFormat);
// Draw rectangle representing size of string.
g.DrawRectangle(new Pen(Color.Red, 1), 0.0F, 0.0F, stringSize.Width, stringSize.Height);
// Draw string to screen.
g.DrawString(measureString, stringFont, Brushes.Black,
new RectangleF(new PointF(0, 0),stringSize), newStringFormat);
}
开心的那个listview简直就是garbage,MS的那个是undocumented的。
这个只是一个带有ScrollBar的Label,用来显示多行Text用的,并非要模仿什么MS_VIRTUAL_LIST_VIEW。
恕我无知,没听说过什么是MS_VIRTUAL_LIST_VIEW,刚才google了一下,发现这个东东也只有你在开心的一个随笔评论中提到过。其实如果我没有说错的话,开心用到的那个ListView应当是MSDN上的一个例子,它已经包含在Mobile Application Development Toolkit 2004中了,有兴趣的话可以down一个研究下。
能不能说说MS_VIRTUAL_LIST_VIEW到底长得什么样子呀?:)