【问题标题】:Synchronizing Multiline Textbox Positions in C#在 C# 中同步多行文本框位置
【发布时间】:2010-09-23 23:36:49
【问题描述】:

我有一个 C# 应用程序,其中有两个并排的多行文本框,每个文本框位于拆分容器的一侧。我想同步他们的垂直滚动,以便当用户向上或向下滚动其中一个文本框时,另一个文本框分别沿相同方向滚动。有没有办法做到这一点?谢谢。

附加信息 - 2010 年 7 月 26 日

我在 MSDN 网站上发现了一些有趣的 API:

TextBox.GetFirstVisibleLineIndex Method
TextBox.GetLastVisibleLineIndex Method
TextBox.ScrollToLine Method

那里的文档看起来很有希望,但是当我尝试使用它时,我的编译器(Microsoft Visual C# 2008 Express Edition)会报错,即使在添加 PresenationFramework 作为参考并在文件顶部插入 using System.Windows.Controls; 之后:

错误 1“System.Windows.Forms.TextBox”不包含“GetFirstVisibleLineIndex”的定义,并且找不到接受“System.Windows.Forms.TextBox”类型的第一个参数的扩展方法“GetFirstVisibleLineIndex”(是您缺少 using 指令或程序集引用?)

附加信息 - 2010 年 7 月 27 日

我正在努力实施 Jay 关于实施新控件的建议,但我无法将事件处理程序绑定到控件中。这是我目前所拥有的:

public partial class MyFormApplication : Form
{
  public MyFormApplication() // MyFormApplication constructor
  {
     this.InitializeComponent();

     this.textBox1.Dispose(); // Replacing with textBoxSync1
     this.textBox2.Dispose(); // Replacing with textBoxSync2

     // Draw textBoxSync1
     this.textBoxSync1.AcceptsReturn = true;
     this.textBoxSync1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
        | System.Windows.Forms.AnchorStyles.Left)
        | System.Windows.Forms.AnchorStyles.Right)));
     this.textBoxSync1.BackColor = System.Drawing.SystemColors.Control;
     this.textBoxSync1.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
     this.textBoxSync1.Font = new System.Drawing.Font("Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
     this.textBoxSync1.Location = new System.Drawing.Point(0, 19);
     this.textBoxSync1.Multiline = true;
     this.textBoxSync1.Name = "textBoxSync1";
     this.textBoxSync1.ReadOnly = true;
     this.textBoxSync1.ScrollBars = System.Windows.Forms.ScrollBars.Both;
     this.textBoxSync1.Size = new System.Drawing.Size(338, 231);
     this.textBoxSync1.TabIndex = 0;
     this.textBoxSync1.TabStop = false;
     this.textBoxSync1.WordWrap = false;
     this.splitContainer1.Panel1.Controls.Remove(this.textBox1);
     this.splitContainer1.Panel1.Controls.Add(this.textBoxSync1);

     // Draw textBoxSync2
     this.textBoxSync2.AcceptsReturn = true;
     this.textBoxSync2.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
        | System.Windows.Forms.AnchorStyles.Left)
        | System.Windows.Forms.AnchorStyles.Right)));
     this.textBoxSync2.BackColor = System.Drawing.SystemColors.Control;
     this.textBoxSync2.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
     this.textBoxSync2.Font = new System.Drawing.Font("Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
     this.textBoxSync2.Location = new System.Drawing.Point(0, 19);
     this.textBoxSync2.Multiline = true;
     this.textBoxSync2.Name = "textBoxSync2";
     this.textBoxSync2.ReadOnly = true;
     this.textBoxSync2.ScrollBars = System.Windows.Forms.ScrollBars.Both;
     this.textBoxSync2.Size = new System.Drawing.Size(113, 231);
     this.textBoxSync2.TabIndex = 30;
     this.textBoxSync2.TabStop = false;
     this.textBoxSync2.WordWrap = false;
     this.splitContainer1.Panel2.Controls.Remove(this.textBox2);
     this.splitContainer1.Panel2.Controls.Add(this.textBoxSync2);

     /* Goes on to perform other initializations... */

  }

  private void textBoxSync1_VerticalScroll(Message msg)
  {
     msg.HWnd = this.textBoxSync2.Handle;
     this.textBoxSync2.PubWndProc(ref msg);
  }

  private void textBoxSync2_VerticalScroll(Message msg)
  {
     msg.HWnd = this.textBoxSync1.Handle;
     this.textBoxSync1.PubWndProc(ref msg);
  }
}

public class TextBoxSynchronizedScroll : System.Windows.Forms.TextBox
{
  public event vScrollEventHandler VerticalScroll;
  public delegate void vScrollEventHandler(System.Windows.Forms.Message message);

  public const int WM_VSCROLL = 0x115;

  protected override void WndProc(ref System.Windows.Forms.Message msg)
  {
     if (msg.Msg == WM_VSCROLL)
     {
        if (VerticalScroll != null)
        {
           VerticalScroll(msg);
        }
     }

     base.WndProc(ref msg);
  }

  public void PubWndProc(ref System.Windows.Forms.Message msg)
  {
     base.WndProc(ref msg);
  }
}

我应该认为类似...

this.textBoxSync1.VerticalScroll += new System.EventHandler(this.textBoxSync1_VerticalScroll);

...需要将垂直滚动事件挂钩到控件中,但正如您可能看到的那样,这不起作用。任何建议,将不胜感激。谢谢。

