wpfgridscrollviewer

WPF Scrollviewer arrange make it go up


SETUP: In my canvas, I'm trying to take a picture of my canvas elements that are all contained in a grid. There is "Photo" button inside a row in the grid that will open the dialog and take a picture of the canvas. In the 2nd row is where the scrollviewer is showing up.

ISSUE: I'm working off of this example in SO. It works to the most part, but my issue is when the photo code fires and specifically the call to the "arrange" method, the scrollviewer is moved up in the screen and becomes on the same level with the row #1, while it should remain in #2 of the grid.

enter image description here enter image description here

CODE: Here is my xaml code:

<Window x:Class="WpfApp1.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:WpfApp1"
    mc:Ignorable="d"
    Title="My Layout" Height="400" Width="420" Left="0" Top="0">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="50" />
            <RowDefinition Height="550"  />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="100" />
            <ColumnDefinition Width="300" />
        </Grid.ColumnDefinitions>
        <Button Grid.Row="0" Grid.Column="0" x:Name="Draw" Content="Draw" FontSize="14" VerticalAlignment="Center" Click="Photo_Click" />
        <ScrollViewer Grid.Row="1" Grid.ColumnSpan="2" VerticalScrollBarVisibility="Visible" HorizontalScrollBarVisibility="Visible" Name="sv">
        </ScrollViewer>
    </Grid>
</Window>

My code behind:

    private void Photo_Click(object sender, RoutedEventArgs e)
    {
        ExportToImage(sv);
    }

    public static void ExportToImage(ScrollViewer sv)
    {
        var dlg = new SaveFileDialog
        {
            Filter = "PNG Files (*.png)|*.png",
            DefaultExt = "png",
            FilterIndex = 2,
            FileName = "DesignerImage.png",
            RestoreDirectory = true
        };

        Nullable<bool> result = dlg.ShowDialog();
        string path = dlg.FileName;

        if (result == true)
        {

            try
            {
                RenderTargetBitmap renderBitmap = new RenderTargetBitmap(
                         (int)sv.ActualWidth, (int)sv.ActualHeight,
                          96d, 96d, PixelFormats.Pbgra32);
                sv.Measure(new Size((int)sv.ActualWidth, (int)sv.ActualHeight));
                sv.Arrange(new Rect(new Size((int)sv.ActualWidth, (int)sv.ActualHeight)));

                renderBitmap.Render(sv);
                BitmapEncoder imageEncoder = new PngBitmapEncoder();
                imageEncoder.Frames.Add(BitmapFrame.Create(renderBitmap));
                using (FileStream file = File.Create(path))
                {
                    imageEncoder.Save(file);

                }
            }
            catch (Exception ex)
            {

            }
        }
    }

Solution

  • Why don't you use the accepted answer from the link you provided? It seems more suited to your needs than the one you are using.

    Here is an example implementation in your context:

    xaml code:

    <Window x:Class="WpfApp1.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:WpfApp1"
    mc:Ignorable="d"
    Title="My Layout" Height="400" Width="420" Left="0" Top="0">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="50" />
            <RowDefinition Height="550"  />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="100" />
            <ColumnDefinition Width="300" />
        </Grid.ColumnDefinitions>
        <Button Grid.Row="0" Grid.Column="0" x:Name="Draw" Content="Draw" FontSize="14" VerticalAlignment="Center" Click="Photo_Click" />
        <ScrollViewer Grid.Row="1" Grid.ColumnSpan="2" VerticalScrollBarVisibility="Visible" HorizontalScrollBarVisibility="Visible" Name="sv">
            <StackPanel x:Name="ScrollViewerContent" Orientation="Vertical">
                <Button Height="100">1</Button>
                <Button Height="100">2</Button>
                <Button Height="100">3</Button>
                <Button Height="100">4</Button>
                <Button Height="100">5</Button>
                <Button Height="100">6</Button>
                <Button Height="100">7</Button>
                <Button Height="100">8</Button>
                <Button Height="100">9</Button>
            </StackPanel>
        </ScrollViewer>
    </Grid>
    

    Code behind:

    private void Photo_Click(object sender, RoutedEventArgs e)
    {
        ScrollViewerContent.SnapShotPNG(new Uri(@"C:\Temp\DesignerImage.png"), 1);
    }
    

    Where the SnapShotPNG method is defined in a extension class:

    public static class WPFExtensions
    {
        public static void SnapShotPNG(this UIElement source, Uri destination, int zoom)
        {
            //Code from the answer here
        }
    }