5

When I drag a toolstrip to the left (perhaps just to get it in the corner) with another toolstrip in that same toolstrip panel, the one I'm dragging jumps down to a 'new' row, as if I had moved it down. It's quite tricky to explain, so here's a couple of diagrams.

Diagram A: I move the toolstrip to the left, and 'accidentally' go too far left (well only by a couple of dozen pixels, which users can easily do).

Diagram B: This happens, the dragged toolstrip drops down a row.

enter image description here

How can I prevent this new row from being 'created'? As a last resort, I would be happy to prevent new rows being created under any circumstances (e.g: If the user intended to drag it down to create a new row).

I've experimented with LayoutStyle to no avail.

Dan W
  • 3,520
  • 7
  • 42
  • 69

3 Answers3

1

How about you suspend layout on OnMouseLeave, and re-enable it OnMouseEnter/OnMouseUp/etc?

public class ToolStripContainerFix : ToolStripContainer
{
    public ToolStripContainerFix()
        : base()
    {
        this.TopToolStripPanel.MouseLeave += TopToolStripPanel_MouseLeave;
        this.TopToolStripPanel.MouseEnter += TopToolStripPanel_MouseEnter;
    }

    void TopToolStripPanel_MouseLeave(object sender, EventArgs e)
    {
        this.TopToolStripPanel.SuspendLayout();
    }

    void TopToolStripPanel_MouseEnter(object sender, EventArgs e)
    {
        this.TopToolStripPanel.ResumeLayout();
    }
}

you would need to fiddle with this so that it checks its outside the form, rather than the control itself... that way you could allow new rows to be created when intended.

Meirion Hughes
  • 24,994
  • 12
  • 71
  • 122
  • Thanks a lot. Unfortunately, other bugs seem to creep in. For example, dragging the other toolstrip over to the left sometimes makes it 'hide' under the other toolstrip, and sometimes I can't drag it at all after a bit. – Dan W Mar 05 '13 at 14:30
  • Did you Resume Layout? You'd need to on the mouse events (Enter / Mouse Up) to re-enable layout changes. – Meirion Hughes Mar 05 '13 at 14:35
  • No I didn't, but I see you've updated the code, so I tried that. Unfortunately, I still get issues of 'stuck' and 'disappearing' toolstrips, and/or 'merged' toolstrips. For example, try moving the first toolstrip too far left, and then hovering the mouse over the 'drag' part of the other toolstrip, and the first disappears. – Dan W Mar 05 '13 at 14:42
  • Ok, I'll give it a go when I get home. :P – Meirion Hughes Mar 05 '13 at 14:48
1

The toolstrip classes in Winforms are pretty quirky. They were released in .NET 2.0 and the Winforms team got disbanded pretty quickly after that. A lot of team members moved into the WPF project. Those quirks never got addressed as a result. There is very little you can do about this yourself, most of the toolstrip code is locked up without any way to override behavior. There's also rather a lot of it so rewriting is not practical either.

You can prevent the row from growing vertically. You do so by setting the MaximumSize property on the top toolstrip content panel:

    public Form1() {
        InitializeComponent();
        toolStripContainer1.TopToolStripPanel.MaximumSize =
            new Size(0, toolStrip1.Height);
    }

But when you play around with this you'll find that this can make a toolstrip disappear entirely. No clean fix for that.

Do note that this is not exactly a real problem, the locations of the toolstrips are the user's choice and you should not help in any way. You can count on the user arranging them in a logical way.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • Thanks, I too experienced one of the other tool bars disappearing by trying this. Unfortunately, the issue is a problem in the sense that it makes things irritating for my users, so I'll try the other two answers. – Dan W Mar 05 '13 at 14:13
1

There's probably a couple ways to do this. I've experimented with a few, but none are perfect. I would say the least complicated way I found is to set the maximum size of the ToolStripPanel like Hans mentioned, and also to subclass the ToolStrip and override OnLocationChanged (or assign an event handler to LocationChanged rather than subclassing, but you'd have to assign a handler to each ToolStrip).

public class ToolStripEx : ToolStrip
{
    protected override void OnLocationChanged(EventArgs e)
    {
        if (this.Location.Y >= this.Parent.MaximumSize.Height)
        {
            this.Location = new Point(this.Location.X, 0);
        }
        else
        {
            base.OnLocationChanged(e);
        }           
    }
}


Note: This causes the mouse to jump back and forth when trying to drag the ToolStrip down, due to the fact that the Location is reset after it already changed, so it is essentially moving down, then immediately jumping back up.

It's also worth mentioning that this may be an annoyance to the user, especially if they are purposely trying to put the ToolStrip in a new row, so I wouldn't really recommend doing this. But since you asked, there it is.

Update with complete steps and code:

I created a new blank Windows Forms project. I added a new file ToolStripEx.cs to the solution, and this is what is inside:

Updated for the other panels and their Orientation

using System;
using System.Drawing;
using System.Windows.Forms;

namespace WindowsFormsApplication2
{
    public class ToolStripEx : ToolStrip
    {
        protected override void OnLocationChanged(EventArgs e)
        {
            if (this.Parent is ToolStripPanel)
            {
                ToolStripPanel parent = this.Parent as ToolStripPanel;

                if (parent.Orientation == Orientation.Horizontal)
                {
                    if (this.Location.Y != 0)
                    {
                        this.Location = new Point(this.Location.X, 0);
                        return;
                    }
                }
                else if (parent.Orientation == Orientation.Vertical)
                {
                    if (this.Location.X != 0)
                    {
                        this.Location = new Point(0, this.Location.Y);
                        return;
                    }
                }
            }
            base.OnLocationChanged(e);
        }
    }
}


Then I built the solution so the ToolStripEx class will show up in the Toolbox.

Then I put a regular ToolStripContainer onto the form from the Toolbox, set Dock to Fill, set the colors, etc.

Then I dragged two ToolStripExs (with the cog icon you mentioned) from the Toolbox to the TopToolStripPanel. I set their colors and renderers and whatnot.

This is what Form1.cs looks like:

Updated to set the other maximum sizes

using System.Drawing;
using System.Windows.Forms;

namespace WindowsFormsApplication2
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            this.toolStripContainer1.TopToolStripPanel.MaximumSize = new Size(0, toolStripEx1.Height);
            this.toolStripContainer1.LeftToolStripPanel.MaximumSize = new Size(toolStripEx1.Height, 0);
            this.toolStripContainer1.BottomToolStripPanel.MaximumSize = new Size(0, toolStripEx1.Height);
            this.toolStripContainer1.RightToolStripPanel.MaximumSize = new Size(toolStripEx1.Height, 0);
        }
    }
}


