【问题标题】:Displaying grid lines around individual pixels when zooming缩放时在单个像素周围显示网格线
【发布时间】:2016-03-07 18:44:56
【问题描述】:

我正在尝试在控件上绘制网格线的概念,并且想知道我可能需要进行哪些调整才能使其真正起作用。我在另一篇文章中发现了一些代码,可以在画布上绘制网格线 OnRender。看起来是这样的:

public class MyCanvas : Canvas
{
    public bool IsGridVisible = true;

    protected override void OnRender(System.Windows.Media.DrawingContext dc)
    {
        base.OnRender(dc);

        if (IsGridVisible)
        {
            // Draw GridLines
            Pen pen = new Pen(Brushes.Black, 1);
            pen.DashStyle = DashStyles.Solid;

            for (double x = 0; x < this.ActualWidth; x += 2)
            {
                dc.DrawLine(pen, new Point(x, 0), new Point(x, this.ActualHeight));
            }

            for (double y = 0; y < this.ActualHeight; y += 2)
            {
                dc.DrawLine(pen, new Point(0, y), new Point(this.ActualWidth, y));
            }
        }
    }

    public MyCanvas()
    {
        DefaultStyleKey = typeof(MyCanvas);
    }
}

这部分:y += 2 表示在绘制下一行之前要等待多少其他像素/点,尽管我不确定它的正确性。

这是 xaml:

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="*"/>
        <RowDefinition Height="30"/>
    </Grid.RowDefinitions>
    <ScrollViewer>
        <local:MyCanvas>
            <local:MyCanvas.LayoutTransform>
                <ScaleTransform ScaleX="{Binding ElementName=Slider, Path=Value}" ScaleY="{Binding ElementName=Slider, Path=Value}"/>
            </local:MyCanvas.LayoutTransform>
            <Image Canvas.Top="2" Canvas.Left="2"  Source="C:\Users\Me\Pictures\nyan-wallpaper2.jpg" Width="325" RenderOptions.BitmapScalingMode="NearestNeighbor"/>
        </local:MyCanvas>
    </ScrollViewer>
    <Slider x:Name="Slider" Maximum="500" Grid.Row="1" Value="1"/>
</Grid>

以下是上述结果的屏幕截图。

如您所见,网格线的大小会随着您的缩放而变化,并且线条本身不会围绕每个单独的像素对齐。我用红色突出显示了一个示例像素,以显示线条应该有多小,而不是它们实际上有多小。

我读到笔的粗细应该除以刻度值,但是,我通过将 Pen pen = new Pen(Brushes.Black, 1); 替换为 Pen pen = new Pen(Brushes.Black, 1 / 3); 来测试这一点,并将 MyCanvas 的 ScaleX 和 ScaleY 设置为 3。此时,没有线条完全显示出来了。

任何帮助都非常有价值!

【问题讨论】:

    标签: c# wpf canvas zooming gridlines


    【解决方案1】:

    对于任何好奇的人来说都是这样的:

    MainWindow.xaml.cs

    namespace Test
    {
        public class MyCanvas : Canvas
        {
            public bool IsGridVisible = false;
    
            #region Dependency Properties
    
            public static DependencyProperty ZoomValueProperty = DependencyProperty.Register("ZoomValue", typeof(double), typeof(MyCanvas), new FrameworkPropertyMetadata(1d, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnZoomValueChanged));
            public double ZoomValue
            {
                get
                {
                    return (double)GetValue(ZoomValueProperty);
                }
                set
                {
                    SetValue(ZoomValueProperty, value);
                }
            }
            private static void OnZoomValueChanged(DependencyObject Object, DependencyPropertyChangedEventArgs e)
            {
            }
    
            #endregion
    
            protected override void OnRender(System.Windows.Media.DrawingContext dc)
            {
                base.OnRender(dc);
                IsGridVisible = ZoomValue > 4.75 ? true : false;
                if (IsGridVisible)
                {
                    // Draw GridLines
                    Pen pen = new Pen(Brushes.Black, 1 / ZoomValue);
                    pen.DashStyle = DashStyles.Solid;
    
                    for (double x = 0; x < this.ActualWidth; x += 1)
                    {
                        dc.DrawLine(pen, new Point(x, 0), new Point(x, this.ActualHeight));
                    }
    
                    for (double y = 0; y < this.ActualHeight; y += 1)
                    {
                        dc.DrawLine(pen, new Point(0, y), new Point(this.ActualWidth, y));
                    }
                }
            }
    
            public MyCanvas()
            {
                DefaultStyleKey = typeof(MyCanvas);
            }
        }
    
        /// <summary>
        /// Interaction logic for MainWindow.xaml
        /// </summary>
        public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
            }
    
            private WriteableBitmap bitmap = new WriteableBitmap(500, 500, 96d, 96d, PixelFormats.Bgr24, null);
    
            private void Button_Click(object sender, RoutedEventArgs e)
            {
                int size = 1;
    
                Random rnd = new Random(DateTime.Now.Millisecond);
                bitmap.Lock(); // Lock() and Unlock() could be moved to the DrawRectangle() method. Just do some performance tests.
    
                for (int y = 0; y < 500; y++)
                {
                    for (int x = 0; x < 500; x++)
                    {
                        byte colR = (byte)rnd.Next(256);
                        byte colG = (byte)rnd.Next(256);
                        byte colB = (byte)rnd.Next(256);
    
                        DrawRectangle(bitmap, size * x, size * y, size, size, Color.FromRgb(colR, colG, colB));
                    }
                }
    
                bitmap.Unlock(); // Lock() and Unlock() could be moved to the DrawRectangle() method. Just do some performance tests.
                Image.Source = bitmap; // This should be done only once
            }
    
            public void DrawRectangle(WriteableBitmap writeableBitmap, int left, int top, int width, int height, Color color)
            {
                // Compute the pixel's color
                int colorData = color.R << 16; // R
                colorData |= color.G << 8; // G
                colorData |= color.B << 0; // B
                int bpp = writeableBitmap.Format.BitsPerPixel / 8;
    
                unsafe
                {
                    for (int y = 0; y < height; y++)
                    {
                        // Get a pointer to the back buffer
                        int pBackBuffer = (int)writeableBitmap.BackBuffer;
    
                        // Find the address of the pixel to draw
                        pBackBuffer += (top + y) * writeableBitmap.BackBufferStride;
                        pBackBuffer += left * bpp;
    
                        for (int x = 0; x < width; x++)
                        {
                            // Assign the color data to the pixel
                            *((int*)pBackBuffer) = colorData;
    
                            // Increment the address of the pixel to draw
                            pBackBuffer += bpp;
                        }
                    }
                }
    
                writeableBitmap.AddDirtyRect(new Int32Rect(left, top, width, height));
            }
        }
    }
    

    MainWindow.xaml

    <Window x:Class="Test.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:Test"
            mc:Ignorable="d"
            Title="MainWindow" 
            Height="Auto" 
            Width="Auto"
            WindowStartupLocation="CenterScreen"
            WindowState="Maximized">
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="*"/>
                <RowDefinition Height="30"/>
            </Grid.RowDefinitions>
            <ScrollViewer>
                <local:MyCanvas ZoomValue="{Binding ElementName=ScaleTransform, Path=ScaleX}">
                    <local:MyCanvas.LayoutTransform>
                        <ScaleTransform x:Name="ScaleTransform" ScaleX="{Binding ElementName=Slider, Path=Value}" ScaleY="{Binding ElementName=Slider, Path=Value}"/>
                    </local:MyCanvas.LayoutTransform>
                    <Image Canvas.Top="1" Canvas.Left="1" x:Name="Image" RenderOptions.BitmapScalingMode="NearestNeighbor"/>
                </local:MyCanvas>
            </ScrollViewer>
            <StackPanel Grid.Row="1" Orientation="Horizontal">
                <Slider x:Name="Slider" Maximum="100" Minimum="0.5" Value="1" Width="200"/>
                <Button Click="Button_Click" Content="Click Me!"/>
            </StackPanel>
        </Grid>
    </Window>
    

    我们生成一个带有随机颜色像素的位图,然后仅在近距离放大时渲染网格线。在性能方面,这实际上比预期的要好。不过,我应该注意,如果您尝试缩放到 50% 以下,应用程序会崩溃。不确定是否以分钟大小绘制网格线(IsGridVisible = true,其中 ZoomValue

    更新

    没有意识到网格线仍在画布内容的后面。还没有找到解决方案...

    更新 2

    替换:

    <local:MyCanvas ZoomValue="{Binding ElementName=ScaleTransform, Path=ScaleX}">
        <local:MyCanvas.LayoutTransform>
            <ScaleTransform x:Name="ScaleTransform" ScaleX="{Binding ElementName=Slider, Path=Value}" ScaleY="{Binding ElementName=Slider, Path=Value}"/>
            </local:MyCanvas.LayoutTransform>
            <Image Canvas.Top="1" Canvas.Left="1" x:Name="Image" RenderOptions.BitmapScalingMode="NearestNeighbor"/>
    </local:MyCanvas>
    

    与:

    <Grid>
        <Canvas>
            <Canvas.LayoutTransform>
                <ScaleTransform ScaleX="{Binding ElementName=Slider, Path=Value}" ScaleY="{Binding ElementName=Slider, Path=Value}"/>
            </Canvas.LayoutTransform>
            <Image Canvas.Top="5" Canvas.Left="5" x:Name="Image" RenderOptions.BitmapScalingMode="NearestNeighbor"/>
        </Canvas>
        <local:MyGrid ZoomValue="{Binding ElementName=ScaleTransform, Path=ScaleX}">
            <local:MyGrid.LayoutTransform>
                <ScaleTransform x:Name="ScaleTransform" ScaleX="{Binding ElementName=Slider, Path=Value}" ScaleY="{Binding ElementName=Slider, Path=Value}"/>
            </local:MyGrid.LayoutTransform>
        </local:MyGrid>
    </Grid>
    

    我相信性能会再次提升,因为我们使用了更简单的控件来显示网格线,此外,网格线可以放置在所需控件的下方或上方。

    更新 3

    我决定发布我的最新解决方案,该解决方案效率显着提高,并且都可以在 XAML 中完成:

    <Grid>
        <Grid.Background>
            <DrawingBrush Viewport="0,0,5,5" ViewportUnits="Absolute" TileMode="Tile">
                <DrawingBrush.Drawing>
                    <DrawingGroup>
                        <DrawingGroup.Children>
                            <GeometryDrawing Geometry="M-.5,0 L50,0 M0,10 L50,10 M0,20 L50,20 M0,30 L50,30 M0,40 L50,40 M0,0 L0,50 M10,0 L10,50 M20,0 L20,50 M30,0 L30,50 M40,0 L40,50">
                                <GeometryDrawing.Pen>
                                    <Pen Thickness="1" Brush="Black" />
                                </GeometryDrawing.Pen>
                            </GeometryDrawing>
                        </DrawingGroup.Children>
                    </DrawingGroup>
                </DrawingBrush.Drawing>
            </DrawingBrush>
        </Grid.Background>
    </Grid>
    

    【讨论】:

      猜你喜欢
      • 2023-04-02
      • 1970-01-01
      • 2018-01-08
      • 2017-03-11
      • 2018-01-15
      • 1970-01-01
      • 1970-01-01
      • 2014-02-17
      相关资源
      最近更新 更多