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

 

GitHub - kei-soft/KJunBlog

Contribute to kei-soft/KJunBlog development by creating an account on GitHub.

github.com

 

728x90
그리드형