views:

526

answers:

4

Are there any alternative controls someone can suggest to replace the WinForms SplitContainer? I don't like how the SplitContainer shows that weird, dotted strip when its selected and when its being dragged. I want to have the panels re-size as the user drags instead of on mouse up and not show any dotted strips when the splitter is being dragged. Basically like how all the re-sizing of panels is done in windows explorer on vista.

This is the dotted thing I'm talking about:

splitter

+5  A: 

Write your own split container UserControl. You basically just drop two Panels onto the control (for the left and right panels) and then let the space between them be the splitter. A little MouseDown, MouseMove and MouseUp logic on the UserControl itself will let you easily move the "splitter" left and right, and the two panels will properly block this everywhere but over the splitter, so your logic for checking if the mouse if over the splitter is as simple as can be.

It may be a little extra work getting the control to act the way you want it to act in design mode.

MusiGenesis
Well I'm giving this a try, however, with the MouseMove event on the control, the event will stop firing if the mouse is moved too fast and it goes over one of the panels. Is there a way to make the mouse move events bubble up to the control or have the control catch all the mouse move events?
Telanor
Nevermind, I just hooked the MouseMove events of all the controls to the same function. It worked out well
Telanor
I should have elaborated that in addition to handling the mouse events, you also want a bool or something to store whether the mouse button is currently down or not (as in Hans' answer). If you click and hold the mouse button on a control, it will generate a MouseDown event on that control. If you keep the mouse button down and drag left or right (or up or down, for that matter), the MouseMove events will all be generated by the control you clicked, even if the mouse cursor goes off the original control and onto another or the parent form.
MusiGenesis
So in your splitter control you have two panels with a little space between them acting as the splitter. Your user control's MouseDown event will only be fired when you click between the panels, so you can set your `_dragging` variable to `true` here. In the MouseMove event of the user control you check to make sure `_dragging == true` and then reset the widths of both panels and the left property of the right panel so that they leave the splitter around `e.X`. Then in the MouseUp event you set `_dragging = false`. This way you're only hooking one control's mouse events ...
MusiGenesis
... and you don't have to do any special checking in the MouseDown event to determine if you're over the splitter, since the event won't ever be fired if you aren't. Also, it won't matter whether you move the cursor quickly or slowly, since the MouseMove events will all be picked up by the UserControl regardless.
MusiGenesis
@Telanor - Rather than handling MouseMove on all the Panels, you should use use this.Capture = true; on the MouseDown event of your splitter control, and then release the mouse capture in the MouseUp event. This will ensure that your splitter receives all mouse messages. http://msdn.microsoft.com/en-us/library/system.windows.forms.control.capture.aspx
Chris Taylor
+1  A: 

You can't tinker with SplitContainer at all. One possibility is to eliminate it entirely if you are only using it to resize a control. You could use mouse events on the control itself instead. Drop a TreeView on a form and dock it on the left. Subscribe to the MouseDown/Move/Up events and write something like this:

    bool mDragging;

    private bool onTreeEdge(Point pos) {
        return pos.X >= treeView1.DisplayRectangle.Right - 3;
    }
    private void treeView1_MouseMove(object sender, MouseEventArgs e) {
        treeView1.Cursor = mDragging || onTreeEdge(e.Location) ? Cursors.VSplit : Cursors.Default;
        if (mDragging) treeView1.Width = e.X;
    }
    private void treeView1_MouseDown(object sender, MouseEventArgs e) {
        mDragging = onTreeEdge(e.Location);
        if (mDragging) treeView1.Capture = true;
    }
    private void treeView1_MouseUp(object sender, MouseEventArgs e) {
        mDragging = false;
    }
Hans Passant
A: 

I found this after I saw your question, so thought I would share: SplitContainer FAQ

The second link on there tells exactly what you need to do.

Here is the text from that just in case the link ever dies.

1.  Use the custom control defined in the SplitContainerNoFocus sample
2. Insert the following code in your project, and attach these events to all of the SplitContainers that you don't want stealing focus.

// Temp variable to store a previously focused control
private Control focused = null; 

private void splitContainer_MouseDown(object sender, MouseEventArgs e)
{
   // Get the focused control before the splitter is focused
   focused = getFocused(this.Controls);
}

private Control getFocused(Control.ControlCollection controls)
{
   foreach (Control c in controls)
   {
      if (c.Focused)
      {
         // Return the focused control
         return c;
      }
      else if (c.ContainsFocus)
      {
         // If the focus is contained inside a control's children
         // return the child
         return getFocused(c.Controls);
      }
   }
   // No control on the form has focus
   return null;
}

private void splitContainer_MouseUp(object sender, MouseEventArgs e)
{
   // If a previous control had focus
   if (focused != null)
   {
      // Return focus and clear the temp variable for 
      // garbage collection
      focused.Focus();
      focused = null;
   }
}
Thymine
A: 

I needed the same and set the following properties:

        splitContainer1.Anchor = (AnchorStyles.Top | AnchorStyles.Left);
        splitContainer1.Dock = DockStyle.Fill;
        splitContainer1.IsSplitterFixed = true;

Hope this helps.

cookes