通过Viewbox缩放/拉伸在WPF中维护固定厚度的线 [英] Maintaining fixed-thickness lines in WPF with Viewbox scaling/stretching

查看:117
本文介绍了通过Viewbox缩放/拉伸在WPF中维护固定厚度的线的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个<Grid>,其中包含一些垂直和水平<Line>.我希望网格可以随窗口大小缩放,并保留其长宽比,因此包含在<Viewbox Stretch="Uniform">中.

I have a <Grid> which contains some vertical and horizontal <Line>s. I want the grid to be scalable with the window size, and retain its aspect ratio, so it's contained in a <Viewbox Stretch="Uniform">.

但是,我也希望线条始终以1像素的宽度进行渲染,所以我使用:

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

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

这使线条的初始外观很理想,但是一旦您开始调整窗口的大小,就会开始拉伸/缩放,线条再次变成1像素和2像素的混合.

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

有什么办法使线条始终保持1像素粗,并且还允许调整窗口/网格的大小?

Is there any way to have the lines always be 1 pixel thick and also allow for resizing of the window/grid?

更新-按照Clemens的建议使用路径几何

@Clemens-感谢您强调线和路径之间的渲染差异.当我尝试使用您的示例重新编写代码时,我感到下沉的感觉是我在为自己挖更多的坑,而不是真正掌握整个概念(完全是我的错,不是您的错,我只是WPF的新手)

@Clemens - Thanks for highlighting the rendering differences between lines and paths. As I try to rework my code using your example, I'm getting that sinking feeling that I'm digging more holes for myself and not really grasping the entire concept (entirely my fault, not yours, I'm just new to WPF).

我将添加一些屏幕截图以说明以下说明:

I'll add some screenshots to illustrate the following description:

我正在制作游戏板(用于开始有助于彻底了解布局的案例).我有一个9x9的网格,并且我打算通过将椭圆添加到特定的网格单元来放置游戏块.

I'm making a game board (for the game of Go, in case that helps understand the layout at all). I have a 9x9 grid, and I'm planning on placing the game pieces by simply adding an ellipse to a particular grid cell.

但是,要在板上绘制底层线条,我需要绘制与整个板上单元格的中部相交的线(在Go中,块放置在相交处,而不是单元格的中部).

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

很可能是我采用了完全错误的方法,请随时告诉我要重新开始另一条路,而不是在当前结构中四处乱窜.

It could well be that I'm taking entirely the wrong approach, please feel free to tell me to start again down a different route, rather than hacking around within the current structure.

到目前为止,这是我的操作方式(由于坐标的计算方式,我以编程方式添加了路径.不确定是否可以在XAML中全部完成操作):

This is how I've done it so far (I'm adding the paths programatically, due to the way the coordinates are calculated. Not sure if it can all be done in 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>

C#:

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...

这是我在使用上面的代码时得到的

This is what I get when using the code above

这几乎就是我想要的样子.还为我提出了两个问题:

This is pretty much how I want it to look. It's raised 2 more questions for me:

1)我是否采用正确的方法来计算x1x2y1y2值,方法是将它们除以板的总宽度,以创建介于0和1之间的数字以便可以将ScaleTransform应用于它们?

1) Am I taking the right approach where I'm calculating the x1, x2, y1 and y2 values by diving them by the total board width to create a number between 0 and 1, so that the ScaleTransform can then be applied to them?

2)现在,我不再使用Viewbox,如何完成固定比例缩放?如果我扩大窗口,则木板将不成比例地伸展(请参见下图). (不过,它不再对行进行反锯齿,这很棒.)

2) Now that I'm not using a Viewbox any more, how do I accomplish fixed-ratio scaling? If I enlarge my window, the board stretches out of proportion (see image below). (It doesn't anti-alias the lines any more though, which is great.)

我知道这将成为一个整体的职位.感谢您的耐心配合和回复.

I know this is getting to be a bit of a monolithic post. I'm very grateful for your patience and responses.

推荐答案

Viewbox只能可视地"缩放其子元素,包括任何渲染笔触的粗细.您需要的缩放比例仅适用于线条(或其他形状)的几何形状,而使笔触不受影响.

A Viewbox can only "visually" scale its child element, including the thickness of any rendered stroke. What you need is a scaling that only applies to the geometry of the lines (or other shapes), but leaves the stroke unaffected.

您可以使用 使用转换后的 LineGeometries 作为其 Data 属性.您可以创建一个 ScaleTransform ,它可以从逻辑坐标缩放为视口坐标通过使用Grid的宽度和高度作为x和y方向上的比例因子.每个LineGeometry(或任何其他Geometry)将使用范围为0..1的逻辑坐标:

Instead of using Line objects, you could draw your lines by Path objects that use transformed LineGeometries for their Data property. You could create a ScaleTransform that scales from logical coordinates to viewport coordinates by using the Grid's width and height as scaling factors in x and y direction. Each LineGeometry (or any other Geometry) would use logical coordinates in the range 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的ScaleXScaleY属性都绑定到网格的ActualWidthActualHeight:


In order to get a uniform scaling you may simply bind both the ScaleTransform's ScaleX and ScaleY properties to either the ActualWidth or ActualHeight of the Grid:

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

您还可以根据宽度和高度的最小值计算出统一的缩放比例,后面有一些代码:

You may also calculate the uniform scaling factor from the minimum value of the width and height, with a bit of code behind:

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

具有这样的SizeChanged处理程序:

with a SizeChanged handler like this:

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;
}

这篇关于通过Viewbox缩放/拉伸在WPF中维护固定厚度的线的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