WPF Canvas simulate molecule Brown motion

 <Canvas Background="Black">
     <ItemsControl ItemsSource="{Binding Molecules}">
         <ItemsControl.ItemsPanel>
             <ItemsPanelTemplate>
                 <Canvas/>
             </ItemsPanelTemplate>
         </ItemsControl.ItemsPanel>
         <ItemsControl.ItemContainerStyle>
             <Style>
                 <Setter Property="Canvas.Left" Value="{Binding X}"/>
                 <Setter Property="Canvas.Top" Value="{Binding Y}"/>
             </Style>
         </ItemsControl.ItemContainerStyle>

         <ItemsControl.ItemTemplate>
             <DataTemplate>
                 <Ellipse Width="{Binding Diameter}"
                          Height="{Binding Diameter}"
                          Fill="{Binding Color}">
                     <Ellipse.Effect>
                         <DropShadowEffect Color="White"
                                           BlurRadius="10"
                                           Opacity="0.6"/>
                     </Ellipse.Effect>
                 </Ellipse>
             </DataTemplate>
         </ItemsControl.ItemTemplate>
     </ItemsControl>
 </Canvas>


//

 public class MainVM: NotifyBase
 {
     private readonly DispatcherTimer tmr;
     private readonly Random rnd = new Random();
     private int maxWidth, maxHeight;

     public ObservableCollection<Molecule> Molecules { get; }=new ObservableCollection<Molecule>();

     public MainVM(Window win)
     {
         win.Loaded+=(s, e) =>
         {
             maxWidth=(int)win.ActualWidth-50;
             maxHeight=(int)win.ActualHeight-50;
             InitData(100);
             StartAnimation();
         };

         tmr=new DispatcherTimer()
         {
             Interval=TimeSpan.FromMilliseconds(16),
         };
         tmr.Tick+=Tmr_Tick;
     }

     private void Tmr_Tick(object? sender, EventArgs e)
     {
         foreach (var molecule in Molecules)
         {
             molecule.X+=molecule.VX;
             molecule.Y+=molecule.VY;
             if (molecule.X<=0 || molecule.X>=maxWidth)
             {
                 molecule.VX*=-1;
             }

             if (molecule.Y<=0 || molecule.Y>=maxHeight)
             {
                 molecule.VY*=-1;
             }
         }
     }

     private void StartAnimation()
     {
         tmr.Start();
     }

     private void InitData(int count)
     {
         Molecules.Clear();
         for(int i=0;i<count;i++)
         {
             Molecules.Add(new Molecule()
             {
                 X=rnd.Next(0, maxWidth),
                 Y=rnd.Next(0, maxHeight),
                 VX=(rnd.NextDouble()-0.5)*5,
                 VY=(rnd.NextDouble()-0.5)*5,
                 Diameter=rnd.Next(10, 30),
                 Color=new SolidColorBrush(Color.FromRgb((byte)rnd.Next(0, 255),
                 (byte)rnd.Next(0, 255), (byte)rnd.Next(0, 255)))
             });
         }
     }
 }

 

image

 

 

image

 

 

 

 

//xaml
<Window x:Class="WpfApp42.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"
        WindowState="Maximized"
        xmlns:local="clr-namespace:WpfApp42"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Canvas Background="Black">
        <ItemsControl ItemsSource="{Binding Molecules}">
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <Canvas/>
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
            <ItemsControl.ItemContainerStyle>
                <Style>
                    <Setter Property="Canvas.Left" Value="{Binding X}"/>
                    <Setter Property="Canvas.Top" Value="{Binding Y}"/>
                </Style>
            </ItemsControl.ItemContainerStyle>

            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <Ellipse Width="{Binding Diameter}"
                             Height="{Binding Diameter}"
                             Fill="{Binding Color}">
                        <Ellipse.Effect>
                            <DropShadowEffect Color="White"
                                              BlurRadius="10"
                                              Opacity="0.6"/>
                        </Ellipse.Effect>
                    </Ellipse>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
    </Canvas>
</Window>


//cs
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Text;
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 WpfApp42
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            var vm = new MainVM(this);
            this.DataContext=vm;
        }
    }

    public class MainVM: NotifyBase
    {
        private readonly DispatcherTimer tmr;
        private readonly Random rnd = new Random();
        private int maxWidth, maxHeight;

        public ObservableCollection<Molecule> Molecules { get; }=new ObservableCollection<Molecule>();

        public MainVM(Window win)
        {
            win.Loaded+=(s, e) =>
            {
                maxWidth=(int)win.ActualWidth-50;
                maxHeight=(int)win.ActualHeight-50;
                InitData(100);
                StartAnimation();
            };

            tmr=new DispatcherTimer()
            {
                Interval=TimeSpan.FromMilliseconds(16),
            };
            tmr.Tick+=Tmr_Tick;
        }

        private void Tmr_Tick(object? sender, EventArgs e)
        {
            foreach (var molecule in Molecules)
            {
                molecule.X+=molecule.VX;
                molecule.Y+=molecule.VY;
                if (molecule.X<=0 || molecule.X>=maxWidth)
                {
                    molecule.VX*=-1;
                }

                if (molecule.Y<=0 || molecule.Y>=maxHeight)
                {
                    molecule.VY*=-1;
                }
            }
        }

        private void StartAnimation()
        {
            tmr.Start();
        }

        private void InitData(int count)
        {
            Molecules.Clear();
            for(int i=0;i<count;i++)
            {
                Molecules.Add(new Molecule()
                {
                    X=rnd.Next(0, maxWidth),
                    Y=rnd.Next(0, maxHeight),
                    VX=(rnd.NextDouble()-0.5)*5,
                    VY=(rnd.NextDouble()-0.5)*5,
                    Diameter=rnd.Next(10, 30),
                    Color=new SolidColorBrush(Color.FromRgb((byte)rnd.Next(0, 255),
                    (byte)rnd.Next(0, 255), (byte)rnd.Next(0, 255)))
                });
            }
        }
    }

    public class Molecule:NotifyBase
    {
        private double x;
        public double X
        {
            get
            {
                return x;
            }
            set
            {
                if (value!=x)
                {
                    x=value;
                    OnPropertyChanged(nameof(X));
                }
            }
        }

        private double y;
        public double Y
        {
            get
            {
                return y;
            }
            set
            {
                if(value!=y)
                {
                    y=value;
                    OnPropertyChanged(nameof(Y));
                }
            }
        }

        public double VX { get; set; }
        public double VY { get; set; }
        public double Diameter { get; set; }
        public Brush Color { get; set; }
    }

    public class NotifyBase : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler? PropertyChanged;
        
        public void OnPropertyChanged([CallerMemberName]string propName="")
        {
            var handler = PropertyChanged;
            if(handler!=null)
            {
                handler?.Invoke(this, new PropertyChangedEventArgs(propName));
            }
        }
    }
}

 

posted @ 2025-08-18 14:51  FredGrit  阅读(6)  评论(0)    收藏  举报