简体   繁体   English

TabControl在运行时重命名

[英]TabControl renaming in run-time

I'm trying to create simple interface where user can rename tab in TabControl. 我正在尝试创建简单的界面,用户可以在TabControl中重命名选项卡。 Problem is that since I can get position of tab by GetTabRect method, I can't set TextBox into a place of tab. 问题是因为我可以通过GetTabRect方法获取制表符的位置,所以我无法将TextBox设置为制表符的位置。 Here is the desired effect (from foobar): 这是期望的效果(来自foobar):

在此输入图像描述

And here is my effect: 这是我的效果:

在此输入图像描述

Below I insert my code where I try to make this effect: 下面我插入我的代码,我尝试产生这种效果:

      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));

        }
    }

How should I change the code to be able to rename Tab name in runtime? 我应该如何更改代码以便能够在运行时重命名Tab名称?

GetTabRect() returns the position relative to the tab control. GetTabRect()返回相对于选项卡控件的位置。 But you are adding the textbox to the tabpage, it is located below the tabstrip. 但是您要将文本框添加到标签页,它位于标签页下方 Which is why your screenshot looks like that. 这就是为什么你的截图看起来像这样。 You cannot give the textbox a negative position, it would be clipped. 你不能给文本框一个负面的位置,它会被剪裁。 Ideally you would add it to the tab control, but TabControl explicitly forbids this. 理想情况下,您可以将其添加到选项卡控件,但TabControl显式禁止此操作。 The only other thing you can do is add it the form. 您可以做的唯一其他事情是将其添加到表单中。

You'll have to calculate the correct position, mapping the tabrect to form coordinates. 您必须计算正确的位置,将tabrect映射到表格坐标。 And ensure it overlaps the tabcontrol. 并确保它与tabcontrol重叠。 Removing it again is surely best done with its Leave event. 删除它肯定最好用它的Leave事件完成。 Like this: 像这样:

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;
}

Personally, I would implement this as a lightweight form, positioned over the top of the tab. 就个人而言,我会将其实现为一个轻量级的表单,位于选项卡的顶部。

在此输入图像描述

To make a lightweight form, set the following properties: 要制作轻量级表单,请设置以下属性:

FormBorderStyle = FormBorderStyle.FixedSingle;
Text            = string.Empty;
StartPosition   = FormStartPosition.Manual;
ControlBox      = false;
ShowInTaskbar   = false;

A lightweight form can have a larger TextBox and could display validation errors with an ErrorProvider . 轻量级表单可以具有更大的TextBox并且可以使用ErrorProvider显示验证错误。

Oops... 糟糕!

I didn't realize this question was for WinForms until just now (Edit history shows the tag was added later). 我直到现在才意识到这个问题是针对WinForms的(编辑历史记录显示标签后来被添加)。 I'm going to leave the answer in place though, in case it is useful for anyone wanting to know how to do this with WPF. 我将留下答案,以防它对任何想知道如何使用WPF执行此操作的人有用。


You can do this rather easily using the MVVM pattern without having to worry about positioning code at all. 您可以使用MVVM模式轻松完成此操作,而无需担心定位代码。 Full Source is available at this link . 完整来源可在此链接中找到

在此输入图像描述

This uses a list of DocumentVm with a Name , and an IsRenaming property. 这使用DocumentVm列表,其中包含NameIsRenaming属性。 The TabControl has its ItemsSource bound to the list of DocumentVm so that it has one tab per Document. TabControl将其ItemsSource绑定到DocumentVm列表,以便每个Document有一个选项卡。 The header of the TabItem contains a TextBlock , a TextBox , and Button . TabItem的标题包含TextBlockTextBoxButton When you click the button, the IsRenaming flag is toggled. 单击该按钮时,将切换IsRenaming标志。 The TextBox and TextBlock have their visibility controlled by IsRenaming (using a normal, and inverted converter). TextBoxTextBlock的可见性由IsRenaming控制(使用普通和反向转换器)。 So clicking the button lets you edit the tab's title. 因此,单击该按钮可以编辑选项卡的标题。

MainWindow.Xaml: 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: 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: 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; }
        }
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM