none
大量数据的显示问题 RRS feed

  • 问题

  • 以前一直想将文件以16进制方式打开,但因为自己写的10进制转16进制的函数的转换效率不高而作罢。近段时间在看VS2005的帮助文档的时候看到可以使用string.Format的方法将byte转换为16进制的字符串,就尝试使用,效率提高了很多,于是就想实现之前的想法。

    一开始轻易就完成的打开一些小文件的程序。我是使用DataGridView控件显示的(因为要显示地址,又要每行都是2个字符分隔的,如GameMaster中的内存编辑所显示的样子)。但我用这个程序打开数M的文件时,需要的时间实在太长了,而且内存的占用令人无法忍受(经调试我发觉是DataGridView的问题)。

    后来我使用过TextBox、RichTextBox等控件显示的时间也无法解决(也就显示的问题,进制的转换数M大小根本就不占用多少时间),当然,如果先将转换的字符串放入到StringBuilder中再一次显示到控件上可以解决时间太长的问题,但内存的占用会达数百M(才加载数M的文件)。

    各位有办法解决这个问题吗,使显示数十M甚至数百M的文件的16进制数据也不会占用太多的内存和时间(我发觉Ultraedit实现了这个效果,但我不知道其原理),难道只能代码手工绘制显示控件??

    2008年4月22日 10:47

