Listview的ownerdraw模式文本渲染问题
我正在尝试实现所有者绘制的 ListView
因为基本控件会占用制表符,我需要在列中对齐值。
使用 中的示例MSDN 作为我能够接近的基础。我仍然遇到的唯一问题是,当文本不适合列时使用的省略号周期比默认文本渲染中的间隔更紧密;要点是,如果字体是粗体,则句点会一起形成下划线。
下面的程序演示了这个问题。它有 4 个 ListView:顶部的两个是使用默认渲染绘制的。底部的两个是业主绘制的,右侧的一对是粗体的。由于长度原因,我删除了不需要的所有内容以演示问题,这就是所有者绘制的 ListView 没有列标题的原因。
查看放大的屏幕截图,所有者绘制的 ListView
中的省略号周期间隔一个像素;默认绘图中的那些有两个像素的间距。当粗体将句点加宽到两个像素时,所有者绘制的句点合并在一起形成一个看起来像下划线的实心块。
文本渲染也存在其他细微差别;但省略号是唯一一个无需缩放即可明显看到的符号。然而,这些差异确实让我怀疑这个问题是一个更普遍的问题。 可能是 GDI 与 GDI+ 渲染?但我认为这只可能在应用程序级别有所不同。显然不是,切换 Application.SetCompatibleTextRenderingDefault()
不会影响任何内容。
using System;
using System.Drawing;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public class Form1 : Form
{
private void ListViewDrawSubItem(object sender,
DrawListViewSubItemEventArgs e)
{
ListView listView = sender as ListView;
using (StringFormat sf = new StringFormat())
{
// Draw the standard background.
e.DrawBackground();
sf.SetTabStops(0, new float[] {12, 12, 12, 12, 12});
sf.FormatFlags = sf.FormatFlags | StringFormatFlags.NoWrap;
sf.Trimming = StringTrimming.EllipsisCharacter;
// Draw the header text.
// passing the controls font directly causes an
// ArguementException);
using (Font headerFont = new Font(listView.Font.Name,
listView.Font.Size,
listView.Font.Style))
{
e.Graphics.DrawString(e.SubItem.Text, headerFont,
Brushes.Black, e.Bounds, sf);
}
}
}
public Form1()
{
InitializeComponent();
LoadData(listView1);
LoadData(listView2);
LoadData(listView3);
LoadData(listView4);
}
private void LoadData(ListView listView)
{
listView.Columns.Add("first", 35);
listView.Columns.Add("second", 75);
for (int i = 0; i < 5; i++)
{
listView.Items.Add("test");
listView.Items[i].SubItems.Add("test test test test");
}
}
#region from Form1.Designer
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be
/// disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.listView1 = new System.Windows.Forms.ListView();
this.listView2 = new System.Windows.Forms.ListView();
this.listView3 = new System.Windows.Forms.ListView();
this.listView4 = new System.Windows.Forms.ListView();
this.SuspendLayout();
//
// listView1
//
this.listView1.Location = new System.Drawing.Point(12, 12);
this.listView1.Name = "listView1";
this.listView1.Size = new System.Drawing.Size(121, 116);
this.listView1.TabIndex = 0;
this.listView1.UseCompatibleStateImageBehavior = false;
this.listView1.View = System.Windows.Forms.View.Details;
//
// listView2
//
this.listView2.Font = new System.Drawing.Font("Microsoft Sans Serif",
8.25F, System.Drawing.FontStyle.Bold,
System.Drawing.GraphicsUnit.Point,
((byte)(0)));
this.listView2.Location = new System.Drawing.Point(151, 12);
this.listView2.Name = "listView2";
this.listView2.Size = new System.Drawing.Size(121, 116);
this.listView2.TabIndex = 1;
this.listView2.UseCompatibleStateImageBehavior = false;
this.listView2.View = System.Windows.Forms.View.Details;
//
// listView3
//
this.listView3.Location = new System.Drawing.Point(12, 134);
this.listView3.Name = "listView3";
this.listView3.OwnerDraw = true;
this.listView3.Size = new System.Drawing.Size(121, 116);
this.listView3.TabIndex = 2;
this.listView3.UseCompatibleStateImageBehavior = false;
this.listView3.View = System.Windows.Forms.View.Details;
this.listView3.DrawSubItem += new
System.Windows.Forms.DrawListViewSubItemEventHandler(
this.ListViewDrawSubItem);
//
// listView4
//
this.listView4.Font = new System.Drawing.Font("Microsoft Sans Serif",
8.25F, System.Drawing.FontStyle.Bold,
System.Drawing.GraphicsUnit.Point,
((byte)(0)));
this.listView4.Location = new System.Drawing.Point(151, 134);
this.listView4.Name = "listView4";
this.listView4.OwnerDraw = true;
this.listView4.Size = new System.Drawing.Size(121, 116);
this.listView4.TabIndex = 3;
this.listView4.UseCompatibleStateImageBehavior = false;
this.listView4.View = System.Windows.Forms.View.Details;
this.listView4.DrawSubItem += new
System.Windows.Forms.DrawListViewSubItemEventHandler(
this.ListViewDrawSubItem);
//
// Form1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(284, 262);
this.Controls.Add(this.listView4);
this.Controls.Add(this.listView3);
this.Controls.Add(this.listView2);
this.Controls.Add(this.listView1);
this.Name = "Form1";
this.Text = "Form1";
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.ListView listView1;
private System.Windows.Forms.ListView listView2;
private System.Windows.Forms.ListView listView3;
private System.Windows.Forms.ListView listView4;
#endregion
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
}
I'm trying to implement an owner drawn ListView
because the base control eats the tab character which I need to align values within a column.
Using an example from MSDN as a base I was able get close. The only problem I still have is that the periods of ellipsis used when the text doesn't fit in the column is much more closely spaced together than in the default text rendering; the the point that if the font is bold the periods run together into an underscore.
The program below demonstrates the problem. It has 4 ListView
s: The two on the top are drawn using the default rendering. The two on the bottom are owner drawn, and the pair in the right side are bolded. For length reasons I removed everything I didn't need in order to demonstrate the problem, which is why the owner drawn ListView
s don't have column headers.
Looking at a zoomed in screenshot the periods of the ellipsis in the owner drawn ListView
s are spaced one pixel apart; those in the default drawing have two pixels of spacing. When bolding widens the periods to two pixels the owner drawn ones merge together into a solid mass that looks like an underscore.
There are other minor differences in the text rendering as well; but the ellipsis is the only one that's readily apparent without zooming. These differences do however make me suspect the problem is a more general issue. Possibly GDI vs GDI+ rendering? Except I thought that could only vary at the application level. Apparently not, toggling Application.SetCompatibleTextRenderingDefault()
didn't affect anything.
using System;
using System.Drawing;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public class Form1 : Form
{
private void ListViewDrawSubItem(object sender,
DrawListViewSubItemEventArgs e)
{
ListView listView = sender as ListView;
using (StringFormat sf = new StringFormat())
{
// Draw the standard background.
e.DrawBackground();
sf.SetTabStops(0, new float[] {12, 12, 12, 12, 12});
sf.FormatFlags = sf.FormatFlags | StringFormatFlags.NoWrap;
sf.Trimming = StringTrimming.EllipsisCharacter;
// Draw the header text.
// passing the controls font directly causes an
// ArguementException);
using (Font headerFont = new Font(listView.Font.Name,
listView.Font.Size,
listView.Font.Style))
{
e.Graphics.DrawString(e.SubItem.Text, headerFont,
Brushes.Black, e.Bounds, sf);
}
}
}
public Form1()
{
InitializeComponent();
LoadData(listView1);
LoadData(listView2);
LoadData(listView3);
LoadData(listView4);
}
private void LoadData(ListView listView)
{
listView.Columns.Add("first", 35);
listView.Columns.Add("second", 75);
for (int i = 0; i < 5; i++)
{
listView.Items.Add("test");
listView.Items[i].SubItems.Add("test test test test");
}
}
#region from Form1.Designer
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be
/// disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.listView1 = new System.Windows.Forms.ListView();
this.listView2 = new System.Windows.Forms.ListView();
this.listView3 = new System.Windows.Forms.ListView();
this.listView4 = new System.Windows.Forms.ListView();
this.SuspendLayout();
//
// listView1
//
this.listView1.Location = new System.Drawing.Point(12, 12);
this.listView1.Name = "listView1";
this.listView1.Size = new System.Drawing.Size(121, 116);
this.listView1.TabIndex = 0;
this.listView1.UseCompatibleStateImageBehavior = false;
this.listView1.View = System.Windows.Forms.View.Details;
//
// listView2
//
this.listView2.Font = new System.Drawing.Font("Microsoft Sans Serif",
8.25F, System.Drawing.FontStyle.Bold,
System.Drawing.GraphicsUnit.Point,
((byte)(0)));
this.listView2.Location = new System.Drawing.Point(151, 12);
this.listView2.Name = "listView2";
this.listView2.Size = new System.Drawing.Size(121, 116);
this.listView2.TabIndex = 1;
this.listView2.UseCompatibleStateImageBehavior = false;
this.listView2.View = System.Windows.Forms.View.Details;
//
// listView3
//
this.listView3.Location = new System.Drawing.Point(12, 134);
this.listView3.Name = "listView3";
this.listView3.OwnerDraw = true;
this.listView3.Size = new System.Drawing.Size(121, 116);
this.listView3.TabIndex = 2;
this.listView3.UseCompatibleStateImageBehavior = false;
this.listView3.View = System.Windows.Forms.View.Details;
this.listView3.DrawSubItem += new
System.Windows.Forms.DrawListViewSubItemEventHandler(
this.ListViewDrawSubItem);
//
// listView4
//
this.listView4.Font = new System.Drawing.Font("Microsoft Sans Serif",
8.25F, System.Drawing.FontStyle.Bold,
System.Drawing.GraphicsUnit.Point,
((byte)(0)));
this.listView4.Location = new System.Drawing.Point(151, 134);
this.listView4.Name = "listView4";
this.listView4.OwnerDraw = true;
this.listView4.Size = new System.Drawing.Size(121, 116);
this.listView4.TabIndex = 3;
this.listView4.UseCompatibleStateImageBehavior = false;
this.listView4.View = System.Windows.Forms.View.Details;
this.listView4.DrawSubItem += new
System.Windows.Forms.DrawListViewSubItemEventHandler(
this.ListViewDrawSubItem);
//
// Form1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(284, 262);
this.Controls.Add(this.listView4);
this.Controls.Add(this.listView3);
this.Controls.Add(this.listView2);
this.Controls.Add(this.listView1);
this.Name = "Form1";
this.Text = "Form1";
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.ListView listView1;
private System.Windows.Forms.ListView listView2;
private System.Windows.Forms.ListView listView3;
private System.Windows.Forms.ListView listView4;
#endregion
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
我找到了绘制子项方法的一个偶然实现。我的主要警告是选项卡大小是固定的(尽管如果需要更改它,我可以下降到 win32);据报道,我在我的机器上工作时所需的标志组合在 MSDN 中是相互不兼容的。
I've found a contingent implementation for the draw sub item method. The main caveats I have are that the tab size is fixed (although I could drop to win32 if necessary to change it); and that the combination of flags that I need while working on my machine is reported to be mutually incompatible in MSDN.