Maintaining fixed thickness lines in WPF with Viewbox Zoom / Stretch

I have <Grid>one that contains some vertical and horizontal <Line>s. I want the grid to be scalable with the size of the window and keep the aspect ratio, so it was contained in <Viewbox Stretch="Uniform">.

However, I also want the lines to always be displayed with a width of 1 pixel, so I use:

Line line = new Line();
line.SetValue(RenderOptions.EdgeModeProperty, EdgeMode.Aliased);
// other line settings here...

This makes the initial appearance of the lines perfect, but as soon as you start resizing the window, stretching / scaling will start and the lines will again become a mixture of 1 and 2 pixels thick.

Is there a way that the lines are always 1 pixel thick and also allow me to resize the window / grid?

Update - using path geometry as suggested by Clemens

@Clemens - Thanks for highlighting the rendering differences between lines and paths. When I try to redo my code using your example, I get this feeling of tone that I dig more holes for myself and do not really understand the whole concept (completely my fault, not yours, I'm just new to WPF).

I will add some screenshots to illustrate the following description:

I make a playing field (for a Go game , if that helps to understand the layout at all) I have a 9x9 grid, and I plan to place fragments of the game by simply adding an ellipse to a specific grid cell.

To draw the main lines on the board, I need to draw lines intersecting the middle of the cells throughout the board (in Go, the pieces are placed at the intersections, not in the middle of the cells).

, , , , , .

( , - , . , XAML):

XAML:

<Grid MinHeight="400" MinWidth="400" ShowGridLines="False" x:Name="boardGrid">
    <Grid.Resources>
        <ScaleTransform x:Key="transform"
            ScaleX="{Binding ActualWidth, ElementName=boardGrid}"
            ScaleY="{Binding ActualHeight, ElementName=boardGrid}" />
    </Grid.Resources>
    <Grid.RowDefinitions>
        <RowDefinition></RowDefinition>
        <!-- more rows, 9 in total -->
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition></ColumnDefinition>
        <!-- more columns, 9 in total -->
    </Grid.ColumnDefinitions>

    <!-- example game pieces -->
    <Ellipse Stroke="Black" Fill="#333333" Grid.Row="3" Grid.Column="2" />
    <Ellipse Stroke="#777777" Fill="#FFFFFF" Grid.Row="4" Grid.Column="4" />
</Grid>

#:

int cols = 9;
int rows = 9;

// Draw horizontal lines
for (int row = 0; row < rows; row++)
{
    var path = new System.Windows.Shapes.Path();
    path.Stroke = Brushes.Black;
    path.StrokeThickness = 1;
    path.SetValue(RenderOptions.EdgeModeProperty, EdgeMode.Aliased);
    Grid.SetRow(path, row);
    Grid.SetColumnSpan(path, cols);
    Grid.SetZIndex(path, -1);

    double cellWidth = boardGrid.ColumnDefinitions[0].ActualWidth;
    double cellHeight = boardGrid.RowDefinitions[0].ActualHeight;
    double x1 = (cellWidth / 2) / boardGrid.ActualWidth;
    double y1 = (cellHeight / 2) / boardGrid.ActualHeight;
    double x2 = ((cellWidth * cols) - (cellWidth / 2)) / boardGrid.ActualWidth;
    double y2 = (cellHeight / 2) / boardGrid.ActualHeight;

    path.Data = new LineGeometry(new Point(x1, y1),
                                 new Point(x2, y2), 
                           (ScaleTransform)boardGrid.TryFindResource("transform"));
    boardGrid.Children.Add(path);
}

// Similar loop code follows for vertical lines...

,

Result when using paths

, , . :

1) , x1, x2, y1 y2, , 0 1, ScaleTransform ?

2) , Viewbox, ? , (. ). ( , .)

Stretched board loses aspect ratio

, . .

+5
1

A Viewbox "" , . , , ( ), .

Line Path , LineGeometries Data. ScaleTransform, , x y. ( ) 0..1:

<Grid x:Name="grid">
    <Grid.Resources>
        <ScaleTransform x:Key="transform"
                        ScaleX="{Binding ActualWidth, ElementName=grid}"
                        ScaleY="{Binding ActualHeight, ElementName=grid}"/>
    </Grid.Resources>
    <Path Stroke="Black" StrokeThickness="1">
        <Path.Data>
            <LineGeometry StartPoint="0.1,0.1" EndPoint="0.9,0.9"
                          Transform="{StaticResource transform}"/>
        </Path.Data>
    </Path>
</Grid>

, ScaleTransform ScaleX ScaleY ActualWidth ActualHeight :

<Grid x:Name="grid">
    <Grid.Resources>
        <ScaleTransform x:Key="transform"
                        ScaleX="{Binding ActualWidth, ElementName=grid}"
                        ScaleY="{Binding ActualWidth, ElementName=grid}"/>
    </Grid.Resources>
    ...
</Grid>

:

<Grid x:Name="grid" SizeChanged="grid_SizeChanged">
    <Grid.Resources>
        <ScaleTransform x:Key="transform"/>
    </Grid.Resources>
    ...
</Grid>

with a SizeChanged handler as follows:

private void grid_SizeChanged(object sender, SizeChangedEventArgs e)
{
    var transform = grid.Resources["transform"] as ScaleTransform;
    var minScale = Math.Min(grid.ActualWidth, grid.ActualHeight);
    transform.ScaleX = minScale;
    transform.ScaleY = minScale;
}
+10
source

All Articles