繁体   English   中英

如何使用 WPF 在画布中以随机角度和方向设置椭圆

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM