我想在 WPF C# 应用程序中实现关闭未聚焦的 TabControl。
创建选项卡时,会为该选项卡标题中的关闭按钮分配一个索引,但事实是,当您删除选项卡时,例如在中间,选项卡本身的索引顺序会发生变化。因此,按钮标签将与此选项卡的索引不匹配。
为了更清楚,这里有一个例子: Button tag |0 1 2 3 4 5 Index on|0 1 2 3 4 5
关闭,例如,第三个选项卡:
按钮标签 |0 1 2 4 5 索引打开|0 1 2 3 4
存在不匹配。有些公式没用。
这是代码:
public void New_TabControl(string n, Page p)
{
TabItem ti = new TabItem();
DockPanel dp = new DockPanel();
Label l = new Label();
Button b = new Button();
Frame f = new Frame();
ti.MaxHeight = 50;
ti.MaxWidth = 150;
l.Content = n;
l.MaxWidth = 100;
b.Content = "x";
b.Width = 30;
b.Click += new RoutedEventHandler(Close_Tab);
ti.Header = dp;
ti.Content = f;
f.Content = p;
dp.Children.Add(l);
dp.Children.Add(b);
TB.Items.Add(ti);
ti.Focus();
b.Tag = this.TB.SelectedIndex;
}
public void Close_Tab(object sender, RoutedEventArgs e)
{
Button b = sender as Button;
MessageBox.Show(b.Tag.ToString() + TB.SelectedIndex.ToString());
TB.Items.RemoveAt(Convert.ToInt32(b.Tag));
}
是否可以防止标签索引更改?如何正确实施?或者您是否需要做一些诸如存储当前选项卡和焦点标签的表格之类的事情?
这不太可能回答您的问题,因为我正在展示它应该如何以及您应该遵循的方向。
因此,对于初学者,让我们看一下您的代码并写出一些问题区域:
您将数据存储在他们不需要知道的控件中。控件是视图,即与客户端交互的内容。他们需要知道什么才能成功?好吧,可能是颜色、大小、位置、样式以及与显示相关的所有内容。您不应该存储在控件中的所有其他内容。比如你
Tag存储了一个索引,控件为什么要知道这个?您通过代码使用控件。这在某种意义上是可以接受的,但这样做是在做很多不应该打扰你的不必要的工作。试想一下,您根本没有界面,或者一段时间后您决定重写您的项目并使其成为一个控制台应用程序,然后呢?你会创建一个新项目,在那里复制逻辑,重写它以使用控制台吗?如果我们最初从接口中解开这个逻辑,使其具有通用性,那么我们就不必重写项目,因为连接一个新接口就足够了,仅此而已。
Frame- 他自己有很多多余的东西,不允许做太多事情。例如尝试通过子框架改变主框架的内容。你可以而且一定会成功,但难度很大。基于此,我强烈建议您至少使用绑定开始开发项目,因为这是 WPF 的主要“武器”,没有它们,您的项目会损失很多,包括工作速度。
现在让我们编写一个小的 MVVM 项目,它将具有以下内容:
首先,让我们定义什么是 MVVM:
简而言之,这是一种将您的代码划分为特定层的方法,其中每一层响应一个事物:
现在我们可以开始开发项目了:
ViewModelMainViewModel- 这将是我们的主类,我们将绑定 View 层。App.xaml并删除那里的行StartupUri="MainWindow.xaml"我们进入
App.xaml.cs并覆盖OnStartup我们初始化MainViewModel所需窗口的方法:你问“这是为什么?” 对于这个问题,我会留给你这个链接。
TabViewModel- 这将是一个选项卡的 ViewModel。在这个类中,我们将创建必要的属性(请记住,绑定只能绑定到公共属性!)。我们需要什么?选项卡的标题和可能的内容,为了方便,我们还重新定义了构造函数:
现在我们需要在主 VM 中创建这些选项卡的集合。在这里您应该立即考虑我们将来是否会更新选项卡?如果是,那么我们需要一个具有实现接口的集合
INotifyCollectionChanged,没有它你将看不到视图层的变化。在现成的解决方案中,有ObservableCollection<T>或BindingList<T>。如果更改不重要,那么您可以使用任何内容。好吧,我们仍然需要将这一切联系起来。我们制作所需类型的选项卡并将它们绑定到集合:
就是这样,我们启动项目并看到集合中的 3 个选项卡,其中包含空内容:
请注意,所有选项卡式代码都可以在没有界面的情况下成功运行,我们不使用框架,我们已将所有内容放置在其位置。
现在让我们在标签中显示不同的内容:
我们将按照这个例子来做。
让我们在目录中创建一个新类
ViewModel,我们称之为 exampleFirstPageViewModel,让这个类只包含 1 个属性,它的文本:接下来,让我们创建一个新目录,我们将其命名为
View.在这个目录中,我们将添加一个新的“用户控件”,我们将简单地调用它
FirstPage。我们为这个页面提供了所需的设计,我还将显示 VM 层中包含的文本:现在我们需要将它们全部组合起来,让我们在主窗口中进行:
在最顶部添加指向 VM 和 V 目录的链接(这里的命名空间应该是你的,我举个例子来说明应该是什么):
我们添加一点
TabControl,给它添加资源(其实它们可以在任何地方,但是如果我们只需要这些页面来做这个控件,那么我们就给它设置资源):这里一切都很简单,我们
DataType在帮助下指定对象的类型,并在里面DataTemplate为指定类型设置所需的视图。好吧,现在让我们设置,例如,具有所需内容的第一个选项卡(请记住,我们
TabViewModel为类创建了一个包含内容的属性):我们启动并看到第一个选项卡已更改其外观:
所以我们有一个选项卡式应用程序,每个选项卡都可以有自己的内容,没有框架!
最后,关闭选项卡:
什么是闭包?这本质上是从列表中删除一个对象。但是,如果根据 MVVM 的规则,我们不能订阅按钮事件和访问控制,我们怎么能从集合中移除一个对象呢?一切都很简单,这里的事件和命令来救援!
让我们创建一个为我们实现接口的类
ICommand。网上有很多实现资料,我就拿这一份,因为我们只需要方便地处理命令,不多说了:添加
TabViewModel一个新命令,该命令将通知所有订阅者该选项卡正在关闭:看看这里发生了什么。当单击某个绑定到 的按钮(稍后会详细介绍)时,将
CloseCommand调用Close()该方法,该方法反过来通知所有订阅者OnClose该选项卡需要关闭的事件。现在我们有两条路径。1. 这是您初始化
TabViewModel自己以进行订阅的时候。2. 我们可以稍微自动化一下。让我们做第二个。让我们重写一点MainViewModel(或者更确切地说,初始化选项卡的集合):这里我们将选项卡的添加移到构造函数中,并且还添加了订阅
CollectionChanged该集合的事件。此事件发生在其中添加/删除某些内容的那一刻。添加新标签时,我们订阅它的关闭事件,而删除它时,相反,我们取消订阅。我在这里写过这种方法。
我们仍然需要在 View 层中创建一个关闭按钮,将其添加到
TabControl.ItemTemplate:这里起到了主要作用
Command,里面有一个对created属性的绑定TabViewModel。一切,我们启动并享受结果:
几个补充:
INotifyPropertyChanged,因为没有它您将不会在界面中获得更改。TabViewModel并将其绑定到TabControl(注意:)SelectedItem="{Binding SelectedItemProperty}"。总的来说,祝你学习 C# 好运!