• <fieldset id="8imwq"><menu id="8imwq"></menu></fieldset>
  • <bdo id="8imwq"><input id="8imwq"></input></bdo>
    最新文章專題視頻專題問答1問答10問答100問答1000問答2000關鍵字專題1關鍵字專題50關鍵字專題500關鍵字專題1500TAG最新視頻文章推薦1 推薦3 推薦5 推薦7 推薦9 推薦11 推薦13 推薦15 推薦17 推薦19 推薦21 推薦23 推薦25 推薦27 推薦29 推薦31 推薦33 推薦35 推薦37視頻文章20視頻文章30視頻文章40視頻文章50視頻文章60 視頻文章70視頻文章80視頻文章90視頻文章100視頻文章120視頻文章140 視頻2關鍵字專題關鍵字專題tag2tag3文章專題文章專題2文章索引1文章索引2文章索引3文章索引4文章索引5123456789101112131415文章專題3
    問答文章1 問答文章501 問答文章1001 問答文章1501 問答文章2001 問答文章2501 問答文章3001 問答文章3501 問答文章4001 問答文章4501 問答文章5001 問答文章5501 問答文章6001 問答文章6501 問答文章7001 問答文章7501 問答文章8001 問答文章8501 問答文章9001 問答文章9501
    當前位置: 首頁 - 科技 - 知識百科 - 正文

    DataGridView多維表頭的實現方法

    來源:懂視網 責編:小采 時間:2020-11-27 22:41:17
    文檔

    DataGridView多維表頭的實現方法

    DataGridView多維表頭的實現方法:背景 對于.NET 原本提供的DataGridView控件,制作成如下形式的表格是毫無壓力的。 但是如果把表格改了一下,變成如下形式 傳統的DataGridView就做不到了,如果擴展一下還是行的,有不少網友也擴展了DataGridView控件,不過有些也只能制作出二維的表頭。或者
    推薦度:
    導讀DataGridView多維表頭的實現方法:背景 對于.NET 原本提供的DataGridView控件,制作成如下形式的表格是毫無壓力的。 但是如果把表格改了一下,變成如下形式 傳統的DataGridView就做不到了,如果擴展一下還是行的,有不少網友也擴展了DataGridView控件,不過有些也只能制作出二維的表頭。或者

    背景

    對于.NET 原本提供的DataGridView控件,制作成如下形式的表格是毫無壓力的。

    但是如果把表格改了一下,變成如下形式

    傳統的DataGridView就做不到了,如果擴展一下還是行的,有不少網友也擴展了DataGridView控件,不過有些也只能制作出二維的表頭。或者使用第三方的控件,之前也用過DevExpress的BoundGridView。不過在沒有可使用的第三方控件的情況下,做到下面的效果,就有點麻煩了。

    那得自己擴展了,不過最后還是用了一個控件庫的報表控件,Telerik的Reporting。不過我自己還是擴展了DataGridView,使之能制作出上面的報表。

    準備

    學習了一些網友的代碼,原來制作這個多維表頭都是利用GDI+對DataGirdView的表頭進行重繪。

    用到的方法包括

    Graphics.FillRectangle //填充一個矩形

    Graphics.DrawLine //畫一條線

    Graphics.DrawString  //寫字符串

    此外為了方便組織表頭,本人還定義了一個表頭的數據結構 HeaderItem 和 HeaderCollection 分別作為每個表頭單元格的數據實體和整個表頭的集合。

    HeaderItem的定義如下

    代碼如下:
    public class HeaderItem
         {
             private int _startX;//起始橫坐標
             private int _startY;//起始縱坐標
             private int _endX; //終止橫坐標
             private int _endY; //終止縱坐標
             private bool _baseHeader; //是否基礎表頭

             public HeaderItem(int startX, int endX, int startY, int endY, string content)
             {
                 this._endX = endX;
                 this._endY = endY;
                 this._startX = startX;
                 this._startY = startY;
                 this.Content = content;
             }

             public HeaderItem(int x, int y, string content):this(x,x,y,y,content)
             {

             }

             public HeaderItem()
             {

             }

             public static HeaderItem CreateBaseHeader(int x,int y,string content)
             {
                 HeaderItem header = new HeaderItem();
                 header._endX= header._startX = x;
                 header._endY= header._startY = y;
                 header._baseHeader = true;
                 header.Content = content;
                 return header;
             }

             public int StartX
             {
                 get { return _startX; }
                 set
                 {
                     if (value > _endX)
                     {
                         _startX = _endX;
                         return;
                     }
                     if (value < 0) _startX = 0;
                     else _startX = value;
                 }
             }

             public int StartY
             {
                 get { return _startY; }
                 set
                 {
                     if (_baseHeader)
                     {
                         _startY = 0;
                         return;
                     }
                     if (value > _endY)
                     {
                         _startY = _endY;
                         return;
                     }
                     if (value < 0) _startY = 0;
                     else _startY = value;
                 }
             }

             public int EndX
             {
                 get { return _endX; }
                 set
                 {
                     if (_baseHeader)
                     {
                         _endX = _startX;
                         return;
                     }
                     if (value < _startX)
                     {
                         _endX = _startX;
                         return;
                     }
                     _endX = value;
                 }
             }

             public int EndY
             {
                 get { return _endY; }
                 set
                 {
                     if (value < _startY)
                     {
                         _endY = _startY;
                         return;
                     }
                     _endY = value;
                 }
             }

             public bool IsBaseHeader
             {get{ return _baseHeader;} }

             public string Content { get; set; }
         }

    設計思想是利用數學的直角坐標系,給每個表頭單元格定位并劃定其大小。與計算機顯示的坐標定位不同,這里的原點是跟數學的一樣放在左下角,X軸正方向是水平向右,Y軸正方向是垂直向上。如下圖所示

    之所以要對GridView中原始的列頭進行特別處理,是因為這里的起止坐標和終止坐標都可以設置,而原始列頭的起始縱坐標(StartY)只能是0,終止橫坐標(EndX)必須與起始橫坐標(StartY)相等。

    另外所有列頭單元格的集合HeaderCollection的定義如下
    代碼如下:
    public class HeaderCollection
         {
             private List<HeaderItem> _headerList;
             private bool _iniLock;

             public DataGridViewColumnCollection BindCollection{get;set;}

             public HeaderCollection(DataGridViewColumnCollection cols)
             {
                 _headerList = new List<HeaderItem>();
                 BindCollection=cols;
                 _iniLock = false;
             }

             public int GetHeaderLevels()
             {
                 int max = 0;
                 foreach (HeaderItem item in _headerList)
                     if (item.EndY > max)
                         max = item.EndY;

                 return max;
             }

             public List<HeaderItem> GetBaseHeaders()
             {
                 List<HeaderItem> list = new List<HeaderItem>();
                 foreach (HeaderItem item in _headerList)
                     if (item.IsBaseHeader) list.Add(item);
                 return list;
             }

             public HeaderItem GetHeaderByLocation(int x, int y)
             {
                 if (!_iniLock) InitHeader();
                 HeaderItem result=null;
                 List<HeaderItem> temp = new List<HeaderItem>();
                 foreach (HeaderItem item in _headerList)
                     if (item.StartX <= x && item.EndX >= x)
                         temp.Add(item);
                 foreach (HeaderItem item in temp)
                     if (item.StartY <= y && item.EndY >= y)
                         result = item;

                 return result;
             }

             public IEnumerator GetHeaderEnumer()
             {
                 return _headerList.GetEnumerator();
             }

             public void AddHeader(HeaderItem header)
             {
                 this._headerList.Add(header);
             }

             public void AddHeader(int startX, int endX, int startY, int endY, string content)
             {
                 this._headerList.Add(new HeaderItem(startX,endX,startY,endY,content));
             }

             public void AddHeader(int x, int y, string content)
             {
                 this._headerList.Add(new HeaderItem(x, y, content));
             }

             public void RemoveHeader(HeaderItem header)
             {
                 this._headerList.Remove(header);
             }

             public void RemoveHeader(int x, int y)
             {
                HeaderItem header= GetHeaderByLocation(x, y);
                if (header != null) RemoveHeader(header);
             }

             private void InitHeader()
             {
                 _iniLock = true;
                 for (int i = 0; i < this.BindCollection.Count; i++)
                     if(this.GetHeaderByLocation(i,0)==null)
                     this._headerList.Add(HeaderItem.CreateBaseHeader(i,0 , this.BindCollection[i].HeaderText));
                 _iniLock = false;
             }
         }

    這里仿照了.NET Frameword的Collection那樣定義了Add方法和Remove方法,此外說明一下那個 GetHeaderByLocation 方法,這個方法可以通過給定的坐標獲取那個坐標的HeaderItem。這個坐標是忽略了整個表頭合并單元格的情況,例如

    上面這幅圖,如果輸入0,0 返回的是灰色區域,輸入2,1 或3,2 或 5,1返回的都是橙色的區域。

    擴展控件

    到真正擴展控件了,最核心的是重寫 OnCellPainting 方法,這個其實是與表格單元格重繪時觸發事件綁定的方法,通過參數 DataGridViewCellPaintingEventArgs 的 ColumnIndex 和 RowIndex 屬性可以知道當前重繪的是哪個單元格,于是就通過HeaderCollection獲取要繪制的表頭單元格的信息進行重繪,對已經重繪的單元格會進行標記,以防重復繪制。
    代碼如下:
    protected override void OnCellPainting(DataGridViewCellPaintingEventArgs e)
             {
                 if (e.ColumnIndex == -1 || e.RowIndex != -1)
                 {
                     base.OnCellPainting(e);
                     return;
                 }
                 int lev=this.Headers.GetHeaderLevels();
                 this.ColumnHeadersHeight = (lev + 1) * _baseColumnHeadHeight;
                 for (int i = 0; i <= lev; i++)
                 {
                     HeaderItem tempHeader= this.Headers.GetHeaderByLocation(e.ColumnIndex, i);
                     if (tempHeader==null|| i != tempHeader.EndY || e.ColumnIndex != tempHeader.StartX) continue;
                     DrawHeader(tempHeader, e);
                 }
                 e.Handled = true;
             }

    上面的代碼中,最初是先判斷當前要重繪的單元格是不是表頭部分,如果不是則調用原本的OnCellPainting方法。 e.Handled=true; 比較關鍵,有了這句代碼,重繪才能生效。

    繪制單元格的過程封裝在方法DrawHeader里面

    代碼如下:
    private void DrawHeader(HeaderItem item,DataGridViewCellPaintingEventArgs e)
             {
                 if (this.ColumnHeadersHeightSizeMode != DataGridViewColumnHeadersHeightSizeMode.DisableResizing)
                     this.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.DisableResizing;
                 int lev=this.Headers.GetHeaderLevels();
                 lev=(lev-item.EndY)*_baseColumnHeadHeight;

                 SolidBrush backgroundBrush = new SolidBrush(e.CellStyle.BackColor);
                 SolidBrush lineBrush = new SolidBrush(this.GridColor);
                 Pen linePen = new Pen(lineBrush);
                 StringFormat foramt = new StringFormat();
                 foramt.Alignment = StringAlignment.Center;
                 foramt.LineAlignment = StringAlignment.Center;

                 Rectangle headRec = new Rectangle(e.CellBounds.Left, lev, ComputeWidth(item.StartX, item.EndX)-1, ComputeHeight(item.StartY, item.EndY)-1);
                 e.Graphics.FillRectangle(backgroundBrush, headRec);
                 e.Graphics.DrawLine(linePen, headRec.Left, headRec.Bottom, headRec.Right, headRec.Bottom);
                 e.Graphics.DrawLine(linePen, headRec.Right, headRec.Top, headRec.Right, headRec.Bottom);
                 e.Graphics.DrawString(item.Content, this.ColumnHeadersDefaultCellStyle.Font, Brushes.Black,headRec, foramt);
             }

    填充矩形時,記得要給矩形的常和寬減去一個像素,這樣才不會與相鄰的矩形重疊區域導致矩形的邊線顯示不出來。還有這里的要設置 ColumnHeadersHeightSizeMode 屬性,如果不把它設成 DisableResizing ,那么表頭的高度是改變不了的,這樣即使設置了二維,三維,n維,最終只是一維。

    這里用到的一些輔助方法如下,分別是通過坐標計算出高度和寬度。

    代碼如下:
    private int ComputeWidth(int startX, int endX)
             {
                 int width = 0;
                 for (int i = startX; i <= endX; i++)
                     width+= this.Columns[i].Width;
                 return width;
             }

             private int ComputeHeight(int startY, int endY)
             {
                 return _baseColumnHeadHeight * (endY - startY+1);
             }

    給一段使用的實例代碼,這里要預先給DataGridView每一列設好綁定的字段,否則自動添加的列是做不出效果來的。

    代碼如下:
    HeaderItem item= this.boundGridView1.Headers.GetHeaderByLocation(0, 0);
                 item.EndY = 2;
                 item = this.boundGridView1.Headers.GetHeaderByLocation(9,0 );
                 item.EndY = 2;
                 item = this.boundGridView1.Headers.GetHeaderByLocation(10, 0);
                 item.EndY = 2;
                 item = this.boundGridView1.Headers.GetHeaderByLocation(11, 0);
                 item.EndY = 2;

                 this.boundGridView1.Headers.AddHeader(1, 2, 1, 1, "語文");
                 this.boundGridView1.Headers.AddHeader(3, 4, 1, 1, "數學");
                 this.boundGridView1.Headers.AddHeader(5, 6, 1, 1, "英語");
                 this.boundGridView1.Headers.AddHeader(7, 8, 1, 1, "X科");
                 this.boundGridView1.Headers.AddHeader(1, 8, 2, 2, "成績");

    效果圖如下所示

     

    總的來說自我感覺有點小題大做,但想不出有什么更好的辦法,各位如果覺得以上說的有什么不好的,歡迎拍磚;如果發現以上有什么說錯了,懇請批評指正;如果覺得好的,請支持一下。謝謝!最后附上整個控件的源碼
    控件的完整代碼
    代碼如下:

         public class BoundGridView : DataGridView
         {
             private int _baseColumnHeadHeight;

             public BoundGridView():base()
             {
                 this.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.DisableResizing;
                 _baseColumnHeadHeight = this.ColumnHeadersHeight;
                 this.Headers = new HeaderCollection(this.Columns);
             }

             public HeaderCollection Headers{ get;private set; }

             protected override void OnCellPainting(DataGridViewCellPaintingEventArgs e)
             {
                 if (e.ColumnIndex == -1 || e.RowIndex != -1)
                 {
                     base.OnCellPainting(e);
                     return;
                 }
                 int lev=this.Headers.GetHeaderLevels();
                 this.ColumnHeadersHeight = (lev + 1) * _baseColumnHeadHeight;
                 for (int i = 0; i <= lev; i++)
                 {
                     HeaderItem tempHeader= this.Headers.GetHeaderByLocation(e.ColumnIndex, i);
                     if (tempHeader==null|| i != tempHeader.EndY || e.ColumnIndex != tempHeader.StartX) continue;
                     DrawHeader(tempHeader, e);
                 }
                 e.Handled = true;
             }

             private int ComputeWidth(int startX, int endX)
             {
                 int width = 0;
                 for (int i = startX; i <= endX; i++)
                     width+= this.Columns[i].Width;
                 return width;
             }

             private int ComputeHeight(int startY, int endY)
             {
                 return _baseColumnHeadHeight * (endY - startY+1);
             }

             private void DrawHeader(HeaderItem item,DataGridViewCellPaintingEventArgs e)
             {
                 if (this.ColumnHeadersHeightSizeMode != DataGridViewColumnHeadersHeightSizeMode.DisableResizing)
                     this.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.DisableResizing;
                 int lev=this.Headers.GetHeaderLevels();
                 lev=(lev-item.EndY)*_baseColumnHeadHeight;

                 SolidBrush backgroundBrush = new SolidBrush(e.CellStyle.BackColor);
                 SolidBrush lineBrush = new SolidBrush(this.GridColor);
                 Pen linePen = new Pen(lineBrush);
                 StringFormat foramt = new StringFormat();
                 foramt.Alignment = StringAlignment.Center;
                 foramt.LineAlignment = StringAlignment.Center;

                 Rectangle headRec = new Rectangle(e.CellBounds.Left, lev, ComputeWidth(item.StartX, item.EndX)-1, ComputeHeight(item.StartY, item.EndY)-1);
                 e.Graphics.FillRectangle(backgroundBrush, headRec);
                 e.Graphics.DrawLine(linePen, headRec.Left, headRec.Bottom, headRec.Right, headRec.Bottom);
                 e.Graphics.DrawLine(linePen, headRec.Right, headRec.Top, headRec.Right, headRec.Bottom);
                 e.Graphics.DrawString(item.Content, this.ColumnHeadersDefaultCellStyle.Font, Brushes.Black,headRec, foramt);
             }
         }

         public class HeaderItem
         {
             private int _startX;
             private int _startY;
             private int _endX;
             private int _endY;
             private bool _baseHeader;

             public HeaderItem(int startX, int endX, int startY, int endY, string content)
             {
                 this._endX = endX;
                 this._endY = endY;
                 this._startX = startX;
                 this._startY = startY;
                 this.Content = content;
             }

             public HeaderItem(int x, int y, string content):this(x,x,y,y,content)
             {

             }

             public HeaderItem()
             {

             }

             public static HeaderItem CreateBaseHeader(int x,int y,string content)
             {
                 HeaderItem header = new HeaderItem();
                 header._endX= header._startX = x;
                 header._endY= header._startY = y;
                 header._baseHeader = true;
                 header.Content = content;
                 return header;
             }

             public int StartX
             {
                 get { return _startX; }
                 set
                 {
                     if (value > _endX)
                     {
                         _startX = _endX;
                         return;
                     }
                     if (value < 0) _startX = 0;
                     else _startX = value;
                 }
             }

             public int StartY
             {
                 get { return _startY; }
                 set
                 {
                     if (_baseHeader)
                     {
                         _startY = 0;
                         return;
                     }
                     if (value > _endY)
                     {
                         _startY = _endY;
                         return;
                     }
                     if (value < 0) _startY = 0;
                     else _startY = value;
                 }
             }

             public int EndX
             {
                 get { return _endX; }
                 set
                 {
                     if (_baseHeader)
                     {
                         _endX = _startX;
                         return;
                     }
                     if (value < _startX)
                     {
                         _endX = _startX;
                         return;
                     }
                     _endX = value;
                 }
             }

             public int EndY
             {
                 get { return _endY; }
                 set
                 {
                     if (value < _startY)
                     {
                         _endY = _startY;
                         return;
                     }
                     _endY = value;
                 }
             }

             public bool IsBaseHeader
             {get{ return _baseHeader;} }

             public string Content { get; set; }
         }

         public class HeaderCollection
         {
             private List<HeaderItem> _headerList;
             private bool _iniLock;

             public DataGridViewColumnCollection BindCollection{get;set;}

             public HeaderCollection(DataGridViewColumnCollection cols)
             {
                 _headerList = new List<HeaderItem>();
                 BindCollection=cols;
                 _iniLock = false;
             }

             public int GetHeaderLevels()
             {
                 int max = 0;
                 foreach (HeaderItem item in _headerList)
                     if (item.EndY > max)
                         max = item.EndY;

                 return max;
             }

             public List<HeaderItem> GetBaseHeaders()
             {
                 List<HeaderItem> list = new List<HeaderItem>();
                 foreach (HeaderItem item in _headerList)
                     if (item.IsBaseHeader) list.Add(item);
                 return list;
             }

             public HeaderItem GetHeaderByLocation(int x, int y)
             {
                 if (!_iniLock) InitHeader();
                 HeaderItem result=null;
                 List<HeaderItem> temp = new List<HeaderItem>();
                 foreach (HeaderItem item in _headerList)
                     if (item.StartX <= x && item.EndX >= x)
                         temp.Add(item);
                 foreach (HeaderItem item in temp)
                     if (item.StartY <= y && item.EndY >= y)
                         result = item;

                 return result;
             }

             public IEnumerator GetHeaderEnumer()
             {
                 return _headerList.GetEnumerator();
             }

             public void AddHeader(HeaderItem header)
             {
                 this._headerList.Add(header);
             }

             public void AddHeader(int startX, int endX, int startY, int endY, string content)
             {
                 this._headerList.Add(new HeaderItem(startX,endX,startY,endY,content));
             }

             public void AddHeader(int x, int y, string content)
             {
                 this._headerList.Add(new HeaderItem(x, y, content));
             }

             public void RemoveHeader(HeaderItem header)
             {
                 this._headerList.Remove(header);
             }

             public void RemoveHeader(int x, int y)
             {
                HeaderItem header= GetHeaderByLocation(x, y);
                if (header != null) RemoveHeader(header);
             }

             private void InitHeader()
             {
                 _iniLock = true;
                 for (int i = 0; i < this.BindCollection.Count; i++)
                     if(this.GetHeaderByLocation(i,0)==null)
                     this._headerList.Add(HeaderItem.CreateBaseHeader(i,0 , this.BindCollection[i].HeaderText));
                 _iniLock = false;
             }
         }

    聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com

    文檔

    DataGridView多維表頭的實現方法

    DataGridView多維表頭的實現方法:背景 對于.NET 原本提供的DataGridView控件,制作成如下形式的表格是毫無壓力的。 但是如果把表格改了一下,變成如下形式 傳統的DataGridView就做不到了,如果擴展一下還是行的,有不少網友也擴展了DataGridView控件,不過有些也只能制作出二維的表頭。或者
    推薦度:
    標簽: 的方法 實現 表頭
    • 熱門焦點

    最新推薦

    猜你喜歡

    熱門推薦

    專題
    Top
    主站蜘蛛池模板: 久久精品99久久香蕉国产色戒| 国精品午夜福利视频不卡| 日韩精品人妻系列无码专区 | 日韩精品久久无码人妻中文字幕 | 2021最新国产精品一区| 国内精品伊人久久久久AV影院 | 国产精品禁18久久久夂久| 亚洲精品国产综合久久一线| 成人国内精品久久久久影院VR| 国产精品美女久久久久| 欧美jizzhd精品欧美| 欧美日韩精品在线观看| 精品国产午夜福利在线观看| 2021国产成人精品久久| 一区二区三区日韩精品| 久久精品国产91久久麻豆自制| 97久久久精品综合88久久| 国产精品禁18久久久夂久| 国产成人精品2021| 国产精品女同一区二区| 99精品国产高清一区二区麻豆 | 精品无码av一区二区三区| 日韩精品一区二区亚洲AV观看| 中国国产精品| 亚洲国产精品一区二区九九| 亚洲av午夜成人片精品电影| 亚洲国产精品嫩草影院久久 | 久久青青草原精品国产不卡| 精品久久久久久无码免费| 国产在线精品一区二区高清不卡 | 国产精品美女久久久m| 51午夜精品免费视频| 992tv精品视频tv在线观看| 99re久久精品国产首页2020| 国产精品免费AV片在线观看| 国产福利91精品一区二区| 91在线视频精品| 久久精品国产亚洲综合色| 青草青草久热精品视频在线网站| 久久久久国产精品| 宅男宅女精品国产AV天堂|