主要功能是点击左侧的 `SideMenu` 项目,然后在右侧的 `TabControl` 中创建一个新的 `TabItem`。这个 `TabItem` 的内容是一个 `TextBlock`,显示的是所点击的 `SideMenuItem` 的 `Header` 文本。代码还包括了关闭 `TabItem` 的功能。
以下是具体实现思路:
1. **创建主窗口**:在 `MainWindow.xaml` 文件中,创建了一个包含 `SideMenu` 和 `TabControl` 的主窗口。`SideMenu` 和 `TabControl` 是分别放在 `Grid` 的两个列中的。
2. **定义数据模型**:在 `MainViewModel` 类中,定义了数据模型。这个数据模型包括一个 `ObservableCollection`,用于存储 `TabItem` 对象,以及两个命令,用于处理用户的操作。
3. **处理用户的点击操作**:在 `MainWindow.xaml.cs` 文件的 `MainWindow` 类中定义了 `MenuButton_Click` 方法,用于处理用户点击 `SideMenuItem` 的操作。在这个方法中,创建了一个新的 `TabItem`,并把它添加到 `TabControl` 中。
4. **关闭 `TabItem`**:在 `MainViewModel` 类中定义了 `RemoveTabCommand` 命令,用于关闭 `TabItem`。在 `MainWindow.xaml` 文件中,为每个 `TabItem` 添加了一个关闭按钮,并绑定了这个命令。
5. **显示 `TabItem` 的内容**:在 `MainWindow.xaml` 文件中为 `TabControl` 定义了 `ContentTemplate`,用于显示 `TabItem` 的内容。
6. **数据绑定**:在 `MainWindow.xaml.cs` 文件的 `MainWindow` 类的构造函数中,把 `MainViewModel` 对象设置为主窗口的 `DataContext`,以便在 XAML 中进行数据绑定。
7. **动态创建 `TabItem` 的内容**:在 `SelectCmd` 命令的实现中,根据 `SideMenuItem` 的 `Name` 动态创建了 `TabItem` 的内容。
这是主要实现思路。代码结构清晰,逻辑明确。可以在这个基础上添加更多的功能,比如支持拖拽排序 `TabItem`,或者在 `TabControl` 中显示更复杂的内容。
XAML代码:
<Window x:Class="WpfApp1.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:WpfApp1"xmlns:hc="https://handyorg.github.io/handycontrol"mc:Ignorable="d" Title="MainWindow" Height="450" Width="800"><Window.Resources><local:MultiValueConverter x:Key="MultiValueConverter"/></Window.Resources><Grid><Grid><Grid.ColumnDefinitions><ColumnDefinition Width="Auto"/><ColumnDefinition/></Grid.ColumnDefinitions><hc:SideMenu Grid.Column="0" Width="200" x:Name="sideMenu" ExpandMode="ShowOne"><hc:SideMenuItem Header="大屏展示"><hc:SideMenuItem.Icon><Image Source="../Images/DevOps-Overview.png" Width="24" Height="24"/></hc:SideMenuItem.Icon><hc:SideMenuItem Header="(正式)视觉大屏" x:Name="VisualLargeScreen" Command="{Binding SelectCmd}"><hc:SideMenuItem.Icon><Image Source="../Images/DevOps-Pipelines.png" Width="12" Height="12"/></hc:SideMenuItem.Icon><hc:SideMenuItem.CommandParameter><MultiBinding Converter="{StaticResource MultiValueConverter}"><Binding Path="Header" RelativeSource="{RelativeSource Self}"/><Binding Path="Name" RelativeSource="{RelativeSource Self}"/></MultiBinding></hc:SideMenuItem.CommandParameter></hc:SideMenuItem><hc:SideMenuItem Header="(正式)打磨大屏" x:Name="PolishinLargeScreen" Command="{Binding SelectCmd}"><hc:SideMenuItem.Icon><Image Source="../Images/DevOps-Repos.png" Width="12" Height="12"/></hc:SideMenuItem.Icon><hc:SideMenuItem.CommandParameter><MultiBinding Converter="{StaticResource MultiValueConverter}"><Binding Path="Header" RelativeSource="{RelativeSource Self}"/><Binding Path="Name" RelativeSource="{RelativeSource Self}"/></MultiBinding></hc:SideMenuItem.CommandParameter></hc:SideMenuItem></hc:SideMenuItem><hc:SideMenuItem Header="基础信息查询"><hc:SideMenuItem.Icon><Image Source="../Images/DevOps-TestPlans.png" Width="24" Height="24"/></hc:SideMenuItem.Icon></hc:SideMenuItem><!-- Add more menu buttons here --></hc:SideMenu><hc:TabControl Grid.Column="1" x:Name="tabControl" ItemsSource="{Binding TabItems}" ShowCloseButton="True"><hc:TabControl.ItemTemplate><!-- This is the header template--><DataTemplate><DockPanel Width="120"><Button Command="{Binding DataContext.RemoveTabCommand, RelativeSource={RelativeSource AncestorType={x:Type hc:TabControl}}}" CommandParameter="{Binding}" Content="X" DockPanel.Dock="Right" /><ContentPresenter Content="{Binding Header}" /></DockPanel></DataTemplate></hc:TabControl.ItemTemplate><hc:TabControl.ContentTemplate><DataTemplate><Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch" ><ContentPresenter Content="{Binding Content}"></ContentPresenter></Grid></DataTemplate></hc:TabControl.ContentTemplate></hc:TabControl></Grid></Grid>
</Window>
后端代码:
using HandyControl.Controls;
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Controls;namespace WpfApp1
{/// <summary>/// Interaction logic for MainWindow.xaml/// </summary>public partial class MainWindow : System.Windows.Window{MainViewModel viewModel;public MainWindow(){InitializeComponent();viewModel=new MainViewModel();this.DataContext = viewModel;viewModel.TabItems = new ObservableCollection<HandyControl.Controls.TabItem>();viewModel.RemoveTabCommand = new RelayCommand(param =>{HandyControl.Controls.TabItem tabItem = param as HandyControl.Controls.TabItem;viewModel.TabItems.Remove(tabItem);});viewModel.SelectCmd = new RelayCommand(param =>{var str = param as Tuple<string, string>;if (str != null){Type type = Type.GetType("WpfApp1." + str.Item2);object instance = Activator.CreateInstance(type);HandyControl.Controls.TabItem SelectedtabItem = viewModel.TabItems.FirstOrDefault(p => p.Header == str.Item1);if (SelectedtabItem != null){SelectedtabItem.IsSelected = true;return;}viewModel.TabItems.Add(new HandyControl.Controls.TabItem() { IsSelected = true, Header = str.Item1, Content = instance });}});}private void MenuButton_Click(object sender, RoutedEventArgs e){var menuItem = (SideMenuItem)sender;var tabItem = new HandyControl.Controls.TabItem{Header = menuItem.Header,Content = new TextBlock { Text = $"Content for {menuItem.Header}" }};var closeButton = new Button{Content = "Close",Width = 75,Height = 25};closeButton.Click += (s, e) => { tabControl.Items.Remove(tabItem); };var stackPanel = new StackPanel();stackPanel.Children.Add(closeButton);stackPanel.Children.Add(tabItem);tabControl.Items.Add(stackPanel);}}
}
数据模型代码:
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows.Input;namespace WpfApp1
{public class MainViewModel : INotifyPropertyChanged{private ObservableCollection<HandyControl.Controls.TabItem> _tabItems;public ObservableCollection<HandyControl.Controls.TabItem> TabItems{get { return _tabItems; }set{_tabItems = value;OnPropertyChanged();}}/// <summary>/// 关闭tabitem显示的页面/// </summary>public ICommand RemoveTabCommand { get; set; }public ICommand SelectCmd { get; set; }public event PropertyChangedEventHandler? PropertyChanged;protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null){PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));}}
}