C#大量相同对象winform的性能问题

mousemouse1980 2011-08-12 02:37:44
C# winform中有大量的对象(例如几万个label),这些对象除了颜色、位置不同,其他属性(包括事件)都相同,如何提高程序的速度?(共享内存?)谢谢!
...全文
437 23 打赏 收藏 转发到动态 举报
写回复
用AI写文章
23 条回复
切换为时间正序
请发表友善的回复…
发表回复
mousemouse1980 2011-08-19
  • 打赏
  • 举报
回复
在大家的帮助下,经过了这么多天的测试,我觉得在一个form中加入几万个控件的方法似乎不太可行,即使是自定义控件,好像也不能加到几万个,所以目前我还是采用自己绘制图形的方法来实现类似功能,虽然程序写的麻烦一些,但是性能基本能够满足需要,大家的帮助,让我学到了不少东西,先结贴,有空时,我再研究完全自定义控件的方法。
gomoku 2011-08-17
  • 打赏
  • 举报
回复
控件(Control)底层实现是一个Window(Control.Handle就是一个Windows句柄)。
Window属于GDI资源。在Windows操作系统中,GDI资源是有限制的,所以不能无限制的创建控件(创建十几万个Window是不可能的)。

用控件来做不是不可以,但是也要“重复利用”有限的控件。也就是利用一个好的数据结构,用百十个控件来显示当前的可视对象
  • 打赏
  • 举报
回复
mark, 学习鸟
劉宏哲 2011-08-17
  • 打赏
  • 举报
回复
可不可以使用tab控件或者别的,换个思路应该是最好的。
劉宏哲 2011-08-17
  • 打赏
  • 举报
回复
继承Control,重写OnPaint方法。用e.Graphics取得Graphics对象,然后用GDI+随便画。
g.DrawRectangle(new Pen(Color.Red), 0, 0, Width - 2, Height - 2);
g.DrawString(Text, Font, new SolidBrush(Color.Blue), new RectangleF(0, 0, Width - 2, Height - 2));

优化内存和那种局部刷新都不是最好的选择,还是要根据你的实际需求,先想最好的解决办法,然后再去研究。
mousemouse1980 2011-08-17
  • 打赏
  • 举报
回复
谢谢大家的热心相助,我现在所使用的方法和“gomoku”所说的类似,完全用画的方法来解决,只是觉得这样比较麻烦,诸如单选、多选、移动等所有的鼠标动作都要自己写,如果是在找不出更好的方法,可能也只能采用这样的方法了。
实际上我的确是希望能够像“liuhongzhe”所说的,自己定义一个控件,这个控件占用资源很少,可以加入几万个到form上,速度也能很快,但是我试了如果继承winform下的控件,速度一样很慢,是不是可以采用“完全自定义控件”的方法?如何实现完全自动以控件在winform上的显示(比如这个控件的界面就是一个Rectangle,可以根据是否选中来改变边框的样式),希望能有高人指点,谢谢!
劉宏哲 2011-08-15
  • 打赏
  • 举报
回复
[Quote=引用 6 楼 liuhongzhe 的回复:]

自己写一个自定义控件。
然后把公共的属性写成静态的。
变动的写成非静态的。
我感觉只有这样自己写个控件才能内存共用。
[/Quote]
你应该好好看看我之前说的这几句话。
mousemouse1980 2011-08-14
  • 打赏
  • 举报
回复
谢谢大家的热心相助,受益匪浅,我正在看gomoku的代码,也许这就是我要的方法,再等我测试看看,谢谢了!
xiehuanxie 2011-08-14
  • 打赏
  • 举报
回复
不管什么样的方法,放几万个都会卡的,为什么不考虑自定义控件。
足球中国 2011-08-13
  • 打赏
  • 举报
回复
首先要明白是对象还是控件。几百万个控件甚至几万个控件是肯定做不到的。
几百万个对象的话倒只是你的内存问题。
控件要注册句柄,接收消息。和对象不同。

楼上所说 datagridview里面放个几百行数据也不会怎么样,就算这些行里每行都有内嵌控件
这个是误解。datagridview肯定不是一个单元格或者一行就是一个控件。他只是模仿一些控件或者用一些控件的处理方式。或者等到有焦点时,现用一个控件放在上面。
飞天凤凰601 2011-08-13
  • 打赏
  • 举报
回复
学习下
jiwenn 2011-08-13
  • 打赏
  • 举报
回复
form上虽然对象多,但是你不可能一屏给显示完吧。。。每次只处理显示出的一小部分。。。
Rose_jojo 2011-08-13
  • 打赏
  • 举报
回复
转转让
gomoku 2011-08-12
  • 打赏
  • 举报
回复
给个例子。放了一百万个不同的对象,刚启动的时候会慢一些。
演示了
- 局部刷新
- 滚动
- 鼠标经过
- 鼠标选择

