WPF Canvas内Rectangle上Label文字模糊拉伸及背景填充异常求助
Hey Liam, let's break down what's causing those issues and walk through some solid fixes to get your rectangle fully filled and text crisp every time.
The Root of the Problems
Your current approach uses a Label (with a fixed 12pt font size) as the source for a BitmapCacheBrush, then stretches that brush to fill a tiny 6x6 Rectangle. Here's why it breaks:
- The
Labelisn't sized to match theRectangle, so when the brush is stretched, both the background and text get distorted. - A 12pt font is way larger than your 6px rectangle, forcing the text to compress into the space, making it unreadable.
Fix 1: Ditch the Rectangle + Brush Combo (Simplest Solution)
You don't need a Rectangle at all—just use a TextBlock directly. It handles background, text alignment, and sizing natively without any stretching:
var trackTextBlock = new TextBlock { Width = 6, Height = 6, Background = new SolidColorBrush(Color.FromRgb(n.Value.R, n.Value.G, n.Value.B)), Foreground = Brushes.Black, // Adjust text color as needed Text = n.Value.Stations[0].TrackId, TextAlignment = TextAlignment.Center, VerticalAlignment = VerticalAlignment.Center, Padding = new Thickness(0), FontSize = 6, // Match font size to your element dimensions }; trackTextBlock.MouseDown += ElipOnMouseDown; Canvas.SetTop(trackTextBlock, n.Value.Y - trackTextBlock.Width / 2); Canvas.SetLeft(trackTextBlock, n.Value.X - trackTextBlock.Height / 2); cMain.Children.Add(trackTextBlock);
Fix 2: Dynamic Font Sizing for Variable Dimensions
If you need to support different rectangle sizes, add a helper method to calculate the largest font that fits perfectly without stretching:
First, add this utility method to your class:
private double GetOptimalFontSize(FrameworkElement element, string text) { var formattedText = new FormattedText( text, CultureInfo.CurrentCulture, FlowDirection.LeftToRight, new Typeface(element.FontFamily, element.FontStyle, element.FontWeight, element.FontStretch), element.FontSize, element.Foreground); // Shrink font until it fits within 90% of the element's bounds (prevents overflow) double fontSize = element.FontSize; while (formattedText.Width > element.ActualWidth * 0.9 || formattedText.Height > element.ActualHeight * 0.9) { fontSize -= 0.5; formattedText = new FormattedText( text, CultureInfo.CurrentCulture, FlowDirection.LeftToRight, new Typeface(element.FontFamily, element.FontStyle, element.FontWeight, element.FontStretch), fontSize, element.Foreground); if (fontSize <= 1) break; // Prevent infinite loop for tiny elements } return fontSize; }
Then update your original code to sync sizes and use dynamic font sizing:
Rectangle elip = new Rectangle(); elip.Height = 6; elip.Width = 6; Brush bgBrush = new SolidColorBrush(Color.FromRgb(n.Value.R, n.Value.G, n.Value.B)); string trackId = n.Value.Stations[0].TrackId; Label TB = new Label(); TB.Width = elip.Width; // Match Label size exactly to Rectangle TB.Height = elip.Height; TB.Margin = new Thickness(0); TB.Background = bgBrush; TB.HorizontalContentAlignment = HorizontalAlignment.Center; TB.VerticalContentAlignment = VerticalAlignment.Center; // Add vertical centering TB.Content = trackId; // Calculate and set the perfect font size TB.FontSize = GetOptimalFontSize(TB, trackId); // Configure BitmapCacheBrush to avoid stretching BitmapCacheBrush bcb = new BitmapCacheBrush(TB) { BitmapCache = new BitmapCache { RenderAtScale = 1.0 }, // Render at original size Stretch = Stretch.None // Disable stretching entirely }; elip.Fill = bcb; elip.Stroke = Brushes.Black; elip.StrokeThickness = 0.5; elip.MouseDown += ElipOnMouseDown; Canvas.SetTop(elip, n.Value.Y - elip.Width / 2); Canvas.SetLeft(elip, n.Value.X - elip.Height / 2); cMain.Children.Add(elip);
Fix 3: Use DrawingBrush for Vector-Perfect Results
For crisp, scalable rendering (no bitmap distortion), swap BitmapCacheBrush with DrawingBrush—it renders vector graphics instead of bitmaps:
Rectangle elip = new Rectangle(); elip.Height = 6; elip.Width = 6; Brush bgBrush = new SolidColorBrush(Color.FromRgb(n.Value.R, n.Value.G, n.Value.B)); string trackId = n.Value.Stations[0].TrackId; // Build a vector drawing with background + text var drawingGroup = new DrawingGroup(); // Add background rectangle drawingGroup.Children.Add(new GeometryDrawing( bgBrush, null, new RectangleGeometry(new Rect(0, 0, elip.Width, elip.Height)))); // Add centered text var formattedText = new FormattedText( trackId, CultureInfo.CurrentCulture, FlowDirection.LeftToRight, new Typeface("Segoe UI"), 6, Brushes.Black); // Calculate centered position double textX = (elip.Width - formattedText.Width) / 2; double textY = (elip.Height - formattedText.Height) / 2; drawingGroup.Children.Add(new GlyphRunDrawing( Brushes.Black, new GlyphRun( formattedText.Typeface.GetGlyphTypeface(), 0, false, formattedText.FontSize, formattedText.GlyphIndices, new Point(textX, textY), formattedText.GlyphAdvances, null, null, null, null, null))); // Assign the drawing as the rectangle's fill elip.Fill = new DrawingBrush(drawingGroup) { Stretch = Stretch.None }; elip.Stroke = Brushes.Black; elip.StrokeThickness = 0.5; elip.MouseDown += ElipOnMouseDown; Canvas.SetTop(elip, n.Value.Y - elip.Width / 2); Canvas.SetLeft(elip, n.Value.X - elip.Height / 2); cMain.Children.Add(elip);
Final Recommendations
- Use Fix 1 if you just need a simple colored block with text—it's the most maintainable and avoids all stretching issues.
- Use Fix 2 if you need to keep the
Rectanglecomponent and support dynamic sizes. - Use Fix 3 if you need scalable, pixel-perfect rendering (great for zoomable canvases).
内容的提问来源于stack exchange,提问作者Liam

