/// <summary> /// Returns the Size in logical units of the given text using the given Font, and according to the formatting flags. /// The proposed size is used to create a bounding rectangle as follows: /// - If there are multiple lines of text, DrawText uses the width of the rectangle pointed to by /// the lpRect parameter and extends the base of the rectangle to bound the last line of text. /// - If the largest word is wider than the rectangle, the width is expanded. /// - If the text is less than the width of the rectangle, the width is reduced. /// - If there is only one line of text, DrawText modifies the right side of the rectangle so that /// it bounds the last character in the line. /// If the font is null, the hdc's current font will be used. /// /// Note for vertical fonts (if ever supported): DrawTextEx uses GetTextExtentPoint32 for measuring the text and this /// function has the following limitation (from MSDN): /// - This function assumes that the text is horizontal, that is, that the escapement is always 0. This is true for both /// the horizontal and vertical measurements of the text. The application must convert it explicitly. /// </summary> public static Size MeasureText( this Gdi32.HDC hdc, ReadOnlySpan <char> text, FontCache.Scope font, Size proposedSize, TextFormatFlags flags) { (User32.DT dt, TextPaddingOptions padding) = SplitTextFormatFlags(flags); if (text.IsEmpty) { return(Size.Empty); } // DrawText returns a rectangle useful for aligning, but not guaranteed to encompass all // pixels (its not a FitBlackBox, if the text is italicized, it will overhang on the right.) // So we need to account for this. User32.DRAWTEXTPARAMS dtparams = GetTextMargins(font, padding); // If Width / Height are < 0, we need to make them larger or DrawText will return // an unbounded measurement when we actually trying to make it very narrow. int minWidth = 1 + dtparams.iLeftMargin + dtparams.iRightMargin; if (proposedSize.Width <= minWidth) { proposedSize.Width = minWidth; } if (proposedSize.Height <= 0) { proposedSize.Height = 1; } var rect = new RECT(0, 0, proposedSize.Width, proposedSize.Height); using var fontSelection = new Gdi32.SelectObjectScope(hdc, font.Object); // If proposedSize.Height >= MaxSize.Height it is assumed bounds needed. If flags contain SINGLELINE and // VCENTER or BOTTOM options, DrawTextEx does not bind the rectangle to the actual text height since // it assumes the text is to be vertically aligned; we need to clear the VCENTER and BOTTOM flags to // get the actual text bounds. if (proposedSize.Height >= TextRenderer.MaxSize.Height && (dt & User32.DT.SINGLELINE) != 0) { // Clear vertical-alignment flags. dt &= ~(User32.DT.BOTTOM | User32.DT.VCENTER); } if (proposedSize.Width == TextRenderer.MaxSize.Width) { // PERF: No constraining width means no word break. // in this case, we dont care about word wrapping - there should be enough room to fit it all dt &= ~(User32.DT.WORDBREAK); } dt |= User32.DT.CALCRECT; User32.DrawTextExW(hdc, text, ref rect, dt, ref dtparams); return(rect.Size); }
private Gdi32.HBITMAP GetCompatibleBitmap(Bitmap bm) { using var screenDC = User32.GetDcScope.ScreenDC; // GDI+ returns a DIBSECTION based HBITMAP. The clipboard deals well // only with bitmaps created using CreateCompatibleBitmap(). So, we // convert the DIBSECTION into a compatible bitmap. Gdi32.HBITMAP hBitmap = bm.GetHBITMAP(); // Create a compatible DC to render the source bitmap. using var sourceDC = new Gdi32.CreateDcScope(screenDC); using var sourceBitmapSelection = new Gdi32.SelectObjectScope(sourceDC, hBitmap); // Create a compatible DC and a new compatible bitmap. using var destinationDC = new Gdi32.CreateDcScope(screenDC); Gdi32.HBITMAP bitmap = Gdi32.CreateCompatibleBitmap(screenDC, bm.Size.Width, bm.Size.Height); // Select the new bitmap into a compatible DC and render the blt the original bitmap. using var destinationBitmapSelection = new Gdi32.SelectObjectScope(destinationDC, bitmap); Gdi32.BitBlt( destinationDC, 0, 0, bm.Size.Width, bm.Size.Height, sourceDC, 0, 0, Gdi32.ROP.SRCCOPY); return(bitmap); }
internal static void DrawRectangle(this Gdi32.HDC hdc, Rectangle rectangle, Gdi32.HPEN hpen) { using var penScope = new Gdi32.SelectObjectScope(hdc, hpen); using var ropScope = new Gdi32.SetRop2Scope(hdc, Gdi32.R2.COPYPEN); using var brushScope = new Gdi32.SelectObjectScope(hdc, Gdi32.GetStockObject(Gdi32.StockObject.HOLLOW_BRUSH)); Gdi32.Rectangle(hdc, rectangle.X, rectangle.Y, rectangle.Right, rectangle.Bottom); }
internal unsafe static void DrawLine(this Gdi32.HDC hdc, Gdi32.HPEN pen, int x1, int y1, int x2, int y2) { using var ropScope = new Gdi32.SetRop2Scope(hdc, Gdi32.R2.COPYPEN); using var bkScope = new Gdi32.SetBkModeScope(hdc, Gdi32.BKMODE.TRANSPARENT); using var selection = new Gdi32.SelectObjectScope(hdc, pen); Point oldPoint = new Point(); Gdi32.MoveToEx(hdc, x1, y1, &oldPoint); Gdi32.LineTo(hdc, x2, y2); Gdi32.MoveToEx(hdc, oldPoint.X, oldPoint.Y, &oldPoint); }
/// <summary> /// Draws the text in the given bounds, using the given Font, foreColor and backColor, and according to the specified /// TextFormatFlags flags. /// /// If font is null, the font currently selected in the hdc is used. /// /// If foreColor and/or backColor are Color.Empty, the hdc current text and/or background color are used. /// </summary> public static void DrawText( this Gdi32.HDC hdc, ReadOnlySpan <char> text, FontCache.Scope font, Rectangle bounds, Color foreColor, User32.DT flags, Color backColor = default, TextPaddingOptions padding = default) { if (text.IsEmpty || foreColor == Color.Transparent) { return; } ValidateFlags(flags); // DrawText requires default text alignment. using var alignment = new Gdi32.SetTextAlignmentScope(hdc, default); // Color empty means use the one currently selected in the dc. using var textColor = foreColor.IsEmpty ? default : new Gdi32.SetTextColorScope(hdc, foreColor); using var fontSelection = new Gdi32.SelectObjectScope(hdc, (Gdi32.HFONT)font); Gdi32.BKMODE newBackGroundMode = (backColor.IsEmpty || backColor == Color.Transparent) ? Gdi32.BKMODE.TRANSPARENT : Gdi32.BKMODE.OPAQUE; using var backgroundMode = new Gdi32.SetBkModeScope(hdc, newBackGroundMode); using var backgroundColor = newBackGroundMode != Gdi32.BKMODE.TRANSPARENT ? new Gdi32.SetBackgroundColorScope(hdc, backColor) : default; User32.DRAWTEXTPARAMS dtparams = GetTextMargins(font, padding); bounds = AdjustForVerticalAlignment(hdc, text, bounds, flags, ref dtparams); // Adjust unbounded rect to avoid overflow since Rectangle ctr does not do param validation. if (bounds.Width == TextRenderer.MaxSize.Width) { bounds.Width -= bounds.X; } if (bounds.Height == TextRenderer.MaxSize.Height) { bounds.Height -= bounds.Y; } RECT rect = bounds; User32.DrawTextExW(hdc, text, ref rect, flags, ref dtparams); }
internal static void DrawRectangle( this Gdi32.HDC hdc, int left, int top, int right, int bottom, Gdi32.HPEN hpen) { using var penScope = new Gdi32.SelectObjectScope(hdc, hpen); using var ropScope = new Gdi32.SetRop2Scope(hdc, Gdi32.R2.COPYPEN); using var brushScope = new Gdi32.SelectObjectScope(hdc, Gdi32.GetStockObject(Gdi32.StockObject.NULL_BRUSH)); Gdi32.Rectangle(hdc, left, top, right, bottom); }
/// <summary> /// Returns the Size of the given text using the specified font if not null, otherwise the font currently /// set in the dc is used. /// This method is used to get the size in points of a line of text; it uses GetTextExtentPoint32 function /// which computes the width and height of the text ignoring TAB\CR\LF characters. /// A text extent is the distance between the beginning of the space and a character that will fit in the space. /// </summary> public static Size GetTextExtent(this Gdi32.HDC hdc, string?text, Gdi32.HFONT hfont) { if (string.IsNullOrEmpty(text)) { return(Size.Empty); } Size size = new Size(); using var selectFont = new Gdi32.SelectObjectScope(hdc, hfont); Gdi32.GetTextExtentPoint32W(hdc, text, text.Length, ref size); return(new Size(size.Width, size.Height)); }
/// <summary> /// Draws lines with the <paramref name="hpen"/> using points defined in <paramref name="lines"/>. /// </summary> /// <param name="lines"> /// MUST be a mulitple of 4. Each group of 4 represents x1, y1, x2, y2. /// </param> internal unsafe static void DrawLines(this Gdi32.HDC hdc, Gdi32.HPEN hpen, ReadOnlySpan <int> lines) { Debug.Assert((lines.Length % 4) == 0); using var ropScope = new Gdi32.SetRop2Scope(hdc, Gdi32.R2.COPYPEN); using var bkScope = new Gdi32.SetBkModeScope(hdc, Gdi32.BKMODE.TRANSPARENT); using var selection = new Gdi32.SelectObjectScope(hdc, hpen); Point oldPoint = new Point(); for (int i = 0; i < lines.Length; i += 4) { Gdi32.MoveToEx(hdc, lines[i], lines[i + 1], &oldPoint); Gdi32.LineTo(hdc, lines[i + 2], lines[i + 3]); Gdi32.MoveToEx(hdc, oldPoint.X, oldPoint.Y, null); } }
private void DrawReversibleFrame(IntPtr handle, Rectangle rectangle, Color backColor) { //Bug # 71547 <subhag> to make drag rect visible if any the dimensions of the control are 0 if (rectangle.Width == 0) { rectangle.Width = 5; } if (rectangle.Height == 0) { rectangle.Height = 5; } // Copy of ControlPaint.DrawReversibleFrame, see VSWhidbey 581670 // If ControlPaint ever gets overrloaded, we should replace the code below by calling it: // ControlPaint.DrawReversibleFrame(handle, rectangle, backColor, FrameStyle.Thick); // ------ Duplicate code---------------------------------------------------------- Gdi32.R2 rop2; Color graphicsColor; if (backColor.GetBrightness() < .5) { rop2 = Gdi32.R2.NOTXORPEN; graphicsColor = Color.White; } else { rop2 = Gdi32.R2.XORPEN; graphicsColor = Color.Black; } using var dc = new User32.GetDcScope(handle); using var pen = new Gdi32.ObjectScope(Gdi32.CreatePen(Gdi32.PS.SOLID, 2, ColorTranslator.ToWin32(backColor))); using var rop2Scope = new Gdi32.SetRop2Scope(dc, rop2); using var brushSelection = new Gdi32.SelectObjectScope(dc, Gdi32.GetStockObject(Gdi32.StockObject.NULL_BRUSH)); using var penSelection = new Gdi32.SelectObjectScope(dc, pen); Gdi32.SetBkColor(dc, ColorTranslator.ToWin32(graphicsColor)); Gdi32.Rectangle(dc, rectangle.X, rectangle.Y, rectangle.Right, rectangle.Bottom); // ------ Duplicate code---------------------------------------------------------- }
public void Font_AdjustForVerticalAlignment(string family, float size, Rectangle bounds, uint dt, Rectangle expected) { using Font font = new Font(family, size); if (font.Name != family) { // Not installed on this machine return; } using var hfont = GdiCache.GetHFONT(font, Gdi32.QUALITY.CLEARTYPE); using var screen = GdiCache.GetScreenHdc(); using var fontSelection = new Gdi32.SelectObjectScope(screen, hfont.Object); User32.DRAWTEXTPARAMS param = default; Rectangle result = screen.HDC.AdjustForVerticalAlignment( "Windows Foundation Classes", bounds, (User32.DT)dt, ref param); Assert.Equal(expected, result); }
private void ActivateDropDown() { if (_editor != null) { try { object newValue = _editor.EditValue(TypeDescriptorContext, this, Value); SetValue(newValue); } catch (Exception ex) { ActionPanel.ShowError(string.Format(SR.DesignerActionPanel_ErrorActivatingDropDown, ex.Message)); } } else { ListBox listBox = new ListBox { BorderStyle = BorderStyle.None, IntegralHeight = false, Font = ActionPanel.Font }; listBox.SelectedIndexChanged += new EventHandler(OnListBoxSelectedIndexChanged); listBox.KeyDown += new KeyEventHandler(OnListBoxKeyDown); TypeConverter.StandardValuesCollection standardValues = GetStandardValues(); if (standardValues != null) { foreach (object o in standardValues) { string newItem = PropertyDescriptor.Converter.ConvertToString(TypeDescriptorContext, CultureInfo.CurrentCulture, o); listBox.Items.Add(newItem); if ((o != null) && o.Equals(Value)) { listBox.SelectedItem = newItem; } } } // All measurement code borrowed from WinForms PropertyGridView.cs int maxWidth = 0; // The listbox draws with GDI, not GDI+. So, we use a normal DC here. using (var hdc = new User32.GetDcScope(listBox.Handle)) { using var hFont = new Gdi32.ObjectScope(listBox.Font.ToHFONT()); using var fontSelection = new Gdi32.SelectObjectScope(hdc, hFont); var tm = new Gdi32.TEXTMETRICW(); if (listBox.Items.Count > 0) { foreach (string s in listBox.Items) { var textSize = new Size(); Gdi32.GetTextExtentPoint32W(hdc, s, s.Length, ref textSize); maxWidth = Math.Max(textSize.Width, maxWidth); } } Gdi32.GetTextMetricsW(hdc, ref tm); // border + padding + scrollbar maxWidth += 2 + tm.tmMaxCharWidth + SystemInformation.VerticalScrollBarWidth; listBox.Height = Math.Max(tm.tmHeight + 2, Math.Min(ListBoxMaximumHeight, listBox.PreferredHeight)); listBox.Width = Math.Max(maxWidth, EditRegionSize.Width); _ignoreDropDownValue = false; } try { ShowDropDown(listBox, SystemColors.ControlDark); } finally { listBox.SelectedIndexChanged -= new EventHandler(OnListBoxSelectedIndexChanged); listBox.KeyDown -= new KeyEventHandler(OnListBoxKeyDown); } if (!_ignoreDropDownValue) { if (listBox.SelectedItem != null) { SetValue(listBox.SelectedItem); } } } }