【问题讨论】:

    标签: c# synchronization scroll textbox


    【解决方案1】:

    subclassed a RichTextBox 并听取了 WM_VSCROLL 消息来做你想做的事情。也许您可以这样做,而不是使用 TextBox。

    回复您的编辑:

    注意:我假设您在复制和粘贴应用程序表单时犯了一个小错误,并且 textBoxSyncBusTraffic == textBoxSync1

    问题在于您声明控件的 VerticalScroll 事件,在这一行:

    this.textBoxSyncBusTraffic.VerticalScroll += new System.EventHandler(this.textBoxSyncBusTraffic_VerticalScroll); 
    
    1. 您的自定义控件需要订阅控件的 TextBoxSynchronizedScroll.vScrollEventHandler 事件(而不是 System.EventHandler)。
    2. 您的事件处理程序中引用的方法不存在(至少在您发布的代码中不存在)。

    所以改变这个:

    this.textBoxSyncBusTraffic.VerticalScroll += new System.EventHandler(this.textBoxSyncBusTraffic_VerticalScroll); 
    

    到这里:

    this.textBoxSync1.VerticalScroll += new TextBoxSynchronizedScroll.vScrollEventHandler(textBoxSync1_VerticalScroll);
    

    这使用正确的事件处理程序并引用您需要和已经拥有的方法。

    另外,请确保 textBoxSync2 的 VerticalScroll 事件的声明如下所示:

    this.textBoxSync2.VerticalScroll += new TextBoxSynchronizedScroll.vScrollEventHandler(textBoxSync2_VerticalScroll);
    

    顺便说一句,您可以使用几种技术来更轻松地声明事件:

    首先是使用表单设计器。如果您在窗体设计器的扩展控件实例的属性窗口中打开事件窗口,您将看到一个名为 VerticalScroll 的事件。双击此项让 Visual Studio 声明事件并创建事件触发时调用的方法。

    当您在代码中设置事件时,您还可以使用一个快捷方式。输入以下代码后你会发现:

    youtextBoxSync1.VerticalScroll +=
    

    系统将提示您按 Tab 键来完成声明。如果您这样做,Visual Studio 将创建一个具有正确签名的方法。

    【讨论】:

    • 好的,我想我已经完成了所有设置,但是我无法将新事件添加到控件中。如何将Message 传递给新的VerticalScroll 事件处理程序? ` this.textBoxSync1.VerticalScroll += new System.EventHandler(/* Message??? */);` 谢谢。
    • 谢谢,杰。你的假设是正确的;我修正了我帖子中的错字。我使用表单设计器进行了您建议的更改。我认为它会起作用,除了现在在 Application.Designer.cs 中引用 TextBoxSynchronizedScroll 的所有地方,它将它引用为 Application.TextBoxSynchronizedScroll,这会产生错误。如果我手动检查它并删除所有 Application. 前缀,错误就会消失,它会编译并运行,但只要我在设计器中执行任何操作,它就会重新插入错误。
    • @Jim Fell - 您可能会收到错误,因为您的表单的类名是 Application,这可能与 System.Windows.Forms 中的 Application 类冲突。您可能想要重命名表单的类。如果这样做,请务必先备份您的 Visual Studio 解决方案。
    • @Jay Riggs:我明白了。实际上,我的表单类名称与我发布的擦洗版本不同。我为混乱道歉。我已在发布的示例中重命名了该类。
    • @Jay Riggs:我从表单设计器中删除了新控件,这样它就可以零错误地构建。然后,我只需将新的TextBoxSynchronizedScroll 拖到表单上并构建项目。它产生了一个错误:Error 1 The type name 'TextBoxSynchronizedScroll' does not exist in the type 'MyFormApplication.MyFormApplication' C:\Documents and Settings\user\My Documents\MyFormApplication\MyFormApplication\MyFormApplication.Designer.cs 71 64 MyFormApplication。它引用的行是this.textBoxSynchronizedScroll1 = new Download_Tool.TextBoxSynchronizedScroll();
    【解决方案2】:

    基于现有代码,我提出了以下内容。似乎对我有用。

    class TextBoxSynchronizedScroll : TextBox
    {
        public const int WM_VSCROLL = 0x115;
    
        List<TextBoxSynchronizedScroll> peers = new List<TextBoxSynchronizedScroll>();
    
        public void AddPeer(TextBoxSynchronizedScroll peer)
        {
            this.peers.Add(peer);
        }
    
        private void DirectWndProc(ref Message m)
        {
            base.WndProc(ref m);
        }
    
        protected override void WndProc(ref Message m)
        {
            if (m.Msg == WM_VSCROLL)
            {
                foreach (var peer in this.peers)
                {
                    var peerMessage = Message.Create(peer.Handle, m.Msg, m.WParam, m.LParam);
                    peer.DirectWndProc(ref peerMessage);
                }
            }
    
            base.WndProc(ref m);
        }
    }
    

    http://gist.github.com/593809

    【讨论】:

    • Gist 的链接已断开。根据该数字和发布日期,这很可能是因为 Gist 在创建新 Gist 时将 id 增加 1,而不是使用随机生成的 id。
    猜你喜欢
    • 1970-01-01
    • 2017-10-15
    • 2011-04-18
    • 2012-09-20
    • 2015-08-31
    • 2018-08-25
    • 1970-01-01
    • 2014-12-22
    • 1970-01-01
    相关资源
    最近更新 更多