使用wpf用户控件编程落石效果动画

XML 复制代码
<UserControl x:Class="WpfControlStone.StoneCoke"
             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:local="clr-namespace:WpfControlStone"
             mc:Ignorable="d" 
             IsHitTestVisible="False"
              >
    <Viewbox Stretch="Uniform">
        <Grid Width="300" Height="300">
            <Canvas  x:Name="MainCanvas" Background="{Binding BackgroundColor, RelativeSource={RelativeSource AncestorType=UserControl}}">
            </Canvas>
        </Grid>
    </Viewbox>
</UserControl>
cpp 复制代码
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.Interop;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Threading;

namespace WpfControlStone
{
    // 自定义用户控件:模拟石头落入瓶子的动画效果
    public partial class StoneCoke : UserControl
    {



        
        private StoneCoke wpfControl;


        private const int MaxActiveRocks = 200;
        //降低形状复杂度(减少渲染计算量)
        private const int MinRockSize = 4;
        private const int MaxRockSize = 10;
        // 随机数生成器:用于生成石头的随机位置、大小、形状等
        private Random random = new Random();
        // 每秒生成的石头数量(初始值)
        private int rocksPerSecond = 500;
        // 初始石头数量(瓶子底部的基础石头)
        private const int InitialRockCount = 1000;
        // 瓶子宽度
        private const double BottleWidth = 200;
        // 瓶子高度
        private const double BottleHeight = 400;
        // 瓶子顶部Y坐标
        private const double BottleTop = 100;
        // 石头堆积表面的Y坐标(瓶内石头堆积的高度)
        private const double RockSurfaceY = 350;
        // 石头消失的高度(超过此高度的石头会被移除)
        private const double DisappearHeight = 300;

        // 当前正在下落的石头集合
        private List<FrameworkElement> currentRocks = new List<FrameworkElement>();
        // 之前的石头集合(备用)
        private List<FrameworkElement> previousRocks = new List<FrameworkElement>();
        // 石头生成定时器:控制石头生成的频率
        private DispatcherTimer rockGeneratorTimer;
        // 额外生成定时器:辅助生成石头,增加生成量
        private DispatcherTimer additionalTimer;


        private DateTime rockTimerStartTime; // 主生成定时器启动时间
        private DateTime additionalTimerStartTime; // 额外生成定时器启动时间


        //层级
        public static readonly DependencyProperty ZIndexProperty =
          DependencyProperty.Register(
              "ZIndex",typeof(int),typeof(StoneCoke),new PropertyMetadata(-1, OnZIndexChanged)); // 默认值-1(最下层)

        // 2. Z-Index属性访问器(WinCC可直接读写)
        public int ZIndex
        {
            get => (int)GetValue(ZIndexProperty);
            set => SetValue(ZIndexProperty, value);
        }

        // 3. Z-Index变更时,同步更新控件层级
        private static void OnZIndexChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (d is StoneCoke control && e.NewValue is int newZIndex)
            {
                // 更新当前控件的Z-Index
                Panel.SetZIndex(control, newZIndex);
                // 同时更新内部画布的Z-Index,确保动画元素层级一致
                if (control.MainCanvas != null)
                {
                    Panel.SetZIndex(control.MainCanvas, newZIndex);
                }
            }
        }

        // 依赖属性:每秒生成石头数量(允许XAML绑定)
        public static readonly DependencyProperty RocksPerSecondProperty =
            DependencyProperty.Register("RocksPerSecond", typeof(int), typeof(StoneCoke),
                new PropertyMetadata(200, OnRocksPerSecondChanged));


        // 依赖属性:是否正在生成石头(允许XAML绑定)
        public static readonly DependencyProperty IsGeneratingProperty =
            DependencyProperty.Register("IsGenerating", typeof(bool), typeof(StoneCoke),
                new PropertyMetadata(true, OnIsGeneratingChanged));


