Nekeniehl
Nekeniehl

Reputation: 1691

Drawing on Canvas is not save correctly WPF

I have an image inside a canvas, the user can click in the image and a green point appear where the user has clicked.

Image shown

I would like to save the image on a memory stream for later use, all the image saving is fine, except for the point that seems to be not correctly drawn, my guess is regarding the screen ratio I had to apply to drew the green point because the image shown is smaller than the source image.

Image saved

XAML:

<Canvas x:Name="ImageCanvas"
        Grid.Column="1"
        Background="Transparent">

    <Image x:Name="SelectedPartImage"
           Width="{Binding ElementName=ImageCanvas, Path=ActualWidth}"
           Height="{Binding ElementName=ImageCanvas, Path=ActualHeight}"
           Panel.ZIndex="0"
           MouseLeftButtonDown="OnMouseLeftButtonDown"
           Source="{Binding SelectedPartImageSource}" />

    <Ellipse x:Name="EllipseClick"
             Width="15"
             Height="15"
             Panel.ZIndex="1"
             Fill="{StaticResource GreenColor}"
             Visibility="Hidden" />
</Canvas>

DrawEllipse (The method I use to draw the ellipe, AdjustToCanvas simply avoid the user to click outside the canvas where the image is)

public void DrawEllipse()
{
    // Adjust To Canvas
    (double x, double y) = AdjustToCanvas(Mouse.GetPosition(ImageCanvas));

    // Center Ellipse
    x -= EllipseClick.Width / 2;
    y -= EllipseClick.Height / 2;

    Canvas.SetLeft(EllipseClick, x);
    Canvas.SetTop(EllipseClick, y);
}

OnMouseLeftButtonDown (The click method to draw the ellipse)

public void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
    DrawEllipse();
    
    Point point = Mouse.GetPosition(SelectedPartImage);
    
    if (!(point.ConvertWPFPointToImagePoint(
                  SelectedPartImageSource.PixelWidth,
                  SelectedPartImageSource.PixelHeight,
                  SelectedPartImage.ActualWidth,
                  SelectedPartImage.ActualHeight))
    {
        return;
    }

    // CheckAlpha checks that the clicked point is inside the image and not outside of it.
    SelectPositionSuccess = CheckAlpha((int)point.X, (int)point.Y);

    EllipseClick.Visibility = SelectPositionSuccess ? Visibility.Visible : Visibility.Hidden;

    if (SelectPositionSuccess)
    {
        //Saving the Canvas
        RenderTargetBitmap rtb = new RenderTargetBitmap(
            (int)SelectedPartImageSource.Width,
            (int)SelectedPartImageSource.Height,
            96d,
            96d,
            System.Windows.Media.PixelFormats.Default);

        rtb.Render(ImageCanvas);

        //endcode as PNG
        PngBitmapEncoder pngEncoder = new PngBitmapEncoder();
        pngEncoder.Frames.Add(BitmapFrame.Create(rtb));

        //save to memory stream
        System.IO.MemoryStream ms = new System.IO.MemoryStream();
        pngEncoder.Save(ms);
        ms.Close();

        var bitmap = new BitmapImage();
        bitmap.BeginInit();
        bitmap.StreamSource = ms;
        bitmap.CacheOption = BitmapCacheOption.OnLoad;
        bitmap.EndInit();

        System.IO.File.WriteAllBytes("test.png", ms.ToArray());
    }
}

Any ideas how I can fix this behaviour?

Thanks in advance!

Upvotes: 1

Views: 384

Answers (1)

Shivani Katukota
Shivani Katukota

Reputation: 859

The UIElement is not rendered as soon as you set its properties in your code behind. It will only be done after that method is returned.

In this case, trying to save the EllipseClick just after updating its position will not save the new position but its original position which is (0,0) since its not set!

To fix this, either move the Save() method to another event or call

ImageCanvas.UpdateLayout(); 

after updating the position of the EllipseClick.

Upvotes: 1

Related Questions