社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  Git

WPF 实现 Gitee 气泡菜单(一)

dotNET跨平台 • 3 年前 • 393 次点击  

 WPF 实现 Gitee 气泡菜单(一)

气泡菜单(一)

作者:WPFDevelopersOrg

原文链接:    https://github.com/WPFDevelopersOrg/WPFDevelopers

  • 框架使用大于等于.NET40
  • Visual Studio 2022;
  • 项目使用 MIT 开源许可协议;
  • 需要实现气泡菜单需要使用Canvas画布进行添加内容;
  • 但是为了使用方便需要在ListBox中使用Canvas做为ItemsPanelTemplate
  • 使用时只需要在ListBox中添加ListBoxItem就行;

1) BubbleControl.cs 代码如下;

  • BubbleControl.cs 继承 Control创建依赖属性Content

  • 根据Content集合循环随机生成WidthHeight在生成SetLeftSetTop 需要判断重叠面积不能超过当前面积的百分之六十~九十;

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.Media;
using System.Windows.Shapes;
using WPFDevelopers.Helpers;

namespace WPFDevelopers.Controls
{
    [TemplatePart(Name = BorderTemplateName, Type = typeof(Border))]
    public class BubbleControl : ContentControl
    {
        private const string BorderTemplateName = "PART_Border";
      


        public new static readonly DependencyProperty ContentProperty =
            DependencyProperty.Register("Content"typeof(ObservableCollection), typeof(BubbleControl),
                new PropertyMetadata(null));

        public new static readonly DependencyProperty BorderBackgroundProperty =
            DependencyProperty.Register("BorderBackground"typeof(Brush), typeof(BubbleControl),
                new PropertyMetadata(null));

        private Border _border;
        private double _bubbleItemX, _bubbleItemY;
        private Ellipse _ellipse;
        private int _number;

        private readonly List _rects = new List();
        private RotateTransform _rotateTransform;
        private double _size;
        private const int _maxSize = 120;


        static BubbleControl()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(BubbleControl),
                new FrameworkPropertyMetadata(typeof(BubbleControl)));
        }

        public BubbleControl()
        {
            
            Loaded += delegate
            {
                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 > Content.Count - 1)
                            return;
                        var item = Content[_number];
                        if (double.IsNaN(item.Width) || double.IsNaN(item.Height))
                            SetBubbleItem(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.60.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;
                        if (item.Background is null)
                            item.Background = ControlsHelper.RandomBrush();
                        _rects.Add(new Rect(_bubbleItemX, _bubbleItemY, _size, _size));
                        _number++;
                    }
                    left = 0d;
                    top = top + _maxSize;
                }
               
            };
        }
        
        public new ObservableCollection Content
        {
            get => (ObservableCollection)GetValue(ContentProperty);
            set => SetValue(ContentProperty, value);
        }
        public Brush BorderBackground
        {
            get => (Brush)this.GetValue(BorderBackgroundProperty);
            set => this.SetValue(BorderBackgroundProperty, (object)value);
        }


        private void SetBubbleItem(BubbleItem item)
        {
            if (item.Text.Length >= 4)
                _size = ControlsHelper.GetRandom.Next(80, _maxSize);
            else
                _size = ControlsHelper.GetRandom.Next(50, _maxSize);
            item.Width = _size;
            item.Height = _size;
        }

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            _border = GetTemplateChild(BorderTemplateName) as Border;
        }

        public override void BeginInit()
        {
            Content = new ObservableCollection();
            base.BeginInit();
        }
    }
}

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) BubbleItem.cs 代码如下;

using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;

namespace WPFDevelopers
{
    public class BubbleItem : ListBoxItem
    {
        public static readonly DependencyProperty TextProperty =
            DependencyProperty.Register("Text"typeof(string), typeof(BubbleItem),
                new PropertyMetadata(string.Empty));

        public static readonly DependencyProperty SelectionCommandProperty =
           DependencyProperty.Register("SelectionCommand"typeof(ICommand), typeof(BubbleItem),
               new PropertyMetadata(null));
       
        static BubbleItem()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(BubbleItem),
                new FrameworkPropertyMetadata(typeof(BubbleItem)));
        }

        public string Text
        {
            get => (string)GetValue(TextProperty);
            set => SetValue(TextProperty, value);
        }
        public ICommand SelectionCommand
        {
            get => (ICommand)GetValue(SelectionCommandProperty);
            set => SetValue(SelectionCommandProperty, value);
        }
        
    }
}