Note: This code prevents any of the panels from expanding their rows (or columns if they have an Orientation of Orientation.Vertical) If you want the side panels to be able to expand, don't set their maximum size and get rid of the else if parent.Orientation == Orientation.Vertical section.

That should be all there is to it. I ran this, and both of the ToolStripExs did not disappear when moving them around.

Like Hans said, the ToolStrip class is pretty quirky and pretty much any solution to your problem is not going to be perfect unless you develop your own controls from the ground up with your needs in mind.

If for some reason you need to extend the ToolStripContainer class, keep it separate from the new ToolStripEx class. I suspect that having the nested classes was causing you to still be using the regular ToolStrip rather than the ToolStripEx class.

Another Update - fixing the mouse jumping: I found this by accident when experimenting to get rid of the mouse cursor problem. Add this to the ToolStripEx class:

protected override void OnBeginDrag(EventArgs e)
{
    //base.OnBeginDrag(e);
}

Strangely enough, that seems to cut down significantly on the toolstrip's resistance to being dragged outside of a panel. I haven"t dug into why this works, but it seems like ToolStrip implements its own dragging behavior without using basic drag/drop functionality and by overriding OnBeginDragDrop the ToolStrip is then using its custom behavior exclusively which makes the mouse act a lot nicer when dragging it.

sparky68967
  • 637
  • 1
  • 5
  • 8
  • Thanks. Yes, unfortunately, using Hans' solution combined with yours, I still get the issue of the disappearing toolstrip. FYI, when it disappears, it actually probably moves to a new row underneath the current one, so it hasn't actually been 'deleted'. – Dan W Mar 05 '13 at 14:36
  • @DanW It shouldn't move to a new row because the Location is essentially locked to a Y of 0. When I tested this, I couldn't get the toolstrip to disappear. And I've seen the disappearing toolstrip happen when I tried other things. Perhaps you could update your question with your exact code so far? – sparky68967 Mar 05 '13 at 18:55
  • 1
    Okay updated. By the way, try simply dragging the toolstrip down off the TopToolStripPanel and into the ContentPanel, and you'll see the problem more easily and immediately (it disappears). Do you have that issue too? – Dan W Mar 06 '13 at 23:31
  • 1
    @DanW since you put the `ToolStripEx` class inside the `ToolStripContainerFix` class, I suspect that your `toolStrip1` is not a `ToolStripEx`, but rather still a regular `ToolStrip`. – sparky68967 Mar 07 '13 at 00:41
  • Right, sorry, that was a stupid mistake of mine. I recreated the project now and yes, it seems to be 'fine' now. There's a glitch when it enters the LeftToolStripPanel but I can probably sort that out. Also, the tool strips can sometimes slide into each other, but that's not too much of a problem. Finally, and more worringly, I get "To prevent possible data loss before loading the designer, the following errors must be resolved" in the designer, even after following your steps. It does still compile/run though. I've created extensions like this before, and rarely have this trouble these days.. – Dan W Mar 07 '13 at 14:34
  • I'm tempted to tick this anyway, but it'd be great if that designer error was solved too... – Dan W Mar 07 '13 at 18:30
  • Error is "Object reference not set to an instance of an object.". I tried quoting out your overridden method, and it works fine then. Tried the other things you suggested to no avail. Perhaps try recreating it by creating a new project again. I'll keep hunting... – Dan W Mar 07 '13 at 21:55
  • Aha, the problem seems to be with the `Parent.MaximumSize.Height` bit. Changing that line to `if (Parent!=null && Location.Y >= Parent.MaximumSize.Height)` seems to help! – Dan W Mar 07 '13 at 21:59
  • All good so far, one final thought - do you think it would be possible to avoid the mouse cursor 'jumping motion' when trying to move the toolstrip directly from the TopToolStripPanel to the BottomToolStripPanel? The mouse cursor should freely allow this. – Dan W Mar 07 '13 at 22:30
  • @DanW I found a solution I think. Updated the answer. – sparky68967 Mar 07 '13 at 23:01
  • Wow, wonderful! Enjoy the bounty and thanks a ton for your help in this! Feels and works how one would imagine it to in the first place. – Dan W Mar 08 '13 at 00:04