|
//------------------------------------------------------------------------------
// <copyright file="Splitter.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.Drawing;
using System.Runtime.InteropServices;
using System.Runtime.Remoting;
using System.Security;
using System.Security.Permissions;
using System.Windows.Forms;
using System.Globalization;
/// <include file='doc\Splitter.uex' path='docs/doc[@for="Splitter"]/*' />
/// <devdoc>
/// Provides user resizing of docked elements at run time. To use a Splitter you can
/// dock any control to an edge of a container, and then dock the splitter to the same
/// edge. The splitter will then resize the control that is previous in the docking
/// order.
/// </devdoc>
[
ComVisible(true),
ClassInterface(ClassInterfaceType.AutoDispatch),
DefaultEvent("SplitterMoved"),
DefaultProperty("Dock"),
SRDescription(SR.DescriptionSplitter),
Designer("System.Windows.Forms.Design.SplitterDesigner, " + AssemblyRef.SystemDesign)
]
public class Splitter : Control {
private const int DRAW_START = 1;
private const int DRAW_MOVE = 2;
private const int DRAW_END = 3;
private const int defaultWidth = 3;
private BorderStyle borderStyle = System.Windows.Forms.BorderStyle.None;
private int minSize = 25;
private int minExtra = 25;
private Point anchor = Point.Empty;
private Control splitTarget;
private int splitSize = -1;
private int splitterThickness = 3;
private int initTargetSize;
private int lastDrawSplit = -1;
private int maxSize;
private static readonly object EVENT_MOVING = new object();
private static readonly object EVENT_MOVED = new object();
// refer to VsWhidbey : 423553 (Cannot expose IMessageFilter.PreFilterMessage through this unsealed class)
private SplitterMessageFilter splitterMessageFilter = null;
/// <include file='doc\Splitter.uex' path='docs/doc[@for="Splitter.Splitter"]/*' />
/// <devdoc>
/// Creates a new Splitter.
/// </devdoc>
public Splitter()
: base() {
SetStyle(ControlStyles.Selectable, false);
TabStop = false;
minSize = 25;
minExtra = 25;
Dock = DockStyle.Left;
}
/// <include file='doc\Splitter.uex' path='docs/doc[@for="Splitter.Anchor"]/*' />
/// <devdoc>
/// The current value of the anchor property. The anchor property
/// determines which edges of the control are anchored to the container's
/// edges.
/// </devdoc>
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never),
DefaultValue(AnchorStyles.None)]
public override AnchorStyles Anchor {
get {
return AnchorStyles.None;
}
set {
// do nothing!
}
}
/// <include file='doc\Splitter.uex' path='docs/doc[@for="Splitter.AllowDrop"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
public override bool AllowDrop {
get {
return base.AllowDrop;
}
set {
base.AllowDrop = value;
}
}
/// <include file='doc\Splitter.uex' path='docs/doc[@for="Splitter.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(defaultWidth, defaultWidth);
}
}
/// <include file='doc\Splitter.uex' path='docs/doc[@for="Splitter.DefaultCursor"]/*' />
protected override Cursor DefaultCursor {
get {
switch (Dock) {
case DockStyle.Top:
case DockStyle.Bottom:
return Cursors.HSplit;
case DockStyle.Left:
case DockStyle.Right:
return Cursors.VSplit;
}
return base.DefaultCursor;
}
}
/// <include file='doc\Splitter.uex' path='docs/doc[@for="Splitter.ForeColor"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
public override Color ForeColor {
get {
return base.ForeColor;
}
set {
base.ForeColor = value;
}
}
/// <include file='doc\Splitter.uex' path='docs/doc[@for="Splitter.ForeColorChanged"]/*' />
/// <internalonly/>
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
new public event EventHandler ForeColorChanged {
add {
base.ForeColorChanged += value;
}
remove {
base.ForeColorChanged -= value;
}
}
/// <include file='doc\Splitter.uex' path='docs/doc[@for="Splitter.BackgroundImage"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
public override Image BackgroundImage {
get {
return base.BackgroundImage;
}
set {
base.BackgroundImage = value;
}
}
/// <include file='doc\Splitter.uex' path='docs/doc[@for="Splitter.BackgroundImageChanged"]/*' />
/// <internalonly/>
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
new public event EventHandler BackgroundImageChanged {
add {
base.BackgroundImageChanged += value;
}
remove {
base.BackgroundImageChanged -= value;
}
}
/// <include file='doc\Splitter.uex' path='docs/doc[@for="Splitter.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\Splitter.uex' path='docs/doc[@for="Splitter.BackgroundImageLayoutChanged"]/*' />
/// <internalonly/>
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
new public event EventHandler BackgroundImageLayoutChanged {
add {
base.BackgroundImageLayoutChanged += value;
}
remove {
base.BackgroundImageLayoutChanged -= value;
}
}
/// <include file='doc\Splitter.uex' path='docs/doc[@for="Splitter.Font"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
public override Font Font {
get {
return base.Font;
}
set {
base.Font = value;
}
}
/// <include file='doc\Splitter.uex' path='docs/doc[@for="Splitter.FontChanged"]/*' />
/// <internalonly/>
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
new public event EventHandler FontChanged {
add {
base.FontChanged += value;
}
remove {
base.FontChanged -= value;
}
}
/// <include file='doc\Splitter.uex' path='docs/doc[@for="Splitter.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;
UpdateStyles();
}
}
}
/// <include file='doc\Splitter.uex' path='docs/doc[@for="Splitter.CreateParams"]/*' />
/// <devdoc>
/// Returns the parameters needed to create the handle. Inheriting classes
/// can override this to provide extra functionality. They should not,
/// however, forget to call base.getCreateParams() first to get the struct
/// filled up with the basic info.
/// </devdoc>
protected override CreateParams CreateParams {
[SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)]
get {
CreateParams cp = base.CreateParams;
cp.ExStyle &= (~NativeMethods.WS_EX_CLIENTEDGE);
cp.Style &= (~NativeMethods.WS_BORDER);
switch (borderStyle) {
case BorderStyle.Fixed3D:
cp.ExStyle |= NativeMethods.WS_EX_CLIENTEDGE;
break;
case BorderStyle.FixedSingle:
cp.Style |= NativeMethods.WS_BORDER;
break;
}
return cp;
}
}
/// <include file='doc\Splitter.uex' path='docs/doc[@for="Splitter.DefaultImeMode"]/*' />
protected override ImeMode DefaultImeMode {
get {
return ImeMode.Disable;
}
}
/// <include file='doc\Splitter.uex' path='docs/doc[@for="Splitter.Dock"]/*' />
/// <devdoc>
/// </devdoc>
/// <internalonly/>
[
Localizable(true),
DefaultValue(DockStyle.Left)
]
public override DockStyle Dock {
get { return base.Dock;}
set {
if (!(value == DockStyle.Top || value == DockStyle.Bottom || value == DockStyle.Left || value == DockStyle.Right)) {
throw new ArgumentException(SR.GetString(SR.SplitterInvalidDockEnum));
}
int requestedSize = splitterThickness;
base.Dock = value;
switch (Dock) {
case DockStyle.Top:
case DockStyle.Bottom:
if (splitterThickness != -1) {
Height = requestedSize;
}
break;
case DockStyle.Left:
case DockStyle.Right:
if (splitterThickness != -1) {
Width = requestedSize;
}
break;
}
}
}
/// <include file='doc\Splitter.uex' path='docs/doc[@for="Splitter.Horizontal"]/*' />
/// <devdoc>
/// Determines if the splitter is horizontal.
/// </devdoc>
/// <internalonly/>
private bool Horizontal {
get {
DockStyle dock = Dock;
return dock == DockStyle.Left || dock == DockStyle.Right;
}
}
/// <include file='doc\Splitter.uex' path='docs/doc[@for="Splitter.ImeMode"]/*' />
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
new public ImeMode ImeMode {
get {
return base.ImeMode;
}
set {
base.ImeMode = value;
}
}
/// <include file='doc\Splitter.uex' path='docs/doc[@for="Splitter.ImeModeChanged"]/*' />
/// <internalonly/>
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
public new event EventHandler ImeModeChanged {
add {
base.ImeModeChanged += value;
}
remove {
base.ImeModeChanged -= value;
}
}
/// <include file='doc\Splitter.uex' path='docs/doc[@for="Splitter.MinExtra"]/*' />
/// <devdoc>
/// The minExtra is this minimum size (in pixels) of the remaining
/// area of the container. This area is center of the container that
/// is not occupied by edge docked controls, this is the are that
/// would be used for any fill docked control.
/// </devdoc>
[
SRCategory(SR.CatBehavior),
Localizable(true),
DefaultValue(25),
SRDescription(SR.SplitterMinExtraDescr)
]
public int MinExtra {
get {
return minExtra;
}
set {
if (value < 0) value = 0;
minExtra = value;
}
}
/// <include file='doc\Splitter.uex' path='docs/doc[@for="Splitter.MinSize"]/*' />
/// <devdoc>
/// The minSize is the minimum size (in pixels) of the target of the
/// splitter. The target of a splitter is always the control adjacent
/// to the splitter, just prior in the dock order.
/// </devdoc>
[
SRCategory(SR.CatBehavior),
Localizable(true),
DefaultValue(25),
SRDescription(SR.SplitterMinSizeDescr)
]
public int MinSize {
get {
return minSize;
}
set {
if (value < 0) value = 0;
minSize = value;
}
}
/// <include file='doc\Splitter.uex' path='docs/doc[@for="Splitter.SplitPosition"]/*' />
/// <devdoc>
/// The position of the splitter. If the splitter is not bound
/// to a control, SplitPosition will be -1.
/// </devdoc>
[
SRCategory(SR.CatLayout),
Browsable(false),
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
SRDescription(SR.SplitterSplitPositionDescr)
]
public int SplitPosition {
get {
if (splitSize == -1) splitSize = CalcSplitSize();
return splitSize;
}
set {
// calculate maxSize and other bounding conditions
SplitData spd = CalcSplitBounds();
// this is not an else-if to handle the maxSize < minSize case...
// ie. we give minSize priority over maxSize...
if (value > maxSize) value = maxSize;
if (value < minSize) value = minSize;
// if (value == splitSize) return; -- do we need this check?
splitSize = value;
DrawSplitBar(DRAW_END);
if (spd.target == null) {
splitSize = -1;
return;
}
Rectangle bounds = spd.target.Bounds;
switch (Dock) {
case DockStyle.Top:
bounds.Height = value;
break;
case DockStyle.Bottom:
bounds.Y += bounds.Height - splitSize;
bounds.Height = value;
break;
case DockStyle.Left:
bounds.Width = value;
break;
case DockStyle.Right:
bounds.X += bounds.Width - splitSize;
bounds.Width = value;
break;
}
spd.target.Bounds = bounds;
Application.DoEvents();
OnSplitterMoved(new SplitterEventArgs(Left, Top, (Left + bounds.Width / 2), (Top + bounds.Height / 2)));
}
}
/// <include file='doc\Splitter.uex' path='docs/doc[@for="Splitter.TabStop"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
new public bool TabStop {
get {
return base.TabStop;
}
set {
base.TabStop = value;
}
}
/// <include file='doc\Splitter.uex' path='docs/doc[@for="Splitter.TabStopChanged"]/*' />
/// <internalonly/>
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
new public event EventHandler TabStopChanged {
add {
base.TabStopChanged += value;
}
remove {
base.TabStopChanged -= value;
}
}
/// <include file='doc\Splitter.uex' path='docs/doc[@for="Splitter.Text"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[
Browsable(false), EditorBrowsable(EditorBrowsableState.Never),
Bindable(false),
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)
]
public override string Text {
get {
return base.Text;
}
set {
base.Text = value;
}
}
/// <include file='doc\Splitter.uex' path='docs/doc[@for="Splitter.TextChanged"]/*' />
/// <internalonly/>
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
new public event EventHandler TextChanged {
add {
base.TextChanged += value;
}
remove {
base.TextChanged -= value;
}
}
/// <include file='doc\Splitter.uex' path='docs/doc[@for="Splitter.Enter"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
public new event EventHandler Enter {
add {
base.Enter += value;
}
remove {
base.Enter -= value;
}
}
/// <include file='doc\Splitter.uex' path='docs/doc[@for="Splitter.KeyUp"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
public new event KeyEventHandler KeyUp {
add {
base.KeyUp += value;
}
remove {
base.KeyUp -= value;
}
}
/// <include file='doc\Splitter.uex' path='docs/doc[@for="Splitter.KeyDown"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
public new event KeyEventHandler KeyDown {
add {
base.KeyDown += value;
}
remove {
base.KeyDown -= value;
}
}
/// <include file='doc\Splitter.uex' path='docs/doc[@for="Splitter.KeyPress"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
public new event KeyPressEventHandler KeyPress {
add {
base.KeyPress += value;
}
remove {
base.KeyPress -= value;
}
}
/// <include file='doc\Splitter.uex' path='docs/doc[@for="Splitter.Leave"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
public new event EventHandler Leave {
add {
base.Leave += value;
}
remove {
base.Leave -= value;
}
}
/// <include file='doc\Splitter.uex' path='docs/doc[@for="Splitter.SplitterMoving"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[SRCategory(SR.CatBehavior), SRDescription(SR.SplitterSplitterMovingDescr)]
public event SplitterEventHandler SplitterMoving {
add {
Events.AddHandler(EVENT_MOVING, value);
}
remove {
Events.RemoveHandler(EVENT_MOVING, value);
}
}
/// <include file='doc\Splitter.uex' path='docs/doc[@for="Splitter.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\Splitter.uex' path='docs/doc[@for="Splitter.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) {
DrawSplitHelper(splitSize);
lastDrawSplit = splitSize;
}
else {
if (lastDrawSplit != -1) {
DrawSplitHelper(lastDrawSplit);
}
lastDrawSplit = -1;
}
}
/// <include file='doc\Splitter.uex' path='docs/doc[@for="Splitter.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 = Bounds;
Rectangle bounds = splitTarget.Bounds;
switch (Dock) {
case DockStyle.Top:
if (r.Height < minWeight) r.Height = minWeight;
r.Y = bounds.Y + splitSize;
break;
case DockStyle.Bottom:
if (r.Height < minWeight) r.Height = minWeight;
r.Y = bounds.Y + bounds.Height - splitSize - r.Height;
break;
case DockStyle.Left:
if (r.Width < minWeight) r.Width = minWeight;
r.X = bounds.X + splitSize;
break;
case DockStyle.Right:
if (r.Width < minWeight) r.Width = minWeight;
r.X = bounds.X + bounds.Width - splitSize - r.Width;
break;
}
return r;
}
/// <include file='doc\Splitter.uex' path='docs/doc[@for="Splitter.CalcSplitSize"]/*' />
/// <devdoc>
/// Calculates the current size of the splitter-target.
/// </devdoc>
/// <internalonly/>
private int CalcSplitSize() {
Control target = FindTarget();
if (target == null) return -1;
Rectangle r = target.Bounds;
switch (Dock) {
case DockStyle.Top:
case DockStyle.Bottom:
return r.Height;
case DockStyle.Left:
case DockStyle.Right:
return r.Width;
default:
return -1; // belts & braces
}
}
/// <include file='doc\Splitter.uex' path='docs/doc[@for="Splitter.CalcSplitBounds"]/*' />
/// <devdoc>
/// Calculates the bounding criteria for the splitter.
/// </devdoc>
/// <internalonly/>
private SplitData CalcSplitBounds() {
SplitData spd = new SplitData();
Control target = FindTarget();
spd.target = target;
if (target != null) {
switch (target.Dock) {
case DockStyle.Left:
case DockStyle.Right:
initTargetSize = target.Bounds.Width;
break;
case DockStyle.Top:
case DockStyle.Bottom:
initTargetSize = target.Bounds.Height;
break;
}
Control parent = ParentInternal;
Control.ControlCollection children = parent.Controls;
int count = children.Count;
int dockWidth = 0, dockHeight = 0;
for (int i = 0; i < count; i++) {
Control ctl = children[i];
if (ctl != target) {
switch (((Control)ctl).Dock) {
case DockStyle.Left:
case DockStyle.Right:
dockWidth += ctl.Width;
break;
case DockStyle.Top:
case DockStyle.Bottom:
dockHeight += ctl.Height;
break;
}
}
}
Size clientSize = parent.ClientSize;
if (Horizontal) {
maxSize = clientSize.Width - dockWidth - minExtra;
}
else {
maxSize = clientSize.Height - dockHeight - minExtra;
}
spd.dockWidth = dockWidth;
spd.dockHeight = dockHeight;
}
return spd;
}
/// <include file='doc\Splitter.uex' path='docs/doc[@for="Splitter.DrawSplitHelper"]/*' />
/// <devdoc>
/// Draws the splitter line at the requested location. Should only be called
/// by drawSpltBar.
/// </devdoc>
/// <internalonly/>
private void DrawSplitHelper(int splitSize) {
if (splitTarget == null) {
return;
}
Rectangle r = CalcSplitLine(splitSize, 3);
IntPtr parentHandle = ParentInternal.Handle;
IntPtr dc = UnsafeNativeMethods.GetDCEx(new HandleRef(ParentInternal, parentHandle), NativeMethods.NullHandleRef, NativeMethods.DCX_CACHE | NativeMethods.DCX_LOCKWINDOWUPDATE);
IntPtr halftone = ControlPaint.CreateHalftoneHBRUSH();
IntPtr saveBrush = SafeNativeMethods.SelectObject(new HandleRef(ParentInternal, dc), new HandleRef(null, halftone));
SafeNativeMethods.PatBlt(new HandleRef(ParentInternal, dc), r.X, r.Y, r.Width, r.Height, NativeMethods.PATINVERT);
SafeNativeMethods.SelectObject(new HandleRef(ParentInternal, dc), new HandleRef(null, saveBrush));
SafeNativeMethods.DeleteObject(new HandleRef(null, halftone));
UnsafeNativeMethods.ReleaseDC(new HandleRef(ParentInternal, parentHandle), new HandleRef(null, dc));
}
/// <devdoc>
/// Raises a splitter event
/// </devdoc>
/// <internalonly/>
/* No one seems to be calling this, so it is okay to comment it out
private void RaiseSplitterEvent(object key, SplitterEventArgs spevent) {
SplitterEventHandler handler = (SplitterEventHandler)Events[key];
if (handler != null) handler(this, spevent);
}
*/
/// <include file='doc\Splitter.uex' path='docs/doc[@for="Splitter.FindTarget"]/*' />
/// <devdoc>
/// Finds the target of the splitter. The target of the splitter is the
/// control that is "outside" or the splitter. For example, if the splitter
/// is docked left, the target is the control that is just to the left
/// of the splitter.
/// </devdoc>
/// <internalonly/>
private Control FindTarget() {
Control parent = ParentInternal;
if (parent == null) return null;
Control.ControlCollection children = parent.Controls;
int count = children.Count;
DockStyle dock = Dock;
for (int i = 0; i < count; i++) {
Control target = children[i];
if (target != this) {
switch (dock) {
case DockStyle.Top:
if (target.Bottom == Top) return(Control)target;
break;
case DockStyle.Bottom:
if (target.Top == Bottom) return(Control)target;
break;
case DockStyle.Left:
if (target.Right == Left) return(Control)target;
break;
case DockStyle.Right:
if (target.Left == Right) return(Control)target;
break;
}
}
}
return null;
}
/// <include file='doc\Splitter.uex' path='docs/doc[@for="Splitter.GetSplitSize"]/*' />
/// <devdoc>
/// Calculates the split size based on the mouse position (x, y).
/// </devdoc>
/// <internalonly/>
private int GetSplitSize(int x, int y) {
int delta;
if (Horizontal) {
delta = x - anchor.X;
}
else {
delta = y - anchor.Y;
}
int size = 0;
switch (Dock) {
case DockStyle.Top:
size = splitTarget.Height + delta;
break;
case DockStyle.Bottom:
size = splitTarget.Height - delta;
break;
case DockStyle.Left:
size = splitTarget.Width + delta;
break;
case DockStyle.Right:
size = splitTarget.Width - delta;
break;
}
return Math.Max(Math.Min(size, maxSize), minSize);
}
/// <include file='doc\Splitter.uex' path='docs/doc[@for="Splitter.OnKeyDown"]/*' />
/// <devdoc>
/// </devdoc>
/// <internalonly/>
protected override void OnKeyDown(KeyEventArgs e) {
base.OnKeyDown(e);
if (splitTarget != null && e.KeyCode == Keys.Escape) {
SplitEnd(false);
}
}
/// <include file='doc\Splitter.uex' path='docs/doc[@for="Splitter.OnMouseDown"]/*' />
/// <devdoc>
/// </devdoc>
/// <internalonly/>
protected override void OnMouseDown(MouseEventArgs e) {
base.OnMouseDown(e);
if (e.Button == MouseButtons.Left && e.Clicks == 1) {
SplitBegin(e.X, e.Y);
}
}
/// <include file='doc\Splitter.uex' path='docs/doc[@for="Splitter.OnMouseMove"]/*' />
/// <devdoc>
/// </devdoc>
/// <internalonly/>
protected override void OnMouseMove(MouseEventArgs e) {
base.OnMouseMove(e);
if (splitTarget != null) {
int x = e.X + Left;
int y = e.Y + Top;
Rectangle r = CalcSplitLine(GetSplitSize(e.X, e.Y), 0);
int xSplit = r.X;
int ySplit = r.Y;
OnSplitterMoving(new SplitterEventArgs(x, y, xSplit, ySplit));
}
}
/// <include file='doc\Splitter.uex' path='docs/doc[@for="Splitter.OnMouseUp"]/*' />
/// <devdoc>
/// </devdoc>
/// <internalonly/>
protected override void OnMouseUp(MouseEventArgs e) {
base.OnMouseUp(e);
if (splitTarget != null) {
int x = e.X + Left;
int y = e.Y + Top;
Rectangle r = CalcSplitLine(GetSplitSize(e.X, e.Y), 0);
int xSplit = r.X;
int ySplit = r.Y;
SplitEnd(true);
}
}
/// <include file='doc\Splitter.uex' path='docs/doc[@for="Splitter.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>
protected virtual void OnSplitterMoving(SplitterEventArgs sevent) {
SplitterEventHandler handler = (SplitterEventHandler)Events[EVENT_MOVING];
if (handler != null) handler(this,sevent);
if (splitTarget != null) {
SplitMove(sevent.SplitX, sevent.SplitY);
}
}
/// <include file='doc\Splitter.uex' path='docs/doc[@for="Splitter.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>
protected virtual void OnSplitterMoved(SplitterEventArgs sevent) {
SplitterEventHandler handler = (SplitterEventHandler)Events[EVENT_MOVED];
if (handler != null) handler(this,sevent);
if (splitTarget != null) {
SplitMove(sevent.SplitX, sevent.SplitY);
}
}
/// <include file='doc\Splitter.uex' path='docs/doc[@for="Splitter.SetBoundsCore"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
protected override void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified) {
if (Horizontal) {
if (width < 1) {
width = 3;
}
splitterThickness = width;
}
else {
if (height < 1) {
height = 3;
}
splitterThickness = height;
}
base.SetBoundsCore(x, y, width, height, specified);
}
/// <include file='doc\Splitter.uex' path='docs/doc[@for="Splitter.SplitBegin"]/*' />
/// <devdoc>
/// Begins the splitter moving.
/// </devdoc>
/// <internalonly/>
private void SplitBegin(int x, int y) {
SplitData spd = CalcSplitBounds();
if (spd.target != null && (minSize < maxSize)) {
anchor = new Point(x, y);
splitTarget = spd.target;
splitSize = GetSplitSize(x, y);
// SECREVIEW : We need a message filter to capture the ESC key
// : to cancel the split action.
// : The method PreFilterMessage is adorned with a LinkDemand.
//
IntSecurity.UnmanagedCode.Assert();
try {
if (splitterMessageFilter != null)
{
splitterMessageFilter = new SplitterMessageFilter(this);
}
Application.AddMessageFilter(splitterMessageFilter);
}
finally {
CodeAccessPermission.RevertAssert();
}
CaptureInternal = true;
DrawSplitBar(DRAW_START);
}
}
/// <include file='doc\Splitter.uex' path='docs/doc[@for="Splitter.SplitEnd"]/*' />
/// <devdoc>
/// Finishes the split movement.
/// </devdoc>
/// <internalonly/>
private void SplitEnd(bool accept) {
DrawSplitBar(DRAW_END);
splitTarget = null;
CaptureInternal = false;
if (splitterMessageFilter != null)
{
Application.RemoveMessageFilter(splitterMessageFilter);
splitterMessageFilter = null;
}
if (accept) {
ApplySplitPosition();
}
else if (splitSize != initTargetSize) {
SplitPosition = initTargetSize;
}
anchor = Point.Empty;
}
/// <include file='doc\Splitter.uex' path='docs/doc[@for="Splitter.ApplySplitPosition"]/*' />
/// <devdoc>
/// Sets the split position to be the current split size. This is called
/// by splitEdit
/// </devdoc>
/// <internalonly/>
private void ApplySplitPosition() {
SplitPosition = splitSize;
}
/// <include file='doc\Splitter.uex' path='docs/doc[@for="Splitter.SplitMove"]/*' />
/// <devdoc>
/// Moves the splitter line to the splitSize for the mouse position
/// (x, y).
/// </devdoc>
/// <internalonly/>
private void SplitMove(int x, int y) {
int size = GetSplitSize(x-Left+anchor.X, y-Top+anchor.Y);
if (splitSize != size) {
splitSize = size;
DrawSplitBar(DRAW_MOVE);
}
}
/// <include file='doc\Splitter.uex' path='docs/doc[@for="Splitter.ToString"]/*' />
/// <devdoc>
/// Returns a string representation for this control.
/// </devdoc>
/// <internalonly/>
public override string ToString() {
string s = base.ToString();
return s + ", MinExtra: " + MinExtra.ToString(CultureInfo.CurrentCulture) + ", MinSize: " + MinSize.ToString(CultureInfo.CurrentCulture);
}
/// <include file='doc\Splitter.uex' path='docs/doc[@for="Splitter.SplitData"]/*' />
/// <devdoc>
/// Return value holder...
/// </devdoc>
private class SplitData {
public int dockWidth = -1;
public int dockHeight = -1;
internal Control target;
}
private class SplitterMessageFilter : IMessageFilter
{
private Splitter owner = null;
public SplitterMessageFilter(Splitter splitter)
{
this.owner = splitter;
}
/// <include file='doc\SplitterMessageFilter.uex' path='docs/doc[@for="SplitterMessageFilter.PreFilterMessage"]/*' />
/// <devdoc>
/// </devdoc>
/// <internalonly/>
[
System.Security.Permissions.SecurityPermissionAttribute(System.Security.Permissions.SecurityAction.LinkDemand, Flags=System.Security.Permissions.SecurityPermissionFlag.UnmanagedCode),
]
public bool PreFilterMessage(ref Message m) {
if (m.Msg >= NativeMethods.WM_KEYFIRST && m.Msg <= NativeMethods.WM_KEYLAST) {
if (m.Msg == NativeMethods.WM_KEYDOWN && unchecked((int)(long)m.WParam) == (int)Keys.Escape) {
owner.SplitEnd(false);
}
return true;
}
return false;
}
}
}
}
|