在C#中同步多行文本框的位置

7
我有一个C#应用程序,其中有两个多行文本框并排放置在一个分割容器的两侧。我想要同步它们的垂直滚动,以便当用户上下滚动其中一个文本框时,另一个文本框也会相应地向同一方向滚动。有没有办法做到这一点?谢谢。
额外信息 - 7/26/10
我在MSDN网站上找到了一些有趣的API:
TextBox.GetFirstVisibleLineIndex方法 TextBox.GetLastVisibleLineIndex方法 TextBox.ScrollToLine方法

那里的文档看起来很有前途,但是我的编译器(Microsoft Visual C# 2008 Express Edition)在我尝试使用它时发出警告,即使我已经将PresenationFramework作为引用添加并在文件顶部插入了using System.Windows.Controls;

Error 1 'System.Windows.Forms.TextBox' does not contain a definition for 'GetFirstVisibleLineIndex' and no extension method 'GetFirstVisibleLineIndex' accepting a first argument of type 'System.Windows.Forms.TextBox' could be found (are you missing a using directive or an assembly reference?)

附加信息 - 7/27/10

我正在实现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);

需要将垂直滚动事件与控件连接起来,但正如您所看到的,这并不起作用。任何建议都将不胜感激。谢谢。

2个回答

4

基于现有的代码,我得出了以下结论。对我来说似乎有效。

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创建新文件时,使用递增的ID号而不是随机生成ID号的时期。 - James Skemp

4

我对 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将创建一个具有正确签名的方法。


好的,我想我已经设置好了一切,但是我在将新事件添加到控件时遇到了问题。我该如何使传递给新的VerticalScroll事件处理程序的“Message”? this.textBoxSync1.VerticalScroll += new System.EventHandler(/ * Message???* /);谢谢。 - Jim Fell
@Jim Fell - 你可能会因为你的表单类名为Application而出现错误,这可能与System.Windows.Forms中的Application类冲突。你可能需要重命名你的表单类。如果你这样做了,请确保先备份你的Visual Studio解决方案。 - Jay Riggs
@Jay Riggs:我明白了。实际上,我的表单类名与我发布的经过清理的版本不同。对于造成的混淆,我深感抱歉。我已经在发布的示例中重命名了该类。 - Jim Fell
@Jay Riggs [з»ӯ]: еҜ№жҲ‘жқҘиҜҙиҝҷжІЎжңүеӨӘеӨҡж„Ҹд№үпјҢеӣ дёә1)е®ғжҳҜз”ұVC#зј–иҜ‘еҷЁеңЁMyFormApplication.Designer.csзҡ„InitializeComponent()еҮҪж•°дёӯз”ҹжҲҗзҡ„д»Јз ҒпјҢ2)еҺҹеһӢпјҲжІЎжңүй”ҷиҜҜпјүжҳҜдҪҚдәҺMyFormApplication.Designer.csзҡ„partial class MyFormApplicationдёӯзҡ„private TextBoxSynchronizedScroll textBoxSynchronizedScroll1;гҖӮ - Jim Fell
@Jay Riggs:是的,默認情況下,這就是VS最初設置項目的方式;命名空間和應用程序類具有相同的名稱。暫時,我已經按照您描述的在表單設計器之外實現了控件,它運行得很好!但是,如果可能的話,我想使用表單設計器使其工作。我需要重命名命名空間或應用程序類嗎?如果需要,是否應調整任何項目設置,還是只需通過文件並相應地重新命名即可?再次感謝您的所有幫助! - Jim Fell
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接