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 代码如下;
- ?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自定义 可以自行定义展示的内容;
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泡泡菜单的资料请关注w3xue其它相关文章!