|
| 1 | +// Copyright (c) Jeremy W. Kuhne. All rights reserved. |
| 2 | +// Licensed under the MIT license. See LICENSE file in the project root for full license information. |
| 3 | + |
| 4 | +using System.Drawing; |
| 5 | +using Windows; |
| 6 | +using Windows.Win32; |
| 7 | +using Windows.Win32.Foundation; |
| 8 | + |
| 9 | +namespace OwnDraw; |
| 10 | + |
| 11 | +/// <summary> |
| 12 | +/// Sample from Programming Windows, 5th Edition. |
| 13 | +/// Original (c) Charles Petzold, 1998 |
| 14 | +/// Figure 9-3, Pages 375-380. |
| 15 | +/// </summary> |
| 16 | +internal static class Program |
| 17 | +{ |
| 18 | + [STAThread] |
| 19 | + private static void Main() => Application.Run(new OwnerDraw("Owner-Draw Button Demo")); |
| 20 | + |
| 21 | + private class OwnerDraw : MainWindow |
| 22 | + { |
| 23 | + private HWND _hwndSmaller, _hwndLarger; |
| 24 | + private int _cxClient, _cyClient; |
| 25 | + private int _btnWidth, _btnHeight; |
| 26 | + private Size _baseUnits; |
| 27 | + private const int ID_SMALLER = 1; |
| 28 | + private const int ID_LARGER = 2; |
| 29 | + |
| 30 | + public OwnerDraw(string title) : base(title) |
| 31 | + { |
| 32 | + } |
| 33 | + |
| 34 | + protected override LRESULT WindowProcedure(HWND window, MessageType message, WPARAM wParam, LPARAM lParam) |
| 35 | + { |
| 36 | + switch (message) |
| 37 | + { |
| 38 | + case MessageType.Create: |
| 39 | + int baseUnits = Interop.GetDialogBaseUnits(); |
| 40 | + _baseUnits = new(baseUnits & 0xFFFF, baseUnits >> 16); |
| 41 | + _btnWidth = _baseUnits.Width * 8; |
| 42 | + _btnHeight = _baseUnits.Height * 4; |
| 43 | + |
| 44 | + // Create the owner-draw pushbuttons |
| 45 | + _hwndSmaller = new ButtonControl( |
| 46 | + style: WindowStyles.Child | WindowStyles.Visible, |
| 47 | + buttonStyle: ButtonControl.Styles.OwnerDrawn, |
| 48 | + parentWindow: this, |
| 49 | + buttonId: ID_SMALLER); |
| 50 | + _hwndLarger = new ButtonControl( |
| 51 | + style: WindowStyles.Child | WindowStyles.Visible, |
| 52 | + buttonStyle: ButtonControl.Styles.OwnerDrawn, |
| 53 | + parentWindow: this, |
| 54 | + buttonId: ID_LARGER); |
| 55 | + |
| 56 | + return (LRESULT)0; |
| 57 | + |
| 58 | + case MessageType.Size: |
| 59 | + _cxClient = lParam.LOWORD; |
| 60 | + _cyClient = lParam.HIWORD; |
| 61 | + |
| 62 | + // Move the buttons to the new center |
| 63 | + _hwndSmaller.MoveWindow( |
| 64 | + new Rectangle(_cxClient / 2 - 3 * _btnWidth / 2, _cyClient / 2 - _btnHeight / 2, _btnWidth, _btnHeight), |
| 65 | + repaint: true); |
| 66 | + _hwndLarger.MoveWindow( |
| 67 | + new Rectangle(_cxClient / 2 + _btnWidth / 2, _cyClient / 2 - _btnHeight / 2, _btnWidth, _btnHeight), |
| 68 | + repaint: true); |
| 69 | + return (LRESULT)0; |
| 70 | + |
| 71 | + case MessageType.Command: |
| 72 | + Rectangle rc = window.GetWindowRectangle(); |
| 73 | + |
| 74 | + // Make the window 10% smaller or larger |
| 75 | + switch ((int)(uint)wParam) |
| 76 | + { |
| 77 | + case ID_SMALLER: |
| 78 | + rc.Inflate(rc.Width / -10, rc.Height / -10); |
| 79 | + break; |
| 80 | + case ID_LARGER: |
| 81 | + rc.Inflate(rc.Width / 10, rc.Height / 10); |
| 82 | + break; |
| 83 | + } |
| 84 | + |
| 85 | + window.MoveWindow(rc, repaint: true); |
| 86 | + return (LRESULT)0; |
| 87 | + |
| 88 | + case MessageType.DrawItem: |
| 89 | + |
| 90 | + var drawItemMessage = new Message.DrawItem(lParam); |
| 91 | + |
| 92 | + // Fill area with white and frame it black |
| 93 | + using (DeviceContext dc = drawItemMessage.DeviceContext) |
| 94 | + { |
| 95 | + Rectangle rect = drawItemMessage.ItemRectangle; |
| 96 | + |
| 97 | + dc.FillRectangle(rect, StockBrush.White); |
| 98 | + dc.FrameRectangle(rect, StockBrush.Black); |
| 99 | + |
| 100 | + // Draw inward and outward black triangles |
| 101 | + int cx = rect.Right - rect.Left; |
| 102 | + int cy = rect.Bottom - rect.Top; |
| 103 | + |
| 104 | + Point[] pt = new Point[3]; |
| 105 | + |
| 106 | + switch ((int)drawItemMessage.ControlId) |
| 107 | + { |
| 108 | + case ID_SMALLER: |
| 109 | + pt[0].X = 3 * cx / 8; pt[0].Y = 1 * cy / 8; |
| 110 | + pt[1].X = 5 * cx / 8; pt[1].Y = 1 * cy / 8; |
| 111 | + pt[2].X = 4 * cx / 8; pt[2].Y = 3 * cy / 8; |
| 112 | + Triangle(dc, pt); |
| 113 | + pt[0].X = 7 * cx / 8; pt[0].Y = 3 * cy / 8; |
| 114 | + pt[1].X = 7 * cx / 8; pt[1].Y = 5 * cy / 8; |
| 115 | + pt[2].X = 5 * cx / 8; pt[2].Y = 4 * cy / 8; |
| 116 | + Triangle(dc, pt); |
| 117 | + pt[0].X = 5 * cx / 8; pt[0].Y = 7 * cy / 8; |
| 118 | + pt[1].X = 3 * cx / 8; pt[1].Y = 7 * cy / 8; |
| 119 | + pt[2].X = 4 * cx / 8; pt[2].Y = 5 * cy / 8; |
| 120 | + Triangle(dc, pt); |
| 121 | + pt[0].X = 1 * cx / 8; pt[0].Y = 5 * cy / 8; |
| 122 | + pt[1].X = 1 * cx / 8; pt[1].Y = 3 * cy / 8; |
| 123 | + pt[2].X = 3 * cx / 8; pt[2].Y = 4 * cy / 8; |
| 124 | + Triangle(dc, pt); |
| 125 | + break; |
| 126 | + case ID_LARGER: |
| 127 | + pt[0].X = 5 * cx / 8; pt[0].Y = 3 * cy / 8; |
| 128 | + pt[1].X = 3 * cx / 8; pt[1].Y = 3 * cy / 8; |
| 129 | + pt[2].X = 4 * cx / 8; pt[2].Y = 1 * cy / 8; |
| 130 | + Triangle(dc, pt); |
| 131 | + pt[0].X = 5 * cx / 8; pt[0].Y = 5 * cy / 8; |
| 132 | + pt[1].X = 5 * cx / 8; pt[1].Y = 3 * cy / 8; |
| 133 | + pt[2].X = 7 * cx / 8; pt[2].Y = 4 * cy / 8; |
| 134 | + Triangle(dc, pt); |
| 135 | + pt[0].X = 3 * cx / 8; pt[0].Y = 5 * cy / 8; |
| 136 | + pt[1].X = 5 * cx / 8; pt[1].Y = 5 * cy / 8; |
| 137 | + pt[2].X = 4 * cx / 8; pt[2].Y = 7 * cy / 8; |
| 138 | + Triangle(dc, pt); |
| 139 | + pt[0].X = 3 * cx / 8; pt[0].Y = 3 * cy / 8; |
| 140 | + pt[1].X = 3 * cx / 8; pt[1].Y = 5 * cy / 8; |
| 141 | + pt[2].X = 1 * cx / 8; pt[2].Y = 4 * cy / 8; |
| 142 | + Triangle(dc, pt); |
| 143 | + break; |
| 144 | + } |
| 145 | + |
| 146 | + // Invert the rectangle if the button is selected |
| 147 | + if (drawItemMessage.ItemState.HasFlag(Message.DrawItem.States.Selected)) |
| 148 | + { |
| 149 | + dc.InvertRectangle(rect); |
| 150 | + } |
| 151 | + |
| 152 | + if (drawItemMessage.ItemState.HasFlag(Message.DrawItem.States.Focus)) |
| 153 | + { |
| 154 | + rect = Rectangle.FromLTRB( |
| 155 | + rect.Left + cx / 16, |
| 156 | + rect.Top + cy / 16, |
| 157 | + rect.Right - cx / 16, |
| 158 | + rect.Bottom - cy / 16); |
| 159 | + |
| 160 | + dc.DrawFocusRectangle(rect); |
| 161 | + } |
| 162 | + } |
| 163 | + |
| 164 | + return (LRESULT)0; |
| 165 | + } |
| 166 | + |
| 167 | + static void Triangle(DeviceContext dc, Point[] pt) |
| 168 | + { |
| 169 | + dc.SelectObject(StockBrush.Black); |
| 170 | + dc.Polygon(pt); |
| 171 | + dc.SelectObject(StockBrush.White); |
| 172 | + } |
| 173 | + |
| 174 | + return base.WindowProcedure(window, message, wParam, lParam); |
| 175 | + } |
| 176 | + } |
| 177 | +} |
0 commit comments