WPF Convert Code Behind to follow MVVM

BigH61 581 Reputation points
2022-02-14T11:17:32.077+00:00

I am continuing with my journey trying to learn WPF by creating simple projects then once I have a working version I look to convert the code behind to follow MVVM.
I have included a few snippets of the code behind that I am struggling to convert to MVVM.
I am able to get so for i.e. Creating RelayCommands but am struggling with the following specifics.

How to add Controls.Image to ImageCanvas?
How to clear the ImageCanvas which will contain the image and some shapes?
How to create a MouseRightButtonDown Event for the Controls.Image?
How to Add and Clear Shapes added to the ImageCanvas?

These sorts of issues are repeated throughout my code and once I can resolve it I'm hopeful I can resolve it for the other snippets of code.
There is also a RotateTransform on the ImageCanvas am I correct in believing this has to remain in the code behind?

private void LoadImage()
        {
            OpenFileDialog openFileDialog = new OpenFileDialog();
            openFileDialog.Title = "Select Image";
            openFileDialog.Filter = "Bitmaps|*.bmp|Emf files|*.Emf|Exif files|*.Exif|GIF files|*.gif|JPEG files|*.jpg*;.jpeg|PNG files|*.png|TIFF files|*.tif|WMF files|*.Wmf|Image files|*.bmp;*.Emf;*.Exif;*.gif;*.jpeg;*.jpg;*.png;*.tif;*.Wmf";

            if (openFileDialog.ShowDialog() == true)
            {
                ImageCanvas.Children.Clear();
                Bitmap bm = new Bitmap(openFileDialog.FileName);
                imageOriginalWidth = bm.Width;
                imageOriginalHeight = bm.Height;
                BitmapImage image = new BitmapImage(new Uri(openFileDialog.FileName));
                MainImage = new System.Windows.Controls.Image();
                MainImage.Source = image;
                ImageCanvas.Children.Add(MainImage);
                MainImage.HorizontalAlignment = HorizontalAlignment.Left;
                MainImage.MouseRightButtonDown += MainImageRightMouseButton;
                bm.Dispose();
            }
        }


System.Windows.Point imagePoint;

        private void MainImageRightMouseButton(object sender, MouseButtonEventArgs e)
        {
            imagePoint = Mouse.GetPosition(MainImage);
            ContextMenu contextMenu = this.FindResource("cmImage") as ContextMenu;
            contextMenu.IsOpen = true;
        }


private void DrawShapes(System.Drawing.Rectangle rectangle)
        {
            ScaleFactor();
            System.Windows.Shapes.Rectangle rect;
            rect = new System.Windows.Shapes.Rectangle();
            rect.Stroke = new SolidColorBrush(Colors.Red);
            rect.Fill = new SolidColorBrush(Colors.Transparent);
            rect.Width = rectangle.Width * scaleFactor;
            rect.Height = rectangle.Height * scaleFactor;
            Canvas.SetLeft(rect, rectangle.X * scaleFactor);
            Canvas.SetTop(rect, rectangle.Y * scaleFactor);
            ImageCanvas.Children.Add(rect);
            rect.MouseRightButtonDown += RectangleRightMouseButton;           
        }


private void ContextMenuDelete_Click(object sender, RoutedEventArgs e)
        {
            if (selectedRectangle != null)
            {
                ImageCanvas.Children.Remove(selectedRectangle);
                selectedRectangle.MouseRightButtonDown += RectangleRightMouseButton;
            }
            else
            {
                MessageBox.Show("No Rectangle was not selected.");
            }
        }

I appreciate there are a lot of questions within this but your assistance would be much appreciated.

Developer technologies Windows Presentation Foundation
0 comments No comments
{count} votes

Accepted answer
  1. Peter Fleischer (former MVP) 19,341 Reputation points
    2022-02-14T16:35:46.197+00:00

    Hi,
    you can use Interactivity.dll (from Nuget) to get reference of Canvas in ViewModel like this:

    <Window x:Class="WpfApp1.Window78"
            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:WpfApp78"
            xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
            mc:Ignorable="d"
            Title="Canvas with PolyLine" Height="450" Width="800">
      <Window.DataContext>
        <local:ViewModel/>
      </Window.DataContext>
      <Grid>
        <Grid.RowDefinitions>
          <RowDefinition/>
          <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <Canvas Grid.Row="0" Background="White">
          <i:Interaction.Behaviors>
            <local:CanvasBehavior/>
          </i:Interaction.Behaviors>
    

    ...

      public class ViewModel
      {
        public ViewModel()
        {
        public Canvas CanvasView { get; set; }
    

    ...

      if (CanvasView.Children.Count == 0) // first call, create polyline and add to Canvas
      {
        pline.Stroke = new SolidColorBrush(Color.FromRgb(60, 125, 200));
        pline.StrokeThickness = 1;
        pline.Points = pointCollection;
        CanvasView.Children.Add(pline);
      }
    

    ...

      /// <summary>
      /// Behavior to get Canvas in ViewModel (MVVM)
      /// </summary>
      public class CanvasBehavior : Behavior<Canvas>
      {
        protected override void OnAttached() =>
          AssociatedObject.Loaded += AssociatedObject_Loaded;
        private void AssociatedObject_Loaded(object sender, RoutedEventArgs e) =>
          ((ViewModel)(AssociatedObject.DataContext)).CanvasView = AssociatedObject;
      }
    
    1 person found this answer helpful.

0 additional answers

Sort by: Most helpful

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.