0
votes

I'm very new to WPF and I'm trying to create a picture cropping application. The program has a rectangle that can be dragged to crop the picture (like in Microsoft Paint). The way its set up is that both image and canvas which holds the rectangle are children of grid container. Grid has events for mousemove, mouseleftbuttondown and up to calculate the cropped image. My program is cropping the image fine but the problem occurs when the window is maximized which throws an exception error below:

An unhandled exception of type 'System.ArgumentException' occurred in PresentationCore.dll

Additional information: Value does not fall within the expected range.

This exception is caused by a line in my Go_Click method:

BitmapSource bs = new CroppedBitmap(LoadedImage.Source as BitmapSource, rcFrom);

I stepped into the program and found out that at first it was caused by setting the width and height of Image (LoadedImage.Width and LoadedImage.Height) to auto or NAN. This makes the value NAN which mess up the calculation and throws of the exception. I changed it to ActualWidth (LoadedImage.ActualWidth and ActualHeight) and I only got it work partially since it still throws an exception whenever I crop the image while window is maximized.

Below is source code for my project.

XAML:

<Window x:Class="WpfApplication2.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow">
<Grid ShowGridLines="True">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="2*"/>
        <ColumnDefinition/>
    </Grid.ColumnDefinitions>
    <GroupBox Header="Loaded Image:" Grid.Column="0">
    <Grid x:Name="LoadedImage" MouseLeftButtonDown="image1_MouseLeftButtonDown" MouseMove="image1_MouseMove" MouseLeftButtonUp="image1_MouseLeftButtonUp" ShowGridLines="True">
        <Image x:Name="loaded" Margin="10" Stretch="Fill"/>
        <Canvas  x:Name="BackPanel" Margin="10">
            <Rectangle x:Name="selectionRectangle" Stroke="LightBlue" Fill="#220000FF" Visibility="Collapsed"/>
        </Canvas>
    </Grid>
    </GroupBox>
    <StackPanel Grid.Column="1" x:Name="MyPanel" HorizontalAlignment="Left" Orientation="Vertical" Height="340" Width="262">
        <GroupBox x:Name="Box2" Header="Preview Box:">
            <Image x:Name="PreviewImage" MaxWidth="240" MaxHeight="240" Stretch="Fill" MinWidth="240" MinHeight="240"/>
        </GroupBox>
        <StackPanel Height="61" Orientation="Horizontal" HorizontalAlignment="Center">
                <Button MinWidth="93" Height="32"  Click="Import_Click">Import</Button>
                <Button Name="Crop" MinWidth="93" Height="32" Margin="10,0,0,0" Click="Go_Click">Crop</Button>
        </StackPanel>
    </StackPanel>
</Grid>

And this is the code behind:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Microsoft.Win32;
using System.IO;

namespace WpfApplication2
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
public partial class MainWindow : Window
{
    private bool isDragging = false;
    private Point anchorPoint = new Point();
    public MainWindow()
    {
        InitializeComponent();
    }

    //Import Button event handler which opens an open file dialog
    //to choose an image file. If the return value from ShowDialog
    //is true (user choose image and click's OK)it will store the image to file,
    //else it would exit open file dialog.
    private void Go_Click(object sender, RoutedEventArgs e)
    {
        if (loaded.Source != null)
        {
         Rect rect1 = new Rect(Canvas.GetLeft(selectionRectangle),      Canvas.GetTop(selectionRectangle), selectionRectangle.Width, selectionRectangle.Height);
            Int32Rect rcFrom = new Int32Rect();
            rcFrom.X = (int)((rect1.X) * (loaded.Source.Width) /           `enter code here`(loaded.ActualWidth));
            rcFrom.Y = (int)((rect1.Y) * (loaded.Source.Height) / (loaded.ActualHeight));
            rcFrom.Width = (int)((rect1.Width) * (loaded.Source.Width) / (loaded.ActualWidth));
            rcFrom.Height = (int)((rect1.Height) * (loaded.Source.Height) / (loaded.ActualHeight));
            BitmapSource bs = new CroppedBitmap(loaded.Source as BitmapSource, rcFrom);
            PreviewImage.Source = bs;
        }
    }
    private void image1_MouseMove(object sender, MouseEventArgs e)
    {
        if (isDragging)
        {
            double x = e.GetPosition(BackPanel).X;
            double y = e.GetPosition(BackPanel).Y;
            selectionRectangle.SetValue(Canvas.LeftProperty, Math.Min(x, anchorPoint.X));
            selectionRectangle.SetValue(Canvas.TopProperty, Math.Min(y, anchorPoint.Y));
            selectionRectangle.Width = Math.Abs(x - anchorPoint.X);
            selectionRectangle.Height = Math.Abs(y - anchorPoint.Y);

            if (selectionRectangle.Visibility != Visibility.Visible)
              selectionRectangle.Visibility = Visibility.Visible;
        }
    }
    private void image1_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
        if (isDragging == false)
        {
            anchorPoint.X = e.GetPosition(BackPanel).X;
            anchorPoint.Y = e.GetPosition(BackPanel).Y;
            isDragging = true;
        }

    }
    private void image1_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
    {
        if (isDragging)
        {
            isDragging = false;
            if (selectionRectangle.Width > 0)
            {
                Crop.Visibility = System.Windows.Visibility.Visible;
                Crop.IsEnabled = true;
            }
            if (selectionRectangle.Visibility != Visibility.Visible)
                selectionRectangle.Visibility = Visibility.Visible;
        }
    }
    private void RestRect()
    {
        selectionRectangle.Visibility = Visibility.Collapsed;
        isDragging = false;
    }
    private void Import_Click(object sender, RoutedEventArgs e)
    {
        OpenFileDialog dlg = new OpenFileDialog();
        dlg.FileName = "Picture";
        dlg.Filter = "All Pictures (*.jpeg)|*.jpg";

        if (dlg.ShowDialog() == true)
        {
            loaded.Source = new BitmapImage(new Uri(dlg.FileName));
        }
    }

 }
}

Like I said i'm new to programming WPF and I apologize if the way i have my code written may not be the best. But any advise would help on how I should handle this issue.

Thanks in advance.

1
It would help a lot if you could boil this down to a minimal, complete and verifyabe example. Most people don't like to read through all of your code.Clemens
I eliminated all the unnecessary codes in my example as suggested by Clemens and I hope what I have left is short enough. While I was restructuring my code it seems to have worked properly and I don't know what change I did to trigger it. But I noticed that if a portion of the image is selected for cropping and i change it from original to maximize the rectangle gets moved some degree to the left. This tends to throw an exception which is caused by the rectangle to be moved out of range. What would be the best way of handling this? Create an exception handler or change my coding.mcvanta

1 Answers

0
votes

You need to get LoadedImage position relative to GridLoadedImage, and use it when creating crop:

private void Go_Click(object sender, RoutedEventArgs e)
    {
        if (LoadedImage.Source != null)
        {
            var imagePosition = loaded.TransformToAncestor(LoadedImage).Transform(new Point(0, 0));
            Rect rect1 = new Rect(Math.Max(Canvas.GetLeft(selectionRectangle) - imagePosition.X, 0), Canvas.GetTop(selectionRectangle), selectionRectangle.Width, selectionRectangle.Height);
            Int32Rect rcFrom = new Int32Rect();
            rcFrom.X = (int)((rect1.X) * (LoadedImage.Source.Width) / (LoadedImage.ActualWidth));
            rcFrom.Y = (int)((rect1.Y) * (LoadedImage.Source.Height) / (LoadedImage.ActualHeight));
            rcFrom.Width = (int)((rect1.Width) * (LoadedImage.Source.Width) / (LoadedImage.ActualWidth));
            rcFrom.Height = (int)((rect1.Height) * (LoadedImage.Source.Height) / (LoadedImage.ActualHeight));
            BitmapSource bs = new CroppedBitmap(LoadedImage.Source as BitmapSource, rcFrom);
            PreviewImage.Source = bs;
        }
    }