⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 listviewex.cs

📁 c#编写的仿OUTLOOK工具条的Winform菜单
💻 CS
📖 第 1 页 / 共 2 页
字号:
		internal bool Tracking
		{
			set { tracking = value; }
			get { return tracking; }
		}

		internal int PressedHeaderItem
		{
			set { pressedHeaderItem = value; }
			get { return pressedHeaderItem; }
		}
        
		internal Point LastMousePosition
		{
			set { lastMousePosition = value; }
			get { return lastMousePosition; }
		}

		public ImageList HeaderImageList
		{
			set { headerImageList = value; }
			get { return headerImageList; }
		}

		#endregion
		
		#region Methods
		public void SetHeaderIcon(int headerIndex, int iconIndex)
		{
			// Associate an specific header with an specific image index
			// in the headerImageList
			headerIconsList.Add(new HeaderIconHelper(headerIndex, iconIndex));
		}

		public void SetColumnSortFormat(int columnIndex, SortedListViewFormatType format)
		{
			rowSorterList.Add(new RowSorterHelper(columnIndex, format));
		}

		public void SetColumnSortFormat(int columnIndex, SortedListViewFormatType format, ListSortEvent callBack)
		{
			rowSorterList.Add(new RowSorterHelper(columnIndex, format, callBack));
		}
		
		public Rectangle GetHeaderItemRect(int index)
		{
			RECT rc = new RECT();
			WindowsAPI.SendMessage(hHeader, (int)HeaderControlMessages.HDM_GETITEMRECT, index, ref rc);
			return new Rectangle(rc.left, rc.top, rc.right-rc.left,rc.bottom-rc.top);
		}

		#endregion

		#region Implementation
		void PaintBackground(IntPtr hDC)
		{
			Graphics g = Graphics.FromHdc(hDC);
		
			if ( lastSortedColumn == -1 )
				return;
			Rectangle rc = GetSubItemRect(0, lastSortedColumn);
			if ( lastSortedColumn == 0 && Columns.Count > 1)
			{
				Rectangle rcSecondItem = GetSubItemRect(0, 1);
				rc = new Rectangle(rc.Left, rc.Top, rcSecondItem.Left - rc.Left, rc.Height);
			}
			g.FillRectangle(new SolidBrush(Color.FromArgb(247,247,247)), rc.Left, rc.Top, rc.Width, Height);
		}

		bool NotifyListCustomDraw(ref Message m)
		{
			m.Result = (IntPtr)CustomDrawReturnFlags.CDRF_DODEFAULT;
			NMCUSTOMDRAW nmcd = (NMCUSTOMDRAW)m.GetLParam(typeof(NMCUSTOMDRAW));
			IntPtr thisHandle = Handle;
			
			if ( nmcd.hdr.hwndFrom != Handle)
				return false;
			switch (nmcd.dwDrawStage)
			{
				case (int)CustomDrawDrawStateFlags.CDDS_PREPAINT:
					// Ask for Item painting notifications
					m.Result = (IntPtr)CustomDrawReturnFlags.CDRF_NOTIFYITEMDRAW;
					break;
				case (int)CustomDrawDrawStateFlags.CDDS_ITEMPREPAINT:
					m.Result = (IntPtr)CustomDrawReturnFlags.CDRF_NOTIFYSUBITEMDRAW;
					break;
				case (int)(CustomDrawDrawStateFlags.CDDS_ITEMPREPAINT | CustomDrawDrawStateFlags.CDDS_SUBITEM):
					// Draw background
					DoListCustomDrawing(ref m);
					break;
				default:
					break;

			}
			return false;
		}

		void DoListCustomDrawing(ref Message m)
		{
			NMLVCUSTOMDRAW lvcd = (NMLVCUSTOMDRAW)m.GetLParam(typeof(NMLVCUSTOMDRAW));
			int row = lvcd.nmcd.dwItemSpec;
			int col = lvcd.iSubItem;

			// If we don't have any items we must be doing something wrong
			// because the list is only going to request custom drawing of items
			// in the list, if we have items in the list, the Items cannot possibly
			// be zero
			Debug.Assert(Items.Count != 0);
            ListViewItem lvi = Items[row];
			Rectangle rc = GetSubItemRect(row, col);
			
			// Draw the item
			// We did not need to actually paint the items that are not selected
			// but doing all the painting ourselves eliminates some random bugs where
			// the list sometimes did not update a subitem  that was not selected anymore
			// leaving the subitem with a different background color 
			// than the rest of the row
			Graphics g = Graphics.FromHdc(lvcd.nmcd.hdc);
						
			// Draw Fill Rectangle
			if ( IsRowSelected(row) )
			{
				int subItemOffset = 2;
				if ( GridLines )
				{
					subItemOffset = 3;
				}
						
				g.FillRectangle(new SolidBrush(ColorUtil.VSNetSelectionColor), rc.Left+1, rc.Top+1, rc.Width-2, rc.Height-subItemOffset);
				
				// Draw Border
				if ( col == 0 )
				{
					Color borderColor = SystemColors.Highlight;
					int heightOffset = 1;
					if ( GridLines )
						heightOffset = 2;
					g.DrawRectangle(new Pen(borderColor), rc.Left+1, rc.Top, rc.Width-2, rc.Height-heightOffset);
				}
			}
			else 
			{
				if ( col == lastSortedColumn )
				{
					if ( col == 0)
						rc = AdjustFirstItemRect(rc);
					g.FillRectangle(new SolidBrush(Color.FromArgb(247,247,247)), rc.Left, rc.Top, rc.Width, rc.Height);
				}
				else 
				{
					if ( col == 0)
						rc = AdjustFirstItemRect(rc);
					g.FillRectangle(new SolidBrush(SystemColors.Window), rc.Left, rc.Top, rc.Width, rc.Height);
				}
			}

			// Adjust rectangle, when getting the rectangle for column zero
			// the rectangle return is the one for the whole control
			if ( col == 0)
			{
				rc = AdjustFirstItemRect(rc);
			}
							
			// Draw Text
			string text = GetSubItemText(row, col);
			Size textSize = TextUtil.GetTextSize(g, text, Font);
			int gap = 4;
			Point pos = new Point(rc.Left+gap ,rc.Top + ((rc.Height - textSize.Height) / 2));
			
			// I use the Windows API instead of the Graphics object to draw the string
			// because the Graphics object draws ellipes without living blank spaces in between
			// the DrawText API adds those blank spaces in between 
			int ellipsingTringgering = 8;
			
			if ( CheckBoxes && col == 0 )
			{
				// draw checkbox
				int checkIndex = 0;
				if ( lvi.Checked)
					checkIndex = 1;
				g.DrawImage(checkBoxesImageList.Images[checkIndex], rc.Left + gap, rc.Top);
				pos.X += IMAGE_WIDTH;
				rc.Width = rc.Width - IMAGE_WIDTH; 
			}
			else if ( col == 0 && CheckBoxes == false && lvi.ImageIndex != -1 && lvi.ImageList != null )
			{
				ImageList imageList = lvi.ImageList;
				Image image = imageList.Images[lvi.ImageIndex];
				if ( image != null )
				{
					g.DrawImage(imageList.Images[lvi.ImageIndex], rc.Left + gap, rc.Top);
					pos.X += IMAGE_WIDTH;
					rc.Width = rc.Width - IMAGE_WIDTH; 
				}
			}

			Rectangle drawRect = new Rectangle(pos.X+2, pos.Y, rc.Width-gap-ellipsingTringgering, rc.Height);
			TextUtil.DrawText(g, text, Font, drawRect);
			g.Dispose();
			
			// Put structure back in the message
			Marshal.StructureToPtr(lvcd, m.LParam, true);
			m.Result = 	(IntPtr)CustomDrawReturnFlags.CDRF_SKIPDEFAULT;

		}

		Rectangle AdjustFirstItemRect(Rectangle rc)
		{
			ListViewItem lvi = Items[0];
			if ( Columns.Count > 1)
			{
				Debug.WriteLine(rc);
				Rectangle rcSecondItem = GetSubItemRect(0, 1);
				Rectangle RC = new Rectangle(rc.Left, rc.Top, rcSecondItem.Left - rc.Left, rc.Height);
				return RC;
			}
			else 
			{
				return rc;
			}
		}
		
		bool NotifyHeaderCustomDraw(ref Message m)
		{
			m.Result = (IntPtr)CustomDrawReturnFlags.CDRF_DODEFAULT;
			NMCUSTOMDRAW nmcd = (NMCUSTOMDRAW)m.GetLParam(typeof(NMCUSTOMDRAW));
			if ( nmcd.hdr.hwndFrom != hHeader)
				return false;
			
			switch (nmcd.dwDrawStage)
			{
				case (int)CustomDrawDrawStateFlags.CDDS_PREPAINT:
					// Ask for Item painting notifications
					m.Result = (IntPtr)CustomDrawReturnFlags.CDRF_NOTIFYITEMDRAW;
					break;
				case (int)CustomDrawDrawStateFlags.CDDS_ITEMPREPAINT:
					DoHeaderCustomDrawing(ref m);			
					break;
				case (int)NotificationMessages.NM_NCHITTEST:
					break;
				default:
					break;

			}
			return false;
		}

		void DoHeaderCustomDrawing(ref Message m)
		{
			NMCUSTOMDRAW nmcd = (NMCUSTOMDRAW)m.GetLParam(typeof(NMCUSTOMDRAW));
			Graphics g = Graphics.FromHdc(nmcd.hdc);
			
			Rectangle rc = GetHeaderCtrlRect();
			rc = GetHeaderItemRect(nmcd.dwItemSpec);
			int itemRight = rc.Left + rc.Width;
			g.FillRectangle(new SolidBrush(SystemColors.ScrollBar), rc.Left, rc.Top, rc.Width, rc.Height);
		
			if ( nmcd.dwItemSpec == PressedHeaderItem && !IsCursorOnDivider() && Tracking == false )
			{
				PressedHeaderItem = -1;
				rc.Inflate(-1, -1);
				g.FillRectangle(new SolidBrush(ColorUtil.VSNetPressedColor), rc.Left, rc.Top, rc.Width, rc.Height);

			}
			else
			{
				ControlPaint.DrawBorder3D(g, rc.Left, rc.Top, rc.Width, 
					rc.Height-1, Border3DStyle.RaisedInner, Border3DSide.All);
			}

			string text = GetHeaderItemText(nmcd.dwItemSpec);
			Debug.WriteLine(text);
			Size textSize = TextUtil.GetTextSize(g, text, Font);
			int gap = 4;
			Point pos = new Point(rc.Left+gap ,rc.Top + ((rc.Height - textSize.Height) / 2));

			int headerImageIndex;
			if ( headerIconsList != null && HasHeaderImage(nmcd.dwItemSpec, out headerImageIndex) )
			{
				if ( headerImageIndex != -1 )
				{
					Image image = headerImageList.Images[headerImageIndex];
					if ( image != null )
					{
						g.DrawImage(headerImageList.Images[headerImageIndex], rc.Left + gap, rc.Top);
						pos.X += IMAGE_WIDTH;
						rc.Width = rc.Width - IMAGE_WIDTH;
					}
				}
			}
			
			// Draw arrow glyph
			if ( nmcd.dwItemSpec == lastSortedColumn)
			{
				int Left = pos.X+2;
				Left += textSize.Width + TEXT_TO_ARROW_GAP;
				Rectangle arrowRect = new Rectangle(Left, rc.Top, ARROW_WIDTH, rc.Height); 
				if ( itemRight >= (Left + ARROW_WIDTH + 4) ) 
				{
					if ( Sorting == SortOrder.Ascending  || Sorting == SortOrder.None )
						DrawUpArrow(g, arrowRect);
					else
						DrawDownArrow(g, arrowRect);
				}
			}

            // I use the Windows API instead of the Graphics object to draw the string
			// because the Graphics object draws ellipes without living blank spaces in between
			// the DrawText API adds those blank spaces in between 
			int ellipsingTringgering = 8;
			Rectangle drawRect = new Rectangle(pos.X+2, pos.Y, rc.Width-gap-ellipsingTringgering, rc.Height);
			TextUtil.DrawText(g, text, Font, drawRect);
			g.Dispose();
          				
			m.Result = 	(IntPtr)CustomDrawReturnFlags.CDRF_SKIPDEFAULT;
          
		}

		bool HasHeaderImage(int headerIndex, out int imageIndex)
		{
			imageIndex = -1;
			for ( int i = 0; i < headerIconsList.Count; i++ )
			{
				HeaderIconHelper hih = (HeaderIconHelper)headerIconsList[i];
				if ( hih != null && hih.HeaderIndex == headerIndex )
				{
					imageIndex = hih.IconIndex;
					return true;
				}
			}

			return false;

		}

		void DrawUpArrow(Graphics g, Rectangle rc)
		{
			int xTop = rc.Left + rc.Width/2;
			int yTop = (rc.Height - 6)/2;
            
			int xLeft = xTop - 6;
			int yLeft = yTop + 6;
            
			int xRight = xTop + 6;
			int yRight = yTop + 6;

			g.DrawLine(new Pen(SystemColors.ControlDarkDark), xLeft, yLeft, xTop, yTop);
			g.DrawLine(new Pen(Color.White), xRight, yRight, xTop, yTop);
			g.DrawLine(new Pen(Color.White), xLeft, yLeft, xRight, yRight);

		}

		void DrawDownArrow(Graphics g, Rectangle rc)
		{
			int xBottom = rc.Left + rc.Width/2;
			            
			int xLeft = xBottom - 6;
			int yLeft = (rc.Height - 6)/2;;
            
			int xRight = xBottom + 6;
			int yRight = (rc.Height - 6)/2;

			int yBottom = yRight + 6;

			g.DrawLine(new Pen(SystemColors.ControlDarkDark), xLeft, yLeft, xBottom, yBottom);
			g.DrawLine(new Pen(Color.White), xRight, yRight, xBottom, yBottom);
			g.DrawLine(new Pen(SystemColors.ControlDarkDark), xLeft, yLeft, xRight, yRight);

		}
       
		string GetSubItemText(int row, int col)
		{
			// I am going to use the Windows API since using the .NET
			// ListViewSubItem.Text property is causing the nasty side
			// effect of changing the text when I draw the string using TextUtil.DrawText,
			// even though that is not my intention at all.
			// I am not sure about why this is happening but using the API solves the problem
			LVITEM lvi = new LVITEM();
			lvi.iItem = row;
			lvi.mask = (int)ListViewItemFlags.LVIF_TEXT;
			lvi.iSubItem = col;
			lvi.cchTextMax = BUFFER_SIZE;
			lvi.pszText = Marshal.AllocHGlobal(BUFFER_SIZE);
			WindowsAPI.SendMessage(Handle, (int)ListViewMessages.LVM_GETITEMTEXTW, row, ref lvi);
			string text = Marshal.PtrToStringAuto(lvi.pszText);
			return text;
		}

		Rectangle GetSubItemRect(int row, int col)
		{
			RECT rc = new RECT();
			rc.top = col;
			rc.left = (int)SubItemPortion.LVIR_BOUNDS;
			WindowsAPI.SendMessage(Handle, (int)ListViewMessages.LVM_GETSUBITEMRECT,  row, ref rc);
			return new Rectangle(rc.left, rc.top, rc.right-rc.left, rc.bottom-rc.top);
		}

		bool IsRowSelected(int row)
		{
			Debug.Assert(row >= 0 && row < Items.Count);
			ListViewItem lvi = Items[row];
			return lvi.Selected;
		}

		Rectangle GetHeaderCtrlRect()
		{
			RECT rc = new RECT();
			WindowsAPI.GetClientRect(hHeader, ref rc);
			return new Rectangle(rc.left, rc.top, rc.right-rc.left,rc.bottom-rc.top);
		
		}

		protected bool IsCursorOnDivider()
		{
			HD_HITTESTINFO hti = new HD_HITTESTINFO();
			hti.pt.x = LastMousePosition.X;
			hti.pt.y = LastMousePosition.Y;
			WindowsAPI.SendMessage(hHeader, (int)HeaderControlMessages.HDM_HITTEST, 0, ref hti); 
			bool hit = (hti.flags == (int)HeaderControlHitTestFlags.HHT_ONDIVIDER);
			return hit;
		}

		protected string GetHeaderItemText(int index)
		{
			// I get the bug that I get on the ListView if 
			// I use the columns collection to retreive the text
			// That's why I prefer to use the Windows API

			HDITEM hdi = new HDITEM();
			hdi.mask = (int)HeaderItemFlags.HDI_TEXT;
			hdi.cchTextMax =  BUFFER_SIZE;
			hdi.pszText = Marshal.AllocHGlobal(BUFFER_SIZE);
            WindowsAPI.SendMessage(hHeader, (int)HeaderControlMessages.HDM_GETITEMW, index, ref hdi);
			string text = Marshal.PtrToStringAuto(hdi.pszText);
			return text;
		}

		internal RowSorterHelper GetRowSorterHelper()
		{
			for ( int i = 0; i < rowSorterList.Count; i++ )
			{
				RowSorterHelper rs = (RowSorterHelper)rowSorterList[i];
				if ( rs != null && rs.ColumnIndex == LastSortedColumn )
				{
					return rs;
				}
			}
			return null;
		}

		#endregion
	}
	
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -