[WPF] Menu

[WPF] Menu

이번 글에서는 WPF에서 Menu를 만드는 방법에 대해 알아보겠습니다. 기본적인 메뉴 생성부터, Check, Combo, UserControl로 만든 메뉴도 만들어 보겠습니다.

프로젝트 생성

우선 제일 먼저 WPF 프로젝트를 하나 만들어서 아래와 같이 View, ViewModel을 생성해줍니다. 아래는 코드는 MainWIndow.xaml입니다.

<Window x:Class="WpfMenu.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:WpfMenu"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    
    <Window.DataContext>
        <local:MainViewModel/>
    </Window.DataContext>

    <DockPanel>
        <!--메뉴-->
        <Menu DockPanel.Dock="Top">
        </Menu>
        
        <!--메시지 표시-->
        <Grid>
            <TextBlock Text="{Binding Message}" 
                       HorizontalAlignment="Center" 
                       VerticalAlignment="Center"
                       FontSize="18"/>
        </Grid>
    </DockPanel>
</Window>

DockPanel을 사용해서 상단에 메뉴를 붙이고, 하단에는 선택한 메뉴를 메시지로 표시하는 TextBlock이 있습니다. 이 TextBlock에 Message를 바인딩하였습니다.

그 다음 아래와 같이 MainWindowViewModel.cs를 만들어줍니다.

public class MainWindowViewModel : Notifier
{
    /// <summary>
    /// 메시지
    /// </summary>
    private string? _message;
    public string? Message
    {
        get => _message;
        set
        {
            if (_message != value)
            {
                _message = value;
                OnPropertyChanged();
            }
        }
    }

    public MainWindowViewModel()
    {
        // 메시지
        Message = "메뉴를 선택하세요.";
    }
}

실행하면 Menu는 표시되지 않고 중앙에 TextBlock이 표시되는 것을 볼 수 있습니다. 이 TextBlock에 선택한 메뉴를 Message를 통해 표시할 예정입니다.

기본 메뉴

일반적으로 알고 있는 클릭 입력을 받는 기본적인 메뉴를 만들어 보겠습니다. View에 위에서 만든 Menu에 MenuItem을 추가해 주세요

<!--메뉴-->
<Menu DockPanel.Dock="Top">
    <MenuItem Header="_Basic">
        <MenuItem Header="_Open" Command="{Binding OpenCommand}"/>
        <MenuItem Header="_Save" Command="{Binding SaveCommand}"/>
        <Separator />
        <MenuItem Header="_Exit" Command="{Binding ExitCommand}"/>
    </MenuItem>
</Menu>

이 상태로 실행해보면 Menu 하단에 MenuItem을 추가하면 프로그램에서 Basic 메뉴와 그 하위로 Open, Save, Exit 메뉴가 추가된 것을 볼 수 있습니다.

또한 Header에 붙인 언더바(_)는 단축키로 지정하는 방식입니다. Alt키를 눌러보면 언더바가 보이게 되고 그때 언더바 뒤에있는 문자를 누르면 해당 메뉴를 사용할 수 있습니다. 아래 이미지는 Alt를 누른 뒤 Basic 메뉴를 선택하기 위해 B키를 입력하여 Basic 메뉴가 펼쳐진 모습입니다.

Open, Save, Exit 메뉴를 눌러도 아무 동작을 하지 않습니다. ViewModel에 아래와 같이 Open, Save, Exit에 대한 Command를 정의해줍니다.

public class MainWindowViewModel : Notifier
{
    public MainWindowViewModel()
    {
        // 기본 메뉴
        OpenCommand = new Command(Open);
        SaveCommand = new Command(Save);
        ExitCommand = new Command(Exit);
    }

    /// <summary>
    /// 기본 메뉴
    /// </summary>
    public ICommand OpenCommand { get; }
    public ICommand SaveCommand { get; }
    public ICommand ExitCommand { get; }

    private void Open() => Message = "Open 클릭됨!";
    private void Save() => Message = "Save 클릭됨!";
    private void Exit() => Application.Current.Shutdown();
}

이제 각 메뉴를 클릭하면 아래와 같이 동작합니다.

체크 메뉴

WPF에서는 MenuItem에 체크를 할 수 있는 속성도 제공해줍니다. IsCheckableIsChecked 속성을 사용하면 쉽게 구현 가능합니다. View에 아래와 같이 MenuItem을 추가해줍니다.

<Menu DockPanel.Dock="Top">
    <MenuItem Header="_Check">
        <MenuItem Header="Check"
          IsCheckable="True"
          IsChecked="{Binding IsCheckMenu}"
          Command="{Binding CheckedMenuCommand}"
          StaysOpenOnClick="True"/>
    </MenuItem>
</Menu>

IsCheckable 속성에 True를 입력하면 메뉴에 체크를 사용한다는 것이고, IsChecked 속성을 사용하여 체크 상태를 바인딩 할 수 있습니다. 또한 StaysOpenOnClick속성을 사용하여 메뉴를 클릭해도 메뉴가 사라지지 않고 유지됩니다.

