WPF模拟实现Gitee泡泡菜单的示例代码

WPF实现 Gitee泡泡菜单

框架使用大于等于.NET40

Visual Studio 2022;

项目使用 MIT 开源许可协议;

WPF模拟实现Gitee泡泡菜单的示例代码_第1张图片

  • 需要实现泡泡菜单需要使用Canvas画布进行添加内容;
  • 保证颜色随机,位置不重叠;
  • 点击泡泡获得当前泡泡的值;

实现代码

1) BubblleCanvas.cs 代码如下;

using System.Windows;
using System.Windows.Controls;
using WPFDevelopers.Helpers;
using WPFDevelopers.Utilities;

namespace WPFDevelopers.Controls
{
    public class BubblleCanvas : Canvas
    {
        private double _bubbleItemX;
        private double _bubbleItemY;

        private int _number;
        private double _size;
        private const int _maxSize = 120;

        protected override Size ArrangeOverride(Size arrangeSize)
        {
            var width = arrangeSize.Width;
            var height = arrangeSize.Height;

            double left = 0d, top = 0d;
            for (var y = 0; y < (int)height / _maxSize; y++)
            {
                double yNum = y + 1;
                yNum = _maxSize * yNum;
                for (var x = 0; x < (int)width / _maxSize; x++)
                {
                    if (_number > InternalChildren.Count - 1)
                        return arrangeSize;

                    var item = InternalChildren[_number] as FrameworkElement;

                    if (DoubleUtil.IsNaN(item.ActualWidth) || DoubleUtil.IsZero(item.ActualWidth) || DoubleUtil.IsNaN(item.ActualHeight) || DoubleUtil.IsZero(item.ActualHeight))
                        ResizeItem(item);

                    _bubbleItemX = Canvas.GetLeft(item);
                    _bubbleItemY = Canvas.GetTop(item);

                    if (double.IsNaN(_bubbleItemX) || double.IsNaN(_bubbleItemY))
                    {
                        double xNum = x + 1;
                        xNum = _maxSize * xNum;
                        _bubbleItemX = ControlsHelper.NextDouble(left, xNum - _size * ControlsHelper.NextDouble(0.6, 0.9));
                        var _width = _bubbleItemX + _size;
                        _width = _width > width ? width - (width - _bubbleItemX) - _size : _bubbleItemX;
                        _bubbleItemX = _width;
                        _bubbleItemY = ControlsHelper.NextDouble(top, yNum - _size * ControlsHelper.NextDouble(0.6, 0.9));
                        var _height = _bubbleItemY + _size;
                        _height = _height > height ? height - (height - _bubbleItemY) - _size : _bubbleItemY;
                        _bubbleItemY = _height;

                    }
                    Canvas.SetLeft(item, _bubbleItemX);
                    Canvas.SetTop(item, _bubbleItemY);
                    left = left + _size;

                    _number++;

                    item.Arrange(new Rect(new Point(_bubbleItemX, _bubbleItemY), new Size(_size, _size)));
                }
                left = 0d;
                top = top + _maxSize;
            }

            return arrangeSize;
        }
        private void ResizeItem(FrameworkElement item)
        {
            if (DoubleUtil.GreaterThanOrClose(item.DesiredSize.Width, 55))
                _size = ControlsHelper.GetRandom.Next(80, _maxSize);
            else
                _size = ControlsHelper.GetRandom.Next(55, _maxSize);
            item.Width = _size;
            item.Height = _size;
        }
    }
}

2) ControlsHelper.cs 代码如下;

  • 随机Double值;
  • 随机颜色;
 private static long _tick = DateTime.Now.Ticks;
        public static Random GetRandom = new Random((int)(_tick & 0xffffffffL) | (int)(_tick >> 32));

        public static double NextDouble(double miniDouble, double maxiDouble)
        {
            if (GetRandom != null)
            {
                return GetRandom.NextDouble() * (maxiDouble - miniDouble) + miniDouble;
            }
            else
            {
                return 0.0d;
            }
        }
        public static Brush RandomBrush()
        {
            var R = GetRandom.Next(255);
            var G = GetRandom.Next(255);
            var B = GetRandom.Next(255);
            var color = Color.FromRgb((byte)R, (byte)G, (byte)B);
            var solidColorBrush = new SolidColorBrush(color);
            return solidColorBrush;
        }

