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

WPF实现 Gitee泡泡菜单

框架使用大于等于.NET40

Visual Studio 2022;

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

需要实现泡泡菜单需要使用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<BubblleItem> _items = new ObservableCollection<BubblleItem>();         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<string>), typeof(BubblleControl), new PropertyMetadata(null, OnItemsSourcePropertyChanged));         public IEnumerable<string> ItemsSource         {             get { return (IEnumerable<string>)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<string>;             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 代码如下;

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"                     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"                     xmlns:controls="clr-namespace:WPFDevelopers.Controls">     <ResourceDictionary.MergedDictionaries>         <ResourceDictionary Source="Basic/ControlBasic.xaml"/>         <ResourceDictionary Source="Basic/Animations.xaml"/>     </ResourceDictionary.MergedDictionaries>     <Style TargetType="controls:BubblleControl" BasedOn="{StaticResource ControlBasicStyle}">         <Setter Property="Width" Value="400"/>         <Setter Property="Height" Value="400"/>         <Setter Property="Background" Value="{StaticResource WhiteSolidColorBrush}"/>         <Setter Property="BorderThickness" Value="1"/>         <Setter Property="BorderBrush" Value="{StaticResource SecondaryTextSolidColorBrush}"/>         <Setter Property="BorderBackground" Value="{StaticResource BaseSolidColorBrush}"/>         <Setter Property="Template">             <Setter.Value>                 <ControlTemplate TargetType="controls:BubblleControl">                     <Grid Width="{TemplateBinding Width}" Height="{TemplateBinding Height}">                         <Border BorderBrush="{TemplateBinding BorderBrush}"                                                 BorderThickness="{TemplateBinding BorderThickness}"                                                  Background="{TemplateBinding BorderBackground}"                                                  Margin="45"                                                 CornerRadius="400"                                                 x:Name="PART_Border">                             <Ellipse Fill="{TemplateBinding Background}" Margin="20"/>                         </Border>                         <Ellipse Fill="{TemplateBinding EarthBackground}"                                                  Width="26" Height="26"                                                  RenderTransformOrigin=".5,.5"                                                  x:Name="PART_Ellipse"                                                  VerticalAlignment="Top" Margin="0,35,0,0">                             <Ellipse.RenderTransform>                                 <RotateTransform x:Name="PART_EllipseRotateTransform"></RotateTransform>                             </Ellipse.RenderTransform>                             <Ellipse.Triggers>                                 <EventTrigger RoutedEvent="Loaded">                                     <BeginStoryboard>                                         <Storyboard>                                             <DoubleAnimation Storyboard.TargetProperty="(Ellipse.RenderTransform).(RotateTransform.Angle)"                                                                              RepeatBehavior="Forever"                                                                              From="0" To="360"                                                                              Duration="00:00:13"></DoubleAnimation>                                         </Storyboard>                                     </BeginStoryboard>                                 </EventTrigger>                             </Ellipse.Triggers>                         </Ellipse>                         <ItemsControl x:Name="PART_ListBox"                                       ItemsSource="{TemplateBinding ItemsSource}">                             <ItemsControl.ItemTemplate>                                 <DataTemplate>                                     <Grid>                                         <Grid Width="{TemplateBinding Width}"                                                Height="{TemplateBinding Height}">                                             <Ellipse Fill="{Binding Bg}"                                                                  Opacity=".4"/>                                             <Ellipse Stroke="{Binding Bg}"                                                                   StrokeThickness=".8"/>                                         </Grid>                                         <TextBlock VerticalAlignment="Center"                                                                 HorizontalAlignment="Center"                                                                Padding="10,0">                                                         <Hyperlink                                                              Foreground="{Binding Bg}"                                                             Command="{x:Static controls:BubblleControl.ClickCommand}"                                                             CommandParameter="{Binding Text}"                                                             FontWeight="Normal">                                                             <TextBlock Text="{Binding Text}"                                                                        TextAlignment="Center"                                                                        TextTrimming="CharacterEllipsis"                                                                        ToolTip="{Binding Text}"/>                                                         </Hyperlink>                                                     </TextBlock>                                     </Grid>                                 </DataTemplate>                             </ItemsControl.ItemTemplate>                             <ItemsControl.ItemsPanel>                                 <ItemsPanelTemplate>                                     <controls:BubblleCanvas/>                                 </ItemsPanelTemplate>                             </ItemsControl.ItemsPanel>                         </ItemsControl>                     </Grid>                 </ControlTemplate>             </Setter.Value>         </Setter>     </Style> </ResourceDictionary>

5) BubblleControlExample.xaml 代码如下;

TabItem随机 是自动设置位置和颜色;

TabItem自定义 可以自行定义展示的内容;

<UserControl x:Class="WPFDevelopers.Samples.ExampleViews.BubblleControlExample"              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:wpfdev="https://github.com/WPFDevelopersOrg/WPFDevelopers"              xmlns:local="clr-namespace:WPFDevelopers.Samples.ExampleViews"              xmlns:sys="clr-namespace:System;assembly=mscorlib"              mc:Ignorable="d"               d:DesignHeight="450" d:DesignWidth="800">     <Grid>         <TabControl>             <TabItem Header="随机">                 <wpfdev:BubblleControl x:Name="MyBubblleControl"  Click="BubblleControl_Click">                     <wpfdev:BubblleControl.ItemsSource>                         <x:Array Type="sys:String">                             <sys:String>WPF</sys:String>                             <sys:String>ASP.NET</sys:String>                             <sys:String>WinUI</sys:String>                             <sys:String>WebAPI</sys:String>                             <sys:String>Blazor</sys:String>                             <sys:String>MAUI</sys:String>                             <sys:String>Xamarin</sys:String>                             <sys:String>WinForm</sys:String>                             <sys:String>UWP</sys:String>                         </x:Array>                     </wpfdev:BubblleControl.ItemsSource>                 </wpfdev:BubblleControl>             </TabItem>             <TabItem Header="自定义">                 <wpfdev:BubblleCanvas Width="400" Height="400">                     <Grid>                         <Grid Width="60"                                Height="60">                             <Ellipse Fill="MediumSpringGreen"                                      Opacity=".4"/>                             <Ellipse Stroke="MediumSpringGreen"                                       StrokeThickness=".8"/>                         </Grid>                         <TextBlock VerticalAlignment="Center"                                     HorizontalAlignment="Center"                                    Padding="10,0">                             <Hyperlink                                  Foreground="MediumSpringGreen"                                 FontWeight="Normal"                                 Command="{Binding ClickCommand,RelativeSource={RelativeSource AncestorType=local:BubblleControlExample}}">                                 <TextBlock Text="WPF"                                            TextAlignment="Center"                                            TextTrimming="CharacterEllipsis"/>                             </Hyperlink>                         </TextBlock>                     </Grid>                     <Grid>                         <Grid Width="60"                                Height="60">                             <Ellipse Fill="Brown"                                      Opacity=".4"/>                             <Ellipse Stroke="Brown"                                       StrokeThickness=".8"/>                         </Grid>                         <TextBlock VerticalAlignment="Center"                                     HorizontalAlignment="Center"                                    Padding="10,0">                             <Hyperlink                                  Foreground="Brown"                                 FontWeight="Normal"                                 Command="{Binding ClickCommand,RelativeSource={RelativeSource AncestorType=local:BubblleControlExample}}">                                 <TextBlock Text="MAUI"                                            TextAlignment="Center"                                            TextTrimming="CharacterEllipsis"/>                             </Hyperlink>                         </TextBlock>                     </Grid>                     <Grid>                         <Grid Width="60"                                Height="60">                             <Ellipse Fill="DeepSkyBlue"                                      Opacity=".4"/>                             <Ellipse Stroke="DeepSkyBlue"                                       StrokeThickness=".8"/>                         </Grid>                         <TextBlock VerticalAlignment="Center"                                     HorizontalAlignment="Center"                                    Padding="10,0">                             <Hyperlink                                  Foreground="DeepSkyBlue"                                 FontWeight="Normal"                                 Command="{Binding ClickCommand,RelativeSource={RelativeSource AncestorType=local:BubblleControlExample}}">                                 <TextBlock Text="Blazor"                                            TextAlignment="Center"                                            TextTrimming="CharacterEllipsis"/>                             </Hyperlink>                         </TextBlock>                     </Grid>                 </wpfdev:BubblleCanvas>             </TabItem>         </TabControl>     </Grid> </UserControl>

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

using System.Windows; using System.Windows.Controls; using System.Windows.Input; using WPFDevelopers.Samples.Helpers; namespace WPFDevelopers.Samples.ExampleViews {     /// <summary>     /// BubbleControlExample.xaml 的交互逻辑     /// </summary>     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泡泡菜单的示例代码的详细内容,更多关于WPF泡泡菜单的资料请关注易知道(ezd.cc)其它相关文章!

推荐阅读