[c # / WPF] Create a side menu button that looks like contents of this article in Microsoft Docs

4 minute read

Introduction

Microsoft Docs Right side menu? I had to make something like ** WPF **, so I will write down my implementation method as a memorandum.

microsoft_docs_sidemenu.png

I have the source code on github.
https://github.com/pisa-kun/SideBarSample

Sample made

I will publish what kind of thing I made first.

image.png

Grid.Column is divided into two parts, the side menu and the main part (content that switches with the selected side menu).
I prepared two buttons on the side menu for the time being, and I have not assigned anything to the main part this time. It’s just a side menu demo.

image.png

When the mouse is over, the background of the button is made slightly transparent (about 15%) in white.

image.png

When the mouse is clicked, it is transparent (30%) in white compared to when the mouse is over.

image.png

When the mouse is clicked and selected, a white rectangle is displayed on the left side, and the background of the button is also transparent in white.

If you click ButtonB while ButtonA is selected, ButtonA is deselected and ButtonB is selected.

The source code will be explained below.
The project structure is as follows. I will explain only the parts that seem necessary.

image.png

-images folder
–A folder that collects the icons displayed on the Button.

  • MainWindow.xaml
    –A window launched at startup. The screen I showed you earlier.
  • SideBarControl.xaml
    –The side menu is created as UserControl.
  • ViewModel.cs
    –Describes Button Command and selected properties

SideBarControl.xaml
I edited the template of ** ToggleButton ** and forcibly combined ** Rectangle **, ** Image **, ** TextBlock ** to make one control like that.

<UserControl x:Class="sample.SideBarControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:sample"
             mc:Ignorable="d" 
             Name="SideBar"
             d:DesignHeight="450" d:DesignWidth="800">
    <UserControl.Resources>
        <ResourceDictionary>
            <!--Side Bar Image-->
            <!--Only change Opacity of BackGround-->
            <!--Opacity operation with the alpha value of SolidColorBrush-->
            <Style x:Key="SideBarNewButtonStyle" TargetType="{x:Type ToggleButton}">
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="{x:Type ToggleButton}">
                            <Grid>
                                <StackPanel Orientation="Horizontal" Background="#00FFFFFF" x:Name="Panel">
                                    <Rectangle x:Name="rectangle" Width="10" Fill="White" Visibility="Hidden" HorizontalAlignment="Left" />
                                    <Image Source="{Binding Path=ImageFileName, ElementName=SideBar}" Width="20" Height="20" Margin="10,0,10,0" HorizontalAlignment="Center" x:Name="StyleImagePath" />
                                    <TextBlock Text="{Binding Path=ButtonText, ElementName=SideBar}" HorizontalAlignment="Right"  VerticalAlignment="Center" Foreground="White" FontSize="12" x:Name="StyleText" />
                                </StackPanel>
                            </Grid>

                            <ControlTemplate.Triggers>
                                <MultiTrigger>
                                    <MultiTrigger.Conditions>
                                        <Condition Property="IsMouseOver" Value="true" />
                                        <Condition Property="IsChecked" Value="True" />
                                    </MultiTrigger.Conditions>
                                    <Setter Property="Background" TargetName="Panel" Value="#26FFFFFF"/>
                                    <Setter Property="Visibility" TargetName="rectangle" Value="Visible"/>
                                </MultiTrigger>

                                <MultiTrigger>
                                    <MultiTrigger.Conditions>
                                        <Condition Property="IsMouseOver" Value="true" />
                                        <Condition Property="IsChecked" Value="false" />
                                    </MultiTrigger.Conditions>
                                    <Setter Property="Background" TargetName="Panel" Value="#26FFFFFF"/>
                                    <Setter Property="Visibility" TargetName="rectangle" Value="Hidden"/>
                                </MultiTrigger>
                                
                                <MultiTrigger>
                                    <MultiTrigger.Conditions>
                                        <Condition Property="IsPressed" Value="true" />
                                    </MultiTrigger.Conditions>
                                    <Setter Property="Background" TargetName="Panel" Value="#4CFFFFFF"/>
                                </MultiTrigger>

                                <MultiTrigger>
                                    <MultiTrigger.Conditions>
                                        <Condition Property="IsChecked" Value="True" />
                                        <Condition Property="IsMouseOver" Value="False" />
                                    </MultiTrigger.Conditions>
                                    <MultiTrigger.Setters>
                                        <Setter Property="Visibility" TargetName="rectangle" Value="Visible"/>
                                        <Setter Property="Background" TargetName="Panel" Value="#39FFFFFF"/>
                                    </MultiTrigger.Setters>
                                </MultiTrigger>

                                <MultiTrigger>
                                    <MultiTrigger.Conditions>
                                        <Condition Property="IsChecked" Value="False" />
                                        <Condition Property="IsMouseOver" Value="False" />
                                    </MultiTrigger.Conditions>
                                    <MultiTrigger.Setters>
                                        <Setter Property="Visibility" TargetName="rectangle" Value="Hidden"/>
                                        <Setter Property="Background" TargetName="Panel" Value="#00FFFFFF"/>
                                    </MultiTrigger.Setters>
                                </MultiTrigger>
                                
                            </ControlTemplate.Triggers>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>
        </ResourceDictionary>
    </UserControl.Resources>

    <!--Associate UserControl Name and ElementName-->
    <ToggleButton x:Name="Sidebar_button" Background="Transparent" BorderThickness="0" Style="{StaticResource SideBarNewButtonStyle}" 
                  IsChecked="{Binding Path=IsSelected, ElementName=SideBar}" Command="{Binding Path=ButtonCommand, ElementName=SideBar}"/>

</UserControl>
// SideBarControl.xaml.cs
using Prism.Commands;
using System.Windows;
using System.Windows.Controls;

namespace sample
{
    /// <summary>
    /// SideBarControl.xaml interaction logic
    /// </summary>
    public partial class SideBarControl : UserControl
    {
        public static readonly DependencyProperty ImageFileNameProperty =
            DependencyProperty.Register(nameof(ImageFileName), typeof(string), typeof(SideBarControl), new UIPropertyMetadata(string.Empty, new PropertyChangedCallback(OnPropertyChanged)));

        public static readonly DependencyProperty ButtonTextProperty =
            DependencyProperty.Register(nameof(ButtonText), typeof(string), typeof(SideBarControl), new UIPropertyMetadata(string.Empty, new PropertyChangedCallback(OnPropertyChanged)));

        public static readonly DependencyProperty ButtonCommandProperty =
            DependencyProperty.Register(nameof(ButtonCommand), typeof(DelegateCommand), typeof(SideBarControl), new UIPropertyMetadata(null, new PropertyChangedCallback(OnPropertyChanged)));

        public static readonly DependencyProperty IsSelectedProperty =
            DependencyProperty.Register(nameof(IsSelected), typeof(bool), typeof(SideBarControl), new UIPropertyMetadata(false, new PropertyChangedCallback(OnPropertyChanged)));

        public string ImageFileName
        {
            get { return (string)GetValue(ImageFileNameProperty); }
            set { SetValue(ImageFileNameProperty, value); }
        }

        public string ButtonText
        {
            get { return (string)GetValue(ButtonTextProperty); }
            set { SetValue(ButtonTextProperty, value); }
        }

        public bool IsSelected
        {
            get { return (bool)GetValue(IsSelectedProperty); }
            set
            {
                SetValue(IsSelectedProperty, value);
            }
        }

        public DelegateCommand ButtonCommand
        {
            get { return (DelegateCommand)GetValue(ButtonCommandProperty); }
            set { SetValue(ButtonCommandProperty, value); }
        }

        public SideBarControl()
        {
            InitializeComponent();
        }

        private static void OnPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
        }

The Source property of the Image, the Text property of the TextBlock, and the IsChecked and Command properties of the ToggleButton should be set in MainWindow.xaml using the DependencyProperty.

You can also use ControlTemplate.Triggers to adjust the Visibility of the Rectangle and the Background of the Panel under conditions such as mouse over, mouse press, and click.

MainWindow.xaml

<Window x:Class="sample.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:sample"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition x:Name="SideBar"  Width="100*"/>
            <ColumnDefinition x:Name="MainArea" Width="300*"/>
        </Grid.ColumnDefinitions>
        <!--side menu-->
        <Grid Grid.Column="0" FocusVisualStyle="{x:Null}">
            <Grid.Background>
                <LinearGradientBrush>
                    <GradientStop Color="#dc143c" Offset="0"/>
                    <GradientStop Color="#ff8c00" Offset="0.8"/>
                </LinearGradientBrush>
            </Grid.Background>
        </Grid>
        <Grid Grid.Column="0" FocusVisualStyle="{x:Null}">
            <Grid.RowDefinitions>
                <RowDefinition Height="30"/>
                <RowDefinition Height="30"/>
            </Grid.RowDefinitions>
            <!--Side menu buttons-->
            <local:SideBarControl Grid.Row="0" ImageFileName="images\e713.png " ButtonText="Button1" ButtonCommand="{Binding ButtonCommandA}" IsSelected="{Binding IsButtonA, Mode=TwoWay}" />
            <local:SideBarControl Grid.Row="1" ImageFileName="images\e772.png " ButtonText="Button2" ButtonCommand="{Binding ButtonCommandB}" IsSelected="{Binding IsButtonB, Mode=TwoWay}" />
        </Grid>
    </Grid>
</Window>

I will omit the code-behind (MainWindow.xaml.cs) because I just added ** this.DataContext = new ViewModel (); ** to the constructor.

Should I explain the following part?

<!--Side menu buttons-->
<local:SideBarControl Grid.Row="0" ImageFileName="images\e713.png " ButtonText="Button1"
 ButtonCommand="{Binding ButtonCommandA}" IsSelected="{Binding IsButtonA, Mode=TwoWay}" />

<local:SideBarControl Grid.Row="1" ImageFileName="images\e772.png " ButtonText="Button2"
 ButtonCommand="{Binding ButtonCommandB}" IsSelected="{Binding IsButtonB, Mode=TwoWay}" />

Set the file path of the png image in ImageFileName.
Binds the Action when pressed by ButtonCommand and the bool value of IsSelected whether it is selected from ViewModel.

ViewModel.cs

In viewModel, describe Bool type Button property and DelegateCommand.

For ButtonCommand, MessageBox is displayed and Button properties are changed.
If you want to complete it as an application, describe the page transition of the main screen here.

public class ViewModel : BindableBase
    {
        private bool isButtonA = false;
        public bool IsButtonA
        {
            get { return this.isButtonA; }
            set { this.SetProperty(ref this.isButtonA, value); }
        }

        private bool isButtonB = false;
        public bool IsButtonB
        {
            get { return this.isButtonB; }
            set { this.SetProperty(ref this.isButtonB, value); }
        }
        
        public DelegateCommand ButtonCommandA { get; private set; }
        public DelegateCommand ButtonCommandB { get; private set; }

        public ViewModel()
        {
            ButtonCommandA = new DelegateCommand(() => {
                MessageBox.Show($"ButtonA is [{IsButtonA}] state");
                IsButtonA = true;
                IsButtonB = false;
            });

            ButtonCommandB = new DelegateCommand(() => {
                MessageBox.Show($"ButtonB is [{IsButtonB}] state");
                IsButtonA = false;
                IsButtonB = true;
            });
        }

Summary

Does UWP already have this kind of control?

Tags: ,

Updated: