[英]How to Set an Ellipse in a random Angle and Direction in a Canvas with WPF
嗨,我是 C# WPF 的新手,我希望在任何角度和方向设置一个椭圆。 不幸的是,我还没有找到解决方案。 我的简单解决方案没有考虑到我计算的角度。 我使用 Canvas.setTop、Canvas.setRight... 等来设置这个角度,但我想用我计算出的角度来设置它。
这是我的 XAML 代码:
<Window x:Class="someApp.MainWindow"
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:someApp"
mc:Ignorable="d" Background="LightBlue"
Title="app" Height="450" Width="800">
<Grid>
<Canvas Name="enviroinment" Background="WhiteSmoke" HorizontalAlignment="Left" Height="297" Margin="120,88,0,0" VerticalAlignment="Top" Width="639">
<Button Content="Start/Stop" Canvas.Left="-97" Canvas.Top="24" Width="75" Click="Button_Click"/>
</Canvas>
</Grid>
</Window>
这是我的主要课程:
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 System.Windows.Threading;
namespace someApp
{
/// <summary>
/// Interaktionslogik für MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
DispatcherTimer timer = new DispatcherTimer();
Random rand = new Random();
List<Ellipse> People = new List<Ellipse>();
double SPEED = 1.5;
double _angle = 90;
public MainWindow()
{
InitializeComponent();
timer.Stop();
timer.Interval = TimeSpan.FromSeconds(0.05);
timer.Tick += movePeople;
timer.IsEnabled = true;
timer.Start();
}
private void createPeople()
{
for(int i= 0; i < 4; i++)
{
Ellipse person = new Ellipse();
person.Width = 10;
person.Height = 10;
person.Fill = Brushes.Gray;
//Canvas.SetLeft(person, rand.Next(0, (int)environment.ActualWidth));
//Canvas.SetTop(person, rand.Next(0,(int)environment.ActualHeight));
People.Add(person);
}
}
private void setDirection()
{
for (int i = 0; i < People.Count; i++)
{
_angle = rand.Next(0, 90);
SPEED = 0.5 + rand.NextDouble();
double radians = Math.PI * _angle / 180.0;
double initPosX = rand.Next(0, (int)environment.ActualWidth);
double initPosY = rand.Next(0, (int)environment.ActualHeight);
double x = initPosX + Math.Sin(radians) * SPEED;
double y = initPosY + Math.Cos(radians) * SPEED;
if (i == 0)
{
Canvas.SetLeft(People[i], x);
Canvas.SetTop(People[i], y);
}
if(i == 1)
{
Canvas.SetLeft(People[i], x);
Canvas.SetBottom(People[i], y);
}
if (i == 2)
{
Canvas.SetRight(People[i], x);
Canvas.SetTop(People[i], y);
}
if (i == 3)
{
Canvas.SetRight(People[i], x);
Canvas.SetBottom(People[i], y);
}
environment.Children.Add(People[i]);
}
}
private void movePeople(object sender, EventArgs e)
{
for (int i = 0; i < People.Count; i++)
{
if (i == 0)
{
double x = Canvas.GetLeft(People[i]);
double y = Canvas.GetTop(People[i]);
x += SPEED;
y += SPEED;
Canvas.SetLeft(People[i], x);
Canvas.SetTop(People[i], y);
}
if (i == 1)
{
double x = Canvas.GetLeft(People[i]);
double y = Canvas.GetBottom(People[i]);
x += SPEED;
y += SPEED;
Canvas.SetLeft(People[i], x);
Canvas.SetBottom(People[i], y);
}
if (i == 2)
{
double x = Canvas.GetRight(People[i]);
double y = Canvas.GetTop(People[i]);
x += SPEED;
y += SPEED;
Canvas.SetRight(People[i], x);
Canvas.SetTop(People[i], y);
}
if (i == 3)
{
double x = Canvas.GetRight(People[i]);
double y = Canvas.GetBottom(People[i]);
x += SPEED;
y += SPEED;
Canvas.SetRight(People[i], x);
Canvas.SetBottom(People[i], y);
}
}
}
private void Button_Click(object sender, RoutedEventArgs e)
{
if (timer.IsEnabled)
{
timer.Stop();
People.Clear();
}
else
{
createPeople();
setDirection();
timer.Start();
}
}
}
}
提前致谢!!
首先,您应该从一开始就正确地编写一个实现 MVVM 架构模式的应用程序。
会有一个类用位置和速度对移动对象进行建模。 该类实现 INotifyPropertyChanged 接口,其属性在其值更改时触发 PropertyChanged 事件。
public class Particle : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private Point position;
public Point Position
{
get { return position; }
set
{
position = value;
NotifyPropertyChanged(nameof(Position));
}
}
private Vector velocity;
public Vector Velocity
{
get { return velocity; }
set
{
velocity = value;
NotifyPropertyChanged(nameof(Velocity));
}
}
protected void NotifyPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
主视图模型类将在 ObservableCollection 属性中保存一组粒子并实现它们的运动行为,由s = v * dt
。
public class ViewModel
{
public ObservableCollection<Particle> Particles { get; }
= new ObservableCollection<Particle>();
private DateTime lastUpdate = DateTime.Now;
public void MoveParticles()
{
var now = DateTime.Now;
var dt = (now - lastUpdate).TotalSeconds;
foreach (var particle in Particles)
{
particle.Position += particle.Velocity * dt;
}
lastUpdate = now;
}
}
要可视化这些粒子,您可以使用带有 Canvas 作为 ItemsPanel 的 ItemsControl。 它的 ItemContainerStyle 将负责根据粒子位置设置 Canvas.Left 和 Canvas.Top 属性,而 ItemTemplate 将定义视觉外观:
<ItemsControl ItemsSource="{Binding Particles}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<Setter Property="Canvas.Left" Value="{Binding Position.X}"/>
<Setter Property="Canvas.Top" Value="{Binding Position.Y}"/>
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Path Fill="Red">
<Path.Data>
<EllipseGeometry RadiusX="5" RadiusY="5"/>
</Path.Data>
</Path>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
要将视图和视图模型粘合在一起,请将 ViewModel 类的实例分配给视图的 DataContext。 启动一个定时器来循环更新视图模型,它也会自动更新视图。
public MainWindow()
{
InitializeComponent();
var vm = new ViewModel();
DataContext = vm;
vm.Particles.Add(new Particle
{
Position = new Point(200, 100),
Velocity = new Vector(10, 10)
});
vm.Particles.Add(new Particle
{
Position = new Point(200, 200),
Velocity = new Vector(10, -10)
});
var timer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(50) };
timer.Tick += (s, e) => vm.MoveParticles();
timer.Start();
}
现在将在视图模型类中实现可选的碰撞检测。 它只会改变那些比预定义的最小距离更近的粒子的速度向量,其中两个粒子的距离是
var distance = (particle1.Position - particle2.Position).Length;
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.