        //依赖属性:控制动画开始/结束
        public static readonly DependencyProperty IsAnimationRunningProperty =
            DependencyProperty.Register("IsAnimationRunning", typeof(bool), typeof(StoneCoke), new PropertyMetadata(false, OnIsAnimationRunningChanged));

        // 依赖属性访问器
        public bool IsAnimationRunning
        {
            get => (bool)GetValue(IsAnimationRunningProperty);
            set => SetValue(IsAnimationRunningProperty, value);
        }

        // 依赖属性值变化时触发动画控制
        private static void OnIsAnimationRunningChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (d is StoneCoke control && e.NewValue is bool isRunning)
            {
                // 根据属性值启动或停止动画
                if (isRunning)
                {
                    control.StartAnimation();
                }
                else
                {
                    control.StopAnimation();
                }
            }
        }

        //Wincc控制开始
        public void StartAnimation()
        {
            // 1. 先重置动画状态(清除残留石头,恢复初始状态)
            ResetAnimation();

            // 2. 重置生成状态标记
            IsGenerating = true;

            // 3. 重新启动生成定时器(如果已存在则先停止)
            if (rockGeneratorTimer != null)
            {
                rockGeneratorTimer.Stop();
            }
            if (additionalTimer != null)
            {
                additionalTimer.Stop();
            }

            // 4. 重新初始化并启动生成逻辑(复用现有方法)
            StartRockGenerator();
            StartAdditionalGenerator();
        }
        //WinCC控制结束
        public void StopAnimation()
        {
            // 1. 停止所有定时器,终止新石头生成
            rockGeneratorTimer?.Stop();
            additionalTimer?.Stop();

            // 2. 标记生成状态为停止
            IsGenerating = false;

            // 3. 停止所有正在进行的动画(可选:根据需求决定是否保留已下落的石头)
            //StopAllRockAnimations();
        }

        // 改变背景色(允许WinCC设置)
        public static readonly DependencyProperty BackgroundColorProperty =
            DependencyProperty.Register("BackgroundColor", typeof(Color), typeof(StoneCoke),
                new PropertyMetadata(Colors.Transparent, OnBackgroundColorChanged));

        // 背景颜色属性访问器
        public Color BackgroundColor
        {
            get => (Color)GetValue(BackgroundColorProperty);
            set => SetValue(BackgroundColorProperty, value);
        }

        // 背景颜色变更时更新控件背景
        private static void OnBackgroundColorChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (d is StoneCoke control && e.NewValue is Color color)
            {
                control.MainCanvas.Background = new SolidColorBrush(color);
            }
        }


        // 焦炭主色依赖属性(WinCC设置)
        public static readonly DependencyProperty CokeMainColorProperty =
            DependencyProperty.Register("CokeMainColor", typeof(Color), typeof(StoneCoke),
                new PropertyMetadata(Color.FromRgb(150, 30, 30))); // 默认深灰色

        // 焦炭高光色依赖属性(用于渐变效果)
        public static readonly DependencyProperty CokeHighlightColorProperty =
            DependencyProperty.Register("CokeHighlightColor", typeof(Color), typeof(StoneCoke),
                new PropertyMetadata(Color.FromRgb(220, 80, 80))); // 默认浅灰色

        // 属性访问器
        public Color CokeMainColor
        {
            get => (Color)GetValue(CokeMainColorProperty);
            set => SetValue(CokeMainColorProperty, value);
        }
        public Color CokeHighlightColor
        {
            get => (Color)GetValue(CokeHighlightColorProperty);
            set => SetValue(CokeHighlightColorProperty, value);
        }



        private void StopAllRockAnimations()
        {
            foreach (var rock in currentRocks.ToList())
            {
                // 停止石头的所有动画(位置、旋转、透明度等)
                rock.BeginAnimation(Canvas.LeftProperty, null);
                rock.BeginAnimation(Canvas.TopProperty, null);
                rock.BeginAnimation(OpacityProperty, null);
                if (rock.RenderTransform is RotateTransform rotate)
                {
                    rotate.BeginAnimation(RotateTransform.AngleProperty, null);
                }

                // 可选:将石头固定在当前位置(不删除)
                // 若需要清除所有石头,可调用RemoveRock(rock);
            }
        }

        // 每秒生成石头数量的属性访问器
        public int RocksPerSecond
        {
            get => (int)GetValue(RocksPerSecondProperty);
            set => SetValue(RocksPerSecondProperty, value);
        }

        // 是否正在生成石头的属性访问器
        public bool IsGenerating
        {
            get => (bool)GetValue(IsGeneratingProperty);
            set => SetValue(IsGeneratingProperty, value);
        }

        // 构造函数
        public StoneCoke()
        {
            
            
            InitializeComponent(); // 初始化控件(由XAML自动生成)
            Loaded += OnLoaded; // 绑定控件加载完成事件

        }
        // 让WPF控件置于底层


        // 控件加载完成事件处理:初始化动画
        private void OnLoaded(object sender, RoutedEventArgs e)
        {
            InitializeAnimation(); // 初始化动画相关资源
        }

        // 键盘按键按下事件处理:响应用户键盘操作

        // 初始化动画:绘制瓶子、初始石头并启动生成器
        private void InitializeAnimation()
        {
            //DrawCylinderBottle(); // 绘制瓶子形状
            //InitializeBottleRocks(); // 初始化瓶内基础石头
            StartRockGenerator(); // 启动石头生成定时器
            StartAdditionalGenerator(); // 启动额外生成定时器

        }

        // 创建随机形状的石头(多边形/椭圆/圆角矩形)
        private FrameworkElement CreateRockShape()
        {
            double size = random.Next(MinRockSize, MaxRockSize);
            int shapeType = random.Next(2); // 随机形状类型(0-2)

            FrameworkElement rock = null;

            // 根据随机类型创建不同形状的石头
            switch (shapeType)
            {
                case 0:
                    rock = CreateIrregularPolygon(size); // 不规则多边形
                    break;
                case 1:
                    rock = CreateEllipseRock(size); // 椭圆形
                    break;
            }

            // 设置石头的基本尺寸和初始透明度
            rock.Width = size;
            rock.Height = size;
            rock.Opacity = 0; // 初始透明(后续通过动画显示)
            rock.IsHitTestVisible = false; // 不需要交互,关闭命中测试
            rock.SnapsToDevicePixels = true; // 减少抗锯齿计算
            return rock;
        }

        // 创建不规则多边形石头
        private Polygon CreateIrregularPolygon(double size)
        {
            Polygon polygon = new Polygon();
            polygon.Fill = new SolidColorBrush(CokeMainColor);
            polygon.Stroke = Brushes.Transparent;  // 移除描边
            polygon.StrokeThickness = 0;

            PointCollection points = new PointCollection();
            int sides = random.Next(5, 8); // 随机边数(5-7条边)

            double centerX = size / 2; // 中心点X坐标
            double centerY = size / 2; // 中心点Y坐标

            // 生成多边形的每个顶点
            for (int i = 0; i < sides; i++)
            {
                double angle = 2 * Math.PI * i / sides; // 角度(弧度)
                // 随机半径(使形状不规则)
                double radius = size / 2 * (0.6 + random.NextDouble() * 0.8);
                // 计算顶点坐标(极坐标转直角坐标)
                double x = centerX + radius * Math.Cos(angle);
                double y = centerY + radius * Math.Sin(angle);
                points.Add(new Point(x, y));
            }

            polygon.Points = points; // 设置顶点集合
            return polygon;
        }

        // 创建椭圆形石头
        private Ellipse CreateEllipseRock(double size)
        {
            Ellipse ellipse = new Ellipse
            {
                // 随机宽高比例(使椭圆更自然)
                Width = size * (0.8 + random.NextDouble() * 0.4),
                Height = size * (0.7 + random.NextDouble() * 0.6),
                // 填充:径向渐变(中心亮边缘暗)
                //Fill = new RadialGradientBrush(
                //    CokeMainColor,
                //    CokeHighlightColor),
                //Stroke = Brushes.DarkRed, // 描边颜色
                //StrokeThickness = 0.8 // 描边厚度
                Fill = new SolidColorBrush(CokeMainColor),  // 纯色填充
                Stroke = Brushes.Transparent,
                StrokeThickness = 0
            };
            return ellipse;
        }

        // 创建圆角矩形石头
        private Rectangle CreateRoundedRock(double size)
        {
            Rectangle rectangle = new Rectangle
            {
                // 随机宽高比例
                Width = size * (0.7 + random.NextDouble() * 0.6),
                Height = size * (0.6 + random.NextDouble() * 0.8),
                // 填充:45度角的线性渐变
                Fill = new LinearGradientBrush(
                    CokeMainColor,
                   CokeHighlightColor,
                    45),
                Stroke = Brushes.DarkRed, // 描边颜色
                StrokeThickness = 0.8, // 描边厚度
                RadiusX = size * 0.3, // X方向圆角半径
                RadiusY = size * 0.2 // Y方向圆角半径
            };
            return rectangle;
        }

        // 启动石头生成定时器
        private void StartRockGenerator()
        {

            // 若已有定时器,先停止并释放
            if (rockGeneratorTimer != null)
            {
                rockGeneratorTimer.Stop();
                rockGeneratorTimer.Tick -= RockGeneratorTimer_Tick; // 解绑旧事件
            }
            rockGeneratorTimer = new DispatcherTimer(); // 创建定时器
            UpdateTimerInterval(); // 设置定时器间隔(根据生成速度)

            rockGeneratorTimer.Tick += RockGeneratorTimer_Tick;


            // 延迟1.5秒启动主定时器
            DispatcherTimer delayTimer = new DispatcherTimer();
            delayTimer.Interval = TimeSpan.FromSeconds(0.5);
            delayTimer.Tick += (s, e) =>
            {
                delayTimer.Stop(); // 关闭延迟定时器(只执行一次)
                rockTimerStartTime = DateTime.Now; // 记录主定时器启动时间
                rockGeneratorTimer.Start(); // 启动主生成定时器
            };
            delayTimer.Start();
        }
        // 提取Tick事件为单独方法(避免匿名委托导致的重复执行问题)
        private void RockGeneratorTimer_Tick(object sender, EventArgs e)
        {
            // 如果当前石头数量已达上限,暂停生成
            if (currentRocks.Count >= MaxActiveRocks) return;
            TimeSpan elapsed = DateTime.Now - rockTimerStartTime;
            if (IsGenerating)
            {
                GenerateRocks();
            }
            else
            {
                rockGeneratorTimer.Stop();
            }
        }

        // 启动额外的石头生成定时器(增加生成量)
        private void StartAdditionalGenerator()
        {
            if (additionalTimer != null)
            {
                additionalTimer.Stop();
                additionalTimer.Tick -= AdditionalTimer_Tick;
            }
            additionalTimer = new DispatcherTimer();
            additionalTimer.Interval = TimeSpan.FromMilliseconds(500); // 每500ms触发一次
            additionalTimer.Tick += AdditionalTimer_Tick; // 绑定单独的事件方法



            // 延迟1.5秒启动额外定时器(与主定时器同步延迟)
            DispatcherTimer delayTimer = new DispatcherTimer();
            delayTimer.Interval = TimeSpan.FromSeconds(0.5);
            delayTimer.Tick += (s, e) =>
            {
                delayTimer.Stop(); // 关闭延迟定时器(只执行一次)
                additionalTimerStartTime = DateTime.Now; // 记录额外定时器启动时间
                additionalTimer.Start(); // 启动额外生成定时器
            };
            delayTimer.Start();
        }

        // 额外定时器的Tick事件单独提取
        private void AdditionalTimer_Tick(object sender, EventArgs e)
        {
            if (currentRocks.Count >= MaxActiveRocks) return;
            TimeSpan elapsed = DateTime.Now - additionalTimerStartTime;
            if (IsGenerating)
            {
                for (int i = 0; i < 5; i++)
                {
                    CreateFallingRockAnimation();
                }
            }
            else
            {
                additionalTimer.Stop();
            }
        }

        // 生成石头(由主定时器调用)
        private void GenerateRocks()
        {
            int mainRocks = Math.Min(6, (int)(RocksPerSecond * rockGeneratorTimer.Interval.TotalSeconds) + 2);
            for (int i = 0; i < mainRocks; i++)
            {
                CreateFallingRockAnimation();
            }
        }

        // 创建下落的石头并启动动画
        private void CreateFallingRockAnimation()
        {
            FrameworkElement rock = CreateRockShape(); // 创建石头形状
            double rockWidth = rock.Width; // 石头宽度
            double rockHeight = rock.Height; // 石头高度

            // 设置石头起始位置(瓶子右侧外部)
            double startX = 300 + random.Next(0, 100); // X:300-400之间
            double startY = 80 - random.Next(0, 50); // Y:30-80之间

            // 设置石头初始位置
            Canvas.SetLeft(rock, startX);
            Canvas.SetTop(rock, startY);

            MainCanvas.Children.Add(rock); // 添加到画布
            currentRocks.Add(rock); // 加入当前石头集合

            // 启动下落动画
            StartFallingAnimation(rock, startX, startY, rockWidth, rockHeight);
        }

        // 启动石头下落动画
        private void StartFallingAnimation(FrameworkElement rock, double startX, double startY, double rockWidth, double rockHeight)
        {
            // 计算石头最终位置(瓶内)
            double endY = RockSurfaceY - random.Next(0, 10); // Y:接近石头表面
            double minEndX = 100; // X最小值(瓶子左边界)
            double maxEndX = 300 - rockWidth; // X最大值(瓶子右边界)
            double endX = random.Next((int)minEndX, (int)maxEndX + 1); // 随机X

            // X方向移动动画(从右侧进入瓶内)
            DoubleAnimation xAnimation = new DoubleAnimation
            {
                To = endX, // 目标X
                Duration = TimeSpan.FromSeconds(0.8 + random.NextDouble() * 0.4), // 持续时间(0.8-1.2秒)
                EasingFunction = new QuadraticEase { EasingMode = EasingMode.EaseOut } // 缓动函数(先快后慢)
            };

            // Y方向下落动画
            DoubleAnimation yAnimation = new DoubleAnimation
            {
                To = endY, // 目标Y
                Duration = TimeSpan.FromSeconds(1.0 + random.NextDouble() * 0.5), // 持续时间(0.6-0.9秒)
                EasingFunction = new CubicEase { EasingMode = EasingMode.EaseIn } // 缓动函数(先慢后快,模拟重力)
            };

            // 旋转动画(石头下落时旋转)
            DoubleAnimation rotateAnimation = new DoubleAnimation
            {
                To = random.Next(-720, 720), // 旋转角度(-720到720度,随机方向)
                Duration = TimeSpan.FromSeconds(1 + random.NextDouble() * 0.5) // 持续时间(1-1.5秒)
            };

            // 淡入动画(石头出现时逐渐显示)
            DoubleAnimation fadeInAnimation = new DoubleAnimation
            {
                To = 1, // 目标透明度(完全显示)
                Duration = TimeSpan.FromSeconds(0.3), // 持续时间0.3秒
                EasingFunction = new CubicEase { EasingMode = EasingMode.EaseIn } // 缓动函数
            };

            // 创建故事板(组合多个动画)
            Storyboard storyboard = new Storyboard();
            storyboard.Children.Add(xAnimation);
            storyboard.Children.Add(yAnimation);
            storyboard.Children.Add(rotateAnimation);
            storyboard.Children.Add(fadeInAnimation);

            // 设置动画目标(绑定到石头控件)
            Storyboard.SetTarget(xAnimation, rock);
            Storyboard.SetTarget(yAnimation, rock);
            Storyboard.SetTarget(rotateAnimation, rock);
            Storyboard.SetTarget(fadeInAnimation, rock);

            // 设置动画目标属性(路径)
            Storyboard.SetTargetProperty(xAnimation, new PropertyPath("(Canvas.Left)"));
            Storyboard.SetTargetProperty(yAnimation, new PropertyPath("(Canvas.Top)"));
            Storyboard.SetTargetProperty(rotateAnimation, new PropertyPath("RenderTransform.Angle"));
            Storyboard.SetTargetProperty(fadeInAnimation, new PropertyPath("Opacity"));

            // 设置旋转中心(石头中心点)
            rock.RenderTransformOrigin = new Point(0.5, 0.5);
            rock.RenderTransform = new RotateTransform(); // 应用旋转变换

            // 创建高度检查定时器(检测石头是否超过消失高度)
            DispatcherTimer checkHeightTimer = new DispatcherTimer();
            checkHeightTimer.Interval = TimeSpan.FromMilliseconds(50); // 每50ms检查一次
            DateTime animationStartTime = DateTime.Now; // 动画开始时间
            double animationDuration = yAnimation.Duration.TimeSpan.TotalSeconds; // 动画总时长

            // 检查高度的定时器事件
            checkHeightTimer.Tick += (s, e) =>
            {
                double elapsedTime = (DateTime.Now - animationStartTime).TotalSeconds; // 已过去时间
                if (elapsedTime >= animationDuration)
                {
                    checkHeightTimer.Stop(); // 动画结束,停止检查
                    return;
                }

                // 计算当前石头位置(根据动画进度)
                double progress = elapsedTime / animationDuration;
                double currentY = startY + (endY - startY) * progress;

                // 如果超过消失高度,移除石头
                if (currentY >= DisappearHeight)
                {
                    checkHeightTimer.Stop();
                    RemoveRock(rock);
                }
            };

            // 动画完成事件
            storyboard.Completed += (s, e) =>
            {
                checkHeightTimer.Stop(); // 停止检查定时器
                // 如果石头位置超过消失高度,移除石头
                if (Canvas.GetTop(rock) >= DisappearHeight)
                {
                    RemoveRock(rock);
                }
            };

            checkHeightTimer.Start(); // 启动检查定时器
            storyboard.Begin(); // 开始动画
        }

        // 移除石头(淡出动画后从画布移除)
        private void RemoveRock(FrameworkElement rock)
        {
            if (MainCanvas.Children.Contains(rock))
            {
                // 创建淡出动画
                DoubleAnimation fadeOutAnimation = new DoubleAnimation
                {
                    To = 0, // 目标透明度(完全透明)
                    Duration = TimeSpan.FromSeconds(0.3), // 持续时间0.3秒
                    EasingFunction = new CubicEase { EasingMode = EasingMode.EaseOut } // 缓动函数
                };

                // 淡出动画完成事件
                fadeOutAnimation.Completed += (s, e) =>
                {
                    // 从画布和集合中移除石头
                    if (MainCanvas.Children.Contains(rock))
                    {
                        MainCanvas.Children.Remove(rock);
                    }
                    currentRocks.Remove(rock);
                };

                // 启动淡出动画
                rock.BeginAnimation(OpacityProperty, fadeOutAnimation);
            }
        }

        // 公共方法:开始生成石头
        public void StartGeneration()
        {
            IsGenerating = true;
        }

        // 公共方法:停止生成石头
        public void StopGeneration()
        {
            IsGenerating = false;
        }

        // 公共方法:重置动画
        public void ResetAnimation()
        {
            RemoveAllFallingRocks(); // 移除所有下落的石头
            //DrawCylinderBottle(); // 重新绘制瓶子
            //InitializeBottleRocks(); // 重新初始化瓶内石头
        }

        // 移除所有正在下落的石头
        private void RemoveAllFallingRocks()
        {
            // 遍历当前石头集合,逐个移除
            foreach (var rock in currentRocks.ToList())
            {
                RemoveRock(rock);
            }
            currentRocks.Clear(); // 清空当前集合
            previousRocks.Clear(); // 清空之前集合
        }

        // 依赖属性变更回调:当每秒生成数量变化时
        private static void OnRocksPerSecondChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (d is StoneCoke control && e.NewValue is int value)
            {
                if (value > 200)
                {
                    control.RocksPerSecond = 200;
                }

                // 确保最小值为1(避免无效值)
                if (value < 1)
                {
                    control.RocksPerSecond = 1;
                }
                control.UpdateTimerInterval(); // 更新定时器间隔
            }
        }

        // 依赖属性变更回调:当生成状态变化时
        private static void OnIsGeneratingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (d is StoneCoke control)
            {
                if ((bool)e.NewValue)
                {
                    // 开始生成:启动定时器
                    control.rockGeneratorTimer?.Start();
                    control.additionalTimer?.Start();
                }
                else
                {
                    // 停止生成:停止定时器
                    control.rockGeneratorTimer?.Stop();
                    control.additionalTimer?.Stop();
                }
            }
        }

        // 更新生成定时器的间隔(根据每秒生成数量计算)
        private void UpdateTimerInterval()
        {
            if (rockGeneratorTimer != null)
            {
                double interval = Math.Max(50, 1000.0 / RocksPerSecond);  // 原20ms → 50ms
                rockGeneratorTimer.Interval = TimeSpan.FromMilliseconds(interval);
            }
        }

        // 清理资源(停止定时器并移除所有石头)
        public void Cleanup()
        {
            rockGeneratorTimer?.Stop();
            additionalTimer?.Stop();
            RemoveAllFallingRocks();
        }

    }

}

