WPF实现环(圆)形菜单
WPF开发者QQ群: 340500857 | 微信群 -> 进入公众号主页 加入组织
每日一笑
刚在路上看见了小学同学正在捡矿泉水瓶子,心里很不是滋味,想不到昔日的玩伴如今竟然成了竞争对手。
前言
需要实现环(圆)形菜单。
欢迎转发、分享、点赞,谢谢大家~。
效果预览(更多效果请下载源码体验):
一、CircularMenuItemCustomControl.cs代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Media; using System.Windows.Shapes; namespace WpfCircularMenu { [TemplatePart(Name = RotateTransformTemplateName, Type = typeof(RotateTransform))] public class CircularMenuItemCustomControl : Control { private static readonly Type _typeofSelf = typeof(CircularMenuItemCustomControl); private const string RotateTransformTemplateName = "PART_RotateTransform"; private RotateTransform _angleRotateTransform; public double Angle { get { return (double)GetValue(AngleProperty); } set { SetValue(AngleProperty, value); } } public static readonly DependencyProperty AngleProperty = DependencyProperty.Register("Angle", typeof(double), typeof(CircularMenuItemCustomControl), new UIPropertyMetadata(OnAngleChanged)); private static void OnAngleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { CircularMenuItemCustomControl control = (CircularMenuItemCustomControl)d; control.UpdateAngle(); } void UpdateAngle() { if (_angleRotateTransform == null) return; _angleRotateTransform.Angle = Angle; } public string MenuTxt { get { return (string)GetValue(MenuTxtProperty); } set { SetValue(MenuTxtProperty, value); } } public static readonly DependencyProperty MenuTxtProperty = DependencyProperty.Register("MenuTxt", typeof(string), typeof(CircularMenuItemCustomControl), new PropertyMetadata(string.Empty)); public Brush BackgroundColor { get { return (Brush)GetValue(BackgroundColorProperty); } set { SetValue(BackgroundColorProperty, value); } } public static readonly DependencyProperty BackgroundColorProperty = DependencyProperty.Register("BackgroundColor", typeof(Brush), typeof(CircularMenuItemCustomControl), new PropertyMetadata(null)); public ImageSource IconImage { get { return (ImageSource)GetValue(IconImageProperty); } set { SetValue(IconImageProperty, value); } } public static readonly DependencyProperty IconImageProperty = DependencyProperty.Register("IconImage", typeof(ImageSource), typeof(CircularMenuItemCustomControl), new PropertyMetadata(null)); static CircularMenuItemCustomControl() { DefaultStyleKeyProperty.OverrideMetadata(_typeofSelf, new FrameworkPropertyMetadata(_typeofSelf)); } public override void OnApplyTemplate() { base.OnApplyTemplate(); _angleRotateTransform = GetTemplateChild(RotateTransformTemplateName) as RotateTransform; UpdateAngle(); } } }
二、CircularMenuItemCustomControlStyle.xaml 代码如下
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:WpfCircularMenu"> <Style TargetType="{x:Type local:CircularMenuItemCustomControl}"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="local:CircularMenuItemCustomControl"> <Grid VerticalAlignment="Center"> <Grid.RenderTransform> <RotateTransform x:Name="PART_RotateTransform" Angle="{TemplateBinding Angle}" CenterX="200" CenterY="200"></RotateTransform> </Grid.RenderTransform> <Path x:Name="PART_Path" Data="M 200,200 0,200 A 200,200 0 0 1 58.6,58.6z" Fill="{TemplateBinding BackgroundColor}" VerticalAlignment="Center"/> <Image Source="{TemplateBinding IconImage}" RenderTransformOrigin="0.5,0.5" Margin="60,70,0,0" HorizontalAlignment="Left" VerticalAlignment="Center" Width="40" Height="40" > <Image.RenderTransform> <RotateTransform Angle="-70"/> </Image.RenderTransform> </Image> </Grid> <ControlTemplate.Triggers> <Trigger Property="IsMouseOver" Value="true"> <Setter TargetName="PART_Path" Property="Fill" Value="#009AD8"/> <Setter Property="Cursor" Value="Hand"/> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> </Style> </ResourceDictionary>
三、MainWindow.xaml 代码如下
<Window x:Class="WpfCircularMenu.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:WpfCircularMenu" mc:Ignorable="d" Title="MainWindow" Height="850" Width="1200" Background="Black" SnapsToDevicePixels="True" TextOptions.TextFormattingMode="Display" UseLayoutRounding="True"> <Window.Resources> <Storyboard x:Key="CheckedStoryboard"> <DoubleAnimation Storyboard.TargetName="PART_EllipseGeometry" Storyboard.TargetProperty="RadiusX" Duration="00:00:0.4" To="200"/> <DoubleAnimation Storyboard.TargetName="PART_EllipseGeometry" Storyboard.TargetProperty="RadiusY" Duration="00:00:0.4" To="200"/> </Storyboard> <Storyboard x:Key="UncheckedStoryboard"> <DoubleAnimation Storyboard.TargetName="PART_EllipseGeometry" Storyboard.TargetProperty="RadiusX" Duration="00:00:0.3" To="0"/> <DoubleAnimation Storyboard.TargetName="PART_EllipseGeometry" Storyboard.TargetProperty="RadiusY" Duration="00:00:0.3" To="0"/> </Storyboard> </Window.Resources> <Viewbox> <Grid Height="768" Width="1024"> <Canvas> <ItemsControl ItemsSource="{Binding MenuArray,RelativeSource={RelativeSource AncestorType=local:MainWindow}}" Canvas.Left="150" Canvas.Top="150"> <ItemsControl.Clip> <EllipseGeometry x:Name="PART_EllipseGeometry" RadiusX="0" RadiusY="0" Center="200,200"></EllipseGeometry> </ItemsControl.Clip> <ItemsControl.ItemTemplate> <DataTemplate> <local:CircularMenuItemCustomControl Angle="{Binding Angle}" MenuTxt="{Binding Title}" BackgroundColor="{Binding FillColor}" IconImage="{Binding IconImage}"/> </DataTemplate> </ItemsControl.ItemTemplate> <ItemsControl.ItemsPanel> <ItemsPanelTemplate> <Grid/> </ItemsPanelTemplate> </ItemsControl.ItemsPanel> </ItemsControl> <ToggleButton Canvas.Left="300" Canvas.Top="300" Cursor="Hand"> <ToggleButton.Template> <ControlTemplate TargetType="ToggleButton"> <Grid> <Ellipse x:Name="PART_Ellipse" Width="100" Height="100" Fill="#009AD8" ToolTip="关闭"/> <Path x:Name="PART_Path" Data="M734.618 760.269c-24.013 24.013-62.925 24.013-86.886 0l-135.731-155.136-135.731 155.085c-24.013 24.013-62.925 24.013-86.886 0-24.013-24.013-24.013-62.925 0-86.886l141.21-161.28-141.261-161.382c-24.013-24.013-24.013-62.874 0-86.886s62.874-24.013 86.886 0l135.782 155.187 135.731-155.187c24.013-24.013 62.874-24.013 86.886 0s24.013 62.925 0 86.886l-141.21 161.382 141.21 161.28c24.013 24.013 24.013 62.925 0 86.938z" Fill="White" Stretch="Fill" Width="20" Height="20" RenderTransformOrigin="0.5,0.5" IsHitTestVisible="False"> </Path> </Grid> <ControlTemplate.Triggers> <Trigger Property="IsChecked" Value="false"> <Setter TargetName="PART_Path" Property="RenderTransform"> <Setter.Value> <RotateTransform Angle="45"/> </Setter.Value> </Setter> <Setter Property="ToolTip" TargetName="PART_Ellipse" Value="展开"/> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> </ToggleButton.Template> <ToggleButton.Triggers> <EventTrigger RoutedEvent="ToggleButton.Checked"> <BeginStoryboard Storyboard="{StaticResource CheckedStoryboard}"/> </EventTrigger> <EventTrigger RoutedEvent="ToggleButton.Unchecked"> <BeginStoryboard Storyboard="{StaticResource UncheckedStoryboard}"/> </EventTrigger> </ToggleButton.Triggers> </ToggleButton> <TextBlock Text="微信公众号:WPF开发者" FontSize="40" Foreground="#A9CC32" FontWeight="Bold" Canvas.Top="50"/> <Image Source="Images/gzh.png" Canvas.Left="140" Canvas.Bottom="40"/> </Canvas> </Grid> </Viewbox> </Window>
四、MainWindow.xaml.cs 代码如下
<Window x:Class="WpfCircularMenu.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:WpfCircularMenu" mc:Ignorable="d" Title="MainWindow" Height="850" Width="1200" Background="Black" SnapsToDevicePixels="True" TextOptions.TextFormattingMode="Display" UseLayoutRounding="True"> <Window.Resources> <Storyboard x:Key="CheckedStoryboard"> <DoubleAnimation Storyboard.TargetName="PART_EllipseGeometry" Storyboard.TargetProperty="RadiusX" Duration="00:00:0.4" To="200"/> <DoubleAnimation Storyboard.TargetName="PART_EllipseGeometry" Storyboard.TargetProperty="RadiusY" Duration="00:00:0.4" To="200"/> </Storyboard> <Storyboard x:Key="UncheckedStoryboard"> <DoubleAnimation Storyboard.TargetName="PART_EllipseGeometry" Storyboard.TargetProperty="RadiusX" Duration="00:00:0.3" To="0"/> <DoubleAnimation Storyboard.TargetName="PART_EllipseGeometry" Storyboard.TargetProperty="RadiusY" Duration="00:00:0.3" To="0"/> </Storyboard> </Window.Resources> <Viewbox> <Grid Height="768" Width="1024"> <Canvas> <ItemsControl ItemsSource="{Binding MenuArray,RelativeSource={RelativeSource AncestorType=local:MainWindow}}" Canvas.Left="150" Canvas.Top="150"> <ItemsControl.Clip> <EllipseGeometry x:Name="PART_EllipseGeometry" RadiusX="0" RadiusY="0" Center="200,200"></EllipseGeometry> </ItemsControl.Clip> <ItemsControl.ItemTemplate> <DataTemplate> <local:CircularMenuItemCustomControl Angle="{Binding Angle}" MenuTxt="{Binding Title}" BackgroundColor="{Binding FillColor}" IconImage="{Binding IconImage}"/> </DataTemplate> </ItemsControl.ItemTemplate> <ItemsControl.ItemsPanel> <ItemsPanelTemplate> <Grid/> </ItemsPanelTemplate> </ItemsControl.ItemsPanel> </ItemsControl> <ToggleButton Canvas.Left="300" Canvas.Top="300" Cursor="Hand"> <ToggleButton.Template> <ControlTemplate TargetType="ToggleButton"> <Grid> <Ellipse x:Name="PART_Ellipse" Width="100" Height="100" Fill="#009AD8" ToolTip="关闭"/> <Path x:Name="PART_Path" Data="M734.618 760.269c-24.013 24.013-62.925 24.013-86.886 0l-135.731-155.136-135.731 155.085c-24.013 24.013-62.925 24.013-86.886 0-24.013-24.013-24.013-62.925 0-86.886l141.21-161.28-141.261-161.382c-24.013-24.013-24.013-62.874 0-86.886s62.874-24.013 86.886 0l135.782 155.187 135.731-155.187c24.013-24.013 62.874-24.013 86.886 0s24.013 62.925 0 86.886l-141.21 161.382 141.21 161.28c24.013 24.013 24.013 62.925 0 86.938z" Fill="White" Stretch="Fill" Width="20" Height="20" RenderTransformOrigin="0.5,0.5" IsHitTestVisible="False"> </Path> </Grid> <ControlTemplate.Triggers> <Trigger Property="IsChecked" Value="false"> <Setter TargetName="PART_Path" Property="RenderTransform"> <Setter.Value> <RotateTransform Angle="45"/> </Setter.Value> </Setter> <Setter Property="ToolTip" TargetName="PART_Ellipse" Value="展开"/> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> </ToggleButton.Template> <ToggleButton.Triggers> <EventTrigger RoutedEvent="ToggleButton.Checked"> <BeginStoryboard Storyboard="{StaticResource CheckedStoryboard}"/> </EventTrigger> <EventTrigger RoutedEvent="ToggleButton.Unchecked"> <BeginStoryboard Storyboard="{StaticResource UncheckedStoryboard}"/> </EventTrigger> </ToggleButton.Triggers> </ToggleButton> <TextBlock Text="微信公众号:WPF开发者" FontSize="40" Foreground="#A9CC32" FontWeight="Bold" Canvas.Top="50"/> <Image Source="Images/gzh.png" Canvas.Left="140" Canvas.Bottom="40"/> </Canvas> </Grid> </Viewbox> </Window>
更多教程欢迎关注微信公众号:
WPF开发者QQ群: 340500857
blogs: https://www.cnblogs.com/yanjinhua/p/14345136.html
源码Github:https://github.com/yanjinhuagood/WPFDevelopers.git
gitee:https://gitee.com/yanjinhua/WPFDevelopers.git