WPF Polygon透明Stroke填充区域替换问题求助(附代码)
Hey there! Let's work through this tricky WPF triangle rendering problem you're dealing with. The core issue here is that WPF's Polygon draws its stroke centered on the shape's outline (half inside, half outside), which makes it impossible to directly "replace" the inner stroke area with transparency when the stroke is clear—unlike rectangles or circles where you could tweak dimensions or use clipping tricks.
The solution is to split the triangle into two separate Polygon elements: one for the fill (adjusted to fit inside the stroke area) and one for the stroke (using the original coordinates). Here's how to implement it:
Step 1: Update Your XAML Layout
Replace your single Polygon with two overlapping polygons—one for the inner fill, one for the outer stroke. We'll use a converter to offset the fill polygon inward by half the stroke thickness:
<UserControl.Clip> <PathGeometry> <PathFigure StartPoint="{Binding Triangle.Points, Converter={StaticResource PointLocationClipConverter}}" IsClosed="True"> <PolyLineSegment Points="{Binding Triangle.Points, Converter={StaticResource PointCollectionClipConverter}}" /> </PathFigure> </PathGeometry> </UserControl.Clip> <Grid x:Name="_grid"> <!-- Inner fill polygon: shifted inward by StrokeThickness/2 --> <Polygon Fill="{Binding Triangle.BackgroundBrush}" Stroke="Transparent"> <Polygon.Points> <MultiBinding Converter="{StaticResource PointCollectionOffsetConverter}"> <Binding Path="Triangle.Points" /> <Binding Path="Triangle.Thick" /> </MultiBinding> </Polygon.Points> </Polygon> <!-- Outer stroke polygon: uses original coordinates, only renders stroke --> <Polygon Points="{Binding Triangle.Points}" Stroke="{Binding Triangle.BorderColorBrush}" StrokeThickness="{Binding Triangle.Thick}" Fill="Transparent" /> </Grid>
Step 2: Implement the Offset Converter
You'll need a multi-value converter that takes the original point collection and stroke thickness, then shifts each vertex inward to create the inner fill shape. This involves calculating the average inward normal for each vertex to ensure the offset is consistent across all edges:
using System.Globalization; using System.Windows; using System.Windows.Data; using System.Windows.Media; public class PointCollectionOffsetConverter : IMultiValueConverter { public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) { // Validate input values if (values[0] is not PointCollection originalPoints || values[1] is not double thickness || originalPoints.Count < 3 || thickness <= 0) { return originalPoints; } double offsetAmount = thickness / 2.0; var offsetPoints = new PointCollection(); int pointCount = originalPoints.Count; for (int i = 0; i < pointCount; i++) { Point current = originalPoints[i]; Point previous = originalPoints[(i - 1 + pointCount) % pointCount]; // Wrap around for first point Point next = originalPoints[(i + 1) % pointCount]; // Wrap around for last point // Calculate vectors for adjacent edges Vector edgeToPrevious = current - previous; Vector edgeToNext = next - current; // Get inward-pointing normals for each edge Vector normal1 = new Vector(-edgeToPrevious.Y, edgeToPrevious.X); normal1.Normalize(); Vector normal2 = new Vector(-edgeToNext.Y, edgeToNext.X); normal2.Normalize(); // Average the normals to get a consistent inward direction for the vertex Vector averageInwardNormal = (normal1 + normal2); averageInwardNormal.Normalize(); // Shift the current point inward by the offset amount Point offsetPoint = current + averageInwardNormal * offsetAmount; offsetPoints.Add(offsetPoint); } return offsetPoints; } public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) { throw new NotImplementedException(); } }
Step 3: Verify All Three Scenarios
This setup handles all your required cases perfectly:
- Scenario 1 (Solid stroke + solid fill): The inner polygon shows your fill color, and the outer polygon adds the stroke around it—no overlap issues, and the clip ensures nothing spills outside your original coordinates.
- Scenario 2 (Transparent stroke + solid fill): The outer polygon's stroke is invisible, so only the inner offset polygon is visible. This effectively "removes" the stroke-thickness area from the fill, matching your requirement.
- Scenario 3 (Solid stroke + transparent fill): The inner polygon is invisible, so only the outer polygon's stroke is displayed—exactly the hollow triangle you need.
Don't forget to register your converter in your XAML resources like you did with your other converters:
<Window.Resources> <local:PointCollectionOffsetConverter x:Key="PointCollectionOffsetConverter" /> <!-- Your other converters here --> </Window.Resources>
This approach avoids the limitations of a single Polygon and gives you precise control over both fill and stroke, regardless of transparency settings.
内容的提问来源于stack exchange,提问作者Roberto