3) BubbleControl.cs 代码如下;

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shapes;
using WPFDevelopers.Helpers;

namespace WPFDevelopers.Controls
{
    [TemplatePart(Name = BorderTemplateName, Type = typeof(Border))]
    [TemplatePart(Name = EllipseTemplateName, Type = typeof(Ellipse))]
    [TemplatePart(Name = RotateTransformTemplateName, Type = typeof(RotateTransform))]
    public class BubblleControl : Control
    {
        private const string BorderTemplateName = "PART_Border";
        private const string EllipseTemplateName = "PART_Ellipse";
        private const string RotateTransformTemplateName = "PART_EllipseRotateTransform";
        private const string ListBoxTemplateName = "PART_ListBox";

        private static readonly Type _typeofSelf = typeof(BubblleControl);

        private ObservableCollection _items = new ObservableCollection();


        private Border _border;
        private Ellipse _ellipse;
        private RotateTransform _rotateTransform;
        private Brush[] brushs;
        private ItemsControl _listBox;
        private static RoutedCommand _clieckCommand;

        class BubblleItem
        {
            public string Text { get; set; }
            public Brush Bg { get; set; }
        }

        static BubblleControl()
        {
            InitializeCommands();
            DefaultStyleKeyProperty.OverrideMetadata(_typeofSelf, new FrameworkPropertyMetadata(_typeofSelf));
        }

        #region Event

        public static readonly RoutedEvent ClickEvent = EventManager.RegisterRoutedEvent("Click", RoutingStrategy.Bubble, typeof(RoutedEventHandler), _typeofSelf);
        public event RoutedEventHandler Click
        {
            add { AddHandler(ClickEvent, value); }
            remove { RemoveHandler(ClickEvent, value); }
        }

        #endregion

        #region Command

        private static RoutedCommand _clickCommand = null;

        private static void InitializeCommands()
        {
            _clickCommand = new RoutedCommand("Click", _typeofSelf);

            CommandManager.RegisterClassCommandBinding(_typeofSelf, new CommandBinding(_clickCommand, OnClickCommand, OnCanClickCommand));
        }

        public static RoutedCommand ClickCommand
        {
            get { return _clickCommand; }
        }

        private static void OnClickCommand(object sender, ExecutedRoutedEventArgs e)
        {
            var ctrl = sender as BubblleControl;

            ctrl.SetValue(SelectedTextPropertyKey, e.Parameter?.ToString());
            ctrl.RaiseEvent(new RoutedEventArgs(ClickEvent));
        }

        private static void OnCanClickCommand(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = true;
        }

        #endregion

        #region readonly Properties

        private static readonly DependencyPropertyKey SelectedTextPropertyKey =
           DependencyProperty.RegisterReadOnly("SelectedText", typeof(string), _typeofSelf, new PropertyMetadata(null));
        public static readonly DependencyProperty SelectedTextProperty = SelectedTextPropertyKey.DependencyProperty;
        public string SelectedText
        {
            get { return (string)GetValue(SelectedTextProperty); }
        }
        public new static readonly DependencyProperty BorderBackgroundProperty =
            DependencyProperty.Register("BorderBackground", typeof(Brush), typeof(BubblleControl),
                new PropertyMetadata(null));

        public new static readonly DependencyProperty EarthBackgroundProperty =
            DependencyProperty.Register("EarthBackground", typeof(Brush), typeof(BubblleControl),
                new PropertyMetadata(Brushes.DarkOrchid));
        public Brush BorderBackground
        {
            get => (Brush)this.GetValue(BorderBackgroundProperty);
            set => this.SetValue(BorderBackgroundProperty, (object)value);
        }
        public Brush EarthBackground
        {
            get => (Brush)this.GetValue(EarthBackgroundProperty);
            set => this.SetValue(EarthBackgroundProperty, (object)value);
        }
        #endregion