ViewModel에 아래와 같이 코딩하여 바인딩 해줍니다.

public class MainWindowViewModel : Notifier
{
    public MainWindowViewModel()
    {
        // 체크박스 메뉴
        _isCheckedMenu = false;
        CheckedMenuCommand = new Command(CommandChecked);
    }

    /// <summary>
    /// 체크 박스 메뉴
    /// </summary>
    private bool _isCheckedMenu;
    public bool IsCheckMenu
    {
        get => _isCheckedMenu;
        set
        {
            if (_isCheckedMenu != value)
            {
                _isCheckedMenu = value;
                OnPropertyChanged();
            }
        }
    }
    public ICommand CheckedMenuCommand { get; }

    private void CommandChecked() => Message = "Check 메뉴 클릭됨! : " + IsCheckMenu.ToString();
}

실행하면 아래와 같이 동작합니다.

콤보 박스 메뉴

콤보 박스 메뉴라고 하였지만 MenuItem의 속성을 이용하는 것이 아닙니다. WPF에서는 MenuItem에 다른 UI 요소(Slider나 Button 등)를 입력하는 것이 가능합니다. 이 글에서는 ComboBox를 넣는 예제를 하겠습니다. 아래와 같이 ComboBox를 넣은 MenuItem을 하나 생성해줍니다.

<MenuItem Header="C_omboBox">
    <MenuItem Header="Item List">
        <ComboBox Width="120"
          ItemsSource="{Binding ComboItemsSource}"
          SelectedItem="{Binding SelectedComboItem, Mode=TwoWay}"/>
    </MenuItem>
</MenuItem>

ViewModel에서 아래와 같이 ComboBox와 관련된 코딩을 해줍니다.

public class MainWindowViewModel : Notifier
{
    public MainWindowViewModel()
    {
        // 콤보 메뉴
        _comboItemsSource = new ObservableCollection<string>
        {
            "Item1", "Item2", "Item3"
        };
        _selectedComboItem = "Item1";
    }

    /// <summary>
    /// 콤보메뉴
    /// </summary>
    public ObservableCollection<string> _comboItemsSource;

    public ObservableCollection<string> ComboItemsSource
    {
        get => _comboItemsSource;
    }

    private string _selectedComboItem;
    public string SelectedComboItem
    {
        get => _selectedComboItem;
        set
        {
            if (_selectedComboItem != value)
            {
                _selectedComboItem = value;
                OnPropertyChanged();
                Message = "콤보 메뉴 선택됨! : " + _selectedComboItem;
            }
        }
    }
}

실행하여 확인해 봅니다.

UserControl 메뉴

ComboBox와 마찬가지로 UserControl또한 메뉴에 붙일 수 있습니다. 아래와 같이 MyMenuItem.xaml을 생성해줍니다.

<UserControl x:Class="WpfMenu.MyMenuItem"
             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:WpfMenu"
             mc:Ignorable="d" 
             d:DesignHeight="30" d:DesignWidth="300"
             Background="White">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="30"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="100"/>
            <ColumnDefinition Width="200"/>
        </Grid.ColumnDefinitions>

        <!-- Button -->
        <Button Grid.Row="0" Grid.Column="0"
                    Content="Button"
                    Command="{Binding ButtonCommand}"
                    VerticalAlignment="Center"
                    Margin="5"/>

        <!-- Slider -->
        <Slider Grid.Row="0" Grid.Column="1"
                    Minimum="0" Maximum="100"
                    Value="{Binding SliderValue, Mode=TwoWay}"
                    VerticalAlignment="Center"
                    Margin="5"/>
    </Grid>
</UserControl>

그 다음 MainWindow.xaml에 MyMenuItem을 넣어줍니다.

<MenuItem Header="_UserControl">
    <local:MyMenuItem/>
</MenuItem>

그 다음 ViewModel에 관련 코딩을 해줍니다.

public class MainWindowViewModel : Notifier
{
    public MainWindowViewModel()
    {
        // MyMenuItem
        ButtonCommand = new Command(OnButtonClicked);
        _sliderValue = 50;  // 기본값
    }

    /// <summary>
    /// MyMenuItem
    /// </summary>
    public ICommand ButtonCommand { get; }

    private double _sliderValue;
    public double SliderValue
    {
        get => _sliderValue;
        set
        {
            if (_sliderValue != value)
            {
                _sliderValue = value;
                OnPropertyChanged(nameof(SliderValue));
                Message = $"SliderValue={SliderValue}";
            }
        }
    }

    private void OnButtonClicked() => Message = "Button clicked!";
}

실행하여 확인해봅니다.

이상으로 WPF에서 Menu에 대해 알아보았습니다.


github: https://github.com/3001ssw/c_sharp/tree/main/WPF/WPF_Basic/WpfMenu