我在 c# 中创建了一个椭圆.BT 我想使用鼠标在画布上移动椭圆...谁能帮忙?我是 c# 的新手
本文关键字:移动 新手 我是 鼠标 创建 我在 BT 一个 | 更新日期: 2023-09-27 17:56:38
>我在 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;
}
我也是 WPF 的新手,所以我会向你解释我到目前为止的理解以及如何做你的椭圆。由于我也是WPF初学者,我的解释有时可能是近似的,所以请不要活活烧死我。这篇文章可能很长
WPF 和 MVVM
首先,您有一个"视图",它是 XAML 代码(一种具有类似 XML 语法的描述语言。每个标记对应于 .NET Framework 中的一个类),并设计你将看到的所有内容。使用 WPF 时,应尝试实现 MVVM 模式。在此模式中,窗口的设计不是 C# 代码的问题,而是"视图"组件(即 XAML 代码)的问题。您有一个"视图模型"组件,用于准备将成为视图参数的数据。最后一个组件是模型,它背后的所有内容都与用户界面无关。
使用 C# 对视图进行编码(就像您所做的那样)称为"代码隐藏",在大多数情况下是 MVVM 模式错误。
视图被"绑定"到视图模型。绑定时,视图知道 ViewModel 类,并可以使用其字段和属性。使用视图模型最好的办法是,当您修改绑定到视图的 ViewModel 中的变量时,它将自动更新视图。
绘图和拖放和椭圆
这是椭圆视图的示例:
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>
详情 :
x:Class="Overlay.Views.CanvasOverlayView"
精确关联的 CanvasOverlayView.xaml 的名称.cs(如果我理解正确,它是一个分部类,它是绘制 xaml 文件描述的内容所需的 C#)。您可以将此文件用作 ViewModel,但最好在完全独立的文件中执行此操作,这就是我将在此示例中执行的操作。因此,xaml.cs 几乎为空(设置数据上下文并初始化组件)。
xmlns:viewModels="clr-namespace:Overlay.视图模型"
别名命名空间,我拥有我所有的视图模型到名称视图模型。这用于在下一行查找视图模型
d:DataContext="{d:DesignInstance Type=viewModels:CanvasOverlayViewModel}"DataContext 是绑定操作将在其中获取其数据的类实例。在这里,我将 DataContext(如果需要,可以作为参数的来源)设置为正确的视图模型。每个视图都有自己的视图模型
交互.触发器在这里,我将为 Command 创建触发器。该命令将允许我在视图模型中处理我的事件(如果我使用 xaml.cs 作为视图模型而不是完全独立的.cs文件,则不需要它)。
请注意绑定语法,这是您使用来自视图模型的"参数"
。路径和椭圆几何
绘制椭圆的方法之一。我使用这种方式是因为我更喜欢使用椭圆的中心和半径,而不是 Canvas.SetLeft、宽度、高度等......这使得拖动操作更容易(只需要在视图模型和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));
}
}
这样,如果您的 xaml 中有"{绑定示例}",当您在 ViewModel 中更改 Exemple 值时,它将自动更新。
对于椭圆,您可以创建一个包含椭圆所有参数的类,在 ViewModel 中实例化它并将其绑定到视图中。
命令和事件
下面是使用命令处理事件的示例,用于上面的视图示例。当然,这不是使用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;
}
我会给你我的委托命令类:
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 的新手。这是在许多人中做到这一点的一种方式,可能不是最好的。告诉我是否有不清楚的地方,或者是否可以使某些东西更准确。