我试图创建简单的界面,用户可以在TabControl中重命名标签。问题是因为我可以通过GetTabRect方法得到制表符的位置,所以我无法将TextBox设置为制表符的位置。这是期望的效果(来自foobar):
这是我的效果:
下面我插入我的代码,试图产生这种效果:
private void renameToolStripMenuItem_Click(object sender, EventArgs e)
{
if (selectedTab >= 0 && selectedTab < MainTabs.TabCount)
{
Debug.WriteLine("Rename " + MainTabs.Controls[selectedTab].Text);
Debug.WriteLine(sender.ToString());
//var CurrentTab = MainTabs.TabPages[selectedTab];
//TabControl tabControl = MainTabs.Con
Rectangle rect = MainTabs.GetTabRect(selectedTab);
Point point = MainTabs.Location;
Debug.WriteLine(point.ToString() + " " + rect.ToString());
RenameBox = new TextBox();
MainTabs.GetControl(selectedTab).Controls.Add(RenameBox);
//MainTabs
RenameBox.Show();
RenameBox.SetBounds(rect.X, rect.Y, rect.Width, rect.Height);
//MainTab.Controls.Add(RenameBox);
//this.Controls.Add(RenameBox);
//components.Add(RenameBox);
//RenameBox.Location = point;
//MainTabs.SelectedTab.Controls.Add(RenameBox);
//MainTabs.GetControl(0).Controls.Add(RenameBox);
//MainTabs.SelectedTab.
RenameBox.KeyPress += textBox1_KeyPress;
//RenameBox.Show();
//RenameBox.Location = new Point(0, 0);
//RenameBox.Focus();
//RenameBox.SetBounds(.GetTabRect(selectedTab));
}
}
如何更改代码以便能够在运行时重命名Tab名称?
答案 0 :(得分:5)
GetTabRect()返回相对于制表符控件的位置。但是您要将文本框添加到标签页,它位于下方标签页。这就是为什么你的截图看起来像这样。你不能给文本框一个负面的位置,它会被剪裁。理想情况下,您可以将其添加到选项卡控件,但TabControl显式禁止此操作。你唯一能做的就是把它添加到表格中。
您必须计算正确的位置,将tabrect映射到表格坐标。并确保它与tabcontrol重叠。删除它肯定最好用它的Leave事件完成。像这样:
private TextBox AddTextBoxToTab(TabControl tabctl, int index = -1) {
if (index < 0) index = tabctl.SelectedIndex;
var rc = tabctl.GetTabRect(index);
rc = tabctl.RectangleToScreen(rc);
rc = tabctl.Parent.RectangleToClient(rc);
var box = new TextBox();
box.Font = tabctl.Font;
box.Leave += delegate { box.Dispose(); };
box.SetBounds(rc.Left, rc.Top, rc.Width, rc.Height);
tabctl.Parent.Controls.Add(box);
box.BringToFront();
box.Focus();
return box;
}
答案 1 :(得分:2)
就个人而言,我会将其实现为一个轻量级表单,位于选项卡的顶部。
要制作轻量级表单,请设置以下属性:
FormBorderStyle = FormBorderStyle.FixedSingle;
Text = string.Empty;
StartPosition = FormStartPosition.Manual;
ControlBox = false;
ShowInTaskbar = false;
轻量级表单可以有更大的TextBox
,并且可以使用ErrorProvider
显示验证错误。
答案 2 :(得分:1)
我直到现在才意识到这个问题是针对WinForms的(编辑历史记录显示标签后来被添加)。我会留下答案,以防它对任何想知道如何使用WPF的人有用。
您可以使用MVVM模式轻松完成此操作,而无需担心定位代码。 Full Source is available at this link
这会使用DocumentVm
列表,其中包含Name
和IsRenaming
属性。 TabControl
将ItemsSource
绑定到DocumentVm
列表,以便每个文档都有一个标签。 TabItem
的标头包含TextBlock
,TextBox
和Button
。单击该按钮时,将切换IsRenaming
标志。 TextBox
和TextBlock
的可见性由IsRenaming
控制(使用正常和反向转换器)。因此,单击该按钮可以编辑选项卡的标题。
MainWindow.Xaml:
<Window
x:Class="RenamableTabs.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:local="clr-namespace:RenamableTabs"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="MainWindow"
Width="525"
Height="350"
d:DataContext="{d:DesignInstance local:MainWindowVm}"
mc:Ignorable="d">
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
<local:InvertedBooleanToVisibilityConverter x:Key="InvertedBooleanToVisibilityConverter" />
</Window.Resources>
<Grid Margin="8">
<TabControl ItemsSource="{Binding Path=Documents}">
<TabControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Name}" Visibility="{Binding Path=IsRenaming, Converter={StaticResource InvertedBooleanToVisibilityConverter}}" />
<TextBox Text="{Binding Path=Name}" Visibility="{Binding Path=IsRenaming, Converter={StaticResource BooleanToVisibilityConverter}}" />
<Button Command="{Binding Path=RenameCommand}" Content="R" />
</StackPanel>
</DataTemplate>
</TabControl.ItemTemplate>
</TabControl>
</Grid>
</Window>
MainWindowVm.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using AgentOctal.WpfLib;
using AgentOctal.WpfLib.Services;
using AgentOctal.WpfLib.Services.Message;
namespace RenamableTabs
{
class MainWindowVm : ViewModel
{
public MainWindowVm()
{
_messageService = ServiceManager.GetService<AgentOctal.WpfLib.Services.Message.IMessageService>();
_messageService.Subscribe<DocumentVm.DocumentRenaming>(message =>
{
var m = message as DocumentVm.DocumentRenaming;
foreach (var documentVm in Documents)
{
documentVm.IsRenaming = documentVm == m.Document;
}
});
Documents = new ObservableCollection<DocumentVm>();
Documents.Add(new DocumentVm() { Name = "Document 1" });
Documents.Add(new DocumentVm() { Name = "Document 2" });
Documents.Add(new DocumentVm() { Name = "Document 3" });
}
private IMessageService _messageService;
public ObservableCollection<DocumentVm> Documents { get; }
private string _name;
public string Name
{
get {return _name;}
set {SetValue(ref _name, value);}
}
}
}
DocumentVm.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
using System.Xaml;
using AgentOctal.WpfLib;
using AgentOctal.WpfLib.Commands;
using AgentOctal.WpfLib.Services;
using AgentOctal.WpfLib.Services.Message;
namespace RenamableTabs
{
class DocumentVm : ViewModel
{
public DocumentVm()
{
_messageService = ServiceManager.GetService<AgentOctal.WpfLib.Services.Message.IMessageService>();
}
private IMessageService _messageService;
private string _name;
public string Name
{
get { return _name; }
set { SetValue(ref _name, value); }
}
private ICommand _renameCommand;
public ICommand RenameCommand
{
get
{
return _renameCommand ?? (_renameCommand = new SimpleCommand((obj) =>
{
_messageService.SendMessage(new DocumentRenaming() { Document = this });
}));
}
}
private bool _isRenaming = false;
public bool IsRenaming
{
get { return _isRenaming; }
set { SetValue(ref _isRenaming, value); }
}
public class DocumentRenaming : IMessage
{
public DocumentVm Document { get; set; }
}
}
}