Я создал эллипс в C#... но я хочу перемещать эллипс вокруг холста с помощью мыши..... кто-нибудь может помочь? И я новичок в C#
Я создал эллипс в C#, но я хочу переместить эллипс вокруг холста с помощью мыши. Кто-нибудь может помочь? Я новичок в C#.
Вот мой код
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;
}
1 ответ
Я также новичок в WPF, поэтому я объясню вам, что я понял до сих пор и как сделать ваш эллипс. Так как я новичок в WPF, мое объяснение иногда может быть приблизительным, поэтому, пожалуйста, не сжигайте меня заживо. Этот пост может быть длинным
WPF и MVVM
Во-первых, у вас есть "Представление", которое представляет собой код XAML (язык описания с XML-подобным синтаксисом. Каждый тег соответствует классу в.NET Framework) и разрабатываете все, что увидите. При использовании WPF вы должны попытаться реализовать шаблон MVVM. В этом шаблоне дизайн окон - это не код C#, а компонент "View", который является кодом XAML. У вас есть компонент "ViewModel", который подготавливает данные, которые будут своего рода параметрами для представления. Последний компонент - это Модель, которая не имеет ничего общего с пользовательским интерфейсом.
Кодирование представления с помощью C# (как вы сделали) называется "кодом позади" и в большинстве случаев является ошибкой шаблона MVVM.
Представление "привязывается" к ViewModel. При связывании View знает класс ViewModel и может использовать его поля и свойства. Лучше всего использовать модели представлений, когда вы изменяете переменную из ViewModel, привязанной к представлению, это автоматически обновляет представление.
Рисование и перетаскивание и эллипс
Вот пример просмотра для эллипса:
В 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>
К деталям:
х: Класс ="Overlay.Views.CanvasOverlayView"
Точное имя связанного CanvasOverlayView.xaml.cs (это частичный класс, который является C#, необходимым для рисования того, что описывается файлом xaml, если я правильно понял). Вы можете использовать этот файл в качестве ViewModel, но, кажется, лучше сделать это в полностью отдельном файле, что я и сделаю в этом примере. Таким образом, xaml.cs будет почти пустым (установите datacontext и инициализируйте компонент).
Xmlns: ViewModels="CLR-пространств имен:Overlay.ViewModels"
Псевдоним пространства имен, где у меня есть все мои ViewModel к имени viewModels. Это используется, чтобы найти ViewModel на следующей строке
d:DataContext="{d:DesignInstance Type=viewModels:CanvasOverlayViewModel}" DataContext - это экземпляр класса, в котором операция привязки получит свои данные. Здесь я устанавливаю DataContext (источник моих параметров, если хотите) в правильную модель представления. Каждое представление имеет свою собственную ViewModel
Interaction.Triggers Здесь я создам триггеры для Command. Команда позволит мне обрабатывать мое событие в моей ViewModel (если бы я использовал xaml.cs в качестве модели представления вместо полностью отдельного файла.cs, мне бы это не понадобилось).
Обратите внимание на синтаксис Binding, это если вы используете свои """параметры" "" из viewmodel.
Path and EllipseGeometry
Один из способов нарисовать эллипс. Я использую этот способ, потому что я предпочел работать с центром и радиусом эллипса вместо Canvas.SetLeft, Width, Height и т. Д. Это упрощает операцию перетаскивания (просто необходимо обновить центр в viewmodel и tadaaaam, эллипс перемещен),
В CanvasOverlayView.xaml.cs
namespace Overlay.Views
{
public partial class CanvasOverlayView
{
public CanvasOverlayView()
{
DataContext = new CanvasOverlayViewModel();
InitializeComponent();
}
}
}
Здесь я даю экземпляр ViewModel как DataContext.
В CanvasOverlayViewModel.cs
Первая и самая важная вещь: ViewModel должен реализовать интерфейс INotifyPropertyChanged. Этот интерфейс очень важен, поскольку именно благодаря этому ваши связанные свойства автоматически обновляют представление. Вот минимальная реализация с примером использования в свойстве.
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));
}
}
При этом, если у вас есть "{Binding Exemple}" в вашем xaml, он будет обновляться автоматически при изменении значения Exemple в вашей ViewModel.
Для вашего эллипса вы можете создать класс, который будет содержать все параметры вашего эллипса, скопировать его в ViewModel и связать его в вашем View.
Команды и события
Вот пример обработки событий с помощью Command, для примера View выше. Конечно, это не единственный способ использовать ICommand.
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;
}
Я дам вам мой класс DelegateCommand:
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);
}
}
Я надеюсь, что мои объяснения были ясны. Извините, если это не на 200% технически точно, я тоже новичок в WPF. Это способ сделать это среди многих, возможно, не самый лучший. Скажите, если что-то не ясно или что-то можно сделать более точным.