介绍 本文展示了如何使用GDI+创建自定义窗口。 背景 不久前,我有一个任务,为MS PPTViewer应用程序创建一个菜单,该菜单允许用户使用菜单项切换幻灯片。我决定使用User32.dll库中的WinAPI函数“UpdateLayeredWindow”创建一个菜单。这个函数允许在屏幕上绘制任何32位的图像。对于这个菜单,我实现了一个想要共享的库。 使用的代码 要创建自定义窗口,您应该继承TransparetWindow类并覆盖DrawPolygons方法。这个方法应该返回一个IPolygons的集合。这样做是为了在所有的计算之后绘制多边形。 隐藏,复制Code
public interface IPolygon { void Draw(Graphics g); }
transparent twindow类包含System.Windows中的一些事件和属性。表单名称: 隐藏,复制Code
public event EventHandler LocationChanged; public event EventHandler Move; public event MouseEventHandler MouseDown; public event MouseEventHandler MouseUp; public event MouseEventHandler MouseMove; public event EventHandler MouseEnter; public event EventHandler MouseLeave; public Brush Background { get; set; } public CornerRadius CornerRadius{ get; set; } public int AlphaChannel{ get; set; } public Size Size{ get; set; } public Point Location{ get; set; }
最有趣的性质是核半径。如果你设置了这个属性,你会得到一个圆角的窗口。代码如下: 隐藏,复制Code
TransparentWindow window = new TransparentWindow(); window.Border = new Pen(Brushes.Black); window.CornerRadius = new CornerRadius(15); window.Size = new Size(400,400); window.Background = Brushes.White; window.Location = new Point ( Screen.PrimaryScreen.WorkingArea.Left + 300, Screen.PrimaryScreen.WorkingArea.Height / 2 - 140 ); window.Show();
如果你运行这段代码,你会得到一个这样的窗口: 下面是菜单的代码(见第一张图片)实现: 隐藏,收缩,复制Code
using System; using System.Collections.Generic; using System.Drawing; using CustomMenuLibrary.CustomPolygons; using CustomMenuLibrary.Menu; namespace CustomMenuLibrary { /// <summary> /// There are three state of window: /// 1 - Collapsed: when mouse is not in menu window /// 2 - Expanded: when mouse is in window /// 3 - Select: Expnaded + submenu windows is shown /// This class displays all these states in one transparency window /// The window size and location are fixed. /// Size and location claculated in constructor /// in compliance with submenu window and menu widht. /// </summary> public class MainMenuWindow : TransparentWindow { private readonly MainMenu mainMenu; private Size expandedWindowSize; private Size selectWindowSize; private Size menuItemsSize; private bool isMouseIn; private Dictionary<Rectangle, SubMenuItem> positionOfSubMenuItems; private SelectedSubMenuItem lastSelectedSubMenuItem; private Dictionary<Rectangle, MenuItem> positionOfMenuItems; private SelectedMenuItem lastSelectedMenuItem; private readonly Brush itemTextColor; private readonly Font itemFont; private readonly Pen subMenuBorder; private readonly Brush subMenuBackground; private readonly CornerRadius submenuCornerRadius; private readonly Brush submenuTextColor; private readonly Font submenuFont; private Rectangle submenuRectangle; public MainMenuWindow(MainMenu mainMenu) { if (mainMenu == null) { throw new ArgumentNullException(); } this.mainMenu = mainMenu; submenuFont = new Font("Arial", 10); submenuCornerRadius = new CornerRadius(Constants.SubMenu.CORNER_RADIUS); subMenuBorder = Pens.Black; submenuTextColor = Brushes.Blue; subMenuBackground = Brushes.White; itemTextColor = Brushes.White; itemFont = new Font("Arial", 10); //Calculate window location and windows size //in compliance with submenu window and menu widht Border = null; Size = SelectWindowSize; Location = new Point(mainMenu.Position.X, mainMenu.Position.Y - Size.Height + mainMenu.Size.Height); MouseEnter += OnMouseEnter; MouseLeave += OnMouseLeave; MouseMove += OnMouseMove; MouseUp += OnMouseClick; } #region Mouse handlers void OnMouseClick(object sender, System.Windows.Forms.MouseEventArgs e) { if (LastSelectedSubMenuItem != null && LastSelectedSubMenuItem.Rectangle.Contains(e.Location)) { RaiseSelectMenuClick(new SelectMenuEventArgs( LastSelectedMenuItem.MenuItem,LastSelectedSubMenuItem.SubItem)); } } void OnMouseMove(object sender, System.Windows.Forms.MouseEventArgs e) { Rectangle selectedRect = Rectangle.Empty; if (positionOfMenuItems == null) return; foreach (KeyValuePair<Rectangle, MenuItem> item in positionOfMenuItems) { if (item.Key.Contains(e.Location)) { selectedRect = item.Key; break; } } if (selectedRect == Rectangle.Empty) { if (!submenuRectangle.Contains(e.Location)) { LastSelectedMenuItem = null; Invalidate(); } else { if (positionOfSubMenuItems == null) return; Rectangle selectedSubItemRect = Rectangle.Empty; foreach (KeyValuePair<Rectangle, SubMenuItem> item in positionOfSubMenuItems) { if (item.Key.Contains(e.Location)) { selectedSubItemRect = item.Key; break; } } if (selectedSubItemRect != Rectangle.Empty) { SubMenuItem subMenuItem = positionOfSubMenuItems[selectedSubItemRect]; if (LastSelectedSubMenuItem != new SelectedSubMenuItem(subMenuItem, selectedSubItemRect)) { LastSelectedSubMenuItem = new SelectedSubMenuItem(subMenuItem, selectedSubItemRect); Invalidate(); } } else { LastSelectedSubMenuItem = null; Invalidate(); } } return; //No menu items with such rectangle } MenuItem mouseSelectedItem = positionOfMenuItems[selectedRect]; SelectedMenuItem selectedMenuItem = new SelectedMenuItem(mouseSelectedItem, selectedRect); if (LastSelectedMenuItem == selectedMenuItem) return; //this is the same item LastSelectedMenuItem = selectedMenuItem; Invalidate(); } void OnMouseLeave(object sender, EventArgs e) { isMouseIn = false; Invalidate(); } void OnMouseEnter(object sender, EventArgs e) { isMouseIn = true; Invalidate(); } #endregion public IEnumerable<IPolygon> Draw(Rectangle clientRect) { return isMouseIn ? DrawExpanded(clientRect) : DrawCollapsed(clientRect); } private IEnumerable<IPolygon> DrawCollapsed(Rectangle clientRect) { List<IPolygon> retValue = DrawMenuRectangle(clientRect); return retValue; } private List<IPolygon> DrawMenuRectangle(Rectangle clientRect) { List<IPolygon> retValue = new List<IPolygon>(); Rectangle rect = new Rectangle(clientRect.X, SelectWindowSize.Height - MainMenu.Size.Height, MainMenu.Size.Width, MainMenu.Size.Height); ImagePolygon menuImage = new ImagePolygon(mainMenu.Image,rect); retValue.Add(menuImage); return retValue; } private IEnumerable<IPolygon> DrawExpanded(Rectangle clientRect) { List<IPolygon> retValue = new List<IPolygon>(); int drawHeight = clientRect.Height - ExpandedWinwowSize.Height + 1; bool initializeMenuItemsPosition = false; if (positionOfMenuItems == null) { initializeMenuItemsPosition = true; positionOfMenuItems = new Dictionary<Rectangle, MenuItem>(); } for (int i = MainMenu.Items.Count - 1; i >= 0; --i) { MenuItem mi = MainMenu.Items[i]; //calculate rectangle for menu item Rectangle rect = new Rectangle(clientRect.X, drawHeight, Constants.MainMenu.ITEM_WIDHT, Constants.MainMenu.ITEM_HEIGH); ImagePolygon menuItem = new ImagePolygon(mi.ImageOff,rect); retValue.Add(menuItem); if (initializeMenuItemsPosition) positionOfMenuItems.Add(rect, mi); //hight light selected item if (LastSelectedMenuItem != null && LastSelectedMenuItem.MenuItem == mi) { ImagePolygon selectedItem = new ImagePolygon(LastSelectedMenuItem.MenuItem.ImageOn, rect); retValue.Add(selectedItem); positionOfSubMenuItems = null; if (mi.SubItems.Count > 0) { //Draw subitems window retValue.AddRange(DrawSubMenu(clientRect)); } } //Draw menu item text SizeF stringSize = Global.GetStringSize(mi.Text, itemFont); StringPolygon menuItemText = new StringPolygon( itemTextColor, itemFont, mi.Text, new Point((int)((rect.Width - stringSize.Width) / 2), rect.Top + 5)); retValue.Add(menuItemText); drawHeight += Constants.MainMenu.ITEM_HEIGH; } retValue.AddRange(DrawMenuRectangle(clientRect)); return retValue; } private List<IPolygon> DrawSubMenu(Rectangle clientRect) { //calculate size of submenu window and draw it List<IPolygon> retValue = new List<IPolygon>(); int stringHeigh = (int)Global.GetStringSize(@"Place here any string " + @"that you want, we should only calculate string heigh",submenuFont).Height; int heigh = Constants.SubMenu.START_HEIGH_SUBMENU_ITEMS * 2 + stringHeigh * LastSelectedMenuItem.MenuItem.SubItems.Count; int y = LastSelectedMenuItem.Rectangle.Y - heigh + Constants.MainMenu.ITEM_HEIGH * 2 - 10; int widht = GetSubmenuWindowWidth(LastSelectedMenuItem.MenuItem); submenuRectangle = new Rectangle(clientRect.X + Constants.MainMenu.ITEM_WIDHT, y, widht, heigh ); retValue.Add(new FillRoundRectanglePolygon(subMenuBorder, subMenuBackground, submenuRectangle, submenuCornerRadius)); int drawHeight = Constants.SubMenu.START_HEIGH_SUBMENU_ITEMS; positionOfSubMenuItems = new Dictionary<Rectangle, SubMenuItem>(); foreach (SubMenuItem subitem in LastSelectedMenuItem.MenuItem.SubItems) { SizeF stringSize = Global.GetStringSize(subitem.Text, submenuFont); Rectangle rect = new Rectangle(new Point(submenuRectangle.Left + Constants.SubMenu.START_WIDTH_SUBMENU_ITEMS, submenuRectangle.Top + drawHeight), new Size((int)stringSize.Width,(int)stringSize.Height)); positionOfSubMenuItems.Add(rect, subitem); IPolygon subItemText = LastSelectedSubMenuItem != null && LastSelectedSubMenuItem.SubItem == subitem ? (IPolygon)(new UnderlineStringPolygon(submenuTextColor, submenuFont, subitem.Text, rect.Location)) : new StringPolygon(submenuTextColor, submenuFont, subitem.Text, rect.Location); retValue.Add(subItemText); drawHeight += Constants.SubMenu.DELTA_HEIGH_SUBMENU_ITEMS; } return retValue; } protected SelectedSubMenuItem LastSelectedSubMenuItem { get { return lastSelectedSubMenuItem; } set { lastSelectedSubMenuItem = value; } } protected SelectedMenuItem LastSelectedMenuItem { get { return lastSelectedMenuItem; } set { lastSelectedMenuItem = value; } } /// <summary> /// Gets expanded menu size /// </summary> protected Size ExpandedWinwowSize { get { if (expandedWindowSize == Size.Empty) { expandedWindowSize = new Size(); expandedWindowSize.Width = MenuItemsSize.Width > mainMenu.Size.Width ? MenuItemsSize.Width : mainMenu.Size.Width; expandedWindowSize.Height = MenuItemsSize.Height + mainMenu.Size.Height; } return expandedWindowSize; } } /// <summary> /// Gets whole window size /// </summary> protected Size SelectWindowSize { get { if (selectWindowSize == Size.Empty) { int maxWidht = 0; foreach (MenuItem menuItem in mainMenu.Items) { int widht = GetSubmenuWindowWidth(menuItem); if (maxWidht < widht) { maxWidht = widht; } } selectWindowSize = new Size(); selectWindowSize.Width = ExpandedWinwowSize.Width + maxWidht + (int)subMenuBorder.Width; selectWindowSize.Height = ExpandedWinwowSize.Height + Constants.SubMenu.MAX_HEIGH - Constants.MainMenu.ITEM_HEIGH + 2; } return selectWindowSize; } } protected int GetSubmenuWindowWidth(MenuItem menuItem) { int maxWidht = 0; foreach (SubMenuItem item in menuItem.SubItems) { SizeF stringSize = Global.GetStringSize(item.Text, submenuFont); if (maxWidht < stringSize.Width) { maxWidht = (int)stringSize.Width; } } maxWidht += 2*Constants.SubMenu.START_WIDTH_SUBMENU_ITEMS; if (maxWidht < Constants.SubMenu.MIN_WIDHT) maxWidht = Constants.SubMenu.MIN_WIDHT; return maxWidht; } /// <summary> /// Gets menu items size (without menu button) /// </summary> protected Size MenuItemsSize { get { if (menuItemsSize == Size.Empty) { menuItemsSize = new Size(); menuItemsSize.Width = Constants.MainMenu.ITEM_WIDHT; menuItemsSize.Height = mainMenu.Items.Count*Constants.MainMenu.ITEM_HEIGH; } return menuItemsSize; } } protected MainMenu MainMenu { get { return mainMenu; } } protected override List<IPolygon> DrawPolygons(Rectangle clientRect) { List<IPolygon> polygons = new List<IPolygon>(base.DrawPolygons(clientRect)); polygons.AddRange(Draw(clientRect)); return polygons; } public event SelectMenuEventHandler SelectMenuClick; protected void RaiseSelectMenuClick(SelectMenuEventArgs e) { if (SelectMenuClick != null) { SelectMenuClick(this, e); } } } }
本文转载于:http://www.diyabc.com/frontweb/news7056.html