自定义TreeDataView 控件,百万级别数据秒绘。
最近需要使用到数据展现,需要对数据折叠展现。网上找了许多控件,如:TreeListView,TreevDataGirdView等,但是都无法到达效果,而且加载百万条数据时,绘制灰常的卡顿。
话不多说。先上效果图
1 准备工作,创建两个类,用户绘制行与列
public class UserRow { /// <summary> /// 构造函数 /// </summary> /// <param name="level">节点等级</param> public UserRow(int level) { _level = level; strFmt.Alignment = StringAlignment.Near; //文本水平居中 strFmt.LineAlignment = StringAlignment.Center; //文本垂直居中 } int _level = 0; public int Level { get { return _level; } } public UserRow Parent { get; set; } = null; public float PointX = 0; public float PointY = 0; //public float RowHeigth = 20; public bool ExpandEvent = false; private bool _expand = false; public bool Expand { get { return _expand; } set { _expand = value; } } private float _marginLeft = 30; public float MarginLeft { get { return _marginLeft; } } public bool IsSelected = false; public List<UserCell> Cells = new List<UserCell>(); List<UserRow> _subrows = new List<UserRow>(); public List<UserRow> SubRows { get { return _subrows; } } StringFormat strFmt = new System.Drawing.StringFormat(); Font myFont = new Font("宋体", 10); public float Draw(Graphics g, float dx, float dy, float MapLocationX, float MapLocationY, float h, float w, List<UserCell> colums) { PointY = dy; float margleft = calcMarginLeft(this); float x = PointX - MapLocationX ; float y = PointY - MapLocationY; if (_level == 0) { g.FillRectangle(Brushes.LightSkyBlue, x, y + 1, w - 2, h - 1); } else { g.FillRectangle(Brushes.White, x, y + 1, w - 2, h - 1); } if (this.SubRows.Count > 0) { RectangleF plusRect = new RectangleF(x + margleft - 15, y + 5, 10, 10);// +-号的大小 是9 * 9 g.FillRectangle(Brushes.White, plusRect); g.DrawRectangle(new Pen(Brushes.Black), plusRect.X, plusRect.Y, plusRect.Width, plusRect.Height); if (this.Expand) { g.DrawString("-", new Font("宋体", 9), Brushes.Black, plusRect); } else { g.DrawString("+", new Font("宋体", 9), Brushes.Black, plusRect); } } float startPointX = x + margleft ; for (int i = 0; i < colums.Count; i++) { if (colums[i].IsViszble && i< Cells.Count) { RectangleF rf = new RectangleF(startPointX, y + 1, colums[i].Width, h - 1); if (colums[i].IsSelect) { g.FillRectangle(Brushes.Black, rf); g.DrawString(Cells[i].Value, myFont, Brushes.White, rf, strFmt); } else { g.FillRectangle(Brushes.Transparent, rf); g.DrawString(Cells[i].Value, myFont, Brushes.Black, rf, strFmt); } //g.DrawLine(new Pen(Brushes.White, 1), startPointX, y, startPointX, y + h); startPointX += colums[i].Width; } } Pen mypen; if (IsSelected) { mypen = new Pen(Brushes.Red, 1); g.DrawRectangle(mypen, x, y + 1, w -2, h - 1); } else { mypen = new Pen(Brushes.White, 1); g.DrawRectangle(mypen, x, y + 1,w -2, h - 1); } return startPointX + margleft; } public float CalcMaxWidth() { float width = PointX; float margleft = calcMarginLeft(this); for (int i = 0; i < Cells.Count; i++) { width += Cells[i].Width; } return width + margleft; } public float calcMarginLeft(UserRow row) { return (row.Level + 1) * row.MarginLeft; //if (row.Parent != null) //{ // float t = calcMarginLeft(row.Parent); // return row.Parent.MarginLeft + t; //} //else //{ // return row.MarginLeft; //} } public bool IsSelect(float dx, float dy, float h, float w) { float margleft = calcMarginLeft(this); ExpandEvent = false; if (dx <= PointX + w && dx >= PointX && dy <= PointY + h && dy >= PointY) { IsSelected = true; RectangleF plusRect = new RectangleF(PointX + margleft - 15, PointY + 5, 10, 10);// +-号的大小 是9 * 9 if (dx <= plusRect.X + plusRect.Width && dx >= plusRect.X && dy <= plusRect.Y + plusRect.Height && dy >= plusRect.Y) { _expand = !_expand; ExpandEvent = true; } } else { IsSelected = false; } return IsSelected; } } public class UserCell { /// <summary> /// 当前值 /// </summary> public string Value { get; set; } /// <summary> /// 默认单元格宽度 /// </summary> public float Width { get; set; } = 80; /// <summary> /// 是否显示 /// </summary> public bool IsViszble { get; set; } = true; /// <summary> /// 是否选中单元格 /// </summary> public bool IsSelect { get; set; } = false; }
2 添加用户控件,然后在控件上面添加 三个控件,PictureBox,vScrollBar1,hScrollBar1
PictureBox 用于绘制数据,vScrollBar1,hScrollBar1用于移动数据
设计代码
namespace UserTreeTable { partial class UserControl1 { /// <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.components = new System.ComponentModel.Container(); this.vScrollBar1 = new System.Windows.Forms.VScrollBar(); this.hScrollBar1 = new System.Windows.Forms.HScrollBar(); this.pbImg = new System.Windows.Forms.PictureBox(); this.contextMenuStrip1 = new System.Windows.Forms.ContextMenuStrip(this.components); this.折叠ToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.折叠层一ToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.折叠层二ToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.展开ToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.展开层一ToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.展开层二ToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); ((System.ComponentModel.ISupportInitialize)(this.pbImg)).BeginInit(); this.contextMenuStrip1.SuspendLayout(); this.SuspendLayout(); // // vScrollBar1 // this.vScrollBar1.Dock = System.Windows.Forms.DockStyle.Right; this.vScrollBar1.Location = new System.Drawing.Point(439, 0); this.vScrollBar1.Name = "vScrollBar1"; this.vScrollBar1.Size = new System.Drawing.Size(17, 325); this.vScrollBar1.TabIndex = 0; this.vScrollBar1.Scroll += new System.Windows.Forms.ScrollEventHandler(this.vScrollBar1_Scroll); this.vScrollBar1.ValueChanged += new System.EventHandler(this.vScrollBar1_ValueChanged); // // hScrollBar1 // this.hScrollBar1.Dock = System.Windows.Forms.DockStyle.Bottom; this.hScrollBar1.Location = new System.Drawing.Point(0, 308); this.hScrollBar1.Name = "hScrollBar1"; this.hScrollBar1.Size = new System.Drawing.Size(439, 17); this.hScrollBar1.TabIndex = 1; this.hScrollBar1.Scroll += new System.Windows.Forms.ScrollEventHandler(this.hScrollBar1_Scroll); this.hScrollBar1.ValueChanged += new System.EventHandler(this.hScrollBar1_ValueChanged); // // pbImg // this.pbImg.BackColor = System.Drawing.SystemColors.Control; this.pbImg.ContextMenuStrip = this.contextMenuStrip1; this.pbImg.Dock = System.Windows.Forms.DockStyle.Fill; this.pbImg.Location = new System.Drawing.Point(0, 0); this.pbImg.Name = "pbImg"; this.pbImg.Size = new System.Drawing.Size(439, 308); this.pbImg.TabIndex = 2; this.pbImg.TabStop = false; this.pbImg.Paint += new System.Windows.Forms.PaintEventHandler(this.pictureBox1_Paint); this.pbImg.MouseDown += new System.Windows.Forms.MouseEventHandler(this.pictureBox1_MouseDown); this.pbImg.Resize += new System.EventHandler(this.pbImg_Resize); // // contextMenuStrip1 // this.contextMenuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { this.折叠ToolStripMenuItem, this.展开ToolStripMenuItem}); this.contextMenuStrip1.Name = "contextMenuStrip1"; this.contextMenuStrip1.Size = new System.Drawing.Size(153, 70); // // 折叠ToolStripMenuItem // this.折叠ToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.折叠层一ToolStripMenuItem, this.折叠层二ToolStripMenuItem}); this.折叠ToolStripMenuItem.Name = "折叠ToolStripMenuItem"; this.折叠ToolStripMenuItem.Size = new System.Drawing.Size(100, 22); this.折叠ToolStripMenuItem.Text = "折叠"; // // 折叠层一ToolStripMenuItem // this.折叠层一ToolStripMenuItem.Name = "折叠层一ToolStripMenuItem"; this.折叠层一ToolStripMenuItem.Size = new System.Drawing.Size(124, 22); this.折叠层一ToolStripMenuItem.Text = "折叠层一"; this.折叠层一ToolStripMenuItem.Click += new System.EventHandler(this.折叠层一ToolStripMenuItem_Click); // // 折叠层二ToolStripMenuItem // this.折叠层二ToolStripMenuItem.Name = "折叠层二ToolStripMenuItem"; this.折叠层二ToolStripMenuItem.Size = new System.Drawing.Size(124, 22); this.折叠层二ToolStripMenuItem.Text = "折叠层二"; this.折叠层二ToolStripMenuItem.Click += new System.EventHandler(this.折叠层二ToolStripMenuItem_Click); // // 展开ToolStripMenuItem // this.展开ToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.展开层一ToolStripMenuItem, this.展开层二ToolStripMenuItem}); this.展开ToolStripMenuItem.Name = "展开ToolStripMenuItem"; this.展开ToolStripMenuItem.Size = new System.Drawing.Size(152, 22); this.展开ToolStripMenuItem.Text = "展开"; // // 展开层一ToolStripMenuItem // this.展开层一ToolStripMenuItem.Name = "展开层一ToolStripMenuItem"; this.展开层一ToolStripMenuItem.Size = new System.Drawing.Size(152, 22); this.展开层一ToolStripMenuItem.Text = "展开层一"; this.展开层一ToolStripMenuItem.Click += new System.EventHandler(this.展开层一ToolStripMenuItem_Click); // // 展开层二ToolStripMenuItem // this.展开层二ToolStripMenuItem.Name = "展开层二ToolStripMenuItem"; this.展开层二ToolStripMenuItem.Size = new System.Drawing.Size(152, 22); this.展开层二ToolStripMenuItem.Text = "展开层二"; this.展开层二ToolStripMenuItem.Click += new System.EventHandler(this.展开层二ToolStripMenuItem_Click); // // UserControl1 // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.Controls.Add(this.pbImg); this.Controls.Add(this.hScrollBar1); this.Controls.Add(this.vScrollBar1); this.Name = "UserControl1"; this.Size = new System.Drawing.Size(456, 325); this.Resize += new System.EventHandler(this.UserControl1_Resize); ((System.ComponentModel.ISupportInitialize)(this.pbImg)).EndInit(); this.contextMenuStrip1.ResumeLayout(false); this.ResumeLayout(false); } #endregion private System.Windows.Forms.VScrollBar vScrollBar1; private System.Windows.Forms.HScrollBar hScrollBar1; private System.Windows.Forms.PictureBox pbImg; private System.Windows.Forms.ContextMenuStrip contextMenuStrip1; private System.Windows.Forms.ToolStripMenuItem 折叠ToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem 折叠层一ToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem 折叠层二ToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem 展开ToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem 展开层一ToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem 展开层二ToolStripMenuItem; } }
3 在自定义控件UserControl1中实现绘制
using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Data; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; namespace UserTreeTable { public partial class UserControl1 : UserControl { public UserControl1(List<UserRow> rows) { InitializeComponent(); this.Load += UserControl1_Load; this.pbImg.MouseWheel += PbImg_MouseWheel; Rows = rows; } /// <summary> /// 所有行 /// </summary> public List<UserRow> Rows = new List<UserRow>(); /// <summary> /// 所有列 /// </summary> public SortedList<int, UserRow> HeadColumns = new SortedList<int, UserRow>(); /// <summary> /// 选中的列 /// </summary> List<UserCell> selectHeadColumns = new List<UserCell>(); /// <summary> /// 选中行 /// </summary> UserRow _selectRow; /// <summary> /// 图像平移后横坐标偏移的像素 /// </summary> public float MapLocationX = 0; /// <summary> /// 图像平移后纵坐标偏移的像素 /// </summary> public float MapLocationY = 0; /// <summary> /// 固定行号 /// </summary> const int rowHeight = 20; /// <summary> /// 默认行宽 /// </summary> float rowWidth = 100; //float MaxHeight = 0; //float MaxWidth = 0; private void UserControl1_Load(object sender, EventArgs e) { CalcMapSize(); if (Rows.Count > 0) _selectRow = Rows[0]; } private void PbImg_MouseWheel(object sender, MouseEventArgs e) { if (e.Delta > 0) { if (vScrollBar1.Value - rowHeight > 0) { vScrollBar1.Value -= rowHeight; } else { vScrollBar1.Value = vScrollBar1.Minimum; } } else { if (vScrollBar1.Value + rowHeight < vScrollBar1.Maximum) { vScrollBar1.Value += rowHeight; } else { vScrollBar1.Value = vScrollBar1.Maximum; } } if (MapLocationY != vScrollBar1.Value) { MapLocationY = vScrollBar1.Value; pbImg.Refresh(); } } public void CalcMapSize() { float w = 0, h = 0; foreach (UserRow c1 in HeadColumns.Values) { float w1 = c1.CalcMaxWidth(); if (w < w1) { w = w1; } } rowWidth = w; if (rowWidth > pbImg.Width) { hScrollBar1.Enabled = true; hScrollBar1.Minimum = 0; hScrollBar1.Maximum = (int)rowWidth - pbImg.Width +10; hScrollBar1.LargeChange = 10; hScrollBar1.SmallChange = 10; } else { rowWidth = pbImg.Width; MapLocationX = 0; hScrollBar1.Enabled = false; //hScrollBar1.Visible = false; } foreach (UserRow u1 in Rows) { h += rowHeight; if(u1.Expand) { foreach (UserRow u2 in u1.SubRows) { h += rowHeight; if (u2.Expand) { foreach (UserRow u3 in u2.SubRows) { h += rowHeight; } } } } } int vscrollmaxNum = (int)h - pbImg.Height + 20; if (vscrollmaxNum != vScrollBar1.Maximum) { if (h > pbImg.Height) { vScrollBar1.Enabled = true; vScrollBar1.Minimum = 0; vScrollBar1.Maximum = vscrollmaxNum; vScrollBar1.LargeChange = rowHeight; vScrollBar1.SmallChange = rowHeight; } else { MapLocationY = 0; vScrollBar1.Enabled = false; } } } void Drawing(Graphics g) { float pointX = 0; float pointY = rowHeight; float scrollY = this.vScrollBar1.Value; //float tempHeight = scrollY + this.panel2.ClientSize.Height; float tempHeight = this.vScrollBar1.Value + pbImg.Height; foreach (UserRow u1 in Rows) { if (pointY <= tempHeight && pointY >= scrollY) { u1.Draw(g, pointX, pointY, MapLocationX, MapLocationY, rowHeight, rowWidth, HeadColumns[u1.Level].Cells); } pointY += rowHeight; if (u1.Expand == true) { foreach (UserRow u2 in u1.SubRows) { if (pointY <= tempHeight && pointY >= scrollY) { u2.Draw(g, pointX, pointY, MapLocationX, MapLocationY, rowHeight, rowWidth, HeadColumns[u2.Level].Cells); } pointY += rowHeight; if (u2.Expand) { foreach (UserRow u3 in u2.SubRows) { if (pointY <= tempHeight && pointY >= scrollY) { u3.Draw(g, pointX, pointY, MapLocationX, MapLocationY, rowHeight, rowWidth, HeadColumns[u3.Level].Cells); } pointY += rowHeight; } } } } } } void DrawHead(Graphics g) { StringFormat strFmt = new System.Drawing.StringFormat(); strFmt.Alignment = StringAlignment.Near; //文本水平居中 strFmt.LineAlignment = StringAlignment.Center; //文本垂直居中 Rectangle rect = new Rectangle(0, 0, (int)rowWidth + 10, rowHeight); Brush b = new System.Drawing.Drawing2D.LinearGradientBrush(rect, Color.Gray, Color.LightGray, System.Drawing.Drawing2D.LinearGradientMode.Vertical); g.FillRectangle(b, rect); if (_selectRow != null) { float margleft = _selectRow.calcMarginLeft(_selectRow); float startPointX = margleft - MapLocationX; UserRow headrow = HeadColumns[_selectRow.Level]; foreach (UserCell c in headrow.Cells) { RectangleF rf = new RectangleF(startPointX, 2, c.Width, rowHeight - 1); if (c.IsSelect) { g.FillRectangle(Brushes.Black, rf); g.DrawString(c.Value, this.Font, Brushes.White, rf, strFmt); } else { g.FillRectangle(Brushes.Transparent, rf); g.DrawString(c.Value, this.Font, Brushes.Black, rf, strFmt); } g.DrawLine(new Pen(Brushes.White, 1), startPointX, 0, startPointX, rowHeight); startPointX += c.Width; } g.DrawLine(new Pen(Brushes.White, 1), startPointX, 0, startPointX, rowHeight); } } private void pictureBox1_Paint(object sender, PaintEventArgs e) { Graphics g = e.Graphics; Drawing(g); DrawHead(g); } private void pictureBox1_MouseDown(object sender, MouseEventArgs e) { if (e.Button == MouseButtons.Left) { float y = e.Y; if (e.Y < rowHeight) { UserCell headcell = SelectHeadIndex(e.X, e.Y); if (headcell != null) { this.pbImg.Refresh(); } } else { UserRow tempselectRow = SelectRow(e.X + MapLocationX, e.Y + MapLocationY); if (tempselectRow != null) { if (tempselectRow != _selectRow) { if (_selectRow != null) { _selectRow.IsSelected = false; if (tempselectRow.Level != _selectRow.Level) { foreach (UserCell c in HeadColumns[_selectRow.Level].Cells) { c.IsSelect = false; } } } } _selectRow = tempselectRow; if (tempselectRow.ExpandEvent) { CalcMapSize(); } this.pbImg.Refresh(); } } } } UserCell SelectHeadIndex(float x, float y) { if (_selectRow != null) { UserRow headrow = HeadColumns[_selectRow.Level]; if (headrow != null) { float margleft = _selectRow.calcMarginLeft(_selectRow); float startPointX = margleft - MapLocationX; foreach (UserCell c in headrow.Cells) { if (x >= startPointX && x <= startPointX + c.Width && y > 0 && y < rowHeight) { c.IsSelect = !c.IsSelect; return c; } startPointX += c.Width; } } } return null; } UserRow SelectRow(float x, float y) { Console.WriteLine("选择坐标:x= " + x + " y= " + y); foreach (UserRow u1 in Rows) { if (u1.IsSelect(x, y, rowHeight, rowWidth)) { return u1; } else if (u1.Expand == true) { foreach (UserRow u2 in u1.SubRows) { if (u2.IsSelect(x, y, rowHeight, rowWidth)) { return u2; } else if (u2.Expand) { foreach (UserRow u3 in u2.SubRows) { if (u3.IsSelect(x, y, rowHeight, rowWidth)) { return u3; } } } } } } return null; } private void vScrollBar1_Scroll(object sender, ScrollEventArgs e) { //System.Threading.Thread.Sleep(5); //MapLocationY = e.OldValue; //Console.WriteLine("old:" + e.OldValue); //Console.WriteLine("new:"+e.NewValue); //this.pbImg.Refresh(); } private void hScrollBar1_Scroll(object sender, ScrollEventArgs e) { //MapLocationX = e.NewValue; //Console.WriteLine("new:" + e.NewValue); //this.pbImg.Refresh(); } private void UserControl1_Resize(object sender, EventArgs e) { } private void pbImg_Resize(object sender, EventArgs e) { CalcMapSize(); this.pbImg.Refresh(); } private void vScrollBar1_ValueChanged(object sender, EventArgs e) { MapLocationY = vScrollBar1.Value; this.pbImg.Refresh(); } private void hScrollBar1_ValueChanged(object sender, EventArgs e) { MapLocationX = hScrollBar1.Value; this.pbImg.Refresh(); } private void 折叠层一ToolStripMenuItem_Click(object sender, EventArgs e) { foreach (UserRow u1 in Rows) { u1.Expand = false; } if (_selectRow != null && _selectRow.Level!= 0) { _selectRow.Expand = false; } CalcMapSize(); this.pbImg.Refresh(); } private void 折叠层二ToolStripMenuItem_Click(object sender, EventArgs e) { foreach (UserRow u1 in Rows) { foreach (UserRow u2 in u1.SubRows) { u2.Expand = false; //foreach (UserRow u3 in u2.SubRows) //{ //} } } if (_selectRow != null && _selectRow.Level > 1) { _selectRow.Expand = false; } CalcMapSize(); this.pbImg.Refresh(); } private void 展开层一ToolStripMenuItem_Click(object sender, EventArgs e) { foreach (UserRow u1 in Rows) { u1.Expand = true; } CalcMapSize(); this.pbImg.Refresh(); } private void 展开层二ToolStripMenuItem_Click(object sender, EventArgs e) { foreach (UserRow u1 in Rows) { u1.Expand = true; foreach (UserRow u2 in u1.SubRows) { u2.Expand = true; //foreach (UserRow u3 in u2.SubRows) //{ //} } } CalcMapSize(); this.pbImg.Refresh(); } } }
4 最后在FORM界面中使用
首先需要生成数据源
public List<UserRow> Rows = new List<UserRow>(); SortedList<int, UserRow> HeadColumns = new SortedList<int, UserRow>(); void BuildData() { for (int i = 0; i < 100; i++) { UserRow u1 = Packdata(0, i.ToString()); u1.Expand = true; for (int j = 0; j < 5; j++) { UserRow u2 = Packdata(1, i.ToString() + "-" + j.ToString()); u2.Parent = u1; u2.Expand = true; u1.SubRows.Add(u2); for (int k = 0; k < 200; k++) { UserRow u3 = Packdata(2, i.ToString() + "-" + j.ToString() + "-" + k.ToString()); u3.Parent = u2; u2.SubRows.Add(u3); } } Rows.Add(u1); } HeadColumns.Add(0, PackHead(0)); HeadColumns.Add(1, PackHead(1)); HeadColumns.Add(2, PackHead(2)); } UserRow Packdata(int level, string index) { UserRow u = new UserRow(level); for (int i = 0; i < 10; i++) { UserCell cell = new UserCell(); cell.Value = index + "-" + i.ToString(); u.Cells.Add(cell); } return u; } UserRow PackHead(int level) { UserRow u = new UserRow(level); for (int i = 0; i < 10; i++) { UserCell cell = new UserCell(); cell.Value = "列" + level + "-" + i; u.Cells.Add(cell); } return u; }
最后把控件添加到form窗体中
private void Form2_Load(object sender, EventArgs e) { BuildData(); UserControl1 uct = new UserControl1(Rows); uct.HeadColumns = HeadColumns; this.Controls.Add(uct); uct.Dock = DockStyle.Fill; }
DEMO代码虽然比较粗糙,但是不影响使用。
转载麻烦指明出处。。。