4) BubbleControl.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">


   
    <Style TargetType="{x:Type controls:BubbleControl}">
        <Setter Property="Width" Value="400"/>
        <Setter  Property="Height" Value="400"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="local:BubbleControl">
                    <Grid Width="{TemplateBinding Width}" Height="{TemplateBinding Height}">
                        <Border BorderBrush="#E9E9E9" BorderThickness="1" 
                                Background="#FAFAFA" 
                                Margin="45"
                                CornerRadius="400"
                                x:Name="PART_Border">

                            <Ellipse Fill="#FFFFFF" Margin="20"/>
                        Border>
                        <ListBox ItemsSource="{TemplateBinding Content}"
                                 Background="Transparent" BorderBrush="Transparent"
                                 ScrollViewer.HorizontalScrollBarVisibility="Disabled"
                                 ScrollViewer.VerticalScrollBarVisibility="Disabled">

                            <ListBox.ItemContainerStyle>
                                <Style TargetType="{x:Type local:BubbleItem}">
                                    <Setter Property="Canvas.Left" Value="{Binding (Canvas.Left)}"/>
                                    <Setter Property="Canvas.Top" Value="{Binding (Canvas.Top)}"/><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="{x:Type controls:BubbleControl}" 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:BubbleControl">
                    <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>
                        <ListBox ItemsSource="{TemplateBinding Content}"
                                 Background="Transparent" BorderBrush="Transparent"
                                 ScrollViewer.HorizontalScrollBarVisibility="Disabled"
                                 ScrollViewer.VerticalScrollBarVisibility="Disabled"
                                 Style="{x:Null}">

                            <ListBox.ItemContainerStyle>
                                <Style TargetType="{x:Type controls:BubbleItem}">
                                    <Setter Property="Canvas.Left" Value="{Binding (Canvas.Left)}"/>
                                    <Setter Property="Canvas.Top" Value="{Binding (Canvas.Top)}"/>
                                    <Setter Property="Template">
                                        <Setter.Value>
                                            <ControlTemplate TargetType="controls:BubbleItem">
                                                <Grid Width="{TemplateBinding Width}" Height="{TemplateBinding Height}">
                                                    <Ellipse Fill="{TemplateBinding Background}" Opacity=".6"/>
                                                    
                                                    <TextBlock VerticalAlignment="Center" 
                                                               HorizontalAlignment="Center"
                                                               Padding="10,0">

                                                        <Hyperlink 
                                                            Foreground="{StaticResource BlackSolidColorBrush}"
                                                            Command="{TemplateBinding SelectionCommand}">

                                                            <TextBlock Text="{TemplateBinding Text}"
                                                                       TextAlignment="Center"
                                                                       TextTrimming="CharacterEllipsis"
                                                                       ToolTip="{TemplateBinding Text}"/>

                                                        Hyperlink>
                                                    TextBlock>
                                                Grid>
                                                <ControlTemplate.Triggers>
                                                ControlTemplate.Triggers>
                                            ControlTemplate>
                                        Setter.Value>
                                    Setter>
                                
Style>
                            ListBox.ItemContainerStyle>
                            <ListBox.ItemsPanel>
                                <ItemsPanelTemplate>
                                    <Canvas IsItemsHost="True" Width="{TemplateBinding Width}" 
                                            Height="{TemplateBinding Height}"/>

                                ItemsPanelTemplate>
                            ListBox.ItemsPanel>
                        ListBox>
                    Grid>
                ControlTemplate>
            Setter.Value>
        Setter>
    Style>

ResourceDictionary>

5) BubbleControlExample.xaml 代码如下;

<local:BubbleControl x:Name="MyBubbleControl">
            <wpfdev:BubbleControl.Content>
                <wpfdev:BubbleItem  Text="WPF" 
                                    SelectionCommand="{Binding WPFCommand,RelativeSource={RelativeSource AncestorType=local:BubbleControlExample}}"/>

                <wpfdev:BubbleItem  Text="ASP.NET"/>
                <wpfdev:BubbleItem  Text="WinUI"/>
                <wpfdev:BubbleItem  Text="WebAPI"/>
                <wpfdev:BubbleItem  Text="Blazor"/>
                <wpfdev:BubbleItem  Text="MAUI"
                                    SelectionCommand="{Binding MAUICommand,RelativeSource={RelativeSource AncestorType=local:BubbleControlExample}}"/>

                <wpfdev:BubbleItem  Text="Xamarin"/>
                <wpfdev:BubbleItem  Text="WinForm"/>
                <wpfdev:BubbleItem  Text="UWP"/>
            wpfdev:BubbleControl.Content>
        local:BubbleControl>

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

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

namespace WPFDevelopers.Samples.ExampleViews
{
    /// 
    /// BubbleControlExample.xaml 的交互逻辑
    /// 
    public partial class BubbleControlExample : UserControl
    {
        public BubbleControlExample()
        {
            InitializeComponent();
        }
        public ICommand WPFCommand => new RelayCommand(delegate
        {
            WPFDevelopers.Minimal.Controls.MessageBox.Show("点击了“WPF开发者”.""提示");
        });
        public ICommand MAUICommand => new RelayCommand(delegate
        {
            WPFDevelopers.Minimal.Controls.MessageBox.Show("点击了“MAUI开发者”.""提示");
        });
    }
}


Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/138048