public partial class Form1 : Form
{
VScrollBar vScrollBar = new VScrollBar();
List<MyElement> elements = new List<MyElement>();
MyElement SelectedElement = null;
public Form1()
{
// 准备一百万个对象。分配不同的位置,颜色和位置
KnownColor[] colors = Enum.GetValues(typeof(KnownColor)) as KnownColor[];
for (int i = 0; i < 1000*1000; i++)
{
MyElement element = new MyElement()
{
Bound = new Rectangle( (i % 5) * 80, (i / 5) * 80, 50,50),
Color = Color.FromKnownColor( colors[ i % colors.Length ] ),
Name = ((char)('A' + i % 26)).ToString() + i,
};
elements.Add(element);
}

// 添加垂直滚动条,并在滚动时要求更新
this.DoubleBuffered = true;
vScrollBar.Dock = System.Windows.Forms.DockStyle.Right;
vScrollBar.Maximum = elements[elements.Count - 1].Location.Y + 80 - this.ClientRectangle.Height;
vScrollBar.ValueChanged += delegate { this.Invalidate(); };
this.Controls.Add(vScrollBar);
}

private int GetWhereAbout(Point location)
{
// 加速寻找,拿出与当前窗口大概相关的对象起始下标。正规算法可以用四分树等等
int where = location.Y / 80 * 5;
return Math.Max(0, where - 10);
}

protected override void OnPaintBackground(PaintEventArgs e){}

protected override void OnPaint(PaintEventArgs e)
{
// 更新需要重画的地方
e.Graphics.TranslateTransform(0, -vScrollBar.Value);
Rectangle clip = Rectangle.Ceiling(e.Graphics.ClipBounds);
int whereAbout = GetWhereAbout(clip.Location);
for (int i = whereAbout; i < elements.Count && i < whereAbout + 100; i++)
{
if (clip.IntersectsWith(elements[i].Bound))
{
elements[i].DrawTo(e.Graphics);
}
}
}

protected override void OnMouseMove(MouseEventArgs e)
{
// 更新鼠标经过的对象
Point cursor = new Point(e.X, e.Y + this.vScrollBar.Value);
int whereAbout = GetWhereAbout(cursor);
for (int i = whereAbout; i < elements.Count && i < whereAbout + 100; i++)
{
if (elements[i].HoverTest(cursor))
{
this.Invalidate();
return;
}
}
}

protected override void OnMouseDown(MouseEventArgs e)
{
// 更新选定的对象
Point cursor = new Point(e.X, e.Y + this.vScrollBar.Value);
if( this.SelectedElement != null)
{
this.SelectedElement.Selected = false;
this.SelectedElement = null;
}

int whereAbout = GetWhereAbout(cursor);
for (int i = whereAbout; i < elements.Count && i < whereAbout + 100; i++)
{
if (elements[i].Bound.Contains(cursor))
{
this.SelectedElement = elements[i];
this.SelectedElement.Selected = true;
this.Invalidate();
return;
}
}
}
}

public class MyElement
{
public string Name { get; set; }
public Rectangle Bound { get; set; }
public Point Location { get { return this.Bound.Location; } }
public Color Color { get; set; }
public bool Selected { get; set; }
public bool Hovered { get; set; }

public bool HoverTest(Point hit)
{
bool oldValue = this.Hovered;
this.Hovered = this.Bound.Contains(hit);
return this.Hovered != oldValue;
}

public void DrawTo(Graphics g)
{
using (Brush brush = new SolidBrush(this.Hovered ? ControlPaint.LightLight(this.Color) : this.Color))
{
g.FillRectangle(brush, this.Bound);
}
{
g.DrawString(this.Name, SystemFonts.MenuFont, Brushes.Black, this.Bound, MyElement.StringFormat);
}
if (this.Selected)
{
g.DrawRectangle(Pens.Red, this.Bound);
}
}

private static StringFormat stringFormat = null;
private static StringFormat StringFormat
{
get
{
if (stringFormat == null)
{
stringFormat = new StringFormat()
{
Alignment = StringAlignment.Center,
LineAlignment = StringAlignment.Center,
Trimming = StringTrimming.EllipsisWord,
};
}
return stringFormat;
}
}
}
mousemouse1980 2011-08-12
  • 打赏
  • 举报
回复
我曾经试图在一个winform中加入1万个Label,结果到了9千多个的时候就出现“创建窗口句柄时出错...”的提示,有什么好的办法能够将超过1万个对象加入到一个winform中?
劉宏哲 2011-08-12
  • 打赏
  • 举报
回复
[Quote=引用 7 楼 icedmilk 的回复:]

几万个又怎了了

datagridview里面放个几百行数据也不会怎么样,就算这些行里每行都有内嵌控件。
[/Quote]
我猜是为了优化,减少内存占用。
Icedmilk 2011-08-12
  • 打赏
  • 举报
回复
几万个又怎了了

datagridview里面放个几百行数据也不会怎么样,就算这些行里每行都有内嵌控件。
劉宏哲 2011-08-12
  • 打赏
  • 举报
回复
自己写一个自定义控件。
然后把公共的属性写成静态的。
变动的写成非静态的。
我感觉只有这样自己写个控件才能内存共用。
  • 打赏
  • 举报
回复
[Quote=引用 2 楼 chiyan42 的回复:]
是几万个必须都要在一个form里面么,我曾经做过一个类似的,但是没有你这么多,我当时是用了好几个线程去初始化这些label,做的就好像web里面的ajax似的那种,一块一块的加载出来的。

至于你说共享内存~~我觉得可以同时用。
[/Quote]

lz的意思是说,一个Form上往往有成千上万的控件(大控件也使用了小控件来组合实现),每一个控件中大部分属性其实都只是默认值,而并不修改值。lz认为简单的设计方式很浪费空间。
gomoku 2011-08-12
  • 打赏
  • 举报
回复
几万个对象会很多吗?

1、颜色,位置,名字等一个对象也就几十个字节。算100个字节,10万个对象也才1兆的内存。
2、10万个可能同时显示吗?同时显示的也就肉眼能容忍的小百个了,画小百个对象还是很轻松的。
3、稍微关键一些的就是一个好的数据结构,能快速的决定要更新(绘画)那些对象。
加载更多回复(3)

110,500

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