c# - 使用 Viewbox 缩放/拉伸(stretch)在 WPF 中维护固定粗细的线条

标签 c# wpf antialiasing viewbox

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

但是,我也希望线条始终以 1 像素的宽度呈现,所以我使用:

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

这使得线条的初始外观非常理想,但是一旦您开始调整窗口大小时,拉伸(stretch)/缩放就会开始,并且线条再次变成 1 像素和 2 像素粗的混合物。

有什么方法可以让线条始终为 1 像素粗,并且还允许调整窗口/网格的大小?

更新 - 根据 Clemens 的建议使用路径几何

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

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

我正在制作游戏板(用于 Go 的游戏,以防有助于理解布局)。我有一个 9x9 网格,我打算通过简单地向特定网格单元添加一个椭圆来放置游戏 block 。

然而,为了在棋盘上绘制底层线,我需要绘制横跨棋盘的单元格中间的线(在 Go 中,棋子放置在交叉点上,而不是单元格的中间)。

很可能我采取了完全错误的方法,请随时告诉我沿着不同的路线重新开始,而不是在当前结构中进行修改。

到目前为止我是这样做的(由于坐标的计算方式,我以编程方式添加路径。不确定是否可以在 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...

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

Result when using paths

这几乎就是我想要的样子。它又向我提出了 2 个问题:

1) 我在计算 x1 时采用了正确的方法吗? , x2 , y1y2通过将它们除以总板宽度来创建一个介于 0 和 1 之间的数字,以便随后可以将 ScaleTransform 应用于它们?

2) 现在我没有使用 Viewbox还有,我如何完成固定比例缩放?如果我放大我的窗口,板会不成比例地拉伸(stretch)(见下图)。 (不过,它不再对线条进行抗锯齿处理,这很棒。)

Stretched board loses aspect ratio

我知道这有点像一个单一的帖子。非常感谢您的耐心等待和回复。

最佳答案

Viewbox 只能“在视觉上”缩放其子元素,包括任何渲染笔划的粗细。您需要的是仅适用于线条(或其他形状)几何形状但不影响笔划的缩放。

您可以使用 Path 来绘制线条,而不是使用 Line 对象使用转换后的对象 LineGeometries对于他们的 Data属性(property)。你可以创建一个 ScaleTransform通过使用网格的宽度和高度作为 x 和 y 方向的缩放因子,从逻辑坐标缩放到视口(viewport)坐标。每个 LineGeometry(或任何其他几何图形)都将使用 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 属性绑定(bind)到 ActualWidth网格的实际高度:

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

使用这样的 SizeChanged 处理程序:

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

关于c# - 使用 Viewbox 缩放/拉伸(stretch)在 WPF 中维护固定粗细的线条,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15320099/

相关文章:

c# - 在 Ubuntu 上使用 VSCode 编译 C# 项目

c# - 正则表达式在第二次出现下划线后匹配下划线之间的单词

c# - 在 ListView 的 gridview 中使用空格/输入选中/取消选中复选框 - wpf

wpf - 如何将 StackPanel 绑定(bind)到我的 ViewModel?

text - CSS 是否支持 "crisp, sharp etc"等文本抗锯齿?

xna - 在 XNA 4.0 winforms 中使用 Nvidia FXAA(着色器抗锯齿)的示例?

python - python中多边形绘图的亚像素精度?

c# - 如何以编程方式获取 Azure DevOps 中所有已完成的拉取请求的列表?

c# - 在 C# 中使用私钥对数据进行签名

wpf - 加载页面时触发 Textbox.TextChanged。我该如何预防?