全部回复

  • 不知道你在创建StringBuilder的时候,有没有为它初始化一个初始容量(利用一个重载构造函数egTongue TiedtringBuilder sb = new StringBuilder(16384)),因为StringBuilder不能完全避免字符串内存的重新分配,只是减少这种分配的次数。如果你一开始根据文件的大小大概的为StringBuilder设置一个初始容量,这个初始容量足够大的话,甚至不会再次分配内存,或者只再次分配1、2次,那么相信内存占用会降低不少。因为你可以得到需要打开的文件的大小,因此估计这个初始容量也不是什么难事。

    另外,我也不知道UltraEdit是怎么实现的这个效果,猜想它不是一次读入整个文件,而是保持对文件的打开状态,然后根据滚屏等实时地读入窗体要显示的内容。

     

    2008年4月22日 13:52
    版主
  •  

    占用内存的主要部分不是StringBuilder,而是显示的控件。如果读文件一个字节就转换成string并追加到控件上显示,这样控件部分不会占用太多内存,甚至说占用内存的情况与没有显示数据时几乎没有什么变化,但这样做的话显示速度太慢(几M就要数分钟时间)。

    我目前也尝试如你所说的UltraEdit的办法,但自绘控件的时候如何将控件与滚动条关联,我做的时候总是有偏差,定位不准确。

    2008年4月22日 14:46
  • 或许可以简化处理,采用一整屏、一整屏的滚动?

     

    2008年4月22日 15:16
    版主
  •  

    我初步做出了一个控件,但十分不完善。现在我想请教一下如何在自绘控件上显示一个闪动的光标?

    另请指正一下我写代码,绘制效率方面能否可以提高?

    代码:

    HexBoardControl.Designer.cs文件:

    namespace WindowsApplicationDemo
    {
        partial class HexBoardControl
        {
            /// <summary>
            /// 必需的设计器变量。
            /// </summary>
            private System.ComponentModel.IContainer components = null;

            /// <summary>
            /// 清理所有正在使用的资源。
            /// </summary>
            /// <param name="disposing">如果应释放托管资源,为 true;否则为 false。</param>
            protected override void Dispose(bool disposing)
            {
                if (disposing && (components != null))
                {
                    components.Dispose();
                }
                base.Dispose(disposing);
            }

            #region 组件设计器生成的代码

            /// <summary>
            /// 设计器支持所需的方法 - 不要
            /// 使用代码编辑器修改此方法的内容。
            /// </summary>
            private void InitializeComponent()
            {
                this.VerticalScrollBar = new System.Windows.Forms.VScrollBar();
                this.SuspendLayout();
                //
                // VerticalScrollBar
                //
                this.VerticalScrollBar.Dock = System.Windows.Forms.DockStyle.Right;
                this.VerticalScrollBar.LargeChange = 100;
                this.VerticalScrollBar.Location = new System.Drawing.Point(277, 0);
                this.VerticalScrollBar.Name = "VerticalScrollBar";
                this.VerticalScrollBar.Size = new System.Drawing.Size(20, 298);
                this.VerticalScrollBar.TabIndex = 0;
                //
                // HexBoardControl
                //
                this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 14F);
                this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
                this.BackColor = System.Drawing.Color.White;
                this.Controls.Add(this.VerticalScrollBar);
                this.Name = "HexBoardControl";
                this.Size = new System.Drawing.Size(297, 298);
                this.FontChanged += new System.EventHandler(this.HexBoardControl_FontChanged);
                this.SizeChanged += new System.EventHandler(this.HexBoardControl_SizeChanged);
                this.ResumeLayout(false);

            }

            #endregion

            private System.Windows.Forms.VScrollBar VerticalScrollBar;
        }
    }

    HexBoardControl.cs文件:

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Drawing;
    using System.Data;
    using System.Text;
    using System.Windows.Forms;

    namespace WindowsApplicationDemo
    {
        public partial class HexBoardControl : UserControl
        {
            private int lines = 0;
            [Description("获取或设置文本的总行数")]
            public int Lines
            {
                get { return this.lines; }
            }

            private int linesPerScreen = 1;
            [Description("获取控件每一屏显示的文本的行数")]
            public int LinesPerScreen
            {
                get { return this.linesPerScreen; }
            }

            private Color addressColor = Color.WhiteSmoke;
            [Description("获取设置显示地址位置的颜色")]
            public Color AddressColor
            {
                get { return this.addressColor; }
                set { this.addressColor = value; this.Refresh(); }
            }

            private Color addressForeColor = Color.Black;
            [Description("获取设置显示地址位置的字体的颜色")]
            public Color AddressForeColor
            {
                get { return this.addressForeColor; }
                set { this.addressForeColor = value; this.Refresh(); }
            }

            private StringBuilder buffer = new StringBuilder();    // 要显示的数据的缓存
            private SizeF addressSize;

            public string Data
            {
                get { return this.buffer.ToString(); }
                set
                {
                    this.buffer.Remove(0, this.buffer.Length);
                    this.buffer.Append(value);
                    this.lines = this.buffer.Length / 32;
                    if (this.lines * 32 != this.buffer.Length) this.lines++;
                    this.SetScrollBarSize();
                    this.Refresh();
                }
            }

            public HexBoardControl()
            {
                InitializeComponent();
                this.buffer.Append("aaaaaaaaaaaaaaaaaaa");
                this.ResetSize();
            }

            private void ResetSize()
            {
                this.addressSize = this.GetAddressSize();
                this.linesPerScreen = (int)(this.Height / this.addressSize.Height) - 2;
                this.SetScrollBarSize();
            }

            private void SetScrollBarSize()
            {
                if (this.LinesPerScreen <= 0) return;
                if (this.LinesPerScreen <= this.lines)
                {
                    this.VerticalScrollBar.LargeChange = this.linesPerScreen;
                    this.VerticalScrollBar.Maximum = this.lines;
                    this.VerticalScrollBar.Enabled = true;
                }
            }

            /// <summary>
            /// 获取地址的尺寸
            /// </summary>
            /// <returns></returns>
            private SizeF GetAddressSize()
            {
                string str = "00000000h:";
                Graphics g = this.CreateGraphics();
                return g.MeasureString(str, this.Font);
            }

            public void AppendText(string value)
            {
                this.buffer.Append(value);
                this.lines = this.buffer.Length / 32 + 1;
            }

            protected override void  OnPaint(PaintEventArgs e)
            {
                // 控制滚动条
                if (this.Height <= ((this.addressSize.Height+2) * this.lines)) this.VerticalScrollBar.Enabled = true;
                else this.VerticalScrollBar.Enabled = false;

                Graphics g = this.CreateGraphics();

                // 绘制地址(如00000001hSmile
                g.FillRectangle(new SolidBrush(this.addressColor), 0, 0, this.Width, this.addressSize.Height);
                float perX = this.addressSize.Width / 10;
                float initX = this.addressSize.Width + perX;
                float x = initX + perX / 2;
                // 横向的地址
                for (int i = 0; i <= 15; i++)
                {
                    g.DrawString(string.Format("{0:X1}", i), this.Font, new SolidBrush(this.addressForeColor), new PointF(x, 0));
                    x += 3 * perX;
                }
                // 纵向的地址
                int len = this.buffer.Length;
                for (int i = 0; i < this.lines; i++)
                {
                    float y = this.addressSize.Height * (i+1);
                    g.FillRectangle(new SolidBrush(this.addressColor), 0, y, this.addressSize.Width, this.addressSize.Height);
                    g.DrawString(string.Format("{0:X7}0h:", i), this.Font, new SolidBrush(this.addressForeColor), new PointF(0, y));
                    x = initX;
                    for (int j = 1; j <= 32; j += 2)
                    {
                        if (len <= 1) break;
                        g.DrawString(string.Format("{0}{1}", this.buffer[i * 16 + j], this.buffer[i * 16 + j + 1]), this.Font, new SolidBrush(this.ForeColor), new PointF(x, y));
                        x += 3 * perX;
                        len -= 2;
                    }
                }
                base.OnPaint(e);
            }

            private void HexBoardControl_FontChanged(object sender, EventArgs e)
            {
                this.ResetSize();
            }

            private void HexBoardControl_SizeChanged(object sender, EventArgs e)
            {
                this.ResetSize();
            }
        }
    }

    2008年4月22日 16:33
  •  RickyLin 写:
    或许可以简化处理,采用一整屏、一整屏的滚动?

     

    之前,我使用RichTextBox显示数据的时候,也想过一次只显示一屏或几屏,但在RichTextBox中找不到与滚动条定位相关的事件、属性或操作,所以就只能自己绘制一个显示控件了,就出现了楼上的代码。

    2008年4月22日 16:56