        #region Property

        public static readonly DependencyProperty ItemsSourceProperty =
            DependencyProperty.Register("ItemsSource", typeof(IEnumerable), typeof(BubblleControl), new PropertyMetadata(null, OnItemsSourcePropertyChanged));
        public IEnumerable ItemsSource
        {
            get { return (IEnumerable)GetValue(ItemsSourceProperty); }
            set { SetValue(ItemsSourceProperty, value); }
        }

        private static void OnItemsSourcePropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            var ctrl = obj as BubblleControl;
            var newValue = e.NewValue as IEnumerable;

            if (newValue == null)
            {
                ctrl._items.Clear();
                return;
            }

            foreach (var item in newValue)
            {
                ctrl._items.Add(new BubblleItem { Text = item, Bg = ControlsHelper.RandomBrush() });
            }
        }

        #endregion

        #region Override

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            _border = GetTemplateChild(BorderTemplateName) as Border;
            _ellipse = GetTemplateChild(EllipseTemplateName) as Ellipse;
            _rotateTransform = GetTemplateChild(RotateTransformTemplateName) as RotateTransform;
            Loaded += delegate
            {
                var point = _border.TranslatePoint(new Point(_border.ActualWidth / 2, _border.ActualHeight / 2),
                    _ellipse);
                _rotateTransform.CenterX = point.X - _ellipse.ActualWidth / 2;
                _rotateTransform.CenterY = point.Y - _ellipse.ActualHeight / 2;
            };
            _listBox = GetTemplateChild(ListBoxTemplateName) as ItemsControl;
            _listBox.ItemsSource = _items;
        }

        #endregion
    }
}

4) BubblleControl.xaml 代码如下;



    
        
        
    

    
        
        
        
        
        
        
        
            
                
                    
                        
                            
                        
                        
                            
                                
                            
                            
                                
                                    
                                        
                                            
                                        
                                    
                                
                            
                        
                        
                            
                                
                                    
                                        

                                            
                                            
                                        

                                        
                                                        
                                                            
                                                        
                                                    
                                    
                                
                            
                            
                                
                                    
                                
                            
                        
                    
                
            
        
    


5) BubblleControlExample.xaml 代码如下;

  • TabItem随机 是自动设置位置和颜色;
  • TabItem自定义 可以自行定义展示的内容;

    
    
        
            
                
                    
                        
                            WPF
                            ASP.NET
                            WinUI
                            WebAPI
                            Blazor
                            MAUI
                            Xamarin
                            WinForm
                            UWP
                        
                    
                
            
            
                
                    
                        
                            
                            
                        
                        
                            
                                
                            
                        
                    
                    
                        
                            
                            
                        
                        
                            
                                
                            
                        
                    
                    
                        
                            
                            
                        
                        
                            
                                
                            
                        
                    
                
            
        
        
    

6) BubblleControlExample.xaml.cs 代码如下;

using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using WPFDevelopers.Samples.Helpers;

namespace WPFDevelopers.Samples.ExampleViews
{
    /// 
    /// BubbleControlExample.xaml 的交互逻辑
    /// 
    public partial class BubblleControlExample : UserControl
    {
        public BubblleControlExample()
        {
            InitializeComponent();
        }
        public ICommand ClickCommand => new RelayCommand(delegate
        {
           WPFDevelopers.Minimal.Controls.MessageBox.Show("点击完成。");
        });

        private void BubblleControl_Click(object sender, System.Windows.RoutedEventArgs e)
        {
            MessageBox.Show($"点击了“ {MyBubblleControl.SelectedText}开发者 ”.", "提示",MessageBoxButton.OK,MessageBoxImage.Information);
        }
    }
}

WPF模拟实现Gitee泡泡菜单的示例代码_第2张图片

以上就是WPF模拟实现Gitee泡泡菜单的示例代码的详细内容,更多关于WPF泡泡菜单的资料请关注脚本之家其它相关文章!

你可能感兴趣的:(WPF模拟实现Gitee泡泡菜单的示例代码)