我创建了项目名为WpfControlStone的用户控件项目名字,并创建了一个名字为StoneCoke的用户控件,使用canvas进行绘制红色石头进行下落的动画效果。

XML 复制代码
<Window x:Class="FallingRocksFramework.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:FallingRocksFramework"
        xmlns:coke="clr-namespace:wpfControlCoke;assembly=wpfControlCoke"
        xmlns:stone="clr-namespace:WpfControlStone;assembly=WpfControlStone"(这句话需要引入)
        mc:Ignorable="d"
        Title="MainWindow" Height="300" Width="300"
        AllowsTransparency="True"
        Background="Transparent"
        WindowStyle="None"
        ResizeMode="NoResize"
        WindowStartupLocation="CenterScreen"
        >
    <Grid>
        <!--<coke:CokePushing  />-->
        <stone:StoneCoke />
    </Grid>
</Window>

在创建一个wpf桌面应用程序项目,右键点击桌面程序项目选择添加里的引用

去选择你要引入的用户控件

去引用WpfControlStone用户控件,就可以进行运行查看了

20251009_101354

相关推荐
小贾要学习5 小时前
【数据结构】C++实现红黑树
数据结构·c++
wuty0075 小时前
WPF 调用 ChangeWindowMessageFilterEx 修改指定窗口 (UIPI) 消息筛选器的用户界面特权隔离
wpf·sendmessage·changewindowmessagefilterex·uip·消息筛选器的用户界面特权隔离·window message
ajassi20005 小时前
开源 C++ QT QML 开发(十七)进程--LocalSocket
c++·qt·开源
微露清风5 小时前
系统性学习C++-第五讲-内存管理
java·c++·学习
星夜钢琴手6 小时前
推荐的 Visual Studio 2026 Insider C++ 程序项目属性配置
c++·visual studio
kyle~7 小时前
Qt---setAttribute设置控件或窗口的内部属性
服务器·前端·c++·qt
hsjkdhs8 小时前
C++之多态
开发语言·jvm·c++
kyle~8 小时前
C++STL---静态数组array
开发语言·c++
kk”9 小时前
C++ List
开发语言·c++