簡體   English   中英

使用Viewbox縮放/拉伸在WPF中維護固定厚度線

[英]Maintaining fixed-thickness lines in WPF with Viewbox scaling/stretching

我有一個<Grid> ,它包含一些垂直和水平<Line> s。 我希望網格可以根據窗口大小進行擴展,並保持其寬高比,因此它包含在<Viewbox Stretch="Uniform">

但是,我也希望線條總是以1像素的寬度渲染,所以我使用:

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

這使得線條的初始外觀非常理想,但是一旦開始調整窗口大小,拉伸/縮放就會開始,並且線條再次成為1和2像素厚度的混合。

有沒有辦法讓線條總是1像素厚,還可以調整窗口/網格的大小?

更新 - 根據Clemens的建議使用路徑幾何

@Clemens - 感謝您突出顯示線條和路徑之間的渲染差異。 當我嘗試使用你的例子重新編寫我的代碼時,我會感覺到我正在為自己挖掘更多的漏洞,並沒有真正掌握整個概念(完全是我的錯,不是你的,我只是WPF的新手) 。

我將添加一些截圖來說明以下描述:

我正在制作一個游戲板(對於Go游戲,以防萬一有助於理解布局)。 我有一個9x9網格,我打算通過簡單地將橢圓添加到特定網格單元來放置游戲塊。

然而,為了在板上繪制下面的線,我需要在板上繪制與單元格中間相交的線(在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...

這是我在使用上面的代碼時得到的

使用路徑時的結果

這幾乎就是我想要的樣子。 它為我提出了兩個問題:

1)我正在采用正確的方法來計算x1x2y1y2值,方法是將它們乘以總板寬度以創建0到1之間的數字,以便然后可以將ScaleTransform應用於它們嗎?

2)現在我不再使用Viewbox了,如何實現固定比例縮放? 如果我放大窗戶,電路板會不成比例地伸展(見下圖)。 (它不再對行進行反別名,這很棒。)

拉伸板失去縱橫比

我知道這將成為一個單一的帖子。 我非常感謝你的耐心和回應。

Viewbox只能“直觀地”縮放其子元素,包括任何渲染筆划的粗細。 您需要的是僅適用於線條(或其他形狀)的幾何體的縮放,但不會影響筆划。

您可以通過使用已轉換的LineGeometries作為其Data屬性的Path對象來繪制線條,而不是使用Line對象。 您可以創建一個ScaleTransform ,它使用Grid的寬度和高度作為x和y方向的縮放因子,從邏輯坐標縮放到視口坐標。 每個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屬性綁定到Grid的ActualWidthActualHeight

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM