Search code examples
c#wpfcanvasdrawellipse

I've created a ellipse in c#...bt i want to move ellipse around the canvas using mouse.....can anyone help? And i'm new to c#


I've created a ellipse in c#, but I want to move the ellipse around the canvas using the mouse. Can anyone help? I'm new to c#.

Here's my code.

private Ellipse drawEllipse(Canvas aCanvas)
    {
        Ellipse newEllipse = new Ellipse();
        newEllipse.Width = 10;
        newEllipse.Height = 10;
        newEllipse.Fill = new SolidColorBrush(Colors.RoyalBlue);
        aCanvas.Children.Add(newEllipse);
        newEllipse.SetValue(Canvas.LeftProperty, aCanvas.ActualWidth / 2.0);
        newEllipse.SetValue(Canvas.TopProperty, aCanvas.ActualHeight / 2.0);
        return newEllipse;


    }

Solution

  • I'm also new to WPF so i'll explain you what i understood so far and how to do your ellipse. Since i'm a WPF beginner too, my explanation may be approximatation sometimes so please don't burn me alive. This post may be long

    WPF and MVVM

    First, you have a "View", which is XAML code (a description language with a XML-like syntax. Each tag correspond to a class in .NET Framework) and design everything you'll see. When using WPF, you should try to implement MVVM pattern. In this pattern, the design of the windows isn't the matter of the C# code, it's the matter of the "View" component, which is XAML code. You have a "ViewModel" component that prepare datas that will be kind of parameters for the View. The last component is the Model, which all the things behind that have nothing to do with the User Interface.

    Coding the view with C# (like you did) is called "code behind" and is a MVVM pattern mistake in most of the cases.

    The View is "bind" to a ViewModel. When binded, the View know the ViewModel class and can use its fields and properties. The best thing with using view models is when you modify a variable from ViewModel binded to the View, it will automatically update the view.

    Drawing and drag'n'drop and ellipse

    Here is an exemple of View for ellipse :

    In CanvasOverlayView.xaml

    <UserControl x:Class="Overlay.Views.CanvasOverlayView"
                 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
                 xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
                 xmlns:viewModels="clr-namespace:Overlay.ViewModels"
                 d:DataContext="{d:DesignInstance Type=viewModels:CanvasOverlayViewModel}"
                 mc:Ignorable="d">
    
    
    
    
    //You can declare a mouse event like this, with the appropriate event handlers in the .xaml.cs file 
    
    //  <Canvas MouseDown="UIElement_OnMouseDown" MouseMove="UIElement_OnMouseMove" MouseUp="UIElement_OnMouseUp">
    
    
    
    
    
    // however I use a completely separated file as ViewModel so i will use this :
         <i:Interaction.Triggers>
                            <i:EventTrigger EventName="MouseDown">
    
    
                     <i:InvokeCommandAction Command="{Binding Path=DataContext.MouseDownCommand}"
                                               CommandParameter="{Binding ElementName=CanvasOverlayView}"/>
                        </i:EventTrigger>
    
                        <i:EventTrigger EventName="MouseUp">
                            <i:InvokeCommandAction Command="{Binding Path=DataContext.MouseUpCommand}"
                                               CommandParameter="{Binding ElementName=CanvasOverlayView}"/>
                        </i:EventTrigger>
    
                        <i:EventTrigger EventName="MouseMove">
                            <i:InvokeCommandAction Command="{Binding Path=DataContext.MouseMoveCommand}"
                                               CommandParameter="{Binding ElementName=CanvasOverlayView}"/>
                        </i:EventTrigger>
                        <Path Stroke="{Binding Color}" StrokeThickness="{Binding Thickness}">
                            <Path.Data>
                                <EllipseGeometry
                                    Center="{Binding Center}"
                                    RadiusX="{Binding RadiusX}"
                                    RadiusY="{Binding RadiusY}"/>
                            </Path.Data>
                        </Path>
        </Canvas>
    

    To the details :

    x:Class="Overlay.Views.CanvasOverlayView"

    Precise the name of the associated CanvasOverlayView.xaml.cs (it's a partial class that is the C# needed to draw what is describe by xaml file if i understood correctly). You can use this file as ViewModel but it seems better to do it in a completely separated file, which is what i'll do in this exemple. Thus the xaml.cs will be almost empty (set the datacontext and initialise component).

    xmlns:viewModels="clr-namespace:Overlay.ViewModels"

    Alias the namespace where i have all my ViewModel to the name viewModels. This is used to find the ViewModel at the next line

    d:DataContext="{d:DesignInstance Type=viewModels:CanvasOverlayViewModel}" The DataContext is the class instance where the binding operation will get its datas. Here i set the DataContext (the source of my parameters if you want) to the right viewmodel. Each view got its own ViewModel

    Interaction.Triggers Here i'll create triggers for Command. The Command will allow me to handle my event in my ViewModel (if i used the xaml.cs as a viewmodel instead of a totally separate .cs file, i wouldn't need this).

    Pay attention to the Binding syntax, this is were you use your """parameters""" coming from viewmodel.

    Path and EllipseGeometry

    One of the way to draw an ellipse. I use this way because i prefered to work with the center and radius of the ellipse instead of Canvas.SetLeft, Width, Height etc... This makes drag operation easier (just need to update center in viewmodel and tadaaaam, the ellipse moved).

    In CanvasOverlayView.xaml.cs

    namespace Overlay.Views
    {
        public partial class CanvasOverlayView
        {
            public CanvasOverlayView()
            {
                DataContext = new CanvasOverlayViewModel();
                InitializeComponent();
            }    
        }
    }
    

    Here i give an instance of the ViewModel as DataContext.

    In CanvasOverlayViewModel.cs

    First and most important thing : the ViewModel have to implement INotifyPropertyChanged interface. This interface is essential since this is what makes your binded properties update automatically the View. Here is a minimal implementation with an exemple of use in a property.

    using System.ComponentModel;
        namespace Overlay.ViewModels
        {    
         public class CanvasOverlayViewModel : INotifyPropertyChanged
         {
           private int exemple;
           public int Exemple
           {
             get 
             {
                return exemple;
             }
             set
             {
               exemple = value;
               OnPropertyChanged(nameof(Exemple));  // IMPORTANT
             }
           } 
    
        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged(string propertyName) => 
    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    

    With this, if you have a "{Binding Exemple}" in your xaml, it will be updated automatically when you change Exemple value in your ViewModel.

    For your ellipse, you can create a class that will contain all the parameters of your ellipse, instance it in the ViewModel and bind it in your View.

    Commands and Event

    Here is an exemple of handling events with Command, for the View exemple above. This is not the only way to use ICommand of course.

    private ICommand m_MouseDownCommand;
    private ICommand m_MouseMoveCommand;
    private ICommand m_MouseUpCommand;
    private bool CanMove
    private Point center; // binded to the center property of EllipseGeometry in View.
    
    public Point Center
    {
       get
       {
         return center;
       }
       set
       {
         center = value;
         OnPropertyChanged(nameof(Exemple));
       }
    }
    
    // first parameter is the method that handle the event, the second is a bool method that define if the event is triggerable.
    // DelegateCommand is a custom class implementing ICommand, i'll give code below.
    
    public ICommand MouseDownCommand => m_MouseDownCommand ?? (m_MouseDownCommand = new DelegateCommand(OnMouseDown, CanMouseDown));
    
    public ICommand MouseMoveCommand => m_MouseMoveCommand ?? (m_MouseMoveCommand = new DelegateCommand(OnMouseMove, CanMouseMove));
    
    public ICommand MouseUpCommand => m_MouseUpCommand ?? (m_MouseUpCommand = new DelegateCommand(OnMouseUp, CanMouseUp));
    
    
    private bool CanMouseDown(object parameter)
    {
       return true;
    }
    
    private bool CanMouseMove(object parameter)
    {
       return CanMove;
    }
    
    private bool CanMouseUp(object parameter)
    {
       return true;
    }
    
    private void OnMouseDown(object parameter)
    {
       CanMove = true;
    }   
    
    
    private void OnMouseMove(object parameter)
    {
       // EllipseGeometry Center property is updated !
       Center = Mouse.GetPosition((IInputElement)parameter);
    }
    
    
    private void OnMouseUp(object parameter)
    { 
       CanMove = false;
    }   
    

    I'll give you my DelegateCommand class :

    using System;
    using System.Windows.Input;
    
        public class DelegateCommand : ICommand
        {
            private readonly Action<object> m_command;
    
            private readonly Predicate<object> m_canExecute;
    
            public DelegateCommand(Action<object> command, Predicate<object> canExecute = null)
            {
                if (command == null)
                {
                    throw new ArgumentNullException("command", "The delegate command is null");
                }
    
                m_canExecute = canExecute;
                m_command = command;
            }
    
            public event EventHandler CanExecuteChanged
            {
                add
                {
                    CommandManager.RequerySuggested += value;
                }
    
                remove
                {
                    CommandManager.RequerySuggested -= value;
                }
            }
    
            public void Execute(object parameter)
            {
                m_command(parameter);
            }
    
            public bool CanExecute(object parameter)
            {
                return m_canExecute == null || m_canExecute(parameter);
            }
        }
    

    I hope my explanation were clear. Sorry if it's not 200% technically accurate, i'm also new at WPF. This is a way to do it among many, probably not the best. Tell me if something is not clear or if somethig can be made more accurate.