TabControl은 여러 화면을 하나의 화면에서 볼 때 사용하는 컨트롤입니다. 예를 들어 설정, 로그, 정보 같은 화면을 각각 구성하는데 하나의 화면에서 보고 싶으면 TabControl을 사용하시면 좋습니다. 이번 글에서는 MVVM 패턴으로 TabControl을 만들어 보도록 하겠습니다.
XAML
우선 XAML 파일부터 만들어보도록 하겠습니다. 프로젝트 생성 후 아래와 같이 MainWIndow.xaml에 TabControl을 추가해줍니다.
<Window x:Class="WpfTabControl.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:WpfTabControl"
xmlns:tabitemViews="clr-namespace:WpfTabControl.TabControlItem.Views"
xmlns:tabitemViewModels="clr-namespace:WpfTabControl.TabControlItem.ViewModels"
mc:Ignorable="d"
Title="MainWindow"
Height="450"
Width="800">
<!-- ViewModel 설정 -->
<Window.DataContext>
<local:MainWindowViewModel/>
</Window.DataContext>
<!-- TabControl -->
<TabControl ItemsSource="{Binding Tabs}"
SelectedItem="{Binding SelectedTab, Mode=TwoWay}">
<!-- TabItem Header -->
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Header}"/>
</DataTemplate>
</TabControl.ItemTemplate>
</TabControl>
</Window>
ViewModel
이 상태로 실행하면 아무것도 보이지 않으니 MainWindowViewModel.cs를 만들어 아래와 같이 코딩해줍니다.
namespace WpfTabControl
{
public class MainWindowViewModel : Notifier
{
public ObservableCollection<TabItemBaseViewModel> Tabs { get; } = new ObservableCollection<TabItemBaseViewModel>();
public TabItemBaseViewModel SelectedTab { get; set; }
public MainWindowViewModel()
{
TabItem1ViewModel vm1 = new TabItem1ViewModel()
{
Header = "View 1",
};
TabItem2ViewModel vm2 = new TabItem2ViewModel()
{
Header = "View 2",
};
Tabs.Add(vm1);
Tabs.Add(vm2);
SelectedTab = vm2;
}
}
}
TabItem 추가
위와 같이 MainWindowViewModel을 입력하고 빌드를 하면 TabItemBaseViewModel.cs, TabItem1ViewModel.cs, TabITem2ViewModel.cs 파일이 없어서 빌드가 되지 않습니다. 해당 파일들은 TabControl 하위에 표시되는 TabItem과 관련된 ViewModel 파일들입니다.
우선 프로젝트에 TabControlItem 폴더를 추가해주고 그 하위에도 ViewModels와 Views 폴더를 생성해줍니다.
이제 View먼저 추가해보도록 하겠습니다. Views 폴더에 UserControl을 두 개 추가해 줍니다. 각각 TabItem1View.xaml, TabItem2View.xaml로 만들어줍니다.
<UserControl x:Class="WpfTabControl.TabControlItem.Views.TabItem1View"
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:WpfTabControl.TabControlItem.Views"
mc:Ignorable="d"
d:DesignHeight="200"
d:DesignWidth="700"
Background="LightGray">
<Grid ShowGridLines="True">
<Grid.RowDefinitions>
<RowDefinition Height="1*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="3*"/>
<ColumnDefinition Width="1*"/>
</Grid.ColumnDefinitions>
<TextBox Grid.Row="0"
Grid.Column="0"
Margin="5"
Text="{Binding TxtBlock, UpdateSourceTrigger=PropertyChanged}"
VerticalAlignment="Center"
HorizontalAlignment="Stretch"/>
<Button Grid.Row="0"
Grid.Column="1"
Margin="5"
Content="Click"
Command="{Binding ButtonCommand}"/>
</Grid>
</UserControl>
<UserControl x:Class="WpfTabControl.TabControlItem.Views.TabItem2View"
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:WpfTabControl.TabControlItem.Views"
mc:Ignorable="d"
d:DesignHeight="450"
d:DesignWidth="800"
Background="LightGray">
<StackPanel Orientation="Vertical">
<TextBox Text="{Binding InsertText, UpdateSourceTrigger=PropertyChanged}"/>
<TextBlock Text="{Binding InsertText}"/>
</StackPanel>
</UserControl>
그 다음 TabItemBaseViewModel.cs, TabItem1ViewModel.cs, TabItem2ViewModel.cs파일을 만들어 줍니다.
namespace WpfTabControl.TabControlItem.ViewModels
{
public class TabItemBaseViewModel : Notifier
{
public string Header { get; set; } = ""; // 탭 제목
}
}
namespace WpfTabControl.TabControlItem.ViewModels
{
public class TabItem1ViewModel : TabItemBaseViewModel
{
private string txtBlock = "";
public string TxtBlock
{
get => txtBlock;
set
{
txtBlock = value;
OnPropertyChangedAll();
}
}
public Command ButtonCommand { get; set; }
public TabItem1ViewModel()
{
ButtonCommand = new Command(OnButtonCommand, OnCanExeButtonCommand);
}
private void OnButtonCommand()
{
MessageBox.Show(TxtBlock);
}
private bool OnCanExeButtonCommand()
{
return !string.IsNullOrEmpty(TxtBlock);
}
}
}
namespace WpfTabControl.TabControlItem.ViewModels
{
public class TabItem2ViewModel : TabItemBaseViewModel
{
private string insertText = "";
public string InsertText
{
get => insertText;
set
{
insertText = value;
OnPropertyChangedAll();
}
}
public TabItem2ViewModel()
{
}
}
}
ViewModel의 클래스 다이어그램을 보면 아래와 같습니다.
BaseViewModel의 Header는 각 뷰의 탭 헤더 View 1, View 2를 표시하기 위함입니다.
리소스 등록 – DataTemplate
ViewModel까지 만들고 실행을 하면 아래와 같이 표시가 됩니다.
이것은 등록된 TabControl ItemsSource 속성에 바인딩 된 ObservableCollection<TabItemBaseViewModel> Tabs에 TabItem1ViewModel과 TabITem2ViewModel이 추가되어 있는데, 해당 ViewModel들이 어떤 View를 표시해야 할 지 모르기 때문에 위와 같이 표시된 겁니다.
이럴 땐 App.xaml이나 MainWindow.xaml에 리소스를 등록해줍니다. 저는 MainWindow.xaml에 등록하였습니다.
<Window x:Class="WpfTabControl.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:WpfTabControl"
xmlns:tabitemViews="clr-namespace:WpfTabControl.TabControlItem.Views"
xmlns:tabitemViewModels="clr-namespace:WpfTabControl.TabControlItem.ViewModels"
mc:Ignorable="d"
Title="MainWindow"
Height="450"
Width="800">
<!-- ViewModel 설정 -->
<Window.DataContext>
<local:MainWindowViewModel/>
</Window.DataContext>
<Window.Resources>
<!-- ViewModel 타입 => View 연결 -->
<DataTemplate DataType="{x:Type tabitemViewModels:TabItem1ViewModel}">
<tabitemViews:TabItem1View/>
</DataTemplate>
<!-- ViewModel 타입 => View 연결 -->
<DataTemplate DataType="{x:Type tabitemViewModels:TabItem2ViewModel}">
<tabitemViews:TabItem2View/>
</DataTemplate>
</Window.Resources>
<!-- TabControl -->
<TabControl ItemsSource="{Binding Tabs}"
SelectedItem="{Binding SelectedTab, Mode=TwoWay}">
<!-- TabItem Header -->
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Header}"/>
</DataTemplate>
</TabControl.ItemTemplate>
</TabControl>
</Window>
실행하면 아래와 같이 표시됩니다.


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





