同时滚动两个面板

5
我有一个包含分割容器的Windows Forms (.net 2.0)控件。通常情况下,Splitcontainer包含两个面板(标准的东西)。自动滚动设置为true。
我一直在努力实现类似于同步这两个面板的功能,这样滚动其中一个面板也会滚动第二个面板。我通过使用Scroll事件(没有问题)实现了它。
然而,当我们在一个面板上通过制表符切换控件时(例如文本框),此事件不会被调用,这与msdn.microsoft.com/en-us/library/system.windows.forms.scrollablecontrol.scroll.aspx中所述的不太相同(“当用户通过与滚动条交互或导航到控件并且活动控件滚动到视图中时,会发生滚动事件。”)
因此,实际上,这些面板并没有真正同步:|
我知道这样一个事实,即将焦点放在包含在可滚动控件中的不可见控件上会调用其ScrollToControl(Control)事件,该事件“使”新控件(文本框)可见。更详细地说,我可以说两个面板都是相同的(大小和控件)。
您如何实现我正在寻找的内容?
4个回答

4

以下是如何在SplitContainer中滚动2个面板的方法。这种方法即使您在跳转到当前视图中不存在的控件时也可以进行滚动。


this.splitContainer1.Panel1.Paint += new System.Windows.Forms.PaintEventHandler(PanelPaint);
this.splitContainer1.Panel2.Paint += new System.Windows.Forms.PaintEventHandler(PanelPaint);

Point mPrevPan1Pos = new Point();
Point mPrevPan2Pos = new Point();

void PanelPaint(object sender, System.Windows.Forms.PaintEventArgs e)
{
   if (splitContainer1.Panel1.AutoScrollPosition != mPrevPan1Pos)
   {
      splitContainer1.Panel2.AutoScrollPosition = new System.Drawing.Point(-splitContainer1.Panel1.AutoScrollPosition.X, -splitContainer1.Panel1.AutoScrollPosition.Y);
      mPrevPan1Pos = splitContainer1.Panel1.AutoScrollPosition;
   }
   else if (splitContainer1.Panel2.AutoScrollPosition != mPrevPan2Pos)
   {
      splitContainer1.Panel1.AutoScrollPosition = new System.Drawing.Point(-splitContainer1.Panel2.AutoScrollPosition.X, -splitContainer1.Panel2.AutoScrollPosition.Y);
      mPrevPan2Pos = splitContainer1.Panel2.AutoScrollPosition;
   }
}

在使用VS 2012和.Net 4.5时,如果我只按照您的建议操作,重新同步控件的刷新最终会发生,但时间不够快,这并不符合我的喜好。 - Paul Chernoch
为什么依赖面板的自动滚动位置必须设置为主面板的负值? - MiB_Coder
1
@MiB_Coder - 请查看此部分中的“注意事项”:ScrollableControl.AutoScrollPosition 属性 - SwDevMan81

1
我从@SwDevMan81那里获得了答案,并补充了最后一部分,以使它在VS 2012、.Net 4.5中能正常工作。
第1部分位于我的窗体类内:
    #region Synchronize the scrolling of the two panels in the SplitContainer.

    private Point _prevPan1Pos = new Point();
    private Point _prevPan2Pos = new Point();

    void PanelPaint(object sender, System.Windows.Forms.PaintEventArgs e)
    {
        SynchronizeSplitContainerScrollbars();
    }

    private void SynchronizeSplitContainerScrollbars()
    {
        if (splitContainer1.Panel1.AutoScrollPosition != _prevPan1Pos)
        {
            splitContainer1.Panel2.AutoScrollPosition = new System.Drawing.Point(-splitContainer1.Panel1.AutoScrollPosition.X, -splitContainer1.Panel1.AutoScrollPosition.Y);
            _prevPan1Pos = splitContainer1.Panel1.AutoScrollPosition;
        }
        else if (splitContainer1.Panel2.AutoScrollPosition != _prevPan2Pos)
        {
            splitContainer1.Panel1.AutoScrollPosition = new System.Drawing.Point(-splitContainer1.Panel2.AutoScrollPosition.X, -splitContainer1.Panel2.AutoScrollPosition.Y);
            _prevPan2Pos = splitContainer1.Panel2.AutoScrollPosition;
        } 
    }

    #endregion

第二部分在表单的构造函数中。我不得不为滚动事件本身添加事件。

        // Setup so we can synchronize the scrolling of the two panels in the SplitContainer.
        splitContainer1.Panel1.Paint += PanelPaint;
        splitContainer1.Panel2.Paint += PanelPaint;
        splitContainer1.Panel1.Scroll += (obj, scrollEventArgs) => SynchronizeSplitContainerScrollbars();
        splitContainer1.Panel2.Scroll += (obj, scrollEventArgs) => SynchronizeSplitContainerScrollbars();

1
为什么不将分割容器完全放置在滚动区域中,而是将滚动条放置在分割容器内部?这样它们自然共享相同的滚动条,并且分割容器可以尽可能宽以适应整个表单。

1
SplitContainer有2个控件=面板。 它们都太大了,无法适应,因此它们需要有单独的滚动条。 我希望这能有所帮助。 - zonkzen
1
我对C# GUI没有太多经验,但在Java/Swing中,您可以将两个大控件放在一个巨大的分割容器中,以使分割容器足够大以容纳它们。当然,这样一个大的分割容器不适合放在窗口中,因此您需要将其放在滚动窗格中。如果您不能在C#中做到这一点,我会非常惊讶。 - Martijn
1
你知道什么是SplitContainer吗?它有两个面板和一个分隔条。当然,你可以创建一个大的SplitContainer来容纳两个面板,但是这个想法是让这些面板拥有独立的可滚动区域。 - zonkzen
我有点晚来参加这个聚会,但我可以确认你的解决方案在C#中也可以工作。 - The Lemon

0

自动滚动的两种实现方式都对我不起作用,但我可以确认Martijn的解决方案可行且完全同步(事件触发其他事件时没有延迟)。

将分割容器放入一个大小限制为您想要限制容器的面板中。从您的分割容器禁用两个面板上的自动滚动,并在父面板上启用它。从那里开始,它实际上对我仍然不起作用(分割容器比面板大,但由于某种奇怪的原因没有触发滚动条,但它可能对您来说是开箱即用的),所以下一步是适当地触发滚动条。

可能有更好的方法来做到这一点,但我通过在我的面板上添加一个标签来达到了这个目的。我的分割容器是动态填充的(因为UI设计师想要一些烦人的东西),所以我在代码中动态创建了标签,但您应该通过直接在设计师中放置类似的标签(或者在设计师中拥有它并在运行时更改其位置,您可以随意)获得相同的效果。

Label invisiLabel = new Label();
invisiLabel.Name = "scrollLabel"; //sorry for the puns
invisiLabel.Text = "";
invisiLabel.Location = new Point(5, height); //you have to work out height yourself
myParentPanel.Controls.Add(invisiLabel);

关于高度,它并不像height = splitContainer.Height这么简单。对我而言,我必须抓取splitContainer中最低元素的位置,才能获得标签的准确“高度”值。同时,在底部添加20或30的空白区域可能也是有好处的。

总之,将您的split container放入一个面板中,将该面板设置为自动滚动,然后在滚动面板中放置一个空标签,其高度应为您要滚动到的高度。别忘了给您的split container添加所有锚点,以便它与父面板保持一致。


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