|
//------------------------------------------------------------------------------
// <copyright file="SplitContainer.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
/*
*/
namespace System.Windows.Forms {
using Microsoft.Win32;
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Runtime.Remoting;
using System.Security;
using System.Security.Permissions;
using System.Windows.Forms;
using System.Collections;
using System.Drawing.Drawing2D;
using System.Globalization;
/// <include file='doc\SplitContainer.uex' path='docs/doc[@for="SplitContainer"]/*' />
/// <devdoc>
/// A SplitContainer is a ContainerControl with 2 panels separated with a splitter
/// in the middle. This is a composite control. The user can drag and drop this control from Toolbox.
/// Controls can be added to the right panel and the left panel. The Orientation can be either Horizontal or Vertical.
/// The Controls inside the Panels would be redrawn with the new Orientation.
/// With this control the user need be aware of docking, z-order of the controls. The controls get parented when thry are
/// dropped on the SpitContainer.
/// </devdoc>
[
ComVisible(true),
ClassInterface(ClassInterfaceType.AutoDispatch),
DefaultEvent("SplitterMoved"),
Docking(DockingBehavior.AutoDock),
Designer("System.Windows.Forms.Design.SplitContainerDesigner, " + AssemblyRef.SystemDesign),
SRDescription(SR.DescriptionSplitContainer)
]
public class SplitContainer : ContainerControl, ISupportInitialize
{
//
// CONTANTS USED DURING DRAWING SPLITTER MOOVEMENTS
//
private const int DRAW_START = 1;
private const int DRAW_MOVE = 2;
private const int DRAW_END = 3;
private const int rightBorder = 5;
private const int leftBorder = 2;
private int BORDERSIZE = 0;
//
// SplitContainer private Cached copies of public properties...
//
private Orientation orientation = Orientation.Vertical;
private SplitterPanel panel1 = null;
private SplitterPanel panel2 = null;
private BorderStyle borderStyle = System.Windows.Forms.BorderStyle.None;
private FixedPanel fixedPanel = FixedPanel.None;
private int panel1MinSize = 25; //Panel1 Minimum Size
private int newPanel1MinSize = 25; //New panel1 Minimum Size used for ISupportInitialize
private int panel2MinSize = 25; //Panel2 Minimum Size
private int newPanel2MinSize = 25; //New panel2 Minimum Size used for ISupportInitialize
private bool tabStop = true;
//
//Fixed panel width or height;
//
private int panelSize;
//
//SPLITTER PROPERTIES
//
private Rectangle splitterRect;
private int splitterInc = 1;
private bool splitterFixed;
private int splitterDistance = 50; //default splitter distance;
private int splitterWidth = 4;
private int newSplitterWidth = 4; //New splitter width used for ISupportInitialize
private int splitDistance = 50;
//
//Properties used using DRAWING a MOVING SPLITTER
//
private int lastDrawSplit =1;
private int initialSplitterDistance;
private Rectangle initialSplitterRectangle;
private Point anchor = Point.Empty;
private bool splitBegin = false;
private bool splitMove = false;
private bool splitBreak = false;
//
// Split Cursor
//
Cursor overrideCursor = null;
//
//Needed For Tabbing
//
Control nextActiveControl = null;
private bool callBaseVersion;
private bool splitterFocused;
//
//Required to keep track of Splitter movements....
//
private bool splitterClick;
private bool splitterDrag;
//
// FixedPanel.None require us
// to keep the Width/Height Ratio Depending on SplitContainer.Orientation
//
double ratioWidth = 0.0f;
double ratioHeight = 0.0f;
bool resizeCalled = false;
bool splitContainerScaling = false;
bool setSplitterDistance = false;
//
//Events
//
private static readonly object EVENT_MOVING = new object();
private static readonly object EVENT_MOVED = new object();
// private IMessageFilter implementation
SplitContainerMessageFilter splitContainerMessageFilter = null;
//This would avoid re-entrant code into SelectNextControl.
private bool selectNextControl = false;
// Initialization flag for ISupportInitialize
private bool initializing = false;
//
// Constructor
//
/// <include file='doc\SplitContainer.uex' path='docs/doc[@for="SplitContainer.SplitContainer"]/*' />
public SplitContainer()
{
// either the left or top panel - LTR
// either the right or top panel - RTL
panel1 = new SplitterPanel(this);
// either the right or bottom panel - LTR
// either the left or bottom panel - RTL
panel2 = new SplitterPanel(this);
splitterRect = new Rectangle();
SetStyle(ControlStyles.SupportsTransparentBackColor, true);
SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
((WindowsFormsUtils.TypedControlCollection)this.Controls).AddInternal(panel1);
((WindowsFormsUtils.TypedControlCollection)this.Controls).AddInternal(panel2);
UpdateSplitter();
}
/////////////////////////////////////////////////////////////////////////////////////////////
// //
//PROPERTIES START IN ALPHABETICAL ORDER //
// //
/////////////////////////////////////////////////////////////////////////////////////////////
/// <include file='doc\SplitContainer.uex' path='docs/doc[@for="SplitContainer.AutoScroll"]/*' />
/// <devdoc>
/// This property is overridden to allow the AutoScroll to be set on all the panels when
/// The autoScroll on SplitContainer is shown.
/// Here we dont set the base value ... but set autoscroll for panels.
/// </devdoc>
[
SRCategory(SR.CatLayout),
Localizable(true),
DefaultValue(false),
SRDescription(SR.FormAutoScrollDescr),
Browsable(false), EditorBrowsable(EditorBrowsableState.Never)
]
public override bool AutoScroll {
get {
//Always return false ... as Splitcontainer doesnt support AutoScroll
return false;
}
set {
base.AutoScroll = value;
}
}
[
Browsable(false),
EditorBrowsable(EditorBrowsableState.Never),
DefaultValue(typeof(Point), "0, 0")
]
public override Point AutoScrollOffset {
get {
return base.AutoScrollOffset;
}
set {
base.AutoScrollOffset = value;
}
}
/// <include file='doc\SplitContainer.uex' path='docs/doc[@for="SplitContainer.AutoScrollMinSize"]/*' />
/// <devdoc>
/// Override AutoScrollMinSize to make it hidden from the user in the designer
/// </devdoc>
[
EditorBrowsable(EditorBrowsableState.Never),
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
Browsable(false)
]
public new Size AutoScrollMinSize {
get {
return base.AutoScrollMinSize;
}
set {
base.AutoScrollMinSize = value;
}
}
/// <include file='doc\SplitContainer.uex' path='docs/doc[@for="SplitContainer.AutoScrollMargin"]/*' />
/// <devdoc>
/// Override AutoScrollMargin to make it hidden from the user in the designer
/// </devdoc>
[
EditorBrowsable(EditorBrowsableState.Never),
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
Browsable(false)
]
public new Size AutoScrollMargin {
get {
return base.AutoScrollMargin;
}
set {
base.AutoScrollMargin = value;
}
}
[
SRCategory(SR.CatLayout),
Browsable(false), EditorBrowsable(EditorBrowsableState.Never),
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
SRDescription(SR.FormAutoScrollPositionDescr)
]
public new Point AutoScrollPosition {
get {
return base.AutoScrollPosition;
}
set {
base.AutoScrollPosition = value;
}
}
/// <devdoc>
/// <para>Hide AutoSize, as it can mean more than one thing and might confuse users</para>
/// </devdoc>
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never),
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public override bool AutoSize
{
get
{
return base.AutoSize;
}
set
{
base.AutoSize = value;
}
}
/// <include file='doc\SplitContainer.uex' path='docs/doc[@for="SplitContainer.AutoSizeChanged"]/*' />
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
new public event EventHandler AutoSizeChanged {
add {
base.AutoSizeChanged += value;
}
remove {
base.AutoSizeChanged -= value;
}
}
/// <include file='doc\SplitContainer.uex' path='docs/doc[@for="SplitContainer.BackgroundImage"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[Browsable(true), EditorBrowsable(EditorBrowsableState.Always)]
public override Image BackgroundImage {
get {
return base.BackgroundImage;
}
set {
base.BackgroundImage = value;
}
}
/// <include file='doc\SplitContainer.uex' path='docs/doc[@for="SplitContainer.BackgroundImageLayout"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
public override ImageLayout BackgroundImageLayout {
get {
return base.BackgroundImageLayout;
}
set {
base.BackgroundImageLayout = value;
}
}
/// <include file='doc\SplitContainer.uex' path='docs/doc[@for="SplitContainer.BindingContext"]/*' />
/// <internalonly/>
/// <devdoc>
/// <para>
/// The binding manager for the container control.
/// </para>
/// </devdoc>
[
Browsable(false),
SRDescription(SR.ContainerControlBindingContextDescr),
SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")
]
public override BindingContext BindingContext {
get {
return BindingContextInternal;
}
set {
BindingContextInternal = value;
}
}
/// <include file='doc\SplitContainer.uex' path='docs/doc[@for="SplitContainer.BorderStyle"]/*' />
/// <devdoc>
/// Indicates what type of border the Splitter control has. This value
/// comes from the System.Windows.Forms.BorderStyle enumeration.
/// </devdoc>
[
DefaultValue(BorderStyle.None),
SRCategory(SR.CatAppearance),
System.Runtime.InteropServices.DispId(NativeMethods.ActiveX.DISPID_BORDERSTYLE),
SRDescription(SR.SplitterBorderStyleDescr)
]
public BorderStyle BorderStyle {
get {
return borderStyle;
}
set {
//valid values are 0x0 to 0x2
if (!ClientUtils.IsEnumValid(value, (int)value, (int)BorderStyle.None, (int)BorderStyle.Fixed3D)){
throw new InvalidEnumArgumentException("value", (int)value, typeof(BorderStyle));
}
if (borderStyle != value) {
borderStyle = value;
Invalidate();
SetInnerMostBorder(this);
if (this.ParentInternal != null) {
if (this.ParentInternal is SplitterPanel) {
SplitContainer sc = (SplitContainer)((SplitterPanel)this.ParentInternal).Owner;
sc.SetInnerMostBorder(sc);
}
}
}
switch (BorderStyle)
{
case BorderStyle.None:
BORDERSIZE = 0;
break;
case BorderStyle.FixedSingle:
BORDERSIZE = 1;
break;
case BorderStyle.Fixed3D:
BORDERSIZE = 4;
break;
}
}
}
/// <include file='doc\SplitContainer.uex' path='docs/doc[@for="SplitContainer.Controls"]/*' />
/// <devdoc>
/// Controls Collection...
/// This is overriden so that the Controls.Add ( ) is not Code Gened...
/// </devdoc>
[EditorBrowsable(EditorBrowsableState.Never)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public new Control.ControlCollection Controls {
get {
return base.Controls;
}
}
/// <include file='doc\WinBar.uex' path='docs/doc[@for="SplitContainer.ControlAdded"]/*' />
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
public new event ControlEventHandler ControlAdded {
add {
base.ControlAdded += value;
}
remove {
base.ControlAdded -= value;
}
}
/// <include file='doc\WinBar.uex' path='docs/doc[@for="SplitContainer.ControlRemoved"]/*' />
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
public new event ControlEventHandler ControlRemoved {
add {
base.ControlRemoved += value;
}
remove {
base.ControlRemoved -= value;
}
}
/// <include file='doc\SplitContainer.uex' path='docs/doc[@for="SplitContainer.Dock"]/*' />
/// <devdoc>
/// The dock property. The dock property controls to which edge
/// of the container this control is docked to. For example, when docked to
/// the top of the container, the control will be displayed flush at the
/// top of the container, extending the length of the container.
/// </devdoc>
public new DockStyle Dock {
get {
return base.Dock;
}
set {
base.Dock = value;
if (this.ParentInternal != null) {
if (this.ParentInternal is SplitterPanel) {
SplitContainer sc = (SplitContainer)((SplitterPanel)this.ParentInternal).Owner;
sc.SetInnerMostBorder(sc);
}
}
ResizeSplitContainer();
}
}
/// <include file='doc\SplitContainer.uex' path='docs/doc[@for="SplitContainer.DefaultSize"]/*' />
/// <devdoc>
/// Deriving classes can override this to configure a default size for their control.
/// This is more efficient than setting the size in the control's constructor.
/// </devdoc>
protected override Size DefaultSize {
get {
return new Size(150, 100);
}
}
/// <include file='doc\SplitContainer.uex' path='docs/doc[@for="SplitContainer.FixedPanel"]/*' />
/// <devdoc>
/// Indicates what type of border the Splitter control has. This value
/// comes from the System.Windows.Forms.BorderStyle enumeration.
/// </devdoc>
[
DefaultValue(FixedPanel.None),
SRCategory(SR.CatLayout),
SRDescription(SR.SplitContainerFixedPanelDescr)
]
public FixedPanel FixedPanel {
get {
return fixedPanel;
}
set {
//valid values are 0x0 to 0x2
if (!ClientUtils.IsEnumValid(value, (int)value, (int)FixedPanel.None, (int)FixedPanel.Panel2)){
throw new InvalidEnumArgumentException("value", (int)value, typeof(FixedPanel));
}
if (fixedPanel != value) {
fixedPanel = value;
// UpdatePanelSize !!
switch (fixedPanel) {
case FixedPanel.Panel2:
if (Orientation == Orientation.Vertical) {
panelSize = Width - SplitterDistanceInternal - SplitterWidthInternal;
}
else {
panelSize = Height - SplitterDistanceInternal - SplitterWidthInternal;
}
break;
default:
panelSize = SplitterDistanceInternal;
break;
}
}
}
}
/// <include file='doc\SplitContainer.uex' path='docs/doc[@for="SplitContainer.IsSplitterFixed"]/*' />
/// <devdoc>
/// This property determines whether the the splitter can move.
/// </devdoc>
[
SRCategory(SR.CatLayout),
DefaultValue(false),
Localizable(true),
SRDescription(SR.SplitContainerIsSplitterFixedDescr)
]
public bool IsSplitterFixed {
get {
return splitterFixed;
}
set {
splitterFixed = value;
}
}
//Private property used to check whether the splitter can be moved by the user.
private bool IsSplitterMovable {
get {
if (Orientation == Orientation.Vertical) {
return (Width >= Panel1MinSize + SplitterWidthInternal + Panel2MinSize);
}
else {
return (Height >= Panel1MinSize + SplitterWidthInternal + Panel2MinSize);
}
}
}
// VsWhidbey 434959 : Refer to IsContainerControl property on Control for more details.
internal override bool IsContainerControl
{
get
{
return true;
}
}
/// <include file='doc\SplitContainer.uex' path='docs/doc[@for="SplitContainer.Orientation"]/*' />
/// <devdoc>
/// This Property sets or gets if the splitter is vertical or horizontal.
/// </devdoc>
[
SRCategory(SR.CatBehavior),
DefaultValue(Orientation.Vertical),
Localizable(true),
SRDescription(SR.SplitContainerOrientationDescr)
]
public Orientation Orientation {
get { return orientation; }
set {
//valid values are 0x0 to 0x1
if (!ClientUtils.IsEnumValid(value, (int)value, (int)Orientation.Horizontal, (int)Orientation.Vertical)){
throw new InvalidEnumArgumentException("value", (int)value, typeof(Orientation));
}
if (orientation != value) {
orientation = value;
//update the splitterDistance to validate it w.r.t the new Orientation.
splitDistance = 0;
SplitterDistance = SplitterDistanceInternal;
UpdateSplitter();
}
}
}
/// <include file='doc\SplitContainer.uex' path='docs/doc[@for="SplitContainer.OverrideCursor"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
private Cursor OverrideCursor {
get {
return overrideCursor;
}
set {
if (overrideCursor != value) {
overrideCursor = value;
if (IsHandleCreated) {
// We want to instantly change the cursor if the mouse is within our bounds.
NativeMethods.POINT p = new NativeMethods.POINT();
NativeMethods.RECT r = new NativeMethods.RECT();
UnsafeNativeMethods.GetCursorPos(p);
UnsafeNativeMethods.GetWindowRect(new HandleRef(this, Handle), ref r);
if ((r.left <= p.x && p.x < r.right && r.top <= p.y && p.y < r.bottom) || UnsafeNativeMethods.GetCapture() == Handle)
SendMessage(NativeMethods.WM_SETCURSOR, Handle, NativeMethods.HTCLIENT);
}
}
}
}
///<devdoc>
/// Indicates if either panel is collapsed
///</devdoc>
private bool CollapsedMode {
get {
return Panel1Collapsed || Panel2Collapsed;
}
}
/// <include file='doc\SplitContainer.uex' path='docs/doc[@for="SplitContainer.Panel1"]/*' />
/// <devdoc>
/// The Left or Top panel in the SplitContainer.
/// </devdoc>
[
SRCategory(SR.CatAppearance),
SRDescription(SR.SplitContainerPanel1Descr),
Localizable(false),
DesignerSerializationVisibility(DesignerSerializationVisibility.Content)
]
public SplitterPanel Panel1 {
get {
return this.panel1;
}
}
///<devdoc>
/// Collapses or restores the given panel
///</devdoc>
private void CollapsePanel(SplitterPanel p, bool collapsing) {
p.Collapsed = collapsing;
if (collapsing) {
p.Visible = false;
}
else {
// restore panel
p.Visible = true;
}
UpdateSplitter();
}
/// <include file='doc\SplitContainer.uex' path='docs/doc[@for="SplitContainer.Padding"]/*' />
/// <devdoc>
/// <para>
/// <para>[To be supplied.]</para>
/// </para>
/// </devdoc>
[
Browsable(false),
EditorBrowsable(EditorBrowsableState.Never),
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)
]
public new Padding Padding {
get { return base.Padding; }
set { base.Padding = value;}
}
[
Browsable(false),
EditorBrowsable(EditorBrowsableState.Never)
]
public new event EventHandler PaddingChanged {
add { base.PaddingChanged += value; }
remove { base.PaddingChanged -= value; }
}
/// <include file='doc\SplitContainer.uex' path='docs/doc[@for="SplitContainer.Panel1"]/*' />
/// <devdoc>
/// Collapses or restores panel1
/// </devdoc>
[
SRCategory(SR.CatLayout),
DefaultValue(false),
SRDescription(SR.SplitContainerPanel1CollapsedDescr)
]
public bool Panel1Collapsed {
get {
return panel1.Collapsed;
}
set {
if (value != panel1.Collapsed) {
if (value && panel2.Collapsed) {
CollapsePanel(panel2, false);
}
CollapsePanel(panel1, value);
}
}
}
/// <include file='doc\SplitContainer.uex' path='docs/doc[@for="SplitContainer.Panel1"]/*' />
/// <devdoc>
/// Collapses or restores panel2
/// </devdoc>
[
SRCategory(SR.CatLayout),
DefaultValue(false),
SRDescription(SR.SplitContainerPanel2CollapsedDescr)
]
public bool Panel2Collapsed {
get {
return panel2.Collapsed;
}
set {
if (value != panel2.Collapsed) {
if (value && panel1.Collapsed) {
CollapsePanel(panel1, false);
}
CollapsePanel(panel2, value);
}
}
}
/// <include file='doc\SplitContainer.uex' path='docs/doc[@for="SplitContainer.Panel1MinSize"]/*' />
/// <devdoc>
/// This property determines the minimum distance of pixels of the splitter from the left or the top edge of Panel1.
/// </devdoc>
[
SRCategory(SR.CatLayout),
DefaultValue(25),
Localizable(true),
SRDescription(SR.SplitContainerPanel1MinSizeDescr),
RefreshProperties(RefreshProperties.All)
]
public int Panel1MinSize {
get {
return panel1MinSize;
}
set {
newPanel1MinSize = value;
if (value != Panel1MinSize && !initializing) {
ApplyPanel1MinSize(value);
}
}
}
/// <include file='doc\SplitContainer.uex' path='docs/doc[@for="SplitContainer.Panel2"]/*' />
/// <devdoc>
/// This is the Right or Bottom panel in the SplitContainer.
/// </devdoc>
[
SRCategory(SR.CatAppearance),
SRDescription(SR.SplitContainerPanel2Descr),
Localizable(false),
DesignerSerializationVisibility(DesignerSerializationVisibility.Content)
]
public SplitterPanel Panel2 {
get {
return this.panel2;
}
}
/// <include file='doc\SplitContainer.uex' path='docs/doc[@for="SplitContainer.Panel2MinSize"]/*' />
/// <devdoc>
/// This property determines the minimum distance of pixels of the splitter from the right or the bottom edge of Panel2
/// </devdoc>
[
SRCategory(SR.CatLayout),
DefaultValue(25),
Localizable(true),
SRDescription(SR.SplitContainerPanel2MinSizeDescr),
RefreshProperties(RefreshProperties.All)
]
public int Panel2MinSize {
get {
return panel2MinSize;
}
set {
newPanel2MinSize = value;
if (value != Panel2MinSize && !initializing) {
ApplyPanel2MinSize(value);
}
}
}
/// <include file='doc\SplitContainer.uex' path='docs/doc[@for="SplitContainer.SplitterDistance"]/*' />
/// <devdoc>
/// This property determines pixel distance of the splitter from the left or top edge.
/// </devdoc>
[
SRCategory(SR.CatLayout),
Localizable(true),
SettingsBindable(true),
SRDescription(SR.SplitContainerSplitterDistanceDescr),
DefaultValue(50)
]
public int SplitterDistance
{
get
{
return splitDistance;
}
set
{
if (value != SplitterDistance)
{
if (value < 0) {
throw new ArgumentOutOfRangeException("SplitterDistance", SR.GetString(SR.InvalidLowBoundArgument, "SplitterDistance", (value).ToString(CultureInfo.CurrentCulture), "0"));
}
try
{
setSplitterDistance = true;
if (Orientation == Orientation.Vertical) {
if (value < Panel1MinSize)
{
value = Panel1MinSize;
}
if (value + SplitterWidthInternal > this.Width - Panel2MinSize)
{
value = this.Width - Panel2MinSize - SplitterWidthInternal;
}
if (value < 0 ) {
throw new InvalidOperationException(SR.GetString(SR.SplitterDistanceNotAllowed));
}
splitDistance = value;
splitterDistance = value;
panel1.WidthInternal = SplitterDistance;
}
else {
if (value < Panel1MinSize)
{
value = Panel1MinSize;
}
if (value + SplitterWidthInternal > this.Height - Panel2MinSize)
{
value = this.Height - Panel2MinSize - SplitterWidthInternal;
}
if (value < 0 ) {
throw new InvalidOperationException(SR.GetString(SR.SplitterDistanceNotAllowed));
}
splitDistance = value;
splitterDistance = value;
panel1.HeightInternal = SplitterDistance;
}
switch (fixedPanel) {
case FixedPanel.Panel1:
panelSize = SplitterDistance;
break;
case FixedPanel.Panel2:
if (Orientation == Orientation.Vertical) {
panelSize = Width - SplitterDistance - SplitterWidthInternal;
}
else {
panelSize = Height - SplitterDistance - SplitterWidthInternal;
}
break;
}
UpdateSplitter();
}
finally
{
setSplitterDistance = false;
}
OnSplitterMoved(new SplitterEventArgs(SplitterRectangle.X + SplitterRectangle.Width/2, SplitterRectangle.Y + SplitterRectangle.Height/2, SplitterRectangle.X, SplitterRectangle.Y));
}
}
}
private int SplitterDistanceInternal {
get {
return splitterDistance;
}
set {
SplitterDistance = value;
}
}
/// <include file='doc\SplitContainer.uex' path='docs/doc[@for="SplitContainer.SplitterIncrement"]/*' />
/// <devdoc>
/// This determines the number of pixels the splitter moves in increments.This is defaulted to 1.
/// </devdoc>
[
SRCategory(SR.CatLayout),
DefaultValue(1),
Localizable(true),
SRDescription(SR.SplitContainerSplitterIncrementDescr)
]
public int SplitterIncrement {
get {
return splitterInc;
}
set {
if (value < 1 ) {
throw new ArgumentOutOfRangeException("SplitterIncrement", SR.GetString(SR.InvalidLowBoundArgumentEx, "SplitterIncrement", (value).ToString(CultureInfo.CurrentCulture), "1"));
}
splitterInc = value;
}
}
/// <include file='doc\SplitContainer.uex' path='docs/doc[@for="SplitContainer.SplitterRectangle"]/*' />
/// <devdoc>
/// This property determines the rectangle bounds of the splitter.
/// </devdoc>
[
SRCategory(SR.CatLayout),
SRDescription(SR.SplitContainerSplitterRectangleDescr),
Browsable(false)
]
public Rectangle SplitterRectangle {
get {
Rectangle r = splitterRect;
r.X = splitterRect.X - Left;
r.Y = splitterRect.Y - Top;
return r;
}
}
/// <include file='doc\SplitContainer.uex' path='docs/doc[@for="SplitContainer.SplitterWidth"]/*' />
/// <devdoc>
/// This property determines the thickness of the splitter.
/// </devdoc>
[
SRCategory(SR.CatLayout),
SRDescription(SR.SplitContainerSplitterWidthDescr),
Localizable(true),
DefaultValue(4)
]
public int SplitterWidth {
get {
return splitterWidth;
}
set {
newSplitterWidth = value;
if (value != SplitterWidth && !initializing) {
ApplySplitterWidth(value);
}
}
}
/// <devdoc>
/// We need to have a internal Property for the SplitterWidth which returns zero if we are in collapased mode.
/// This property is used to Layout SplitContainer.
/// </devdoc>
private int SplitterWidthInternal {
get {
// if CollapsedMode then splitterwidth == 0;
return (CollapsedMode) ? 0 : splitterWidth;
}
}
/// <include file='doc\SplitContainer.uex' path='docs/doc[@for="SplitContainer.TabStop"]/*' />
/// <devdoc>
/// <para>Indicates whether the user can give the focus to this control using the TAB
/// key. This property is read-only.</para>
/// </devdoc>
[
SRCategory(SR.CatBehavior),
DefaultValue(true),
DispId(NativeMethods.ActiveX.DISPID_TABSTOP),
SRDescription(SR.ControlTabStopDescr)
]
public new bool TabStop {
get {
return tabStop;
}
set {
if (TabStop != value) {
tabStop = value;
OnTabStopChanged(EventArgs.Empty);
}
}
}
/// <include file='doc\SplitContainer.uex' path='docs/doc[@for="SplitContainer.Text"]/*' />
/// <devdoc>
/// </devdoc>
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never), Bindable(false)]
public override string Text {
get {
return base.Text;
}
set {
base.Text = value;
}
}
/////////////////////////////////////////////////////////////////////////////////////////////
// //
//END PROPERTIES //
// //
/////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////
// //
//Start PUBLIC FUNCTIONS //
// //
/////////////////////////////////////////////////////////////////////////////////////////////
/// <include file='doc\SplitContainer.uex' path='docs/doc[@for="SplitContainer.BeginInit"]/*' />
/// <devdoc>
/// ISupportInitialize support. Disables splitter panel min size and splitter width
/// validation during initialization.
/// </devdoc>
public void BeginInit() {
initializing = true;
}
/// <include file='doc\SplitContainer.uex' path='docs/doc[@for="SplitContainer.EndInit"]/*' />
/// <devdoc>
/// ISupportInitialize support. Enables splitter panel min size and splitter width
/// validation after initialization.
/// </devdoc>
public void EndInit() {
initializing = false;
// validate and apply new value
if (newPanel1MinSize != panel1MinSize) {
ApplyPanel1MinSize(newPanel1MinSize);
}
if (newPanel2MinSize != panel2MinSize) {
ApplyPanel2MinSize(newPanel2MinSize);
}
if (newSplitterWidth != splitterWidth) {
ApplySplitterWidth(newSplitterWidth);
}
}
/////////////////////////////////////////////////////////////////////////////////////////////
// //
//End PUBLIC FUNCTIONS //
// //
/////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////
// //
//Start EVENT HANDLERS //
// //
/////////////////////////////////////////////////////////////////////////////////////////////
/// <include file='doc\SplitContainer.uex' path='docs/doc[@for="SplitContainer.BackgroundImageChanged"]/*' />
/// <internalonly/>
[Browsable(true), EditorBrowsable(EditorBrowsableState.Always)]
new public event EventHandler BackgroundImageChanged {
add {
base.BackgroundImageChanged += value;
}
remove {
base.BackgroundImageChanged -= value;
}
}
/// <include file='doc\SplitContainer.uex' path='docs/doc[@for="SplitContainer.BackgroundImageLayoutChanged"]/*' />
/// <internalonly/>
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
new public event EventHandler BackgroundImageLayoutChanged {
add {
base.BackgroundImageLayoutChanged += value;
}
remove {
base.BackgroundImageLayoutChanged -= value;
}
}
/// <include file='doc\SplitContainer.uex' path='docs/doc[@for="SplitContainer.SplitterMoving"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[SRCategory(SR.CatBehavior), SRDescription(SR.SplitterSplitterMovingDescr)]
public event SplitterCancelEventHandler SplitterMoving {
add {
Events.AddHandler(EVENT_MOVING, value);
}
remove {
Events.RemoveHandler(EVENT_MOVING, value);
}
}
/// <include file='doc\SplitContainer.uex' path='docs/doc[@for="SplitContainer.SplitterMoved"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[SRCategory(SR.CatBehavior), SRDescription(SR.SplitterSplitterMovedDescr)]
public event SplitterEventHandler SplitterMoved {
add {
Events.AddHandler(EVENT_MOVED, value);
}
remove {
Events.RemoveHandler(EVENT_MOVED, value);
}
}
/// <include file='doc\SplitContainer.uex' path='docs/doc[@for="SplitContainer.TextChanged"]/*' />
/// <internalonly/>
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
new public event EventHandler TextChanged {
add {
base.TextChanged += value;
}
remove {
base.TextChanged -= value;
}
}
/////////////////////////////////////////////////////////////////////////////////////////////
// //
//End EVENT HANDLERS //
// //
/////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////
// //
//start EVENT Delegates //
// //
/////////////////////////////////////////////////////////////////////////////////////////////
/// <include file='doc\SplitContainer.uex' path='docs/doc[@for="SplitContainer.OnGotFocus"]/*' />
/// <devdoc>
/// Overides the Control.OnGotFocus to Invalidate...
/// </devdoc>
protected override void OnGotFocus(EventArgs e) {
base.OnGotFocus(e);
Invalidate();
}
/// <include file='doc\SplitContainer.uex' path='docs/doc[@for="SplitContainer.OnKeyDown"]/*' />
/// <devdoc>
/// Overrides the Control.OnKeydown for implementing splitter movements.
/// </devdoc>
protected override void OnKeyDown(KeyEventArgs e) {
Debug.Assert(Enabled, "SplitContainer.OnKeyDown should not be called if the button is disabled");
base.OnKeyDown(e);
//If the Panel1MinSize + Panel2MinSize < SplitContainer.Size then carry on the splitter move...
if (IsSplitterMovable && !IsSplitterFixed) {
if (e.KeyData == Keys.Escape && splitBegin) {
splitBegin = false;
splitBreak = true;
return;
}
//valid Keys that move the splitter...
if (e.KeyData == Keys.Right || e.KeyData == Keys.Down ||
e.KeyData == Keys.Left || e.KeyData == Keys.Up
&& splitterFocused) {
if (splitBegin) {
splitMove = true;
}
//left OR up
if (e.KeyData == Keys.Left || e.KeyData == Keys.Up && splitterFocused) {
splitterDistance -= SplitterIncrement;
splitterDistance = (splitterDistance < Panel1MinSize) ? splitterDistance + SplitterIncrement : Math.Max(splitterDistance, BORDERSIZE);
}
//right OR down
if (e.KeyData == Keys.Right || e.KeyData == Keys.Down && splitterFocused) {
splitterDistance += SplitterIncrement;
if (Orientation == Orientation.Vertical) {
splitterDistance = (splitterDistance + SplitterWidth > Width - Panel2MinSize -BORDERSIZE) ? splitterDistance - SplitterIncrement : splitterDistance;
}
else {
splitterDistance = (splitterDistance + SplitterWidth > Height - Panel2MinSize - BORDERSIZE) ? splitterDistance - SplitterIncrement : splitterDistance;
}
}
if (!splitBegin) {
splitBegin = true;
}
//draw Helper start
if (splitBegin && !splitMove) {
initialSplitterDistance = SplitterDistanceInternal;
DrawSplitBar(DRAW_START);
}
else { //draw helper move
DrawSplitBar(DRAW_MOVE);
//Moving by mouse .....gives the origin of the splitter..
//
Rectangle r = CalcSplitLine(splitterDistance, 0);
int xSplit = r.X;
int ySplit = r.Y;
SplitterCancelEventArgs se = new SplitterCancelEventArgs(this.Left + SplitterRectangle.X + SplitterRectangle.Width/2, this.Top + SplitterRectangle.Y + SplitterRectangle.Height/2, xSplit, ySplit);
OnSplitterMoving(se);
if (se.Cancel) {
SplitEnd(false);
}
}
} //End Valid Keys....
} //End SplitterFixed Check...
}
/// <include file='doc\SplitContainer.uex' path='docs/doc[@for="SplitContainer.OnKeyUp"]/*' />
/// <devdoc>
/// Overrides the Control.OnKeydown for implementing splitter movements.
/// </devdoc>
protected override void OnKeyUp(KeyEventArgs e) {
base.OnKeyUp(e);
if (splitBegin && IsSplitterMovable) {
if (e.KeyData == Keys.Right || e.KeyData == Keys.Down ||
e.KeyData == Keys.Left || e.KeyData == Keys.Up
&& splitterFocused) {
DrawSplitBar(DRAW_END);
ApplySplitterDistance();
splitBegin = false;
splitMove = false;
}
}
if (splitBreak) {
splitBreak = false;
SplitEnd(false);
}
//problem with the Focus rect after Keyup ....
//Focus rect and reverible lines leave a trace behind on the splitter...
using (Graphics g = CreateGraphicsInternal()) {
if (BackgroundImage == null) {
using (SolidBrush brush = new SolidBrush(this.BackColor)) {
g.FillRectangle(brush, SplitterRectangle);
}
}
DrawFocus(g, SplitterRectangle);
}
}
/// <include file='doc\SplitContainer.uex' path='docs/doc[@for="SplitContainer.OnLayout"]/*' />
/// <devdoc>
/// Overrides the Control.OnLayout.
/// </devdoc>
protected override void OnLayout(LayoutEventArgs e) {
SetInnerMostBorder(this);
if (IsSplitterMovable && !setSplitterDistance) {
ResizeSplitContainer();
}
base.OnLayout(e);
}
/// <include file='doc\SplitContainer.uex' path='docs/doc[@for="SplitContainer.OnLostFocus"]/*' />
/// <devdoc>
/// Overrides the Control.OnLostFocus to Invalidate.
/// </devdoc>
protected override void OnLostFocus(EventArgs e) {
base.OnLostFocus(e);
Invalidate();
}
/// <include file='doc\SplitContainer.uex' path='docs/doc[@for="SplitContainer.OnMouseMove"]/*' />
/// <devdoc>
/// <para>Raises the <see cref='System.Windows.Forms.SplitContainer.MouseMove'/> event.</para>
/// </devdoc>
[EditorBrowsable(EditorBrowsableState.Advanced)]
protected override void OnMouseMove(MouseEventArgs e) {
base.OnMouseMove(e);
if (!IsSplitterFixed && IsSplitterMovable) {
//change cursor if default and user hasnt changed the cursor.
if (Cursor == DefaultCursor && SplitterRectangle.Contains(e.Location))
{
if (Orientation == Orientation.Vertical) {
OverrideCursor = Cursors.VSplit;
}
else {
OverrideCursor = Cursors.HSplit;
}
}
else {
OverrideCursor = null;;
}
if (splitterClick) {
int x = e.X ;
int y = e.Y ;
splitterDrag = true;
SplitMove(x, y);
if (Orientation == Orientation.Vertical) {
x = Math.Max(Math.Min(x, Width - Panel2MinSize), Panel1MinSize);
y = Math.Max(y, 0);
}
else {
y = Math.Max(Math.Min(y, Height - Panel2MinSize), Panel1MinSize);
x = Math.Max(x, 0);
}
Rectangle r = CalcSplitLine(GetSplitterDistance(e.X, e.Y), 0);
int xSplit = r.X;
int ySplit = r.Y;
SplitterCancelEventArgs se = new SplitterCancelEventArgs(x, y, xSplit, ySplit);
OnSplitterMoving(se);
if (se.Cancel) {
SplitEnd(false);
}
}
}
}
/// <include file='doc\SplitContainer.uex' path='docs/doc[@for="SplitContainer.OnMouseLeave"]/*' />
/// <devdoc>
/// <para>Raises the <see cref='System.Windows.Forms.SplitContainer.OnMouseLeave'/> event.</para>
/// </devdoc>
protected override void OnMouseLeave(EventArgs e) {
base.OnMouseLeave(e);
if (!Enabled) {
return;
}
OverrideCursor = null;
}
/// <include file='doc\SplitContainer.uex' path='docs/doc[@for="SplitContainer.OnMouseDown"]/*' />
/// <devdoc>
/// <para>Raises the <see cref='System.Windows.Forms.SplitContainer.OnMouseDown'/> event.</para>
/// </devdoc>
protected override void OnMouseDown(MouseEventArgs e) {
base.OnMouseDown(e);
//If the Panel1MinSize + Panel2MinSize < SplitContainer.Size then carry on the splitter move...
if (IsSplitterMovable && SplitterRectangle.Contains(e.Location)) {
if (!Enabled) {
return;
}
if (e.Button == MouseButtons.Left && e.Clicks == 1 && !IsSplitterFixed) {
// Focus the current splitter OnMouseDown.
splitterFocused = true;
IContainerControl c = this.ParentInternal.GetContainerControlInternal();
if (c != null) {
ContainerControl cc = c as ContainerControl;
if (cc == null) {
c.ActiveControl = this;
}
else {
cc.SetActiveControlInternal(this);
}
}
SetActiveControlInternal(null);
nextActiveControl = panel2;
SplitBegin(e.X, e.Y);
splitterClick = true;
}
}
}
/// <include file='doc\SplitContainer.uex' path='docs/doc[@for="SplitContainer.OnMouseUp"]/*' />
/// <devdoc>
/// <para>Raises the <see cref='System.Windows.Forms.SplitContainer.OnMouseUp'/> event.</para>
/// </devdoc>
protected override void OnMouseUp(MouseEventArgs e) {
base.OnMouseUp(e);
if (!Enabled) {
return;
}
if (!IsSplitterFixed && IsSplitterMovable && splitterClick) {
CaptureInternal = false;
if (splitterDrag) {
CalcSplitLine(GetSplitterDistance(e.X, e.Y), 0);
SplitEnd(true);
}
else {
SplitEnd(false);
}
splitterClick = false;
splitterDrag = false;
}
}
/// <devdoc>
/// Overrides the Control.OnMove() to synchronize the
/// splitterRect with the position of the SplitContainer.
/// </devdoc>
protected override void OnMove(EventArgs e)
{
base.OnMove(e);
SetSplitterRect(this.Orientation == Orientation.Vertical);
}
/// <include file='doc\SplitContainer.uex' path='docs/doc[@for="SplitContainer.OnPaint"]/*' />
/// <devdoc>
/// Overrides the Control.OnPaint() to focus the Splitter.
/// </devdoc>
protected override void OnPaint(PaintEventArgs e) {
base.OnPaint(e);
if (Focused) {
DrawFocus(e.Graphics,SplitterRectangle);
}
}
/// <include file='doc\SplitContainer.uex' path='docs/doc[@for="SplitContainer.OnSplitterMoving"]/*' />
/// <devdoc>
/// Inherriting classes should override this method to respond to the
/// splitterMoving event. This event occurs while the splitter is
/// being moved by the user.
/// </devdoc>
public void OnSplitterMoving(SplitterCancelEventArgs e) {
SplitterCancelEventHandler handler = (SplitterCancelEventHandler)Events[EVENT_MOVING];
if (handler != null) handler(this, e);
}
/// <include file='doc\SplitContainer.uex' path='docs/doc[@for="SplitContainer.OnSplitterMoved"]/*' />
/// <devdoc>
/// Inherriting classes should override this method to respond to the
/// splitterMoved event. This event occurs when the user finishes
/// moving the splitter.
/// </devdoc>
public void OnSplitterMoved(SplitterEventArgs e) {
SplitterEventHandler handler = (SplitterEventHandler)Events[EVENT_MOVED];
if (handler != null) handler(this, e);
}
////////////////////////////////////////////////////////////////////////////////////////////////
// //
///END DELEGATES //
// //
////////////////////////////////////////////////////////////////////////////////////////////////
/// <include file='doc\SplitContainer.uex' path='docs/doc[@for="SplitContainer.OnRightToLeftChanged"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[EditorBrowsable(EditorBrowsableState.Advanced)]
protected override void OnRightToLeftChanged(EventArgs e) {
base.OnRightToLeftChanged(e);
// pass the RightToLeft value to the Parent.
this.panel1.RightToLeft = this.RightToLeft;
this.panel2.RightToLeft = this.RightToLeft;
UpdateSplitter();
}
////////////////////////////////////////////////////////////////////////////////////////////////
// //
///START PRIVATE FUNCTIONS //
// //
////////////////////////////////////////////////////////////////////////////////////////////////
/// <devdoc>
/// Validate and set the minimum size for Panel1.
/// </devdoc>
private void ApplyPanel1MinSize(int value) {
if (value < 0) {
throw new ArgumentOutOfRangeException("Panel1MinSize", SR.GetString(SR.InvalidLowBoundArgument, "Panel1MinSize", (value).ToString(CultureInfo.CurrentCulture), "0"));
}
if (Orientation== Orientation.Vertical) {
if (DesignMode && Width != DefaultSize.Width && value + Panel2MinSize + SplitterWidth > Width) {
throw new ArgumentOutOfRangeException("Panel1MinSize", SR.GetString(SR.InvalidArgument, "Panel1MinSize", (value).ToString(CultureInfo.CurrentCulture)));
}
}
else if (Orientation == Orientation.Horizontal) {
if (DesignMode && Height != DefaultSize.Height && value + Panel2MinSize + SplitterWidth > Height) {
throw new ArgumentOutOfRangeException("Panel1MinSize", SR.GetString(SR.InvalidArgument, "Panel1MinSize", (value).ToString(CultureInfo.CurrentCulture)));
}
}
panel1MinSize = value;
if (value > SplitterDistanceInternal) {
SplitterDistanceInternal = value; //Set the Splitter Distance to the end of Panel1
}
}
/// <devdoc>
/// Validate and set the minimum size for Panel2.
/// </devdoc>
private void ApplyPanel2MinSize(int value) {
if (value < 0) {
throw new ArgumentOutOfRangeException("Panel2MinSize", SR.GetString(SR.InvalidLowBoundArgument, "Panel2MinSize", (value).ToString(CultureInfo.CurrentCulture), "0"));
}
if (Orientation == Orientation.Vertical) {
if (DesignMode && Width != DefaultSize.Width && value + Panel1MinSize + SplitterWidth > Width) {
throw new ArgumentOutOfRangeException("Panel2MinSize", SR.GetString(SR.InvalidArgument, "Panel2MinSize", (value).ToString(CultureInfo.CurrentCulture)));
}
}
else if (Orientation == Orientation.Horizontal) {
if (DesignMode && Height != DefaultSize.Height && value + Panel1MinSize + SplitterWidth > Height) {
throw new ArgumentOutOfRangeException("Panel2MinSize", SR.GetString(SR.InvalidArgument, "Panel2MinSize", (value).ToString(CultureInfo.CurrentCulture)));
}
}
panel2MinSize = value;
if (value > Panel2.Width) {
SplitterDistanceInternal = Panel2.Width + SplitterWidthInternal; //Set the Splitter Distance to the start of Panel2
}
}
/// <devdoc>
/// Validate and set the splitter width.
/// </devdoc>
private void ApplySplitterWidth(int value) {
if (value < 1) {
throw new ArgumentOutOfRangeException("SplitterWidth", SR.GetString(SR.InvalidLowBoundArgumentEx, "SplitterWidth", (value).ToString(CultureInfo.CurrentCulture), "1"));
}
if (Orientation == Orientation.Vertical) {
if (DesignMode && value + Panel1MinSize + Panel2MinSize > Width) {
throw new ArgumentOutOfRangeException("SplitterWidth", SR.GetString(SR.InvalidArgument, "SplitterWidth", (value).ToString(CultureInfo.CurrentCulture)));
}
}
else if (Orientation == Orientation.Horizontal) {
if (DesignMode && value + Panel1MinSize + Panel2MinSize > Height) {
throw new ArgumentOutOfRangeException("SplitterWidth", SR.GetString(SR.InvalidArgument, "SplitterWidth", (value).ToString(CultureInfo.CurrentCulture)));
}
}
splitterWidth = value;
UpdateSplitter();
}
/// <include file='doc\SplitContainer.uex' path='docs/doc[@for="SplitContainer.ApplySplitPosition"]/*' />
/// <devdoc>
/// Sets the split position to be the current split size. This is called
/// by splitEdit
/// </devdoc>
/// <internalonly/>
private void ApplySplitterDistance() {
using (new System.Windows.Forms.Layout.LayoutTransaction(this, this, "SplitterDistance", false)) {
SplitterDistanceInternal = splitterDistance;
}
// Refer to VsWhidbey : 467815: We need to invalidate when we have transparent backgournd.
if (this.BackColor == Color.Transparent) {
// the panel1 retains the focus rect... so Invalidate the rect ...
Invalidate();
}
if (Orientation == Orientation.Vertical) {
if (RightToLeft == RightToLeft.No) {
splitterRect.X = this.Location.X + SplitterDistanceInternal;
}
else {
splitterRect.X = this.Right - SplitterDistanceInternal - SplitterWidthInternal;
}
}
else {
splitterRect.Y = this.Location.Y + SplitterDistanceInternal;
}
}
/// <include file='doc\SplitContainer.uex' path='docs/doc[@for="SplitContainer.CalcSplitLine"]/*' />
/// <devdoc>
/// Calculates the bounding rect of the split line. minWeight refers
/// to the minimum height or width of the splitline.
/// </devdoc>
private Rectangle CalcSplitLine(int splitSize, int minWeight) {
Rectangle r = new Rectangle();
switch (Orientation) {
case Orientation.Vertical:
r.Width = SplitterWidthInternal ;
r.Height = Height;
if (r.Width < minWeight) {
r.Width = minWeight;
}
if (RightToLeft == RightToLeft.No) {
r.X = panel1.Location.X + splitSize;
}
else {
r.X = Width - splitSize - SplitterWidthInternal;
}
break;
case Orientation.Horizontal:
r.Width = Width;
r.Height = SplitterWidthInternal;
if (r.Width < minWeight) {
r.Width = minWeight;
}
r.Y = panel1.Location.Y + splitSize;
break;
}
return r;
}
/// <include file='doc\SplitContainer.uex' path='docs/doc[@for="SplitContainer.DrawSplitBar"]/*' />
/// <devdoc>
/// Draws the splitter bar at the current location. Will automatically
/// cleanup anyplace the splitter was drawn previously.
/// </devdoc>
/// <internalonly/>
private void DrawSplitBar(int mode) {
if (mode != DRAW_START && lastDrawSplit != -1) {
DrawSplitHelper(lastDrawSplit);
lastDrawSplit = -1;
}
// Bail if drawing with no old point...
//
else if (mode != DRAW_START && lastDrawSplit == -1) {
return;
}
if (mode != DRAW_END) {
if (splitMove || splitBegin) { // Splitter is moved by keys and not by mouse
DrawSplitHelper(splitterDistance);
lastDrawSplit = splitterDistance;
}
else {
DrawSplitHelper(splitterDistance);
lastDrawSplit = splitterDistance;
}
}
else {
if (lastDrawSplit != -1) {
DrawSplitHelper(lastDrawSplit);
}
lastDrawSplit = -1;
}
}
/// <devdoc>
/// <para>
/// Draws the focus rectangle if the control has focus.
///
/// </para>
/// </devdoc>
private void DrawFocus(Graphics g, Rectangle r) {
r.Inflate (-1, -1);
ControlPaint.DrawFocusRectangle(g, r, this.ForeColor, this.BackColor);
}
/// <include file='doc\SplitContainer.uex' path='docs/doc[@for="SplitContainer.DrawSplitHelper"]/*' />
/// <devdoc>
/// Draws the splitter line at the requested location. Should only be called
/// by drawSpltBar.
/// </devdoc>
/// <internalonly/>
private void DrawSplitHelper(int splitSize) {
Rectangle r = CalcSplitLine(splitSize, 3);
IntPtr parentHandle = this.Handle;
IntPtr dc = UnsafeNativeMethods.GetDCEx(new HandleRef(this, parentHandle), NativeMethods.NullHandleRef, NativeMethods.DCX_CACHE | NativeMethods.DCX_LOCKWINDOWUPDATE);
IntPtr halftone = ControlPaint.CreateHalftoneHBRUSH();
IntPtr saveBrush = SafeNativeMethods.SelectObject(new HandleRef(this, dc), new HandleRef(null, halftone));
SafeNativeMethods.PatBlt(new HandleRef(this, dc), r.X, r.Y, r.Width, r.Height, NativeMethods.PATINVERT);
SafeNativeMethods.SelectObject(new HandleRef(this, dc), new HandleRef(null, saveBrush));
SafeNativeMethods.DeleteObject(new HandleRef(null, halftone));
UnsafeNativeMethods.ReleaseDC(new HandleRef(this, parentHandle), new HandleRef(null, dc));
}
/// <include file='doc\SplitContainer.uex' path='docs/doc[@for="SplitContainer.GetSplitSize"]/*' />
/// <devdoc>
/// Calculates the split size based on the mouse position (x, y).
/// </devdoc>
/// <internalonly/>
private int GetSplitterDistance(int x, int y) {
int delta;
if (Orientation == Orientation.Vertical) {
delta = x - anchor.X;
}
else {
delta = y - anchor.Y;
}
// Negative delta - moving to the left
// Positive delta - moving to the right
int size = 0;
switch (Orientation) {
case Orientation.Vertical:
if (RightToLeft == RightToLeft.No) {
size = Math.Max(panel1.Width + delta, BORDERSIZE);
}
else {
// In RTL negative delta actually means increasing the size....
size = Math.Max(panel1.Width - delta, BORDERSIZE);
}
break;
case Orientation.Horizontal:
size = Math.Max(panel1.Height + delta, BORDERSIZE);
break;
}
if (Orientation == Orientation.Vertical) {
return Math.Max(Math.Min(size, Width - Panel2MinSize), Panel1MinSize);
}
else {
return Math.Max(Math.Min(size, Height - Panel2MinSize), Panel1MinSize);
}
}
/// <devdoc>
/// Process an arrowKey press by selecting the next control in the group
/// that the activeControl belongs to.
/// </devdoc>
/// <internalonly/>
private bool ProcessArrowKey(bool forward) {
Control group = this;
if (ActiveControl != null) {
group = ActiveControl.ParentInternal;
}
return group.SelectNextControl(ActiveControl, forward, false, false, true);
}
/// <include file='doc\SplitContainer.uex' path='docs/doc[@for="SplitContainer.RepaintSplitterRect"]/*' />
/// <devdoc>
/// Re paint SplitterRect for SplitContainer
/// </devdoc>
/// <internalonly/>
private void RepaintSplitterRect()
{
if (IsHandleCreated) {
Graphics g = this.CreateGraphicsInternal();
if (BackgroundImage != null) {
using (TextureBrush textureBrush = new TextureBrush(BackgroundImage,WrapMode.Tile)) {
g.FillRectangle(textureBrush, ClientRectangle);
}
}
else{
using (SolidBrush solidBrush = new SolidBrush(this.BackColor)) {
g.FillRectangle(solidBrush, splitterRect);
}
}
g.Dispose();
}
}
private void SetSplitterRect(bool vertical) {
if (vertical)
{
splitterRect.X = ((RightToLeft == RightToLeft.Yes) ? this.Width - splitterDistance - SplitterWidthInternal : this.Location.X + splitterDistance);
splitterRect.Y = this.Location.Y;
splitterRect.Width = SplitterWidthInternal;
splitterRect.Height = this.Height;
}
else
{
splitterRect.X = this.Location.X;
splitterRect.Y = this.Location.Y + SplitterDistanceInternal;
splitterRect.Width = this.Width;
splitterRect.Height = SplitterWidthInternal;
}
}
/// <include file='doc\SplitContainer.uex' path='docs/doc[@for="SplitContainer.ResizeSplitContainer"]/*' />
/// <devdoc>
/// Reize SplitContainer
/// </devdoc>
/// <internalonly/>
private void ResizeSplitContainer()
{
if (splitContainerScaling)
{
return;
}
panel1.SuspendLayout();
panel2.SuspendLayout();
if (this.Width == 0) { // Set the correct Width iif the WIDTH has changed to ZERO.
panel1.Size = new Size(0, panel1.Height);
panel2.Size = new Size(0, panel2.Height);
}
else if (this.Height == 0) { // Set the correct Height iif the HEIGHT has changed to ZERO.
panel1.Size = new Size(panel1.Width, 0);
panel2.Size = new Size(panel2.Width, 0);
}
else
{
if (Orientation == Orientation.Vertical)
{
// If no panel is collapsed then do the default ...
if (!CollapsedMode)
{
if (this.FixedPanel == FixedPanel.Panel1) {
panel1.Size = new Size(panelSize, Height);
panel2.Size = new Size(Math.Max(Width - panelSize - SplitterWidthInternal, Panel2MinSize), Height);
}
if (this.FixedPanel == FixedPanel.Panel2) {
panel2.Size = new Size(panelSize, Height);
splitterDistance = Math.Max(Width - panelSize - SplitterWidthInternal, Panel1MinSize);
panel1.WidthInternal = splitterDistance;
panel1.HeightInternal = Height;
}
if (this.FixedPanel == FixedPanel.None) {
if (ratioWidth != 0.0) {
splitterDistance = Math.Max((int)(Math.Floor(this.Width / ratioWidth)), Panel1MinSize);
}
panel1.WidthInternal = splitterDistance; //Default splitter distance from left or top.
panel1.HeightInternal = Height;
panel2.Size = new Size(Math.Max(Width - splitterDistance - SplitterWidthInternal, Panel2MinSize), Height);
}
if (RightToLeft == RightToLeft.No) {
panel2.Location = new Point(panel1.WidthInternal + SplitterWidthInternal, 0);
}
else {
panel1.Location = new Point(Width - panel1.WidthInternal, 0);
}
RepaintSplitterRect();
SetSplitterRect(true);
}
else
{
if (Panel1Collapsed) {
panel2.Size = this.Size;
panel2.Location = new Point(0,0);
}
else if (Panel2Collapsed) {
panel1.Size = this.Size;
panel1.Location = new Point(0,0);
}
}
}
else if (Orientation == Orientation.Horizontal) {
// If no panel is collapsed then do the default ...
if (!CollapsedMode)
{
if (this.FixedPanel == FixedPanel.Panel1) {
//Default splitter distance from left or top.
panel1.Size = new Size(Width, panelSize);
int panel2Start = panelSize + SplitterWidthInternal;
panel2.Size = new Size(Width, Math.Max(Height - panel2Start, Panel2MinSize));
panel2.Location = new Point(0,panel2Start);
}
if (this.FixedPanel == FixedPanel.Panel2) {
panel2.Size = new Size(Width, panelSize);
splitterDistance = Math.Max(Height - Panel2.Height - SplitterWidthInternal, Panel1MinSize);
panel1.HeightInternal = splitterDistance;
panel1.WidthInternal = Width;
int panel2Start = splitterDistance + SplitterWidthInternal;
panel2.Location = new Point(0, panel2Start);
}
if (this.FixedPanel == FixedPanel.None) {
//NO PANEL FIXED !!
if (ratioHeight != 0.0)
{
splitterDistance = Math.Max((int)(Math.Floor(this.Height / ratioHeight )), Panel1MinSize);
}
panel1.HeightInternal = splitterDistance; //Default splitter distance from left or top.
panel1.WidthInternal = Width;
int panel2Start = splitterDistance + SplitterWidthInternal;
panel2.Size = new Size(Width,Math.Max(Height - panel2Start, Panel2MinSize));
panel2.Location = new Point(0,panel2Start);
}
RepaintSplitterRect();
SetSplitterRect(false);
}
else
{
if (Panel1Collapsed) {
panel2.Size = this.Size;
panel2.Location = new Point(0,0);
}
else if (Panel2Collapsed) {
panel1.Size = this.Size;
panel1.Location = new Point(0,0);
}
}
}
try {
resizeCalled = true;
ApplySplitterDistance();
}
finally {
resizeCalled = false;
}
}
panel1.ResumeLayout();
panel2.ResumeLayout();
}
/// <devdoc>
/// Scales an individual control's location, size, padding and margin.
/// If the control is top level, this will not scale the control's location.
/// This does not scale children or the size of auto sized controls. You can
/// omit scaling in any direction by changing BoundsSpecified.
///
/// After the control is scaled the RequiredScaling property is set to
/// BoundsSpecified.None.
/// </devdoc>
[EditorBrowsable(EditorBrowsableState.Advanced)]
protected override void ScaleControl(SizeF factor, BoundsSpecified specified) {
try
{
splitContainerScaling = true;
base.ScaleControl(factor, specified);
float scale;
if (orientation == Orientation.Vertical) {
scale = factor.Width;
}
else {
scale = factor.Height;
}
SplitterWidth = (int)Math.Round((float)SplitterWidth * scale);
}
finally
{
splitContainerScaling = false;
}
}
/// <include file='doc\SplitContainer.uex' path='docs/doc[@for="SplitContainer.Select"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
protected override void Select(bool directed, bool forward) {
// avoid re-entrant code.
// SelectNextControl can call back on us.. and we might end up infinitely recursing.
if (selectNextControl)
{
return;
}
// continue selection iff panels have controls or tabstop is true.
if ((this.Panel1.Controls.Count > 0 || this.Panel2.Controls.Count > 0) || TabStop) {
SelectNextControlInContainer(this, forward, true, true, false);
}
else { //If this SplitContainer cannot be selected let the parent select the next in line
try {
Control parent = this.ParentInternal;
selectNextControl = true;
while (parent != null) {
if (parent.SelectNextControl(this, forward, true, true, parent.ParentInternal == null)) {
break;
}
parent = parent.ParentInternal;
}
}
finally {
selectNextControl = false;
}
}
}
/// <include file='doc\SplitContainer.uex' path='docs/doc[@for="SplitContainer.SelectNextControl"]/*' />
/// <devdoc>
/// Selects the next control following ctl.
/// </devdoc>
private bool SelectNextControlInContainer(Control ctl, bool forward, bool tabStopOnly,
bool nested, bool wrap) {
if (!Contains(ctl) || !nested && ctl.ParentInternal != this) ctl = null;
Control start = ctl;
SplitterPanel firstPanel = null;
do {
ctl = GetNextControl(ctl, forward);
SplitterPanel panel = ctl as SplitterPanel;
if (panel != null && panel.Visible) {
//We have crossed over to the second Panel...
if (firstPanel != null) {
break;
}
firstPanel = panel;
}
if (!forward && firstPanel != null && ctl.ParentInternal != firstPanel) {
//goback to start correct re-ordering ....
ctl = firstPanel;
break;
}
if (ctl == null) {
break;
}
else {
if (ctl.CanSelect && ctl.TabStop) {
if (ctl is SplitContainer)
{
((SplitContainer)ctl).Select(forward, forward);
}
else
{
SelectNextActiveControl(ctl, forward, tabStopOnly, nested, wrap);
}
return true;
}
}
} while (ctl != null);
if (ctl != null && this.TabStop) {
//we are on Splitter.....Focus it
splitterFocused = true;
IContainerControl c = this.ParentInternal.GetContainerControlInternal();
if (c != null) {
ContainerControl cc = c as ContainerControl;
if (cc == null) {
c.ActiveControl = this;
}
else {
IntSecurity.ModifyFocus.Demand();
cc.SetActiveControlInternal(this);
}
}
SetActiveControlInternal(null);
nextActiveControl = ctl;
return true;
}
else
{
// If the splitter cannot be selected select the next control in the splitter
bool selected = SelectNextControlInPanel(ctl, forward, tabStopOnly, nested, wrap);
if (!selected)
{
Control parent = this.ParentInternal;
if (parent != null)
{
try
{
selectNextControl = true;
parent.SelectNextControl(this, forward, true, true, true);
}
finally
{
selectNextControl = false;
}
}
}
}
return false;
}
/// <include file='doc\SplitContainer.uex' path='docs/doc[@for="SplitContainer.SelectNextControl"]/*' />
/// <devdoc>
/// Selects the next control following ctl.
/// </devdoc>
[SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily")]
private bool SelectNextControlInPanel(Control ctl, bool forward, bool tabStopOnly,
bool nested, bool wrap) {
if (!Contains(ctl) || !nested && ctl.ParentInternal != this) ctl = null;
Control start = ctl;
do {
ctl = GetNextControl(ctl, forward);
if (ctl == null || (ctl is SplitterPanel && ctl.Visible)) {
break;
}
else {
if (ctl.CanSelect && (!tabStopOnly || ctl.TabStop)) {
if (ctl is SplitContainer)
{
((SplitContainer)ctl).Select(forward, forward);
}
else
{
SelectNextActiveControl(ctl, forward, tabStopOnly, nested, wrap);
}
return true;
}
}
} while (ctl != null);
//If CTL == null .. we r out of the Current SplitContainer...
if (ctl == null || (ctl is SplitterPanel && !ctl.Visible)) {
callBaseVersion = true;
}
//IF the CTL == typeof(SpliterPanel) find the NEXT Control... so that we know
// we can focus the NEXT control within this SPLITCONTAINER....
else
{
ctl = GetNextControl(ctl, forward);
if (forward) {
nextActiveControl = panel2;
}
else {
if (ctl == null || !(ctl.ParentInternal.Visible)) {
callBaseVersion = true;
}
else
nextActiveControl = panel2;
}
}
return false;
}
// This will select the correct active control in the containerControl (if the passed in control is a containerControl)
private static void SelectNextActiveControl(Control ctl, bool forward, bool tabStopOnly, bool nested, bool wrap)
{
ContainerControl container = ctl as ContainerControl;
if (container != null)
{
bool correctParentActiveControl = true;
if (container.ParentInternal != null)
{
IContainerControl c = container.ParentInternal.GetContainerControlInternal();
if (c != null)
{
c.ActiveControl = container;
correctParentActiveControl = (c.ActiveControl == container);
}
}
if (correctParentActiveControl)
{
ctl.SelectNextControl(null, forward, tabStopOnly, nested, wrap);
}
}
else
{
ctl.Select();
}
}
/// <include file='doc\SplitContainer.uex' path='docs/doc[@for="SplitContainer.SetInnerMostBorder"]/*' />
/// <devdoc>
/// Selects the innermost PANEL.
/// </devdoc>
private void SetInnerMostBorder(SplitContainer sc) {
foreach(Control ctl in sc.Controls) {
bool foundChildSplitContainer = false;
if (ctl is SplitterPanel) {
foreach (Control c in ctl.Controls) {
SplitContainer c1 = c as SplitContainer;
if (c1 != null && c1.Dock == DockStyle.Fill) {
// We need to Overlay borders
// if the Children have matching BorderStyles ...
if (c1.BorderStyle != BorderStyle) {
break;
}
((SplitterPanel)ctl).BorderStyle = BorderStyle.None;
SetInnerMostBorder(c1);
foundChildSplitContainer = true;
}
}
if (!foundChildSplitContainer) {
((SplitterPanel)ctl).BorderStyle = BorderStyle;
}
}
}
}
/// <include file='doc\SplitContainer.uex' path='docs/doc[@for="SplitContainer.SetBoundsCore"]/*' />
/// <devdoc>
/// This protected override allows us to check is an unvalid value is set for Width and Height.
/// The SplitContainer would not throw on invalid Size (i.e Width and Height) settings, but would correct the error like Form
/// Say, the Panel1MinSize == 150 , Panel2MinSize == 50 and SplitterWidth == 4 and the user tries
/// to set SplitContainer.Width = 50 ... then this function would try to correct the value to 204.. instead of throwing.
/// </devdoc>
protected override void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified) {
// If we are changing Height, check if its greater than minimun else ... make it equal to the minimum
if ((specified & BoundsSpecified.Height) != BoundsSpecified.None && Orientation == Orientation.Horizontal) {
if (height < Panel1MinSize + SplitterWidthInternal + Panel2MinSize)
{
height = Panel1MinSize + SplitterWidthInternal + Panel2MinSize;
}
}
// If we are changing Width, check if its greater than minimun else ... make it equal to the minimum
if ((specified & BoundsSpecified.Width) != BoundsSpecified.None && Orientation == Orientation.Vertical) {
if (width < Panel1MinSize + SplitterWidthInternal + Panel2MinSize)
{
width = Panel1MinSize + SplitterWidthInternal + Panel2MinSize;
}
}
base.SetBoundsCore(x, y, width, height, specified);
SetSplitterRect(this.Orientation == Orientation.Vertical);
}
/// <include file='doc\SplitContainer.uex' path='docs/doc[@for="SplitContainer.SplitBegin"]/*' />
/// <devdoc>
/// Begins the splitter moving.
/// </devdoc>
/// <internalonly/>
private void SplitBegin(int x, int y) {
anchor = new Point(x, y);
splitterDistance = GetSplitterDistance(x, y);
initialSplitterDistance = splitterDistance;
initialSplitterRectangle = SplitterRectangle;
// SECREVIEW : We need a message filter to capture the ESC key
// to cancel the split action.
// The method PreFilterMessage is adorned with a LinkDemand.
// But this is not enough since this is a public unsealed class.
// We should have private implementation of the IMessageFilter
// So that we dont expose this to the classed deriving from this public class
// Refer to VsWhidbey : 423553 for more information.
IntSecurity.UnmanagedCode.Assert();
try {
if (splitContainerMessageFilter == null)
{
splitContainerMessageFilter = new SplitContainerMessageFilter(this);
}
Application.AddMessageFilter(splitContainerMessageFilter);
}
finally {
CodeAccessPermission.RevertAssert();
}
CaptureInternal = true;
DrawSplitBar(DRAW_START);
}
/// <include file='doc\SplitContainer.uex' path='docs/doc[@for="SplitContainer.SplitMove"]/*' />
/// <devdoc>
/// The split movement.
/// </devdoc>
/// <internalonly/>
private void SplitMove(int x, int y) {
int size = GetSplitterDistance(x, y);
int delta = size - initialSplitterDistance;
int mod = delta % SplitterIncrement;
if (splitterDistance != size) {
if (Orientation == Orientation.Vertical)
{
if (size + SplitterWidthInternal <= this.Width - Panel2MinSize - BORDERSIZE)
{
splitterDistance = size - mod;
}
}
else
{
if (size + SplitterWidthInternal <= this.Height - Panel2MinSize - BORDERSIZE)
{
splitterDistance = size - mod;
}
}
}
DrawSplitBar(DRAW_MOVE);
}
/// <include file='doc\SplitContainer.uex' path='docs/doc[@for="SplitContainer.SplitEnd"]/*' />
/// <devdoc>
/// Finishes the split movement.
/// </devdoc>
/// <internalonly/>
private void SplitEnd(bool accept) {
DrawSplitBar(DRAW_END);
if (splitContainerMessageFilter != null)
{
Application.RemoveMessageFilter(splitContainerMessageFilter);
splitContainerMessageFilter = null;
}
if (accept) {
ApplySplitterDistance();
}
else if (splitterDistance != initialSplitterDistance) {
splitterClick = false;
splitterDistance = SplitterDistanceInternal = initialSplitterDistance;
}
anchor = Point.Empty;
}
/// <include file='doc\SplitContainer.uex' path='docs/doc[@for="SplitContainer.UpdateSplitter"]/*' />
/// <devdoc>
/// Update Splitter
/// </devdoc>
/// <internalonly/>
private void UpdateSplitter() {
if (splitContainerScaling)
{
return;
}
panel1.SuspendLayout();
panel2.SuspendLayout();
if (Orientation == Orientation.Vertical) {
bool isRTL = RightToLeft == RightToLeft.Yes;
//NO PANEL FIXED !!
if (!CollapsedMode) {
panel1.HeightInternal = Height;
panel1.WidthInternal = splitterDistance; //Default splitter distance from left or top.
panel2.Size = new Size(Width - splitterDistance - SplitterWidthInternal, Height);
if (!isRTL) {
panel1.Location = new Point(0,0);
panel2.Location = new Point(splitterDistance + SplitterWidthInternal, 0);
}
else {
panel1.Location = new Point(Width - splitterDistance, 0);
panel2.Location = new Point(0, 0);
}
RepaintSplitterRect();
SetSplitterRect(true /*Vertical*/);
if (!resizeCalled) {
ratioWidth = ((double)(this.Width) / (double)(panel1.Width) > 0) ? (double)(this.Width) / (double)(panel1.Width) : ratioWidth;
}
}
else {
if (Panel1Collapsed) {
panel2.Size = this.Size;
panel2.Location = new Point(0,0);
}
else if (Panel2Collapsed) {
panel1.Size = this.Size;
panel1.Location = new Point(0,0);
}
// Update Ratio when the splitContainer is in CollapsedMode.
if (!resizeCalled)
{
ratioWidth = ((double)(this.Width) / (double)(splitterDistance) > 0) ? (double)(this.Width) / (double)(splitterDistance) : ratioWidth;
}
}
}
else {
//NO PANEL FIXED !!
if (!CollapsedMode) {
panel1.Location = new Point(0,0);
panel1.WidthInternal = Width;
panel1.HeightInternal = SplitterDistanceInternal; //Default splitter distance from left or top.
int panel2Start = splitterDistance + SplitterWidthInternal;
panel2.Size = new Size(Width, Height - panel2Start);
panel2.Location = new Point(0,panel2Start);
RepaintSplitterRect();
SetSplitterRect(false/*Horizontal*/);
if (!resizeCalled) {
ratioHeight = ((double)(this.Height) / (double)(panel1.Height) > 0) ? (double)(this.Height) / (double)(panel1.Height) : ratioHeight;
}
}
else {
if (Panel1Collapsed) {
panel2.Size = this.Size;
panel2.Location = new Point(0,0);
}
else if (Panel2Collapsed) {
panel1.Size = this.Size;
panel1.Location = new Point(0,0);
}
// Update Ratio when the splitContainer is in CollapsedMode.
if (!resizeCalled)
{
ratioHeight = ((double)(this.Height) / (double)(splitterDistance) > 0) ? (double)(this.Height) / (double)(splitterDistance) : ratioHeight;
}
}
}
panel1.ResumeLayout();
panel2.ResumeLayout();
}
/// <include file='doc\SplitContainer.uex' path='docs/doc[@for="SplitContainer.WmSetCursor"]/*' />
/// <devdoc>
/// Handles the WM_SETCURSOR message
/// </devdoc>
/// <internalonly/>
private void WmSetCursor(ref Message m) {
// Accessing through the Handle property has side effects that break this
// logic. You must use InternalHandle.
//
if (m.WParam == InternalHandle && ((int)m.LParam & 0x0000FFFF) == NativeMethods.HTCLIENT) {
if (OverrideCursor != null) {
Cursor.CurrentInternal = OverrideCursor;
}
else {
Cursor.CurrentInternal = Cursor;
}
}
else {
DefWndProc(ref m);
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// //
// END PRIVATE FUNCTIONS ... //
// //
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
// //
// Start PROTECTED OVERRIDE FUNCTIONS //
// //
///////////////////////////////////////////////////////////////////////////////////////////////////
internal override Rectangle GetToolNativeScreenRectangle() {
// Return splitter rectangle instead of the whole container rectangle to be consistent with the mouse ToolTip
Rectangle containerRectangle = base.GetToolNativeScreenRectangle();
Rectangle splitterRectangle = this.SplitterRectangle;
return new Rectangle(containerRectangle.X + splitterRectangle.X, containerRectangle.Y + splitterRectangle.Y, splitterRectangle.Width, splitterRectangle.Height);
}
internal override void AfterControlRemoved(Control control, Control oldParent) {
base.AfterControlRemoved(control, oldParent);
if (control is SplitContainer && control.Dock == DockStyle.Fill)
{
SetInnerMostBorder(this);
}
}
/// <include file='doc\SplitContainer.uex' path='docs/doc[@for="SplitContainer.ProcessDialogKey"]/*' />
/// <internalonly/>
/// <devdoc>
/// Processes a dialog key. Overrides Control.processDialogKey(). This
/// method implements handling of the TAB, LEFT, RIGHT, UP, and DOWN
/// keys in dialogs.
/// The method performs no processing on keys that include the ALT or
/// CONTROL modifiers. For the TAB key, the method selects the next control
/// on the form. For the arrow keys,
/// !!!
/// </devdoc>
[UIPermission(SecurityAction.LinkDemand, Window=UIPermissionWindow.AllWindows)]
protected override bool ProcessDialogKey(Keys keyData) {
#if DEBUG
Debug.WriteLineIf(ControlKeyboardRouting.TraceVerbose, "ContainerControl.ProcessDialogKey [" + keyData.ToString() + "]");
#endif
if ((keyData & (Keys.Alt | Keys.Control)) == Keys.None) {
Keys keyCode = (Keys)keyData & Keys.KeyCode;
switch (keyCode) {
case Keys.Tab:
if (ProcessTabKey((keyData & Keys.Shift) == Keys.None)) return true;
break;
case Keys.Left:
case Keys.Right:
case Keys.Up:
case Keys.Down:
if (!splitterFocused) {
if (ProcessArrowKey(keyCode == Keys.Right ||
keyCode == Keys.Down)) return true;
}
else
return false;
break;
}
}
return base.ProcessDialogKey(keyData);
}
/// <include file='doc\SplitContainer.uex' path='docs/doc[@for="SplitContainer.ProcessDialogKey"]/*' />
/// /// <devdoc>
/// This will process the TabKey for the SplitContainer. The Focus needs to Shift from controls to the Left of the Splitter
/// to the splitter and then to the controls on the right of the splitter. This override implements this Logic.
/// </devdoc>
[UIPermission(SecurityAction.LinkDemand, Window=UIPermissionWindow.AllWindows)]
protected override bool ProcessTabKey(bool forward) {
//Dont Focus the Splitter if TabStop == False or if the Splitter is Fixed !!
if (!TabStop || IsSplitterFixed) {
return base.ProcessTabKey(forward);
}
if (nextActiveControl != null) {
SetActiveControlInternal(nextActiveControl);
nextActiveControl = null;
}
if (SelectNextControlInPanel(ActiveControl, forward, true, true, true)) {
nextActiveControl = null;
splitterFocused = false;
return true;
}
else {
if (callBaseVersion) {
callBaseVersion = false;
return base.ProcessTabKey(forward);
}
else {
//We are om Splitter ......
splitterFocused = true;
IContainerControl c = this.ParentInternal.GetContainerControlInternal();
if (c != null) {
ContainerControl cc = c as ContainerControl;
if (cc == null) {
c.ActiveControl = this;
}
else {
cc.SetActiveControlInternal(this);
}
}
SetActiveControlInternal(null);
return true;
}
}
}
protected override void OnMouseCaptureChanged(EventArgs e) {
base.OnMouseCaptureChanged(e);
if (splitContainerMessageFilter != null)
{
Application.RemoveMessageFilter(splitContainerMessageFilter);
splitContainerMessageFilter = null;
}
}
/// <include file='doc\SplitContainer.uex' path='docs/doc[@for="SplitContainer.WndProc"]/*' />
/// <internalonly/>
/// <devdoc>
/// </devdoc>
[SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)]
protected override void WndProc(ref Message msg) {
switch (msg.Msg) {
case NativeMethods.WM_SETCURSOR:
WmSetCursor(ref msg);
break;
case NativeMethods.WM_SETFOCUS:
splitterFocused = true;
base.WndProc(ref msg);
break;
case NativeMethods.WM_KILLFOCUS:
splitterFocused = false;
base.WndProc(ref msg);
break;
default:
base.WndProc(ref msg);
break;
}
}
/// <include file='doc\SplitContainer.uex' path='docs/doc[@for="SplitContainer.CreateControlsInstance"]/*' />
/// <internalonly/>
/// <devdoc>
/// </devdoc>
[EditorBrowsable(EditorBrowsableState.Advanced)]
protected override Control.ControlCollection CreateControlsInstance() {
return new SplitContainerTypedControlCollection(this, typeof(SplitterPanel), /*isReadOnly*/true);
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// //
// End PROTECTED OVERRIDE FUNCTIONS //
// //
///////////////////////////////////////////////////////////////////////////////////////////////////
private class SplitContainerMessageFilter : IMessageFilter
{
private SplitContainer owner = null;
public SplitContainerMessageFilter(SplitContainer splitContainer)
{
this.owner = splitContainer;
}
/// <include file='doc\SplitContainer.uex' path='docs/doc[@for="SplitContainer.PreFilterMessage"]/*' />
/// <devdoc>
/// </devdoc>
/// <internalonly/>
[
System.Security.Permissions.SecurityPermissionAttribute(System.Security.Permissions.SecurityAction.LinkDemand, Flags=System.Security.Permissions.SecurityPermissionFlag.UnmanagedCode)
]
bool IMessageFilter.PreFilterMessage(ref Message m) {
if (m.Msg >= NativeMethods.WM_KEYFIRST && m.Msg <= NativeMethods.WM_KEYLAST) {
if ((m.Msg == NativeMethods.WM_KEYDOWN && (int)m.WParam == (int)Keys.Escape)
|| (m.Msg == NativeMethods.WM_SYSKEYDOWN)) {
//Notify that splitMOVE was reverted ..
//this is used in ONKEYUP!!
owner.splitBegin = false;
owner.SplitEnd(false);
owner.splitterClick = false;
owner.splitterDrag = false;
}
return true;
}
return false;
}
}
/// <devdoc>
/// This control collection only allows a specific type of control
/// into the controls collection. It optionally supports readonlyness.
/// </devdoc>
internal class SplitContainerTypedControlCollection : WindowsFormsUtils.TypedControlCollection {
SplitContainer owner;
public SplitContainerTypedControlCollection(Control c, Type type, bool isReadOnly): base(c, type, isReadOnly)
{
this.owner = c as SplitContainer;
}
public override void Remove(Control value) {
if (value is SplitterPanel) {
if (!owner.DesignMode) {
if (IsReadOnly) throw new NotSupportedException(SR.GetString(SR.ReadonlyControlsCollection));
}
}
base.Remove(value);
}
internal override void SetChildIndexInternal(Control child, int newIndex)
{
if (child is SplitterPanel) {
if (!owner.DesignMode) {
if (IsReadOnly) {
throw new NotSupportedException(SR.GetString(SR.ReadonlyControlsCollection));
}
}
else {
// just no-op it at DT.
return;
}
}
base.SetChildIndexInternal(child, newIndex);
}
}
}
}
|