Creating a Circular GUI Creating a Circular GUI wpf wpf

Creating a Circular GUI


I wrote out some code doing something close to what you described.

I’m not sure to understand how you do want the circle to appear, so I just let a part of it always visible.And I didn’t get the part about the mobile outer ring.

circle gui

Creating and placing the window

The XAML is very simple, it just needs a grid to host the circle’s pieces, and some attributes to remove window decorations and taskbar icon:

<Window x:Class="circle.MainWindow"        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"        Title="Circle"        Width="250"        Height="250"        AllowsTransparency="True"        Background="Transparent"        MouseDown="WindowClicked"        ShowInTaskbar="False"        WindowStyle="None">    <Grid Name="Container"/></Window>

To place the window in the bottom right corner, you can use SystemParameters.WorkArea in the constructor:

public MainWindow(){    InitializeComponent();    var desktopDim = SystemParameters.WorkArea;    Left = desktopDim.Right - Width;    Top = desktopDim.Bottom - Height;}

Creating the shape

I build the circle as a bunch of circle pieces that I generate from code behind:

private Path CreateCirclePart()        {            var circle = new CombinedGeometry                {                    GeometryCombineMode = GeometryCombineMode.Exclude,                    Geometry1 = new EllipseGeometry { Center = _center, RadiusX = _r2, RadiusY = _r2 },                    Geometry2 = new EllipseGeometry { Center = _center, RadiusX = _r1, RadiusY = _r1 }                };            var sideLength = _r2 / Math.Cos((Math.PI/180) * (ItemAngle / 2.0));            var x = _center.X - Math.Abs(sideLength * Math.Cos(ItemAngle * Math.PI / 180));            var y = _center.Y - Math.Abs(sideLength * Math.Sin(ItemAngle * Math.PI / 180));            var triangle = new PathGeometry(                new PathFigureCollection(new List<PathFigure>{                    new PathFigure(                        _center,                        new List<PathSegment>                            {                                new LineSegment(new Point(_center.X - Math.Abs(sideLength),_center.Y), true),                                new LineSegment(new Point(x,y), true)                            },                        true)                }));            var path = new Path                {                    Fill = new SolidColorBrush(Colors.Cyan),                    Stroke = new SolidColorBrush(Colors.Black),                    StrokeThickness = 1,                    RenderTransformOrigin = new Point(1, 1),                    RenderTransform = new RotateTransform(0),                    Data = new CombinedGeometry                        {                            GeometryCombineMode = GeometryCombineMode.Intersect,                            Geometry1 = circle,                            Geometry2 = triangle                        }                };            return path;        }

First step is to build two concentric circles and to combine them in a CombinedGeometry with CombineMode set to exclude. Then I create a triangle just tall enough to contain the section of the ring that I want, and I keep the intersection of these shapes.

Seeing it with the second CombineMode set to xor may clarify:

creating the shape

Building the circle

The code above uses some instance fields that make it generic: you can change the number of pieces in the circle or their radius; it will always fill the corner.

I then populate a list with the required number of shape, and add them to the grid:

private const double MenuWidth = 80;private const int ItemCount = 6;private const double AnimationDelayInSeconds = 0.3;private readonly Point _center;private readonly double _r1, _r2;private const double ItemSpacingAngle = 2;private const double ItemAngle = (90.0 - (ItemCount - 1) * ItemSpacingAngle) / ItemCount;private readonly List<Path> _parts = new List<Path>();private bool _isOpen;public MainWindow(){    InitializeComponent();    // window in the lower right desktop corner    var desktopDim = SystemParameters.WorkArea;    Left = desktopDim.Right - Width;    Top = desktopDim.Bottom - Height;    _center = new Point(Width, Height);    _r2 = Width;    _r1 = _r2 - MenuWidth;    Loaded += (s, e) => CreateMenu();}private void CreateMenu(){    for (var i = 0; i < ItemCount; ++i)    {        var part = CreateCirclePart();        _parts.Add(part);        Container.Children.Add(part);    }}

ItemSpacingAngle define the blank between two consecutive pieces.

Animating the circle

The final step is to unfold the circle. Using a rotateAnimation over the path rendertransform make it easy.Remember this part of the CreateCirclePart function:

RenderTransformOrigin = new Point(1, 1),RenderTransform = new RotateTransform(0),

The RenderTransform tells that the animation we want to perform is a rotation, and RenderTransformOrigin set the rotation origin to the lower right corner of the shape (unit is percent).We can now animate it on click event:

private void WindowClicked(object sender, MouseButtonEventArgs e)        {            for (var i = 0; i < ItemCount; ++i)            {                if (!_isOpen)                    UnfoldPart(_parts[i], i);                else                    FoldPart(_parts[i], i);            }            _isOpen = !_isOpen;        }        private void UnfoldPart(Path part, int pos)        {            var newAngle = pos * (ItemAngle + ItemSpacingAngle);            var rotateAnimation = new DoubleAnimation(newAngle, TimeSpan.FromSeconds(AnimationDelayInSeconds));            var tranform = (RotateTransform)part.RenderTransform;            tranform.BeginAnimation(RotateTransform.AngleProperty, rotateAnimation);        }        private void FoldPart(Path part, int pos)        {            var rotateAnimation = new DoubleAnimation(0, TimeSpan.FromSeconds(AnimationDelayInSeconds));            var tranform = (RotateTransform)part.RenderTransform;            tranform.BeginAnimation(RotateTransform.AngleProperty, rotateAnimation);        }


Not actually answering this, but I liked your question enough that I wanted to get a minimal proof of concept together for fun and I really enjoyed doing it so i thought I'd share my xaml with you:

<Window        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"        xmlns:ed="http://schemas.microsoft.com/expression/2010/drawing" x:Class="WpfApplication1.Window2"        Title="Window2" Height="150" Width="150" Topmost="True" MouseLeftButtonDown="Window2_OnMouseLeftButtonDown"        AllowsTransparency="True" OpacityMask="White" WindowStyle="None" Background="Transparent" >    <Grid>        <ed:Arc ArcThickness="40"         ArcThicknessUnit="Pixel" EndAngle="0" Fill="Blue" HorizontalAlignment="Left"         Height="232" Margin="33,34,-115,-116" Stretch="None"         StartAngle="270" VerticalAlignment="Top" Width="232" RenderTransformOrigin="0.421,0.471"/>        <Button HorizontalAlignment="Left" VerticalAlignment="Top" Width="41" Margin="51.515,71.385,0,0" Click="Button_Click" RenderTransformOrigin="0.5,0.5">            <Button.Template>                <ControlTemplate>                    <Path Data="M50.466307,88.795148 L61.75233,73.463763 89.647286,102.42368 81.981422,113.07109 z"                           Fill="DarkBlue" HorizontalAlignment="Left" Height="39.606"                           Stretch="Fill" VerticalAlignment="Top" Width="39.181"/>                </ControlTemplate>            </Button.Template>        </Button>    </Grid></Window>

And it looks like this:enter image description here