C#/WPF
[WPF] RadialPanel
kjun.kr
2022. 12. 13. 23:21
728x90
728x170
RadialPanel.cs
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace Wpf.RadialPanelTest
{
public class RadialPanel : Panel
{
public static readonly DependencyProperty OrientationProperty;
bool showPieLines;
double angleEach;
Size sizeLargest;
double radius;
double outerEdgeFromCenter;
double innerEdgeFromCenter;
static RadialPanel()
{
OrientationProperty = DependencyProperty.Register("Orientation",
typeof(RadialPanelOrientation), typeof(RadialPanel),
new FrameworkPropertyMetadata(
RadialPanelOrientation.ByWidth,
FrameworkPropertyMetadataOptions.AffectsMeasure));
}
public RadialPanelOrientation Orientation
{
set { SetValue(OrientationProperty, value); }
get { return (RadialPanelOrientation)GetValue(OrientationProperty); }
}
public bool ShowPieLines
{
set
{
if (value != showPieLines)
{
InvalidateVisual();
}
showPieLines = value;
}
get
{
return showPieLines;
}
}
protected override Size MeasureOverride(Size sizeAvailable)
{
if (InternalChildren.Count == 0)
{
return new Size(0, 0);
}
angleEach = 360.0 / InternalChildren.Count;
sizeLargest = new Size(0, 0);
foreach (UIElement child in InternalChildren)
{
child.Measure(new Size(Double.PositiveInfinity, Double.PositiveInfinity));
sizeLargest.Width = Math.Max(sizeLargest.Width, child.DesiredSize.Width);
sizeLargest.Height = Math.Max(sizeLargest.Height, child.DesiredSize.Height);
}
if (Orientation == RadialPanelOrientation.ByWidth)
{
innerEdgeFromCenter = sizeLargest.Width / 2 / Math.Tan(Math.PI * angleEach / 360);
outerEdgeFromCenter = innerEdgeFromCenter + sizeLargest.Height;
radius = Math.Sqrt(Math.Pow(sizeLargest.Width / 2, 2) + Math.Pow(outerEdgeFromCenter, 2));
}
else
{
innerEdgeFromCenter = sizeLargest.Height / 2 / Math.Tan(Math.PI * angleEach / 360);
outerEdgeFromCenter = innerEdgeFromCenter + sizeLargest.Width;
radius = Math.Sqrt(Math.Pow(sizeLargest.Height / 2, 2) + Math.Pow(outerEdgeFromCenter, 2));
}
return new Size(2 * radius, 2 * radius);
}
protected override Size ArrangeOverride(Size sizeFinal)
{
double angleChild = 0;
Point ptCenter = new Point(sizeFinal.Width / 2, sizeFinal.Height / 2);
double multiplier = Math.Min(sizeFinal.Width / (2 * radius), sizeFinal.Height / (2 * radius));
foreach (UIElement child in InternalChildren)
{
child.RenderTransform = Transform.Identity;
if (Orientation == RadialPanelOrientation.ByWidth)
{
child.Arrange(
new Rect(ptCenter.X - multiplier * sizeLargest.Width / 2,
ptCenter.Y - multiplier * outerEdgeFromCenter,
multiplier * sizeLargest.Width,
multiplier * sizeLargest.Height));
}
else
{
child.Arrange(
new Rect(ptCenter.X + multiplier * innerEdgeFromCenter,
ptCenter.Y - multiplier * sizeLargest.Height / 2,
multiplier * sizeLargest.Width,
multiplier * sizeLargest.Height));
}
Point pt = TranslatePoint(ptCenter, child);
child.RenderTransform = new RotateTransform(angleChild, pt.X, pt.Y);
angleChild += angleEach;
}
return sizeFinal;
}
protected override void OnRender(DrawingContext dc)
{
base.OnRender(dc);
if (showPieLines)
{
Point ptCenter = new Point(RenderSize.Width / 2, RenderSize.Height / 2);
double multiplier = Math.Min(RenderSize.Width / (2 * radius), RenderSize.Height / (2 * radius));
Pen pen = new Pen(SystemColors.WindowTextBrush, 1);
pen.DashStyle = DashStyles.Dash;
dc.DrawEllipse(null, pen, ptCenter, multiplier * radius, multiplier * radius);
double angleChild = -angleEach / 2;
if (Orientation == RadialPanelOrientation.ByWidth)
{
angleChild += 90;
}
foreach (UIElement child in InternalChildren)
{
dc.DrawLine(pen, ptCenter, new Point(ptCenter.X + multiplier * radius * Math.Cos(2 * Math.PI * angleChild / 360),
ptCenter.Y + multiplier * radius * Math.Sin(2 * Math.PI * angleChild / 360)));
angleChild += angleEach;
}
}
}
}
public enum RadialPanelOrientation
{
ByWidth,
ByHeight
}
}
MainWindow.xaml
<Window
x:Class="Wpf.RadialPanelTest.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:local="clr-namespace:Wpf.RadialPanelTest"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="MainWindow"
Width="600"
Height="800"
mc:Ignorable="d">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<local:RadialPanel
x:Name="byHeightRadialPanel"
Grid.Row="0"
Orientation="ByHeight"
ShowPieLines="True" />
<local:RadialPanel
x:Name="byWidthRadialPanel"
Grid.Row="1"
Orientation="ByWidth"
ShowPieLines="False" />
</Grid>
</Window>
MainWindow.xaml.cs
using System.Windows;
using System.Windows.Controls;
namespace Wpf.RadialPanelTest
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.Loaded += MainWindow_Loaded;
}
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
for (int i = 0; i < 10; i++)
{
Button btn = new Button();
btn.Content = "Button " + (i + 1);
byHeightRadialPanel.Children.Add(btn);
}
for (int i = 0; i < 10; i++)
{
Button btn = new Button();
btn.Content = "Button " + (i + 1);
byWidthRadialPanel.Children.Add(btn);
}
}
}
}
[Source]
https://github.com/kei-soft/KJunBlog/tree/master/Wpf.RadialPanelTest
728x90
그리드형