Skip to content

Scenarios & Use Cases

Steven Edouard edited this page Oct 25, 2015 · 1 revision

Below are write-ups of how to achieve various scenarios - the control is pretty powerful and flexible, so cramming all variations and options into one demo wouldn't have been less useful.

Seamless Background

If you give RadialMenuButtons the same background color, you may have noticed that Windows will render a 1px thick seam between the individual buttons. Those seams show up when shapes fight for the same pixel.

To get around this issue, you can set the option HasBackgroundEllipse on the RadialMenu to true. In that mode, the menu will render a full ellipse behind your whole control. Since the ellipse is one shape, no seams will be visible. Obviously, for the ellipse to function as background, you will need to set the InnerNormalColor of your menu and your buttons to a near invisible color (we recommend #02FFFFFF).

<userControl:RadialMenu
    x:Name="MyRadialMenu"
    Diameter="300"
    StartAngle="-22.5"
    HasBackgroundEllipse="True"
    BackgroundEllipseFill="White"
    OuterArcThickness="20"
    CenterButtonBorder="Black"
    CenterButtonForeground="Black"
    CenterButtonIcon="&#x1f369;"
    InnerHoverColor="#E3EBEB"
    InnerNormalColor="#02FFFFFF" />

Access Keys

The control comes with a number of helper properties to enable access key behaviour. To ensure that the control doesn't mess with your application, it does not access any global properties, meaning that you will have capture KeyDown events yourself. The good news: Helper methods on the control make enabling access keys really easy.

To implement access keys, start with the RadialMenuButtons: Give your buttons two properties each, InnerAccessKey and OuterAccessKey (for instance M and P). To show little tooltips with the access key, call ShowAccessKeyTooltips() on the RadialMenu. To hide them, call HideAccessKeyTooltips(). In order to programmatically click either the inner or the outer portion of a RadialMenuButton, call ClickInnerRadialMenuButton(RadialMenuButton) or ClickOuterRadialMenuButton(RadialMenuButton), To wire this all up in your application, consult the following example code:

Add a RadialMenuButton to your RadialMenu, passing in access keys:

<RadialMenuButton x:Name="Pen1" Label="Pen" Icon="" IconFontFamily="Segoe MDL2 Assets" Type="Toggle" InnerAccessKey="K" OuterAccessKey="L" />

Then, wire up a KeyDown handler for your application. Depending on the key, we either display the little tooltips - or programmatically perform actions on buttons.

private void MyApp_KeyUp(CoreWindow sender, KeyEventArgs args)
{
    switch (args.VirtualKey)
    {
        case VirtualKey.Shift:
            MyRadialMenu.HideAccessKeyTooltips();
            break;
        case VirtualKey.P:
            MyRadialMenu.ClickInnerRadialMenuButton(Pan);
            break;
        case VirtualKey.O:
            MyRadialMenu.ClickOuterRadialMenuButton(Pan);
            break;
    };
}

As soon as the user lifts the Shift key again, we hide the tooltips:

private void Melbourne_KeyDown(CoreWindow sender, KeyEventArgs args)
{
    if (args.VirtualKey == VirtualKey.Shift) MyRadialMenu.HideAccessKeyTooltips();
}

Adding a Third Arc to Buttons

Let's assume you don't like the buttons we gave you - and you'd rather have buttons with a third arc (right now, you have an inner and an outer arc). Scenarios like that aren't configurable with the current code, but easy to implement yourself. You will have to touch two classes: RadialMenuButton, which is the control you use from your application, and PieSlice, which is the class we instantiate using a given button.

Add A Third Arc to the RadialMenuButton

In RadialMenuButton.xaml.cs, go and find all the dependency properties that impact the inner arc - and merely copy those, slightly changing the name. In this example, we're using MoreInner instead of Inner. Add the following dependency properties to RadialMenuButton.xaml.cs:

// More Inner Arc Colors
public static readonly DependencyProperty MoreInnerNormalColorProperty =
    DependencyProperty.Register("MoreInnerNormalColor", typeof(Color?), typeof(RadialMenuButton), null);

public static readonly DependencyProperty MoreInnerHoverColorProperty =
    DependencyProperty.Register("MoreInnerHoverColor", typeof(Color?), typeof(RadialMenuButton), null);

public static readonly DependencyProperty MoreInnerTappedColorProperty =
    DependencyProperty.Register("MoreInnerTappedColor", typeof(Color?), typeof(RadialMenuButton), null);

public static readonly DependencyProperty MoreInnerReleasedColorProperty =
    DependencyProperty.Register("MoreInnerReleasedColor", typeof(Color?), typeof(RadialMenuButton), null);

/// <summary>
/// Hover color for the inner portion of the button
/// </summary>
public Color? MoreInnerHoverColor
{
    get { return (Color?)GetValue(MoreInnerHoverColorProperty); }
    set { SetValue(MoreInnerHoverColorProperty, value); }
}

/// <summary>
/// Normal color for the inner portion of the button
/// </summary>
public Color? MoreInnerNormalColor
{
    get { return (Color?)GetValue(MoreInnerNormalColorProperty); }
    set { SetValue(MoreInnerNormalColorProperty, value); }
}

/// <summary>
/// Tapped color for the inner portion of the button
/// </summary>
public Color? MoreInnerTappedColor
{
    get { return (Color?)GetValue(MoreInnerTappedColorProperty); }
    set { SetValue(MoreInnerTappedColorProperty, value); }
}

/// <summary>
/// Released color for the inner portion of the button
/// </summary>
public Color? MoreInnerReleasedColor
{
    get { return (Color?)GetValue(MoreInnerReleasedColorProperty); }
    set { SetValue(MoreInnerReleasedColorProperty, value); }
}

Next, we need to add an event handler & delegate for the Pressed event - in case you want to somehow handle that the more inner arc has been pressed.

public delegate void MoreInnerArcPressedEventHandler(object sender, Windows.UI.Xaml.Input.PointerRoutedEventArgs e);
/// <summary>
/// Invoked when the inner arc of the button has been pressed (mouse, touch, stylus)
/// </summary>
public event MoreInnerArcPressedEventHandler MoreInnerArcPressedEvent;

public void OnMoreInnerArcPressed(Windows.UI.Xaml.Input.PointerRoutedEventArgs e)
{
    MoreInnerArcPressedEvent?.Invoke(this, e);
}

Now that the button has this information, we need to make sure we render it properly. Whenever the menu is given RadialMenuButtons, it uses PieSlices to actually render objects. Right now, a PieSlice consists of two Path objects (InnerPieSlicePath and OuterPieSlicePath), as well as icon and label objects.

A quick word how the two pieces are drawn: The outer arc element is "thick arc", drawn using two LineSegments and two ArcSegments. The inner arc is using only three points - one arc (right below the oute arc) and two lines to the center of the control.

To create a third arc (MoreInnerArc) that is able to sit on top of the InnerArc, we can simply reuse InnerArc with a smaller radius. However, nothing keeps you from creating a custom class with a custom Redraw() method.

To add the third arc, open up PieSlice.xaml. Find the section with the two arcs and add the third arc below them:

<userControl:OuterPieSlicePath x:Name="OuterPieSlicePath" />
<userControl:InnerPieSlicePath x:Name="InnerPieSlicePath" />
<userControl:InnerPieSlicePath x:Name="MoreInnerPieSlicePath" />

Then, open up PieSlice.xaml.cs. Here, we need also need to add the above defined color dependency properties. You can simply reuse what you already added to RadialMenuButton - just copy in the same dependency properties. Then, go and find the OnLoaded method, in which we also configure and setup the two other arcs. Take a look at how we setup the other arcs and configure the third arc to your needs. In the example below, we're configuring the the third arc to have a radius that is 50px smaller than the InnerArc.

private void OnLoaded(object sender, RoutedEventArgs routedEventArgs)
{
    // ...

    MoreInnerPieSlicePath.Radius = Radius - OuterArcThickness - 50;
    MoreInnerPieSlicePath.StartAngle = StartAngle;
    MoreInnerPieSlicePath.Angle = Angle;
    MoreInnerPieSlicePath.Fill = new SolidColorBrush(MoreInnerNormalColor);

    // ...
}

Make sure to also setup event handlers. You can do that either in code-behind or in XAML, as long as you call the original method on RadialMenuButton (which is referenced in OriginalRadialMenuButton). If you want your third inner arc to be animated and have hover/pressed states, go checkout PieSlice.xaml - a bunch of visual states are defined and you can easily add additional ones.

private void innerPieSlicePath_PointerPressed(object sender, PointerRoutedEventArgs e)
{
    OriginalRadialMenuButton.OnMoreInnerArcPressed(e);

    // The line below only works if you previously defined a "MoreInnerPressed" visual state
    // in PieSlice.xaml
    // VisualStateManager.GoToState(this, "MoreInnerPressed", true);

}

Well done! Now you have successfully created a third arc inside your button 🤘! You will probably notice that the icons and labels on the inner arc may now be behind your more inner arc - obviously, you can configure those to your liking, too.

Creating Custom Buttons

Let's say you need to create custom buttons that are not yet available in the RadialMenu control for your application. You can follow the steps below and use the custom TextBox input button we have created as a guide. In order to add a new custom button, you will need to modify three classes: RadialMenuButton, which is the control you use from your application, PieSlice, which is the class we instantiate using a given button, and Pie, which is the class we instantiate to create all the slices in a pie.

Add a New Button Type for Custom RadialMenuButton

In RadialMenuButton.xaml.cs, find the ButtonType enum. By adding MyType as one of the ButtonType enum values, we are able to specify the button type of the RadialMenuButton control from our application to instantiate the necessary controls in PieSlice.

public enum ButtonType
{
    Simple = 0,
    Radio,
    Toggle,
    Custom,
    MyType
};
Add Controls to the Pie Slice

In PieSlice.xaml.cs, from OnLoad, add code like the following to check if the application requests for a MyType button type RadialMenuButton. If so, then programmatically add the necessary controls to PieSlice.

// Setup controls for `MyType` button type RadialMenuButton
if (OriginalRadialMenuButton.Type == RadialMenuButton.ButtonType.MyType) CreateMyTypeControls();

Now you can add the necessary controls to PieSlice in the CreateMyTypeControls function. When done, add these controls to the TextLabelGrid to render them. This is probably where you will need to spend some time to ensure your new controls look right in PieSlice by configuring its Style, Margin, and Alignment.

private void CreateMyTypeControls()
{

    // Your custom controls here ...
    CustomControl = new SomeControl
    {
        Name = "CustomControl",
        FontSize = LabelSize,
        Margin = new Thickness(0, 67, 0, 0),
        HorizontalAlignment = HorizontalAlignment.Center,
        VerticalAlignment = VerticalAlignment.Center,
        // ... add more properties to your control

    TextLabelGrid.Children.Add(...);
}
Interact with Custom Controls When Pie Slice is Pressed

Now that we have the custom controls on our pie slice, in case you need to allow users to interact with the controls, we can enable the controls to focus when the inner pie slice is pressed. In PieSlice.xaml.cs, find the innerPieSlicePath_PointerPressed event handler. The following ensures your custom control now has focus.

private void innerPieSlicePath_PointerPressed(object sender, PointerRoutedEventArgs e)
{
    if (OriginalRadialMenuButton.Type == RadialMenuButton.ButtonType.MyType)
    {
        // YourControl.Focus(FocusState.Keyboard);
        e.Handled = true;
    }

    // ...
}
Pass Value to Custom Control in Pie Slice

For the MyType button custom controls, what if I want to set a default value? Sure you can. First we need to create a dependency property for the value of this control in PieSlice.xaml.cs.

public static readonly DependencyProperty CustomControlValueProperty =
    DependencyProperty.Register("CustomControlValue", typeof(string), typeof(PieSlice), null);

Then in CreateMyTypeControls, we need to programmatically bind the CustomControlValue Dependency property to your custom control's Text or Value property.

YourCustomControl.SetBinding(YourCustomControl.TextProperty, new Windows.UI.Xaml.Data.Binding() { Source = this.CustomControlValue });

To pass in a value for the dependency property 'CustomControlValueProperty', in 'Pie.xaml.cs', from the Draw function, if the slice RadialMenuButton is of type MyType, we set the CustomControlValue property of this pie slice to the value specified from your application.

if (slice.Type == RadialMenuButton.ButtonType.MyType)
{
    pieSlice.CustomControlValue = (string)slice.Value;
}

Hey! Look at that. You just created your own custom button! Good job! 👏👏 Now go and create more awesome looking radial menus.