// Written by Christopher E. Miller
// See the included license.txt for copyright and license details.


///
module dfl.control;

private import dfl.base, dfl.form, dfl.drawing;
private import dfl.application, dfl.event, dfl.label;
private import dfl.collections;
private import core.memory;

private import dfl.internal.dlib, dfl.internal.clib;
private import dfl.internal.winapi, dfl.internal.wincom;
private import dfl.internal.utf;
private import dfl.internal.com;

version(NO_DRAG_DROP)
	version = DFL_NO_DRAG_DROP;

version(DFL_NO_DRAG_DROP)
{
}
else
{
	private import dfl.data;
}

version(DFL_NO_MENUS)
{
}
else
{
	private import dfl.menu;
}

//version = RADIO_GROUP_LAYOUT;
version = DFL_NO_ZOMBIE_FORM;


private int GET_X_LPARAM(LPARAM lparam) pure
{
	return cast(int)cast(short)LOWORD(lparam);
}

private int GET_Y_LPARAM(in LPARAM lparam) pure
{
	return cast(int)cast(short)HIWORD(lparam);
}


///
enum AnchorStyles: ubyte
{
	NONE = 0, ///
	TOP = 1, /// ditto
	BOTTOM = 2, /// ditto
	LEFT = 4, /// ditto
	RIGHT = 8, /// ditto
	
	/+
	// Extras:
	VERTICAL = TOP | BOTTOM,
	HORIZONTAL = LEFT | RIGHT,
	ALL = TOP | BOTTOM | LEFT | RIGHT,
	DEFAULT = TOP | LEFT,
	TOP_LEFT = TOP | LEFT,
	TOP_RIGHT = TOP | RIGHT,
	BOTTOM_LEFT = BOTTOM | LEFT,
	BOTTOM_RIGHT = BOTTOM | RIGHT,
	+/
}


/// Flags for setting control bounds.
enum BoundsSpecified: ubyte
{
	NONE = 0, ///
	X = 1, /// ditto
	Y = 2, /// ditto
	LOCATION = 1 | 2, /// ditto
	WIDTH = 4, /// ditto
	HEIGHT = 8, /// ditto
	SIZE = 4 | 8, /// ditto
	ALL = 1 | 2 | 4 | 8, /// ditto
}


/// Layout docking style.
enum DockStyle: ubyte
{
	NONE, ///
	BOTTOM, ///
	FILL, ///
	LEFT, ///
	RIGHT, ///
	TOP, ///
}


private
{
	struct GetZIndex
	{
		Control find;
		int index = -1;
		private int _tmp = 0;
	}
	
	
	extern(Windows) BOOL getZIndexCallback(HWND hwnd, LPARAM lparam)
	{
		GetZIndex* gzi = cast(GetZIndex*)lparam;
		if(hwnd == gzi.find.hwnd)
		{
			gzi.index = gzi._tmp;
			return FALSE; // Stop, found it.
		}
		
		Control ctrl;
		ctrl = Control.fromHandle(hwnd);
		if(ctrl && ctrl.parent is gzi.find.parent)
		{
			gzi._tmp++;
		}
		
		return TRUE; // Keep looking.
	}
}


/// Effect flags for drag/drop operations.
enum DragDropEffects: DWORD
{
	NONE = 0, ///
	COPY = 1, /// ditto
	MOVE = 2, /// ditto
	LINK = 4, /// ditto
	SCROLL = 0x80000000, /// ditto
	ALL = COPY | MOVE | LINK | SCROLL, /// ditto
}


/// Drag/drop action.
enum DragAction: HRESULT
{
	CONTINUE = S_OK, ///
	CANCEL = DRAGDROP_S_CANCEL, /// ditto
	DROP = DRAGDROP_S_DROP, /// ditto
}


/// Effect flags for drag/drop operations with key states.
enum DragDropKeyStates
{
	NONE = 0,
	LEFT_MOUSE_BUTTON = 1,
	RIGHT_MOUSE_BUTTON = 2,
	SHIFT_KEY = 4,
	CONTROL_KEY = 8,
	MIDDLE_MOUSE_BUTTON = 16,
	ALT_KEY = 32
}


// Flags.
deprecated enum UICues: uint
{
	NONE = 0,
	SHOW_FOCUS = 1,
	SHOW_KEYBOARD = 2,
	SHOWN = SHOW_FOCUS | SHOW_KEYBOARD,
	CHANGE_FOCUS = 4,
	CHANGE_KEYBOARD = 8, // Key mnemonic underline cues are on.
	CHANGED = CHANGE_FOCUS | CHANGE_KEYBOARD,
}


// May be OR'ed together.
/// Style flags of a control.
enum ControlStyles: uint
{
	NONE = 0, ///
	
	CONTAINER_CONTROL =                0x1, /// ditto
	
	// TODO: implement.
	USER_PAINT =                       0x2, /// ditto
	
	OPAQUE =                           0x4, /// ditto
	RESIZE_REDRAW =                    0x10, /// ditto
	FIXED_WIDTH =                      0x20, /// ditto (Implemented for TrackBar)
	FIXED_HEIGHT =                     0x40, /// ditto (Implemented for TrackBar)
	STANDARD_CLICK =                   0x100, /// ditto
	SELECTABLE =                       0x200, /// ditto
	
	// TODO: implement.
	USER_MOUSE =                       0x400, ///  ditto
	
	//SUPPORTS_TRANSPARENT_BACK_COLOR =  0x800, // Only if USER_PAINT and parent is derived from Control. TODO: implement.
	STANDARD_DOUBLE_CLICK =            0x1000, /// ditto
	ALL_PAINTING_IN_WM_PAINT =         0x2000, /// ditto
	CACHE_TEXT =                       0x4000, /// ditto
	ENABLE_NOTIFY_MESSAGE =            0x8000, // deprecated. Calls onNotifyMessage() for every message.
	//DOUBLE_BUFFER =                    0x10000, // TODO: implement.
	//OPTIMIZED_DOUBLE_BUFFER =          0x20000, // TODO: implement.
	USE_TEXT_FOR_ACCESSIBILITY =       0x40000, /// ditto
}


/// Control creation parameters.
struct CreateParams
{
	Dstring className; ///
	Dstring caption; /// ditto
	void* param; /// ditto
	HWND parent; /// ditto
	HMENU menu; /// ditto
	HINSTANCE inst; /// ditto
	int x; /// ditto
	int y; /// ditto
	int width; /// ditto
	int height; /// ditto
	DWORD classStyle; /// ditto
	DWORD exStyle; /// ditto
	DWORD style; /// ditto
}


deprecated class UICuesEventArgs: EventArgs
{
	deprecated:
	
	this(UICues uic)
	{
		chg = uic;
	}
	
	
	final UICues changed() // getter
	{
		return chg;
	}
	
	
	final bool changeFocus()
	{
		return (chg & UICues.CHANGE_FOCUS) != 0;
	}
	
	
	final bool changeKeyboard()
	{
		return (chg & UICues.CHANGE_KEYBOARD) != 0;
	}
	
	
	final bool showFocus()
	{
		return (chg & UICues.SHOW_FOCUS) != 0;
	}
	
	
	final bool showKeyboard()
	{
		return (chg & UICues.SHOW_KEYBOARD) != 0;
	}
	
	
	private:
	UICues chg;
}


///
class ControlEventArgs: EventArgs
{
	///
	this(Control ctrl)
	{
		this.ctrl = ctrl;
	}
	
	
	///
	final @property Control control() // getter
	{
		return ctrl;
	}
	
	
	private:
	Control ctrl;
}


///
class HelpEventArgs: EventArgs
{
	///
	this(Point mousePos)
	{
		mpos = mousePos;
	}
	
	
	///
	final @property void handled(bool byes) // setter
	{
		hand = byes;
	}
	
	/// ditto
	final @property bool handled() // getter
	{
		return hand;
	}
	
	
	///
	final @property Point mousePos() // getter
	{
		return mpos;
	}
	
	
	private:
	Point mpos;
	bool hand = false;
}


///
class InvalidateEventArgs: EventArgs
{
	///
	this(Rect invalidRect)
	{
		ir = invalidRect;
	}
	
	
	///
	final @property Rect invalidRect() // getter
	{
		return ir;
	}
	
	
	private:
	Rect ir;
}


// ///
// New dimensions before resizing.
deprecated class BeforeResizeEventArgs: EventArgs
{
	deprecated:
	
	///
	this(int width, int height)
	{
		this.w = width;
		this.h = height;
	}
	
	
	///
	void width(int cx) // setter
	{
		w = cx;
	}
	
	/// ditto
	int width() // getter
	{
		return w;
	}
	
	
	///
	void height(int cy) // setter
	{
		h = cy;
	}
	
	/// ditto
	int height() // getter
	{
		return h;
	}
	
	
	private:
	int w, h;
}


///
class LayoutEventArgs: EventArgs
{
	///
	this(Control affectedControl)
	{
		ac = affectedControl;
	}
	
	
	///
	final @property Control affectedControl() // getter
	{
		return ac;
	}
	
	
	private:
	Control ac;
}


version(DFL_NO_DRAG_DROP) {} else
{
	///
	class DragEventArgs: EventArgs
	{
		///
		this(dfl.data.IDataObject dataObj, int keyState, int x, int y,
			DragDropEffects allowedEffect, DragDropEffects effect)
		{
			_dobj = dataObj;
			_keyState = keyState;
			_x = x;
			_y = y;
			_allowedEffect = allowedEffect;
			_effect = effect;
		}
		
		
		///
		final @property DragDropEffects allowedEffect() // getter
		{
			return _allowedEffect;
		}
		
		
		///
		final @property void effect(DragDropEffects newEffect) // setter
		{
			_effect = newEffect;
		}
		
		
		/// ditto
		final @property DragDropEffects effect() // getter
		{
			return _effect;
		}
		
		
		///
		final @property dfl.data.IDataObject data() // getter
		{
			return _dobj;
		}
		
		
		///
		// State of ctrl, alt, shift, and mouse buttons.
		final @property int keyState() // getter
		{
			return _keyState;
		}
		
		
		///
		final @property int x() // getter
		{
			return _x;
		}
		
		
		///
		final @property int y() // getter
		{
			return _y;
		}
		
		
		private:
		dfl.data.IDataObject _dobj;
		int _keyState;
		int _x, _y;
		DragDropEffects _allowedEffect, _effect;
	}
	
	
	///
	class GiveFeedbackEventArgs: EventArgs
	{
		///
		this(DragDropEffects effect, bool useDefaultCursors)
		{
			_effect = effect;
			udefcurs = useDefaultCursors;
		}
		
		
		///
		final @property DragDropEffects effect() // getter
		{
			return _effect;
		}
		
		
		///
		final @property void useDefaultCursors(bool byes) // setter
		{
			udefcurs = byes;
		}
		
		/// ditto
		final @property bool useDefaultCursors() // getter
		{
			return udefcurs;
		}
		
		
		private:
		DragDropEffects _effect;
		bool udefcurs;
	}
	
	
	///
	class QueryContinueDragEventArgs: EventArgs
	{
		///
		this(int keyState, bool escapePressed, DragAction action)
		{
			_keyState = keyState;
			escp = escapePressed;
			_action = action;
		}
		
		
		///
		final @property void action(DragAction newAction) // setter
		{
			_action = newAction;
		}
		
		/// ditto
		final @property DragAction action() // getter
		{
			return _action;
		}
		
		
		///
		final @property bool escapePressed() // getter
		{
			return escp;
		}
		
		
		///
		// State of ctrl, alt and shift.
		final @property int keyState() // getter
		{
			return _keyState;
		}
		
		
		private:
		int _keyState;
		bool escp;
		DragAction _action;
	}
}


version(NO_WINDOWS_HUNG_WORKAROUND)
{
}
else
{
	version = WINDOWS_HUNG_WORKAROUND;
}
debug
{
	version=_DFL_WINDOWS_HUNG_WORKAROUND;
}
version(WINDOWS_HUNG_WORKAROUND)
{
	version=_DFL_WINDOWS_HUNG_WORKAROUND;
}

version(_DFL_WINDOWS_HUNG_WORKAROUND)
{
	class WindowsHungDflException: DflException
	{
		this(Dstring msg)
		{
			super(msg);
		}
	}
}

alias EnumWindowsCallback = BOOL delegate(HWND);
package struct EnumWindowsCallbackData
{
	EnumWindowsCallback callback;
	DThrowable exception;
}


// Callback for EnumWindows() and EnumChildWindows().
private extern(Windows) BOOL enumingWindows(HWND hwnd, LPARAM lparam) nothrow
{
	auto cbd = *(cast(EnumWindowsCallbackData*)lparam);
	try
	{
		return cbd.callback(hwnd);
	}
	catch (DThrowable e)
	{
		cbd.exception = e;
		return FALSE;
	}
	assert(0);
}


private struct Efi
{
	HWND hwParent;
	EnumWindowsCallbackData cbd;
}


// Callback for EnumChildWindows(). -lparam- = pointer to Efi;
private extern(Windows) BOOL enumingFirstWindows(HWND hwnd, LPARAM lparam) nothrow
{
	auto efi = cast(Efi*)lparam;
	if(efi.hwParent == GetParent(hwnd))
	{
		try
		{
			return efi.cbd.callback(hwnd);
		}
		catch (DThrowable e)
		{
			efi.cbd.exception = e;
			return FALSE;
		}
	}
	return TRUE;
}


package BOOL enumWindows(EnumWindowsCallback dg)
{
	EnumWindowsCallbackData cbd;
	cbd.callback = dg;
	scope (exit) if (cbd.exception) throw cbd.exception;
	static assert((&cbd).sizeof <= LPARAM.sizeof);
	return EnumWindows(&enumingWindows, cast(LPARAM)&cbd);
}


package BOOL enumChildWindows(HWND hwParent, EnumWindowsCallback dg)
{
	EnumWindowsCallbackData cbd;
	cbd.callback = dg;
	scope (exit) if (cbd.exception) throw cbd.exception;
	static assert((&cbd).sizeof <= LPARAM.sizeof);
	return EnumChildWindows(hwParent, &enumingWindows, cast(LPARAM)&cbd);
}


// Only the parent's children, not its children.
package BOOL enumFirstChildWindows(HWND hwParent, EnumWindowsCallback dg)
{
	Efi efi;
	efi.hwParent = hwParent;
	efi.cbd.callback = dg;
	scope (exit) if (efi.cbd.exception) throw efi.cbd.exception;
	return EnumChildWindows(hwParent, &enumingFirstWindows, cast(LPARAM)&efi);
}


///
enum ControlFont: ubyte
{
	COMPATIBLE, ///
	OLD, /// ditto
	NATIVE, /// ditto
}


debug
{
	import std.string;
}


/// Control class.
class Control: DObject, IWindow // docmain
{
	///
	static class ControlCollection
	{
		protected this(Control owner)
		{
			_owner = owner;
		}
		
		
		deprecated alias count = length;
		
		///
		@property int length() // getter
		{
			if(_owner.isHandleCreated)
			{
				// Inefficient :(
				uint len = 0;
				foreach(Control ctrl; this)
				{
					len++;
				}
				return len;
			}
			else
			{
				return children.length.toI32;
			}
		}
		
		
		///
		@property Control opIndex(int i) // getter
		{
			if(_owner.isHandleCreated)
			{
				int oni = 0;
				foreach(Control ctrl; this)
				{
					if(oni == i)
						return ctrl;
					oni++;
				}
				// Index out of bounds, bad things happen.
				assert(0);
			}
			else
			{
				return children[i];
			}
		}
		
		
		///
		void add(Control ctrl)
		{
			ctrl.parent = _owner;
		}
		
		
		///
		// opIn ?
		bool contains(Control ctrl)
		{
			return indexOf(ctrl) != -1;
		}
		
		
		///
		int indexOf(Control ctrl)
		{
			if(_owner.isHandleCreated)
			{
				int i = 0;
				int foundi = -1;
				
				
				BOOL enuming(HWND hwnd)
				{
					if(hwnd == ctrl.handle)
					{
						foundi = i;
						return false; // Stop.
					}
					
					i++;
					return true; // Continue.
				}
				
				
				enumFirstChildWindows(_owner.handle, &enuming);
				return foundi;
			}
			else
			{
				foreach(i, Control onCtrl; children)
				{
					if(i > int.max)
						throw new DflException("indexof() failure");
					if(onCtrl == ctrl)
						return cast(int)i;
				}
				return -1;
			}
		}
		
		
		///
		void remove(Control ctrl)
		{
			if(_owner.isHandleCreated)
			{
				_removeCreated(ctrl.handle);
			}
			else
			{
				int i = indexOf(ctrl);
				if(i != -1)
					_removeNotCreated(i);
			}
		}
		
		
		private void _removeCreated(HWND hwnd)
		{
			DestroyWindow(hwnd); // ?
		}
		
		
		package void _removeNotCreated(int i)
		{
			if(!i)
				children = children[1 .. children.length];
			else if(i == children.length - 1)
				children = children[0 .. i];
			else
				children = children[0 .. i] ~ children[i + 1 .. children.length];
		}
		
		
		///
		void removeAt(int i)
		{
			if(_owner.isHandleCreated)
			{
				int ith = 0;
				HWND hwndith;
				
				
				BOOL enuming(HWND hwnd)
				{
					if(ith == i)
					{
						hwndith = hwnd;
						return false; // Stop.
					}
					
					ith++;
					return true; // Continue.
				}
				
				
				enumFirstChildWindows(_owner.handle, &enuming);
				if(hwndith)
					_removeCreated(hwndith);
			}
			else
			{
				_removeNotCreated(i);
			}
		}
		
		
		protected final @property Control owner() // getter
		{
			return _owner;
		}
		
		
		///
		int opApply(int delegate(ref Control) dg)
		{
			int result = 0;
			
			if(_owner.isHandleCreated)
			{
				BOOL enuming(HWND hwnd)
				{
					Control ctrl = fromHandle(hwnd);
					if(ctrl)
					{
						result = dg(ctrl);
						if(result)
							return false; // Stop.
					}
					
					return true; // Continue.
				}
				
				
				enumFirstChildWindows(_owner.handle, &enuming);
			}
			else
			{
				foreach(Control ctrl; children)
				{
					result = dg(ctrl);
					if(result)
						break;
				}
			}
			
			return result;
		}
		
		mixin OpApplyAddIndex!(opApply, Control);
		
		
		package:
		Control _owner;
		Control[] children; // Only valid if -owner- isn't created yet (or is recreating).
		
		
		/+
		final void _array_swap(int ifrom, int ito)
		{
			if(ifrom == ito ||
				ifrom < 0 || ito < 0 ||
				ifrom >= length || ito >= length)
				return;
			
			Control cto;
			cto = children[ito];
			children[ito] = children[ifrom];
			children[ifrom] = cto;
		}
		+/
		
		
		final void _simple_front_one(int i)
		{
			if(i < 0 || i >= length - 1)
				return;
			
			children = children[0 .. i] ~ children[i + 1 .. i + 2] ~ children[i .. i + 1] ~ children[i + 2 .. children.length];
		}
		
		
		final void _simple_front_one(Control c)
		{
			return _simple_front_one(indexOf(c));
		}
		
		
		final void _simple_back_one(int i)
		{
			if(i <= 0 || i >= length)
				return;
			
			children = children[0 .. i - 1] ~ children[i + 1 .. i + 2] ~ children[i .. i + 1] ~ children[i + 2 .. children.length];
		}
		
		
		final void _simple_back_one(Control c)
		{
			return _simple_back_one(indexOf(c));
		}
		
		
		final void _simple_back(int i)
		{
			if(i <= 0 || i >= length)
				return;
			
			children = children[i .. i + 1] ~ children[0 .. i] ~ children[i + 1 .. children.length];
		}
		
		
		final void _simple_back(Control c)
		{
			return _simple_back(indexOf(c));
		}
		
		
		final void _simple_front(int i)
		{
			if(i < 0 || i >= length - 1)
				return;
			
			children = children[0 .. i] ~ children[i + 1 .. children.length] ~ children[i .. i + 1];
		}
		
		
		final void _simple_front(Control c)
		{
			return _simple_front(indexOf(c));
		}
	}
	
	
	private void _ctrladded(ControlEventArgs cea)
	{
		if(Application._compat & DflCompat.CONTROL_PARENT_096)
		{
			if(!(_exStyle() & WS_EX_CONTROLPARENT))
			{
				if(!(cbits & CBits.FORM))
				{
					//if((cea.control._style() & WS_TABSTOP) || (cea.control._exStyle() & WS_EX_CONTROLPARENT))
						_exStyle(_exStyle() | WS_EX_CONTROLPARENT);
				}
			}
		}
		else
		{
			assert(getStyle(ControlStyles.CONTAINER_CONTROL), "Control added to non-container parent");
		}
		
		onControlAdded(cea);
	}
	
	
	private void _ctrlremoved(ControlEventArgs cea)
	{
		alayout(cea.control);
		
		onControlRemoved(cea);
	}
	
	
	///
	protected void onControlAdded(ControlEventArgs cea)
	{
		controlAdded(this, cea);
	}
	
	
	///
	protected void onControlRemoved(ControlEventArgs cea)
	{
		controlRemoved(this, cea);
	}
	
	
	///
	@property final HWindow handle() // IWindow getter
	{
		if(!isHandleCreated)
		{
			debug(APP_PRINT)
				cprintf("Control created due to handle request.\n");
			
			createHandle();
		}
		
		return hwnd;
	}
	
	
	version(DFL_NO_DRAG_DROP) {} else
	{
		///
		private void allowDropImplement(bool byes)
		{
			/+
			if(dyes)
				_exStyle(_exStyle() | WS_EX_ACCEPTFILES);
			else
				_exStyle(_exStyle() & ~WS_EX_ACCEPTFILES);
			+/
			
			if(byes)
			{
				if(!droptarget)
				{
					if(isHandleCreated)
					{
						droptarget = new DropTarget(this);
						switch(RegisterDragDrop(hwnd, droptarget))
						{
							case S_OK:
							case DRAGDROP_E_ALREADYREGISTERED: // Hmm.
								break;
							case DRAGDROP_E_INVALIDHWND:
							case E_OUTOFMEMORY:
								droptarget = null;
								throw new DflException("Unable to register drag-drop");
							default:
								assert(0);
						}
					}
				}
			}
			else
			{
				if(droptarget)
				{
					destroy(droptarget); // delete is deprecated.
					droptarget = null;
					switch(RevokeDragDrop(hwnd))
					{
						case S_OK:
							break;
						case DRAGDROP_E_NOTREGISTERED:
						case DRAGDROP_E_INVALIDHWND:
						case E_OUTOFMEMORY:
							throw new DflException("Unable to revoke drag-drop");
						default:
							assert(0);
					}
				}
			}
		}
		
		///
		@property void allowDrop(bool byes) // setter
		{
			_allowDrop = byes;
			allowDropImplement(_allowDrop);
		}
		
		/// ditto
		@property bool allowDrop() // getter
		{
			/+
			return (_exStyle() & WS_EX_ACCEPTFILES) != 0;
			+/
			
			return _allowDrop;
		}
	}
	
	
	/+
	deprecated void anchor(AnchorStyles a) // setter
	{
		/+
		anch = a;
		if(!(anch & (AnchorStyles.LEFT | AnchorStyles.RIGHT)))
			anch |= AnchorStyles.LEFT;
		if(!(anch & (AnchorStyles.TOP | AnchorStyles.BOTTOM)))
			anch |= AnchorStyles.TOP;
		+/
		
		sdock = DockStyle.NONE; // Can't be set at the same time.
	}
	
	
	deprecated AnchorStyles anchor() // getter
	{
		//return anch;
		return cast(AnchorStyles)(AnchorStyles.LEFT | AnchorStyles.TOP);
	}
	+/
	
	
	private void _propagateBackColorAmbience()
	{
		Color bc;
		bc = backColor;
		
		
		void pa(Control pc)
		{
			foreach(Control ctrl; pc.ccollection)
			{
				if(Color.empty == ctrl.backc) // If default.
				{
					if(bc == ctrl.backColor) // If same default.
					{
						ctrl.deleteThisBackgroundBrush(); // Needs to be recreated with new color.
						ctrl.onBackColorChanged(EventArgs.empty);
						
						pa(ctrl); // Recursive.
					}
				}
			}
		}
		
		
		pa(this);
	}
	
	
	///
	protected void onBackColorChanged(EventArgs ea)
	{
		debug(EVENT_PRINT)
		{
			cprintf("{ Event: onBackColorChanged - Control %.*s }\n", name);
		}
		
		backColorChanged(this, ea);
	}
	
	
	///
	@property void backColor(Color c) // setter
	{
		if(backc == c)
			return;
		
		deleteThisBackgroundBrush(); // Needs to be recreated with new color.
		backc = c;
		onBackColorChanged(EventArgs.empty);
		
		_propagateBackColorAmbience();
		if(isHandleCreated)
			invalidate(true); // Redraw!
	}
	
	/// ditto
	@property Color backColor() // getter
	{
		if(Color.empty == backc)
		{
			if(parent)
			{
				return parent.backColor;
			}
			return defaultBackColor;
		}
		return backc;
	}
	
	
	///
	final @property int bottom() // getter
	{
		return wrect.bottom;
	}
	
	
	///
	final @property void bounds(Rect r) // setter
	{
		setBoundsCore(r.x, r.y, r.width, r.height, BoundsSpecified.ALL);
	}
	
	/// ditto
	final @property Rect bounds() // getter
	{
		return wrect;
	}
	
	
	/+
	final @property Rect originalBounds() // getter package
	{
		return oldwrect;
	}
	+/
	
	
	///
	protected void setBoundsCore(int x, int y, int width, int height, BoundsSpecified specified)
	{
		// Make sure at least one flag is set.
		//if(!(specified & BoundsSpecified.ALL))
		if(!specified)
			return;
		
		if(isHandleCreated)
		{
			UINT swpf = SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOMOVE | SWP_NOSIZE;
			
			if(specified & BoundsSpecified.X)
			{
				if(!(specified & BoundsSpecified.Y))
					y = this.top();
				swpf &= ~SWP_NOMOVE;
			}
			else if(specified & BoundsSpecified.Y)
			{
				x = this.left();
				swpf &= ~SWP_NOMOVE;
			}
			
			if(specified & BoundsSpecified.WIDTH)
			{
				if(!(specified & BoundsSpecified.HEIGHT))
					height = this.height();
				swpf &= ~SWP_NOSIZE;
			}
			else if(specified & BoundsSpecified.HEIGHT)
			{
				width = this.width();
				swpf &= ~SWP_NOSIZE;
			}
			
			SetWindowPos(hwnd, HWND.init, x, y, width, height, swpf);
			// Window events will update -wrect-.
		}
		else
		{
			if(specified & BoundsSpecified.X)
				wrect.x = x;
			if(specified & BoundsSpecified.Y)
				wrect.y = y;
			if(specified & BoundsSpecified.WIDTH)
			{
				if(width < 0)
					width = 0;
				
				wrect.width = width;
				wclientsz.width = width;
			}
			if(specified & BoundsSpecified.HEIGHT)
			{
				if(height < 0)
					height = 0;
				
				wrect.height = height;
				wclientsz.height = height;
			}
			
			//oldwrect = wrect;
		}
	}
	
	
	///
	final @property bool canFocus() // getter
	{
		/+
		LONG wl = _style();
		return /+ hwnd && +/ (wl & WS_VISIBLE) && !(wl & WS_DISABLED);
		+/
		//return visible && enabled;
		// Don't need to check -isHandleCreated- because IsWindowVisible() will fail from a null HWND.
		return /+ isHandleCreated && +/ IsWindowVisible(hwnd) && IsWindowEnabled(hwnd);
	}
	
	
	///
	final @property bool canSelect() // getter
	out(result)
	{
		if(result)
		{
			assert(isHandleCreated);
		}
	}
	do
	{
		// All parent controls need to be visible and enabled, too.
		// Don't need to check -isHandleCreated- because IsWindowVisible() will fail from a null HWND.
		return /+ isHandleCreated && +/ (ctrlStyle & ControlStyles.SELECTABLE) &&
			IsWindowVisible(hwnd) && IsWindowEnabled(hwnd);
	}
	
	
	package final bool _hasSelStyle()
	{
		return getStyle(ControlStyles.SELECTABLE);
	}
	
	
	///
	// Returns true if this control has the mouse capture.
	final @property bool capture() // getter
	{
		return isHandleCreated && hwnd == GetCapture();
	}
	
	/// ditto
	final @property void capture(bool cyes) // setter
	{
		if(cyes)
			SetCapture(hwnd);
		else
			ReleaseCapture();
	}
	
	
	// When true, validating and validated events are fired when the control
	// receives focus. Typically set to false for controls such as a Help button.
	// Default is true.
	deprecated final bool causesValidation() // getter
	{
		//return cvalidation;
		return false;
	}
	
	
	deprecated protected void onCausesValidationChanged(EventArgs ea)
	{
		//causesValidationChanged(this, ea);
	}
	
	
	deprecated final void causesValidation(bool vyes) // setter
	{
		/+
		if(cvalidation == vyes)
			return;
		
		cvalidation = vyes;
		
		onCausesValidationChanged(EventArgs.empty);
		+/
	}
	
	
	///
	final @property Rect clientRectangle() // getter
	{
		return Rect(Point(0, 0), wclientsz);
	}
	
	
	///
	final bool contains(Control ctrl)
	{
		//return ccollection.contains(ctrl);
		return ctrl && ctrl.parent is this;
	}
	
	
	///
	final @property Size clientSize() // getter
	{
		return wclientsz;
	}
	
	/// ditto
	final @property void clientSize(Size sz) // setter
	{
		setClientSizeCore(sz.width, sz.height);
	}
	
	
	///
	protected void setClientSizeCore(int width, int height)
	{
		/+
		if(isHandleCreated)
			setBoundsCore(0, 0, width, height, BoundsSpecified.SIZE);
		
		//wclientsz = Size(width, height);
		+/
		
		RECT r;
		
		r.left = 0;
		r.top = 0;
		r.right = width;
		r.bottom = height;
		
		AdjustWindowRectEx(&r, _style(), FALSE, _exStyle());
		
		setBoundsCore(0, 0, r.right - r.left, r.bottom - r.top, BoundsSpecified.SIZE);
	}
	
	
	///
	// This window or one of its children has focus.
	final @property bool containsFocus() // getter
	{
		if(!isHandleCreated)
			return false;
		
		HWND hwfocus = GetFocus();
		return hwfocus == hwnd || IsChild(hwnd, hwfocus);
	}
	
	
	version(DFL_NO_MENUS)
	{
	}
	else
	{
		///
		protected void onContextMenuChanged(EventArgs ea)
		{
			contextMenuChanged(this, ea);
		}
		
		
		///
		@property void contextMenu(ContextMenu menu) // setter
		{
			if(cmenu is menu)
				return;
			
			cmenu = menu;
			
			if(isHandleCreated)
			{
				onContextMenuChanged(EventArgs.empty);
			}
		}
		
		/// ditto
		@property ContextMenu contextMenu() // getter
		{
			return cmenu;
		}
	}
	
	
	///
	final @property ControlCollection controls() // getter
	{
		//return new ControlCollection(this);
		return ccollection;
	}
	
	
	///
	final @property bool created() // getter
	{
		// To-do: only return true when createHandle finishes.
		// Will also need to update uses of created/isHandleCreated.
		// Return false again when disposing/killing.
		//return isHandleCreated;
		return isHandleCreated || recreatingHandle;
	}
	
	
	private void _propagateCursorAmbience()
	{
		Cursor cur;
		cur = cursor;
		
		
		void pa(Control pc)
		{
			foreach(Control ctrl; pc.ccollection)
			{
				if(ctrl.wcurs is null) // If default.
				{
					if(cur is ctrl.cursor) // If same default.
					{
						ctrl.onCursorChanged(EventArgs.empty);
						
						pa(ctrl); // Recursive.
					}
				}
			}
		}
		
		
		pa(this);
	}
	
	
	///
	protected void onCursorChanged(EventArgs ea)
	{
		/+
		debug(EVENT_PRINT)
		{
			cprintf("{ Event: onCursorChanged - Control %.*s }\n", name);
		}
		+/
		
		if(isHandleCreated)
		{
			if(visible && enabled)
			{
				Point curpt = Cursor.position;
				if(hwnd == WindowFromPoint(curpt.point))
				{
					SendMessageA(hwnd, WM_SETCURSOR, cast(WPARAM)hwnd,
						MAKELPARAM(
							SendMessageA(hwnd, WM_NCHITTEST, 0, MAKELPARAM(curpt.x, curpt.y)).toI32,
							WM_MOUSEMOVE)
							);
				}
			}
		}
		
		cursorChanged(this, ea);
	}
	
	
	///
	@property void cursor(Cursor cur) // setter
	{
		if(cur is wcurs)
			return;
		
		wcurs = cur;
		onCursorChanged(EventArgs.empty);
		
		_propagateCursorAmbience();
	}
	
	/// ditto
	@property Cursor cursor() // getter
	{
		if(!wcurs)
		{
			if(parent)
			{
				return parent.cursor;
			}
			return _defaultCursor;
		}
		return wcurs;
	}
	
	
	///
	static @property Color defaultBackColor() // getter
	{
		return Color.systemColor(COLOR_BTNFACE);
	}
	
	
	///
	static @property Color defaultForeColor() //getter
	{
		return Color.systemColor(COLOR_BTNTEXT);
	}
	
	
	private static Font _deffont = null;
	
	
	private static Font _createOldFont()
	{
		return new Font(cast(HFONT)GetStockObject(DEFAULT_GUI_FONT), false);
	}
	
	
	private static Font _createCompatibleFont()
	{
		Font result;
		result = _createOldFont();
		
		try
		{
			OSVERSIONINFOA osi;
			osi.dwOSVersionInfoSize = osi.sizeof;
			if(GetVersionExA(&osi) && osi.dwMajorVersion >= 5)
			{
				// "MS Shell Dlg" / "MS Shell Dlg 2" not always supported.
				result = new Font("MS Shell Dlg 2", result.getSize(GraphicsUnit.POINT), GraphicsUnit.POINT);
			}
		}
		catch(Exception)
		{
		}
		
		//if(!result)
		//	result = _createOldFont();
		assert(result !is null);
		
		return result;
	}
	
	
	private static Font _createNativeFont()
	{
		Font result;
		
		NONCLIENTMETRICSA ncm;
		ncm.cbSize = ncm.sizeof;
		if(!SystemParametersInfoA(SPI_GETNONCLIENTMETRICS, ncm.sizeof, &ncm, 0))
		{
			result = _createCompatibleFont();
		}
		else
		{
			result = new Font(&ncm.lfMessageFont, true);
		}
		
		return result;
	}
	
	
	private static void _setDeffont(ControlFont cf)
	{
		synchronized
		{
			assert(_deffont is null);
			switch(cf)
			{
				case ControlFont.COMPATIBLE:
					_deffont = _createCompatibleFont();
					break;
				case ControlFont.NATIVE:
					_deffont = _createNativeFont();
					break;
				case ControlFont.OLD:
					_deffont = _createOldFont();
					break;
				default:
					assert(0);
			}
		}
	}
	
	
	deprecated alias controlFont = defaultFont;
	
	///
	static @property void defaultFont(ControlFont cf) // setter
	{
		if(_deffont)
			throw new DflException("Control font already selected");
		_setDeffont(cf);
	}
	
	/// ditto
	static @property void defaultFont(Font f) // setter
	{
		if(_deffont)
			throw new DflException("Control font already selected");
		_deffont = f;
	}
	
	/// ditto
	static @property Font defaultFont() // getter
	{
		if(!_deffont)
		{
			_setDeffont(ControlFont.COMPATIBLE);
		}
		
		return _deffont;
	}
	
	
	package static class SafeCursor: Cursor
	{
		this(HCURSOR hcur)
		{
			super(hcur, false);
		}
		
		
		override void dispose()
		{
		}
		
		
		/+
		~this()
		{
			super.dispose();
		}
		+/
	}
	
	
	package static @property Cursor _defaultCursor() // getter
	{
		static Cursor def = null;
		
		if(!def)
		{
			synchronized
			{
				if(!def)
					def = new SafeCursor(LoadCursor(HINSTANCE.init, IDC_ARROW));
			}
		}
		
		return def;
	}
	
	
	///
	@property Rect displayRectangle() // getter
	{
		return clientRectangle;
	}
	
	
	///
	//protected void onDockChanged(EventArgs ea)
	protected void onHasLayoutChanged(EventArgs ea)
	{
		if(parent)
			parent.alayout(this);
		
		//dockChanged(this, ea);
		hasLayoutChanged(this, ea);
	}
	
	alias onDockChanged = onHasLayoutChanged;
	
	
	private final void _alreadyLayout()
	{
		throw new DflException("Control already has a layout");
	}
	
	
	///
	@property DockStyle dock() // getter
	{
		return sdock;
	}
	
	/// ditto
	@property void dock(DockStyle ds) // setter
	{
		if(ds == sdock)
			return;
		
		DockStyle _olddock = sdock;
		sdock = ds;
		/+
		anch = AnchorStyles.NONE; // Can't be set at the same time.
		+/
		
		if(DockStyle.NONE == ds)
		{
			if(DockStyle.NONE != _olddock) // If it was even docking before; don't unset hasLayout for something else.
				hasLayout = false;
		}
		else
		{
			// Ensure not replacing some other layout, but OK if replacing another dock.
			if(DockStyle.NONE == _olddock)
			{
				if(hasLayout)
					_alreadyLayout();
			}
			hasLayout = true;
		}
		
		/+ // Called by hasLayout.
		if(isHandleCreated)
		{
			onDockChanged(EventArgs.empty);
		}
		+/
	}
	
	
	/// Get or set whether or not this control currently has its bounds managed. Fires onHasLayoutChanged as needed.
	final @property bool hasLayout() // getter
	{
		if(cbits & CBits.HAS_LAYOUT)
			return true;
		return false;
	}
	
	/// ditto
	final @property void hasLayout(bool byes) // setter
	{
		//if(byes == hasLayout)
		//	return; // No! setting this property again must trigger onHasLayoutChanged again.
		
		if(byes)
			cbits |= CBits.HAS_LAYOUT;
		else
			cbits &= ~CBits.HAS_LAYOUT;
		
		if(byes) // No need if layout is removed.
		{
			if(isHandleCreated)
			{
				onHasLayoutChanged(EventArgs.empty);
			}
		}
	}
	
	
	package final void _venabled(bool byes)
	{
		if(isHandleCreated)
		{
			EnableWindow(hwnd, byes);
			// Window events will update -wstyle-.
		}
		else
		{
			if(byes)
				wstyle &= ~WS_DISABLED;
			else
				wstyle |= WS_DISABLED;
		}
	}
	
	
	///
	final @property void enabled(bool byes) // setter
	{
		if(byes)
			cbits |= CBits.ENABLED;
		else
			cbits &= ~CBits.ENABLED;
		
		/+
		if(!byes)
		{
			_venabled(false);
		}
		else
		{
			if(!parent || parent.enabled)
				_venabled(true);
		}
		
		_propagateEnabledAmbience();
		+/
		
		_venabled(byes);
	}
	
	///
	final @property bool enabled() // getter
	{
		/*
		return IsWindowEnabled(hwnd) ? true : false;
		*/
		
		return (wstyle & WS_DISABLED) == 0;
	}
	
	
	private void _propagateEnabledAmbience()
	{
		/+ // Isn't working...
		if(cbits & CBits.FORM)
			return;
		
		bool en = enabled;
		
		void pa(Control pc)
		{
			foreach(Control ctrl; pc.ccollection)
			{
				if(ctrl.cbits & CBits.ENABLED)
				{
					_venabled(en);
					
					pa(ctrl);
				}
			}
		}
		
		pa(this);
		+/
	}
	
	
	///
	final void enable()
	{
		enabled = true;
	}
	
	/// ditto
	final void disable()
	{
		enabled = false;
	}
	
	
	/// Has focus.
	@property bool focused() // getter
	{
		//return isHandleCreated && hwnd == GetFocus();
		return created && fromChildHandle(GetFocus()) is this;
	}
	
	
	///
	@property void font(Font f) // setter
	{
		if(wfont is f)
			return;
		
		wfont = f;
		if(isHandleCreated)
			SendMessageA(hwnd, WM_SETFONT, cast(WPARAM)wfont.handle, MAKELPARAM(true, 0));
		onFontChanged(EventArgs.empty);
		
		_propagateFontAmbience();
	}
	
	/// ditto
	@property Font font() // getter
	{
		if(!wfont)
		{
			if(parent)
			{
				return parent.font;
			}
			return defaultFont;
		}
		return wfont;
	}
	
	
	private void _propagateForeColorAmbience()
	{
		Color fc;
		fc = foreColor;
		
		
		void pa(Control pc)
		{
			foreach(Control ctrl; pc.ccollection)
			{
				if(Color.empty == ctrl.forec) // If default.
				{
					if(fc == ctrl.foreColor) // If same default.
					{
						ctrl.onForeColorChanged(EventArgs.empty);
						
						pa(ctrl); // Recursive.
					}
				}
			}
		}
		
		
		pa(this);
	}
	
	
	///
	protected void onForeColorChanged(EventArgs ea)
	{
		debug(EVENT_PRINT)
		{
			cprintf("{ Event: onForeColorChanged - Control %.*s }\n", name);
		}
		
		foreColorChanged(this, ea);
	}
	
	
	///
	@property void foreColor(Color c) // setter
	{
		if(c == forec)
			return;
		
		forec = c;
		onForeColorChanged(EventArgs.empty);
		
		_propagateForeColorAmbience();
		if(isHandleCreated)
			invalidate(true); // Redraw!
	}
	
	/// ditto
	@property Color foreColor() // getter
	{
		if(Color.empty == forec)
		{
			if(parent)
			{
				return parent.foreColor;
			}
			return defaultForeColor;
		}
		return forec;
	}
	
	
	///
	// Doesn't cause a ControlCollection to be constructed so
	// it could improve performance when walking through children.
	final @property bool hasChildren() // getter
	{
		//return isHandleCreated && GetWindow(hwnd, GW_CHILD) != HWND.init;
		
		if(isHandleCreated)
		{
			return GetWindow(hwnd, GW_CHILD) != HWND.init;
		}
		else
		{
			return ccollection.children.length != 0;
		}
	}
	
	
	///
	final @property void height(int h) // setter
	{
		/*
		RECT rect;
		GetWindowRect(hwnd, &rect);
		SetWindowPos(hwnd, HWND.init, 0, 0, rect.right - rect.left, h, SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOMOVE);
		*/
		
		setBoundsCore(0, 0, 0, h, BoundsSpecified.HEIGHT);
	}
	
	/// ditto
	final @property int height() // getter
	{
		return wrect.height;
	}
	
	
	///
	final @property bool isHandleCreated() // getter
	{
		return hwnd != HWND.init;
	}
	
	
	///
	final @property void left(int l) // setter
	{
		/*
		RECT rect;
		GetWindowRect(hwnd, &rect);
		SetWindowPos(hwnd, HWND.init, l, rect.top, 0, 0, SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSIZE);
		*/
		
		setBoundsCore(l, 0, 0, 0, BoundsSpecified.X);
	}
	
	/// ditto
	final @property int left() // getter
	{
		return wrect.x;
	}
	
	
	/// Property: get or set the X and Y location of the control.
	final @property void location(Point pt) // setter
	{
		/*
		SetWindowPos(hwnd, HWND.init, pt.x, pt.y, 0, 0, SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSIZE);
		*/
		
		setBoundsCore(pt.x, pt.y, 0, 0, BoundsSpecified.LOCATION);
	}
	
	/// ditto
	final @property Point location() // getter
	{
		return wrect.location;
	}
	
	
	/// Currently depressed modifier keys.
	static @property Keys modifierKeys() // getter
	{
		// Is there a better way to do this?
		Keys ks = Keys.NONE;
		if(GetAsyncKeyState(VK_SHIFT) & 0x8000)
			ks |= Keys.SHIFT;
		if(GetAsyncKeyState(VK_MENU) & 0x8000)
			ks |= Keys.ALT;
		if(GetAsyncKeyState(VK_CONTROL) & 0x8000)
			ks|= Keys.CONTROL;
		return ks;
	}
	
	
	/// Currently depressed mouse buttons.
	static @property MouseButtons mouseButtons() // getter
	{
		MouseButtons result;
		
		result = MouseButtons.NONE;
		if(GetSystemMetrics(SM_SWAPBUTTON))
		{
			if(GetAsyncKeyState(VK_LBUTTON) & 0x8000)
				result |= MouseButtons.RIGHT; // Swapped.
			if(GetAsyncKeyState(VK_RBUTTON) & 0x8000)
				result |= MouseButtons.LEFT; // Swapped.
		}
		else
		{
			if(GetAsyncKeyState(VK_LBUTTON) & 0x8000)
				result |= MouseButtons.LEFT;
			if(GetAsyncKeyState(VK_RBUTTON) & 0x8000)
				result |= MouseButtons.RIGHT;
		}
		if(GetAsyncKeyState(VK_MBUTTON) & 0x8000)
			result |= MouseButtons.MIDDLE;
		
		return result;
	}
	
	
	///
	static @property Point mousePosition() // getter
	{
		Point pt;
		GetCursorPos(&pt.point);
		return pt;
	}
	
	
	/// Property: get or set the name of this control used in code.
	final @property void name(Dstring txt) // setter
	{
		_ctrlname = txt;
	}
	
	/// ditto
	final @property Dstring name() // getter
	{
		return _ctrlname;
	}
	
	
	///
	protected void onParentChanged(EventArgs ea)
	{
		debug(EVENT_PRINT)
		{
			cprintf("{ Event: onParentChanged - Control %.*s }\n", name);
		}
		
		parentChanged(this, ea);
	}
	
	
	/+
	///
	// ea is the new parent.
	protected void onParentChanging(ControlEventArgs ea)
	{
	}
	+/
	
	
	///
	final Form findForm()
	{
		Form f;
		Control c;
		
		c = this;
		for(;;)
		{
			f = cast(Form)c;
			if(f)
				break;
			c = c.parent;
			if(!c)
				return null;
		}
		return f;
	}
	
	
	///
	final @property void parent(Control c) // setter
	{
		if(c is wparent)
			return;
		
		if(!(_style() & WS_CHILD) || (_exStyle() & WS_EX_MDICHILD))
			throw new DflException("Cannot add a top level control to a control");
		
		//scope ControlEventArgs pcea = new ControlEventArgs(c);
		//onParentChanging(pcea);
		
		Control oldparent;
		_FixAmbientOld oldinfo;
		
		oldparent = wparent;
		
		if(oldparent)
		{
			oldinfo.set(oldparent);
			
			if(!oldparent.isHandleCreated)
			{
				int oi = oldparent.controls.indexOf(this);
				//assert(-1 != oi); // Fails if the parent (and thus this) handles destroyed.
				if(-1 != oi)
					oldparent.controls._removeNotCreated(oi);
			}
		}
		else
		{
			oldinfo.set(this);
		}
		
		scope ControlEventArgs cea = new ControlEventArgs(this);
		
		if(c)
		{
			wparent = c;
			
			// I want the destroy notification. Don't need it anymore.
			//c._exStyle(c._exStyle() & ~WS_EX_NOPARENTNOTIFY);
			
			if(c.isHandleCreated)
			{
				cbits &= ~CBits.NEED_INIT_LAYOUT;
				
				//if(created)
				if(isHandleCreated)
				{
					SetParent(hwnd, c.hwnd);
				}
				else
				{
					// If the parent is created, create me!
					createControl();
				}
				
				onParentChanged(EventArgs.empty);
				if(oldparent)
					oldparent._ctrlremoved(cea);
				c._ctrladded(cea);
				_fixAmbient(&oldinfo);
				
				initLayout();
			}
			else
			{
				// If the parent exists and isn't created, need to add
				// -this- to its children array.
				c.ccollection.children ~= this;
				
				onParentChanged(EventArgs.empty);
				if(oldparent)
					oldparent._ctrlremoved(cea);
				c._ctrladded(cea);
				_fixAmbient(&oldinfo);
				
				cbits |= CBits.NEED_INIT_LAYOUT;
			}
		}
		else
		{
			assert(c is null);
			//wparent = c;
			wparent = null;
			
			if(isHandleCreated)
				SetParent(hwnd, HWND.init);
			
			onParentChanged(EventArgs.empty);
			assert(oldparent !is null);
			oldparent._ctrlremoved(cea);
			_fixAmbient(&oldinfo);
		}
	}
	
	/// ditto
	final @property Control parent() // getter
	{
		return wparent;
	}
	
	
	private final Control _fetchParent()
	{
		HWND hwParent = GetParent(hwnd);
		return fromHandle(hwParent);
	}
	
	
	// TODO: check implementation.
	private static HRGN dupHrgn(HRGN hrgn)
	{
		HRGN rdup = CreateRectRgn(0, 0, 1, 1);
		CombineRgn(rdup, hrgn, HRGN.init, RGN_COPY);
		return rdup;
	}
	
	
	///
	final @property void region(Region rgn) // setter
	{
		if(isHandleCreated)
		{
			// Need to make a copy of the region.
			SetWindowRgn(hwnd, dupHrgn(rgn.handle), true);
		}
		
		wregion = rgn;
	}
	
	/// ditto
	final @property Region region() // getter
	{
		return wregion;
	}
	
	
	private final Region _fetchRegion()
	{
		HRGN hrgn = CreateRectRgn(0, 0, 1, 1);
		GetWindowRgn(hwnd, hrgn);
		return new Region(hrgn); // Owned because GetWindowRgn() gives a copy.
	}
	
	
	///
	final @property int right() // getter
	{
		return wrect.right;
	}
	
	
	/+
	@property void rightToLeft(bool byes) // setter
	{
		LONG wl = _exStyle();
		if(byes)
			wl |= WS_EX_RTLREADING;
		else
			wl &= ~WS_EX_RTLREADING;
		_exStyle(wl);
	}
	
	
	@property bool rightToLeft() // getter
	{
		return (_exStyle() & WS_EX_RTLREADING) != 0;
	}
	+/
	
	
	deprecated @property void rightToLeft(bool byes) // setter
	{
		rightToLeft = byes ? RightToLeft.YES : RightToLeft.NO;
	}
	
	
	package final void _fixRtol(RightToLeft val)
	{
		switch(val)
		{
			case RightToLeft.INHERIT:
				if(parent && parent.rightToLeft == RightToLeft.YES)
				{
					goto case RightToLeft.YES;
				}
				goto case RightToLeft.NO;
			
			case RightToLeft.YES:
				_exStyle(_exStyle() | WS_EX_RTLREADING);
				break;
			
			case RightToLeft.NO:
				_exStyle(_exStyle() & ~WS_EX_RTLREADING);
				break;
			
			default:
				assert(0);
		}
		
		//invalidate(true); // Children too in case they inherit.
		invalidate(false); // Since children are enumerated.
	}
	
	
	private void _propagateRtolAmbience()
	{
		RightToLeft rl;
		rl = rightToLeft;
		
		
		void pa(Control pc)
		{
			if(RightToLeft.INHERIT == pc.rtol)
			{
				//pc._fixRtol(rtol);
				pc._fixRtol(rl); // Set the specific parent value so it doesn't have to look up the chain.
				
				foreach(Control ctrl; pc.ccollection)
				{
					ctrl.onRightToLeftChanged(EventArgs.empty);
					
					pa(ctrl);
				}
			}
		}
		
		
		pa(this);
	}
	
	
	///
	@property void rightToLeft(RightToLeft val) // setter
	{
		if(rtol != val)
		{
			rtol = val;
			onRightToLeftChanged(EventArgs.empty);
			_propagateRtolAmbience(); // Also sets the class style and invalidates.
		}
	}
	
	/// ditto
	// Returns YES or NO; if inherited, returns parent's setting.
	@property RightToLeft rightToLeft() // getter
	{
		if(RightToLeft.INHERIT == rtol)
		{
			return parent ? parent.rightToLeft : RightToLeft.NO;
		}
		return rtol;
	}
	
	
	package struct _FixAmbientOld
	{
		Font font;
		Cursor cursor;
		Color backColor;
		Color foreColor;
		RightToLeft rightToLeft;
		//CBits cbits;
		bool enabled;
		
		
		void set(Control ctrl)
		{
			if(ctrl)
			{
				font = ctrl.font;
				cursor = ctrl.cursor;
				backColor = ctrl.backColor;
				foreColor = ctrl.foreColor;
				rightToLeft = ctrl.rightToLeft;
				//cbits = ctrl.cbits;
				enabled = ctrl.enabled;
			}
			/+else
			{
				font = null;
				cursor = null;
				backColor = Color.empty;
				foreColor = Color.empty;
				rightToLeft = RightToLeft.INHERIT;
				//cbits = CBits.init;
				enabled = true;
			}+/
		}
	}
	
	
	// This is called when the inherited ambience changes.
	package final void _fixAmbient(_FixAmbientOld* oldinfo)
	{
		// Note: exception will screw things up.
		
		_FixAmbientOld newinfo;
		if(parent)
			newinfo.set(parent);
		else
			newinfo.set(this);
		
		if(RightToLeft.INHERIT == rtol)
		{
			if(newinfo.rightToLeft !is oldinfo.rightToLeft)
			{
				onRightToLeftChanged(EventArgs.empty);
				_propagateRtolAmbience();
			}
		}
		
		if(Color.empty == backc)
		{
			if(newinfo.backColor !is oldinfo.backColor)
			{
				onBackColorChanged(EventArgs.empty);
				_propagateBackColorAmbience();
			}
		}
		
		if(Color.empty == forec)
		{
			if(newinfo.foreColor !is oldinfo.foreColor)
			{
				onForeColorChanged(EventArgs.empty);
				_propagateForeColorAmbience();
			}
		}
		
		if(!wfont)
		{
			if(newinfo.font !is oldinfo.font)
			{
				onFontChanged(EventArgs.empty);
				_propagateFontAmbience();
			}
		}
		
		if(!wcurs)
		{
			if(newinfo.cursor !is oldinfo.cursor)
			{
				onCursorChanged(EventArgs.empty);
				_propagateCursorAmbience();
			}
		}
		
		/+
		if(newinfo.enabled != oldinfo.enabled)
		{
			if(cbits & CBits.ENABLED)
			{
				_venabled(newinfo.enabled);
				_propagateEnabledAmbience();
			}
		}
		+/
	}
	
	
	/+
	package final void _fixAmbientChildren()
	{
		foreach(Control ctrl; ccollection.children)
		{
			ctrl._fixAmbient();
		}
	}
	+/
	
	
	///
	final @property void size(Size sz) // setter
	{
		/*
		SetWindowPos(hwnd, HWND.init, 0, 0, sz.width, sz.height, SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOMOVE);
		*/
		
		setBoundsCore(0, 0, sz.width, sz.height, BoundsSpecified.SIZE);
	}
	
	/// ditto
	final @property Size size() // getter
	{
		return wrect.size; // struct Size, not sizeof.
	}
	
	
	/+
	final @property void tabIndex(int i) // setter
	{
		// TODO: ?
	}
	
	
	final @property int tabIndex() // getter
	{
		return tabidx;
	}
	+/
	
	
	// Use -zIndex- instead.
	// -tabIndex- may return different values in the future.
	deprecated int tabIndex() // getter
	{
		return zIndex;
	}
	
	
	///
	final @property int zIndex() // getter
	out(result)
	{
		assert(result >= 0);
	}
	do
	{
		if(!parent)
			return 0;
		
		if(isHandleCreated)
		{
			GetZIndex gzi;
			gzi.find = this;
			int index;
			int tmp;
			
			BOOL getZIndexCallback(HWND hWnd)
			{
				if(hWnd is hwnd)
				{
					index = tmp;
					return FALSE; // Stop, found it.
				}
				
				auto ctrl = Control.fromHandle(hWnd);
				if(ctrl && ctrl.parent is parent)
				{
					tmp++;
				}
				
				return TRUE; // Keep looking.
			}
			
			enumChildWindows(parent.hwnd, &getZIndexCallback);
			return index;
		}
		else
		{
			return parent.controls.indexOf(this);
		}
	}
	
	
	///
	// True if control can be tabbed to.
	final @property void tabStop(bool byes) // setter
	{
		LONG wl = _style();
		if(byes)
			wl |= WS_TABSTOP;
		else
			wl &= ~WS_TABSTOP;
		_style(wl);
	}
	
	/// ditto
	final @property bool tabStop() // getter
	{
		return (_style() & WS_TABSTOP) != 0;
	}
	
	
	/// Property: get or set additional data tagged onto the control.
	final @property void tag(Object o) // setter
	{
		otag = o;
	}
	
	/// ditto
	final @property Object tag() // getter
	{
		return otag;
	}
	
	
	private final Dstring _fetchText()
	{
		return dfl.internal.utf.getWindowText(hwnd);
	}
	
	
	///
	@property void text(Dstring txt) // setter
	{
		if(isHandleCreated)
		{
			if(ctrlStyle & ControlStyles.CACHE_TEXT)
			{
				//if(wtext == txt)
				//	return;
				wtext = txt;
			}
			
			dfl.internal.utf.setWindowText(hwnd, txt);
		}
		else
		{
			wtext = txt;
		}
	}
	
	/// ditto
	@property Dstring text() // getter
	{
		if(isHandleCreated)
		{
			if(ctrlStyle & ControlStyles.CACHE_TEXT)
				return wtext;
			
			return _fetchText();
		}
		else
		{
			return wtext;
		}
	}
	
	
	///
	final @property void top(int t) // setter
	{
		setBoundsCore(0, t, 0, 0, BoundsSpecified.Y);
	}
	
	/// ditto
	final @property int top() // getter
	{
		return wrect.y;
	}
	
	
	/// Returns the topmost Control related to this control.
	// Returns the owner control that has no parent.
	// Returns this Control if no owner ?
	final @property Control topLevelControl() // getter
	{
		if(isHandleCreated)
		{
			HWND hwCurrent = hwnd;
			HWND hwParent;
			
			for(;;)
			{
				hwParent = GetParent(hwCurrent); // This gets the top-level one, whereas the previous code jumped owners.
				if(!hwParent)
					break;
				
				hwCurrent = hwParent;
			}
			
			return fromHandle(hwCurrent);
		}
		else
		{
			Control ctrl;
			ctrl = this;
			while(ctrl.parent)
			{
				ctrl = ctrl.parent; // This shouldn't jump owners..
			}
			return ctrl;
		}
	}
	
	
	/+
	private DWORD _fetchVisible()
	{
		//return IsWindowVisible(hwnd) != FALSE;
		wstyle = GetWindowLongPtrA(hwnd, GWL_STYLE);
		return wstyle & WS_VISIBLE;
	}
	+/
	
	
	///
	final @property void visible(bool byes) // setter
	{
		setVisibleCore(byes);
	}
	
	/// ditto
	final @property bool visible() // getter
	{
		//if(isHandleCreated)
		//	wstyle = GetWindowLongPtrA(hwnd, GWL_STYLE); // ...
		//return (wstyle & WS_VISIBLE) != 0;
		return (cbits & CBits.VISIBLE) != 0;
	}
	
	
	///
	final @property void width(int w) // setter
	{
		setBoundsCore(0, 0, w, 0, BoundsSpecified.WIDTH);
	}
	
	/// ditto
	final @property int width() // getter
	{
		return wrect.width;
	}
	
	
	///
	final void sendToBack()
	{
		if(!isHandleCreated)
		{
			if(parent)
				parent.ccollection._simple_front(this);
			return;
		}
		
		SetWindowPos(hwnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
	}
	
	
	///
	final void bringToFront()
	{
		if(!isHandleCreated)
		{
			if(parent)
				parent.ccollection._simple_back(this);
			return;
		}
		
		SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
		//BringWindowToTop(hwnd);
	}
	
	
	deprecated alias zIndexUp = bringUpOne;
	
	///
	// Move up one.
	final void bringUpOne()
	{
		if(!isHandleCreated)
		{
			if(parent)
				parent.ccollection._simple_front_one(this);
			return;
		}
		
		HWND hw;
		
		// Need to move back twice because the previous one already precedes this one.
		hw = GetWindow(hwnd, GW_HWNDPREV);
		if(!hw)
		{
			hw = HWND_TOP;
		}
		else
		{
			hw = GetWindow(hw, GW_HWNDPREV);
			if(!hw)
				hw = HWND_TOP;
		}
		
		SetWindowPos(hwnd, hw, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
	}
	
	
	deprecated alias zIndexDown = sendBackOne;
	
	///
	// Move back one.
	final void sendBackOne()
	{
		if(!isHandleCreated)
		{
			if(parent)
				parent.ccollection._simple_back_one(this);
			return;
		}
		
		HWND hw;
		
		hw = GetWindow(hwnd, GW_HWNDNEXT);
		if(!hw)
			hw = HWND_BOTTOM;
		
		SetWindowPos(hwnd, hw, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
	}
	
	
	// Note: true if no children, even if this not created.
	package final @property bool areChildrenCreated() // getter
	{
		return !ccollection.children.length;
	}
	
	
	package final void createChildren()
	{
		assert(isHandleCreated);
		
		Control[] ctrls;
		ctrls = ccollection.children;
		ccollection.children = null;
		
		foreach(Control ctrl; ctrls)
		{
			assert(ctrl.parent is this);
			assert(!(ctrl is null));
			assert(ctrl);
			ctrl.createControl();
		}
	}
	
	
	///
	// Force creation of the window and its child controls.
	final void createControl()
	{
		createHandle();
		
		// Called in WM_CREATE also.
		createChildren();
	}
	
	
	/// Returns a new Graphics object for this control, creating the control handle if necessary.
	final Graphics createGraphics()
	{
		HDC hdc = GetDC(handle); // Create handle as necessary.
		SetTextColor(hdc, foreColor.toRgb());
		return new CommonGraphics(hwnd, hdc);
	}
	
	
	version(DFL_NO_DRAG_DROP) {} else
	{
		private static class DropTarget: DflComObject, IDropTarget
		{
			this(Control ctrl)
			{
				this.ctrl = ctrl;
			}
			~this()
			{
				if (dataObj)
				{
					GC.removeRoot(cast(void*)dataObj);
					destroy(dataObj);
				}
			}
			
			
			extern(Windows):
			override HRESULT QueryInterface(IID* riid, void** ppv)
			{
				if(*riid == _IID_IDropTarget)
				{
					*ppv = cast(void*)cast(IDropTarget)this;
					AddRef();
					return S_OK;
				}
				else if(*riid == _IID_IUnknown)
				{
					*ppv = cast(void*)cast(IUnknown)this;
					AddRef();
					return S_OK;
				}
				else
				{
					*ppv = null;
					return E_NOINTERFACE;
				}
			}
			
			
			HRESULT DragEnter(dfl.internal.wincom.IDataObject pDataObject, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
			{
				HRESULT result;
				
				try
				{
					//dataObj = new ComToDdataObject(pDataObject);
					ensureDataObj(pDataObject);
					
					scope DragEventArgs ea = new DragEventArgs(dataObj, cast(int)grfKeyState, pt.x, pt.y, 
						cast(DragDropEffects)*pdwEffect, DragDropEffects.NONE); // ?
					ctrl.onDragEnter(ea);
					*pdwEffect = ea.effect;
					
					result = S_OK;
				}
				catch(DThrowable e)
				{
					Application.onThreadException(e);
					
					result = E_UNEXPECTED;
				}
				
				return result;
			}
			
			
			HRESULT DragOver(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
			{
				HRESULT result;
				
				try
				{
					assert(dataObj !is null);
					
					scope DragEventArgs ea = new DragEventArgs(dataObj, cast(int)grfKeyState, pt.x, pt.y, 
						cast(DragDropEffects)*pdwEffect, DragDropEffects.NONE); // ?
					ctrl.onDragOver(ea);
					*pdwEffect = ea.effect;
					
					result = S_OK;
				}
				catch(DThrowable e)
				{
					Application.onThreadException(e);
					
					result = E_UNEXPECTED;
				}
				
				return result;
			}
			
			
			HRESULT DragLeave()
			{
				HRESULT result;
				
				try
				{
					ctrl.onDragLeave(EventArgs.empty);
					
					killDataObj();
					
					result = S_OK;
				}
				catch(DThrowable e)
				{
					Application.onThreadException(e);
					
					result = E_UNEXPECTED;
				}
				
				return result;
			}
			
			
			HRESULT Drop(dfl.internal.wincom.IDataObject pDataObject, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
			{
				HRESULT result;
				
				try
				{
					//assert(dataObj !is null);
					ensureDataObj(pDataObject);
					
					scope DragEventArgs ea = new DragEventArgs(dataObj, cast(int)grfKeyState, pt.x, pt.y, 
						cast(DragDropEffects)*pdwEffect, DragDropEffects.NONE); // ?
					ctrl.onDragDrop(ea);
					*pdwEffect = ea.effect;
					
					result = S_OK;
				}
				catch(DThrowable e)
				{
					Application.onThreadException(e);
					
					result = E_UNEXPECTED;
				}
				
				return result;
			}
			
			
			private:
			
			Control ctrl;
			//dfl.data.IDataObject dataObj;
			ComToDdataObject dataObj;
			
			
			void ensureDataObj(dfl.internal.wincom.IDataObject pDataObject)
			{
				if(!dataObj)
				{
					dataObj = new ComToDdataObject(pDataObject);
					GC.addRoot(cast(void*)dataObj);
				}
				else if (!dataObj.isSameDataObject(pDataObject))
				{
					GC.removeRoot(cast(void*)dataObj);
					dataObj = new ComToDdataObject(pDataObject);
					GC.addRoot(cast(void*)dataObj);
				}
			}
			
			
			void killDataObj()
			{
				// Can't do this because the COM object might still need to be released elsewhere.
				//delete dataObj;
				//dataObj = null;
			}
		}
		
		
		///
		protected void onDragLeave(EventArgs ea)
		{
			dragLeave(this, ea);
		}
		
		
		///
		protected void onDragEnter(DragEventArgs ea)
		{
			dragEnter(this, ea);
		}
		
		
		///
		protected void onDragOver(DragEventArgs ea)
		{
			dragOver(this, ea);
		}
		
		
		///
		protected void onDragDrop(DragEventArgs ea)
		{
			dragDrop(this, ea);
		}
		
		
		private static class DropSource: DflComObject, IDropSource
		{
			this(Control ctrl)
			{
				this.ctrl = ctrl;
				mbtns = Control.mouseButtons;
			}
			
			
			extern(Windows):
			override HRESULT QueryInterface(IID* riid, void** ppv)
			{
				if(*riid == _IID_IDropSource)
				{
					*ppv = cast(void*)cast(IDropSource)this;
					AddRef();
					return S_OK;
				}
				else if(*riid == _IID_IUnknown)
				{
					*ppv = cast(void*)cast(IUnknown)this;
					AddRef();
					return S_OK;
				}
				else
				{
					*ppv = null;
					return E_NOINTERFACE;
				}
			}
			
			
			HRESULT QueryContinueDrag(BOOL fEscapePressed, DWORD grfKeyState)
			{
				HRESULT result;
				
				try
				{
					DragAction act;
					
					if(fEscapePressed)
					{
						act = cast(DragAction)DragAction.CANCEL;
					}
					else
					{
						if(mbtns & MouseButtons.LEFT)
						{
							if(!(grfKeyState & MK_LBUTTON))
							{
								act = cast(DragAction)DragAction.DROP;
								goto qdoit;
							}
						}
						else
						{
							if(grfKeyState & MK_LBUTTON)
							{
								act = cast(DragAction)DragAction.CANCEL;
								goto qdoit;
							}
						}
						if(mbtns & MouseButtons.RIGHT)
						{
							if(!(grfKeyState & MK_RBUTTON))
							{
								act = cast(DragAction)DragAction.DROP;
								goto qdoit;
							}
						}
						else
						{
							if(grfKeyState & MK_RBUTTON)
							{
								act = cast(DragAction)DragAction.CANCEL;
								goto qdoit;
							}
						}
						if(mbtns & MouseButtons.MIDDLE)
						{
							if(!(grfKeyState & MK_MBUTTON))
							{
								act = cast(DragAction)DragAction.DROP;
								goto qdoit;
							}
						}
						else
						{
							if(grfKeyState & MK_MBUTTON)
							{
								act = cast(DragAction)DragAction.CANCEL;
								goto qdoit;
							}
						}
						
						act = cast(DragAction)DragAction.CONTINUE;
					}
					
					qdoit:
					scope QueryContinueDragEventArgs ea = new QueryContinueDragEventArgs(cast(int)grfKeyState,
						fEscapePressed != FALSE, act); // ?
					ctrl.onQueryContinueDrag(ea);
					
					result = cast(HRESULT)ea.action;
				}
				catch(DThrowable e)
				{
					Application.onThreadException(e);
					
					result = E_UNEXPECTED;
				}
				
				return result;
			}
			
			
			HRESULT GiveFeedback(DWORD dwEffect)
			{
				HRESULT result;
				
				try
				{
					scope GiveFeedbackEventArgs ea = new GiveFeedbackEventArgs(cast(DragDropEffects)dwEffect, true);
					ctrl.onGiveFeedback(ea);
					
					result = ea.useDefaultCursors ? DRAGDROP_S_USEDEFAULTCURSORS : S_OK;
				}
				catch(DThrowable e)
				{
					Application.onThreadException(e);
					
					result = E_UNEXPECTED;
				}
				
				return result;
			}
			
			
			private:
			Control ctrl;
			MouseButtons mbtns;
		}
		
		
		///
		protected void onQueryContinueDrag(QueryContinueDragEventArgs ea)
		{
			queryContinueDrag(this, ea);
		}
		
		
		///
		protected void onGiveFeedback(GiveFeedbackEventArgs ea)
		{
			giveFeedback(this, ea);
		}
		
		
		/// Perform a drag/drop operation.
		final DragDropEffects doDragDrop(dfl.data.IDataObject dataObj, DragDropEffects allowedEffects)
		{
			Object foo = cast(Object)dataObj; // Hold a reference to the Object...
			
			DWORD effect;
			DropSource dropsrc;
			dfl.internal.wincom.IDataObject dropdata;
			
			dropsrc = new DropSource(this);
			dropdata = new DtoComDataObject(dataObj);
			
			// dataObj seems to be killed too early.
			switch(DoDragDrop(dropdata, dropsrc, cast(DWORD)allowedEffects, &effect))
			{
				case DRAGDROP_S_DROP: // All good.
					break;
				
				case DRAGDROP_S_CANCEL:
					return DragDropEffects.NONE; // ?
				
				default:
					throw new DflException("Unable to complete drag-drop operation");
			}
			
			return cast(DragDropEffects)effect;
		}
		
		/// ditto
		final DragDropEffects doDragDrop(Data obj, DragDropEffects allowedEffects)
		{
			dfl.data.IDataObject dd;
			dd = new DataObject;
			dd.setData(obj);
			return doDragDrop(dd, allowedEffects);
		}
	}
	
	
	override Dequ opEquals(Object o)
	{
		Control ctrl = cast(Control)o;
		if(!ctrl)
			return 0; // Not equal.
		return opEquals(ctrl);
	}
	
	
	Dequ opEquals(Control ctrl)
	{
		if(!isHandleCreated)
			return super.opEquals(ctrl);
		return hwnd == ctrl.hwnd;
	}
	
	
	override int opCmp(Object o)
	{
		Control ctrl = cast(Control)o;
		if(!ctrl)
			return -1;
		return opCmp(ctrl);
	}
	
	
	int opCmp(Control ctrl)
	{
		if(!isHandleCreated || hwnd != ctrl.hwnd)
			return super.opCmp(ctrl);
		return 0;
	}
	
	
	/// Set focus.
	final bool focus()
	{
		return SetFocus(hwnd) != HWND.init;
	}
	
	
	/// Returns the Control instance from one of its window handles, or null if none.
	// Finds controls that own more than one handle.
	// A combo box has several HWNDs, this would return the
	// correct combo box control if any of those handles are
	// provided.
	static Control fromChildHandle(HWND hwChild)
	{
		Control result;
		for(;;)
		{
			if(!hwChild)
				return null;
			
			result = fromHandle(hwChild);
			if(result)
				return result;
			
			hwChild = GetParent(hwChild);
		}
	}
	
	
	/// Returns the Control instance from its window handle, or null if none.
	static Control fromHandle(HWND hw)
	{
		return Application.lookupHwnd(hw);
	}
	
	
	///
	final Control getChildAtPoint(Point pt)
	{
		HWND hwChild;
		hwChild = ChildWindowFromPoint(hwnd, pt.point);
		if(!hwChild)
			return null;
		return fromChildHandle(hwChild);
	}
	
	
	///
	final void hide()
	{
		setVisibleCore(false);
	}
	
	/// ditto
	final void show()
	{
		/*
		ShowWindow(hwnd, SW_SHOW);
		doShow();
		*/
		
		setVisibleCore(true);
	}
	
	
	package final void redrawEntire()
	{
		if(hwnd)
		{
			SetWindowPos(hwnd, HWND.init, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_DRAWFRAME | SWP_NOMOVE
				| SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
		}
	}
	
	
	package final void recalcEntire()
	{
		if(hwnd)
		{
			SetWindowPos(hwnd, HWND.init, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE
				| SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
		}
	}
	
	
	///
	final void invalidate()
	{
		if(!hwnd)
			return;
		
		RedrawWindow(hwnd, null, HRGN.init, RDW_ERASE | RDW_INVALIDATE | RDW_NOCHILDREN);
	}
	
	/// ditto
	final void invalidate(bool andChildren)
	{
		if(!hwnd)
			return;
		
		RedrawWindow(hwnd, null, HRGN.init, RDW_ERASE | RDW_INVALIDATE | (andChildren ? RDW_ALLCHILDREN : RDW_NOCHILDREN));
	}
	
	/// ditto
	final void invalidate(Rect r)
	{
		if(!hwnd)
			return;
		
		RECT rect;
		r.getRect(&rect);
		
		RedrawWindow(hwnd, &rect, HRGN.init, RDW_ERASE | RDW_INVALIDATE | RDW_NOCHILDREN);
	}
	
	/// ditto
	final void invalidate(Rect r, bool andChildren)
	{
		if(!hwnd)
			return;
		
		RECT rect;
		r.getRect(&rect);
		
		RedrawWindow(hwnd, &rect, HRGN.init, RDW_ERASE | RDW_INVALIDATE | (andChildren ? RDW_ALLCHILDREN : RDW_NOCHILDREN));
	}
	
	/// ditto
	final void invalidate(Region rgn)
	{
		if(!hwnd)
			return;
		
		RedrawWindow(hwnd, null, rgn.handle, RDW_ERASE | RDW_INVALIDATE | RDW_NOCHILDREN);
	}
	
	/// ditto
	final void invalidate(Region rgn, bool andChildren)
	{
		if(!hwnd)
			return;
		
		RedrawWindow(hwnd, null, rgn.handle, RDW_ERASE | RDW_INVALIDATE | (andChildren ? RDW_ALLCHILDREN : RDW_NOCHILDREN));
	}
	
	
	///
	// Redraws the entire control, including nonclient area.
	final void redraw()
	{
		if(!hwnd)
			return;
		
		RedrawWindow(hwnd, null, HRGN.init, RDW_ERASE | RDW_INVALIDATE | RDW_FRAME);
	}
	
	
	/// Returns true if the window does not belong to the current thread.
	@property bool invokeRequired() // getter
	{
		DWORD tid = GetWindowThreadProcessId(hwnd, null);
		return tid != GetCurrentThreadId();
	}
	
	
	private static void badInvokeHandle()
	{
		//throw new DflException("Must invoke after creating handle");
		throw new DflException("Must invoke with created handle");
	}
	
	
	/// Synchronously calls a delegate in this Control's thread. This function is thread safe and exceptions are propagated to the caller.
	// Exceptions are propagated back to the caller of invoke().
	final Object invoke(Object delegate(Object[]) dg, Object[] args ...)
	{
		if(!hwnd)
			badInvokeHandle();
		
		InvokeData inv;
		inv.dg = dg;
		inv.args = args;
		
		if(LRESULT_DFL_INVOKE != SendMessageA(hwnd, wmDfl, WPARAM_DFL_INVOKE, cast(LRESULT)&inv))
			throw new DflException("Invoke failure");
		if(inv.exception)
			throw inv.exception;
		
		return inv.result;
	}
	
	/// ditto
	final void invoke(void delegate() dg)
	{
		if(!hwnd)
			badInvokeHandle();
		
		InvokeSimpleData inv;
		inv.dg = dg;
		
		if(LRESULT_DFL_INVOKE != SendMessageA(hwnd, wmDfl, WPARAM_DFL_INVOKE_SIMPLE, cast(LRESULT)&inv))
			throw new DflException("Invoke failure");
		if(inv.exception)
			throw inv.exception;
	}
	
	
	/** Asynchronously calls a function after the window message queue processes its current messages.
	    It is generally not safe to pass references to the delayed function.
	    Exceptions are not propagated to the caller.
	**/
	// Extra.
	// Exceptions will be passed to Application.onThreadException() and
	// trigger the threadException event or the default exception dialog.
	final void delayInvoke(void function() fn)
	{
		if(!hwnd)
			badInvokeHandle();
		
		assert(!invokeRequired);
		
		static assert(fn.sizeof <= LPARAM.sizeof);
		PostMessageA(hwnd, wmDfl, WPARAM_DFL_DELAY_INVOKE, cast(LPARAM)fn);
	}
	
	/// ditto
	// Extra.
	// Exceptions will be passed to Application.onThreadException() and
	// trigger the threadException event or the default exception dialog.
	// Copy of params are passed to fn, they do not exist after it returns.
	// It is unsafe to pass references to a delayed function.
	final void delayInvoke(void function(Control, size_t[]) fn, size_t[] params ...)
	{
		if(!hwnd)
			badInvokeHandle();
		
		assert(!invokeRequired);
		
		static assert((DflInvokeParam*).sizeof <= LPARAM.sizeof);
		
		DflInvokeParam* p;
		p = cast(DflInvokeParam*)dfl.internal.clib.malloc(
			(DflInvokeParam.sizeof - size_t.sizeof)
				+ params.length * size_t.sizeof);
		if(!p)
			throw new OomException();
		
		p.fp = fn;
		p.nparams = params.length;
		p.params.ptr[0 .. params.length] = params[];
		
		PostMessageA(hwnd, wmDfl, WPARAM_DFL_DELAY_INVOKE_PARAMS, cast(LPARAM)p);
	}
	
	deprecated alias beginInvoke = delayInvoke;
	
	
	///
	static bool isMnemonic(dchar charCode, Dstring text)
	{
		size_t ui;
		for(ui = 0; ui != text.length; ui++)
		{
			if('&' == text[ui])
			{
				if(++ui == text.length)
					break;
				if('&' == text[ui]) // && means literal & so skip it.
					continue;
				dchar dch;
				dch = utf8stringGetUtf32char(text, ui);
				return utf32charToLower(charCode) == utf32charToLower(dch);
			}
		}
		return false;
	}
	
	
	/// Converts a screen Point to a client Point.
	final Point pointToClient(Point pt)
	{
		ScreenToClient(hwnd, &pt.point);
		return pt;
	}
	
	
	/// Converts a client Point to a screen Point.
	final Point pointToScreen(Point pt)
	{
		ClientToScreen(hwnd, &pt.point);
		return pt;
	}
	
	
	/// Converts a screen Rectangle to a client Rectangle.
	final Rect rectangleToClient(Rect r)
	{
		RECT rect;
		r.getRect(&rect);
		
		MapWindowPoints(HWND.init, hwnd, cast(POINT*)&rect, 2);
		return Rect(&rect);
	}
	
	
	/// Converts a client Rectangle to a screen Rectangle.
	final Rect rectangleToScreen(Rect r)
	{
		RECT rect;
		r.getRect(&rect);
		
		MapWindowPoints(hwnd, HWND.init, cast(POINT*)&rect, 2);
		return Rect(&rect);
	}
	
	
	/// Process shortcut key (ctrl+A etc).
	// Returns false when parent is none, otherwise call parent form's one.
	protected bool processCmdKey(ref Message msg, Keys keyData)
	{
		if (parent)
			return parent.processCmdKey(msg, keyData);
		else
			return false;
	}

	/// Process dialog key (TAB, RETURN, ESC, UP, DOWN, LEFT, RIGHT and so on).
	// Returns false when parent is none, otherwise call parent form's one.
	protected bool processDialogKey(Keys keyData)
	{
		if (parent)
			return parent.processDialogKey(keyData);
		else
			return false;
	}

	/// Process mnemonic (access key) such as Alt+T.
	// Returns false when parent is none, otherwise call parent form's one.
	protected bool processDialogChar(char charCode)
	{
		if (parent)
			return parent.processDialogChar(charCode);
		else
			return false;
	}

	/// Pre-process keybord message
	// This function called from Application.DflWndProc().
	// If return true, processed message,
	// then wndProc() be not called after preProcessMessage().
	// If return false, wndProc() be called after preProcessMessage().
	bool preProcessMessage(ref Message msg)
	{
		bool result/+ = false+/;

		if (msg.msg == WM_KEYDOWN || msg.msg == WM_SYSKEYDOWN)
		{
			// TODO: Implement
			// if (!getExtendedState(ExtendedStates.UiCues))
			// {
			// 	processUICues(msg);
			// }
			
			Keys keyData = cast(Keys)msg.wParam | modifierKeys;

			// processCmdKey returns true when keyData is a shortcut key (ctrl+C etc).
			if (processCmdKey(msg, keyData))
			{
				result = true;
			}
			// isInputKey returns true when keyData is a regular input key.
			else if (isInputKey(keyData))
			{
				// TODO: Implement
				// SetExtendedState(ExtendedStates.InputKey, true);
				result = false; // End preprocessing and call wndProc().
			}
			else
			{
				// Process dialog keys such as TAB, RETURN, ESC, UP, DOWN, RIGHT, LEFT.
				result = processDialogKey(keyData);
			}
		}
		else if (msg.msg == WM_CHAR || msg.msg == WM_SYSCHAR)
		{
			if (msg.msg == WM_CHAR && isInputChar(cast(char)msg.wParam))
			{
				// TODO: Implement
				// setExtendedState(ExtendedStates.InputChar, true);
				result = false;
			}
			else
			{
				result = processDialogChar(cast(char)msg.wParam);
			}
		}
		else
		{
			result = false;
		}

		return result;
	}
	
	
	///
	final Size getAutoScaleSize(Font f)
	{
		Size result;
		Graphics g;
		g = createGraphics();
		result = g.getScaleSize(f);
		g.dispose();
		return result;
	}
	
	/// ditto
	final Size getAutoScaleSize()
	{
		return getAutoScaleSize(font);
	}
	
	
	///
	void refresh()
	{
		invalidate(true);
	}
	
	
	///
	void resetBackColor()
	{
		//backColor = defaultBackColor;
		backColor = Color.empty;
	}
	
	
	///
	void resetCursor()
	{
		//cursor = new Cursor(LoadCursorA(HINSTANCE.init, IDC_ARROW), false);
		cursor = null;
	}
	
	
	///
	void resetFont()
	{
		//font = defaultFont;
		font = null;
	}
	
	
	///
	void resetForeColor()
	{
		//foreColor = defaultForeColor;
		foreColor = Color.empty;
	}
	
	
	///
	void resetRightToLeft()
	{
		//rightToLeft = false;
		rightToLeft = RightToLeft.INHERIT;
	}
	
	
	///
	void resetText()
	{
		//text = "";
		text = null;
	}
	
	
	///
	// Just allow layout recalc, but don't do it right now.
	final void resumeLayout()
	{
		//_allowLayout = true;
		if(_disallowLayout)
			_disallowLayout--;
	}
	
	/// ditto
	// Allow layout recalc, only do it now if -byes- is true.
	final void resumeLayout(bool byes)
	{
		if(_disallowLayout)
			_disallowLayout--;
		
		// This is correct.
		if(byes)
		{
			if(!_disallowLayout)
				alayout(null);
		}
	}
	
	
	///
	final void suspendLayout()
	{
		//_allowLayout = false;
		_disallowLayout++;
	}
	
	
	final void performLayout(Control affectedControl)
	{
		alayout(affectedControl, false);
	}
	
	
	final void performLayout()
	{
		return performLayout(this);
	}
	
	
	/+
	// TODO: implement.
	
	// Scale both height and width to -ratio-.
	final void scale(float ratio)
	{
		scaleCore(ratio, ratio);
	}
	
	
	// Scale -width- and -height- ratios.
	final void scale(float width, float height)
	{
		scaleCore(width, height);
	}
	
	
	// Also scales child controls recursively.
	protected void scaleCore(float width, float height)
	{
		suspendLayout();
		
		// ...
		
		resumeLayout();
	}
	+/
	
	
	private static bool _eachild(HWND hw, bool delegate(HWND hw) callback, ref size_t xiter, bool nested)
	{
		for(; hw; hw = GetWindow(hw, GW_HWNDNEXT))
		{
			if(!xiter)
				return false;
			xiter--;
			
			LONG st = GetWindowLongPtrA(hw, GWL_STYLE).toI32;
			if(!(st & WS_VISIBLE))
				continue;
			if(st & WS_DISABLED)
				continue;
			
			if(!callback(hw))
				return false;
			
			if(nested)
			{
				//LONG exst = GetWindowLongPtrA(hw, GWL_EXSTYLE);
				//if(exst & WS_EX_CONTROLPARENT) // It's no longer added.
				{
					HWND hwc = GetWindow(hw, GW_CHILD);
					if(hwc)
					{
						//if(!_eachild(hwc, callback, xiter, nested))
						if(!_eachild(hwc, callback, xiter, true))
							return false;
					}
				}
			}
		}
		return true;
	}
	
	package static void eachGoodChildHandle(HWND hwparent, bool delegate(HWND hw) callback, bool nested = true)
	{
		HWND hw = GetWindow(hwparent, GW_CHILD);
		size_t xiter = 2000;
		_eachild(hw, callback, xiter, nested);
	}
	
	
	private static bool _isHwndControlSel(HWND hw)
	{
		Control c = Control.fromHandle(hw);
		return c && c.getStyle(ControlStyles.SELECTABLE);
	}
	
	
	package static bool _dlgselnext(Form dlg, HWND hwcursel, bool forward,
		bool tabStopOnly = true, bool selectableOnly = false,
		bool nested = true, bool wrap = true,
		HWND hwchildrenof = null)
	{
		//assert(cast(Form)Control.fromHandle(hwdlg) !is null);
		
		if(!hwchildrenof)
			hwchildrenof = dlg.handle;
		if(forward)
		{
			bool foundthis = false, tdone = false;
			HWND hwfirst;
			eachGoodChildHandle(hwchildrenof,
				(HWND hw)
				{
					assert(!tdone);
					if(hw == hwcursel)
					{
						foundthis = true;
					}
					else
					{
						if(!tabStopOnly || (GetWindowLongPtrA(hw, GWL_STYLE) & WS_TABSTOP))
						{
							if(!selectableOnly || _isHwndControlSel(hw))
							{
								if(foundthis)
								{
									//DefDlgProcA(dlg.handle, WM_NEXTDLGCTL, cast(WPARAM)hw, MAKELPARAM(true, 0));
									dlg._selectChild(hw);
									tdone = true;
									return false; // Break.
								}
								else
								{
									if(HWND.init == hwfirst)
										hwfirst = hw;
								}
							}
						}
					}
					return true; // Continue.
				}, nested);
			if(!tdone && HWND.init != hwfirst)
			{
				// If it falls through without finding hwcursel, let it select the first one, even if not wrapping.
				if(wrap || !foundthis)
				{
					//DefDlgProcA(dlg.handle, WM_NEXTDLGCTL, cast(WPARAM)hwfirst, MAKELPARAM(true, 0));
					dlg._selectChild(hwfirst);
					return true;
				}
			}
		}
		else
		{
			HWND hwprev;
			eachGoodChildHandle(hwchildrenof,
				(HWND hw)
				{
					if(hw == hwcursel)
					{
						if(HWND.init != hwprev) // Otherwise, keep looping and get last one.
							return false; // Break.
						if(!wrap) // No wrapping, so don't get last one.
						{
							assert(HWND.init == hwprev);
							return false; // Break.
						}
					}
					if(!tabStopOnly || (GetWindowLongPtrA(hw, GWL_STYLE) & WS_TABSTOP))
					{
						if(!selectableOnly || _isHwndControlSel(hw))
						{
							hwprev = hw;
						}
					}
					return true; // Continue.
				}, nested);
			// If it falls through without finding hwcursel, let it select the last one, even if not wrapping.
			if(HWND.init != hwprev)
			{
				//DefDlgProcA(dlg.handle, WM_NEXTDLGCTL, cast(WPARAM)hwprev, MAKELPARAM(true, 0));
				dlg._selectChild(hwprev);
				return true;
			}
		}
		return false;
	}
	
	
	package final bool _selectNextControl(Form ctrltoplevel,
		Control ctrl, bool forward, bool tabStopOnly, bool nested, bool wrap)
	{
		if(!created)
			return false;
		
		assert(ctrltoplevel !is null);
		assert(ctrltoplevel.isHandleCreated);
		
		return _dlgselnext(ctrltoplevel,
			(ctrl && ctrl.isHandleCreated) ? ctrl.handle : null,
			forward, tabStopOnly, !tabStopOnly, nested, wrap,
			this.handle);
	}
	
	
	package final void _selectThisControl()
	{
		
	}
	
	
	// Only considers child controls of this control.
	final bool selectNextControl(Control ctrl, bool forward, bool tabStopOnly, bool nested, bool wrap)
	{
		if(!created)
			return false;
		
		auto ctrltoplevel = findForm();
		if(ctrltoplevel)
			return _selectNextControl(ctrltoplevel, ctrl, forward, tabStopOnly, nested, wrap);

		return false;
	}
	
	
	///
	final void select()
	{
		select(false, false);
	}
	
	/// ditto
	// If -directed- is true, -forward- is used; otherwise, selects this control.
	// If -forward- is true, the next control in the tab order is selected,
	// otherwise the previous control in the tab order is selected.
	// Controls without style ControlStyles.SELECTABLE are skipped.
	void select(bool directed, bool forward)
	{
		if(!created)
			return;
		
		auto ctrltoplevel = findForm();
		if(ctrltoplevel && ctrltoplevel !is this)
		{
			/+ // Old...
			// Even if directed, ensure THIS one is selected first.
			if(!directed || hwnd != GetFocus())
			{
				DefDlgProcA(ctrltoplevel.handle, WM_NEXTDLGCTL, cast(WPARAM)hwnd, MAKELPARAM(true, 0));
			}
			
			if(directed)
			{
				DefDlgProcA(ctrltoplevel.handle, WM_NEXTDLGCTL, !forward, MAKELPARAM(false, 0));
			}
			+/
			
			if(directed)
			{
				_dlgselnext(ctrltoplevel, this.handle, forward);
			}
			else
			{
				ctrltoplevel._selectChild(this);
			}
		}
		else
		{
			focus(); // This must be a form so just focus it ?
		}
	}
	
	
	///
	final void setBounds(int x, int y, int width, int height)
	{
		setBoundsCore(x, y, width, height, BoundsSpecified.ALL);
	}
	
	/// ditto
	final void setBounds(int x, int y, int width, int height, BoundsSpecified specified)
	{
		setBoundsCore(x, y, width, height, specified);
	}
	
	
	override Dstring toString()
	{
		return text;
	}
	
	
	///
	final void update()
	{
		if(!created)
			return;
		
		UpdateWindow(hwnd);
	}
	
	
	///
	// If mouseEnter, mouseHover and mouseLeave events are supported.
	// Returns true on Windows 95 with IE 5.5, Windows 98+ or Windows NT 4.0+.
	static @property bool supportsMouseTracking() // getter
	{
		return trackMouseEvent != null;
	}
	
	
	package final Rect _fetchBounds()
	{
		RECT r;
		GetWindowRect(hwnd, &r);
		HWND hwParent = GetParent(hwnd);
		if(hwParent && (_style() & WS_CHILD))
			MapWindowPoints(HWND.init, hwParent, cast(POINT*)&r, 2);
		return Rect(&r);
	}
	
	
	package final Size _fetchClientSize()
	{
		RECT r;
		GetClientRect(hwnd, &r);
		return Size(r.right, r.bottom);
	}
	
	
	deprecated protected void onInvalidated(InvalidateEventArgs iea)
	{
		//invalidated(this, iea);
	}
	
	
	///
	protected void onPaint(PaintEventArgs pea)
	{
		paint(this, pea);
	}
	
	
	protected void onMoving(MovingEventArgs cea)
	{
		moving(this, cea);
	}
	
	///
	protected void onMove(EventArgs ea)
	{
		move(this, ea);
	}
	
	
	/+
	protected void onLocationChanged(EventArgs ea)
	{
		locationChanged(this, ea);
	}
	+/
	alias onLocationChanged = onMove;
	
	
	protected void onSizing(SizingEventArgs cea)
	{
		sizing(this, cea);
	}
	
	///
	protected void onResize(EventArgs ea)
	{
		resize(this, ea);
	}
	
	
	/+
	protected void onSizeChanged(EventArgs ea)
	{
		sizeChanged(this, ea);
	}
	+/
	alias onSizeChanged = onResize;
	
	
	/+
	// ///
	// Allows comparing before and after dimensions, and also allows modifying the new dimensions.
	deprecated protected void onBeforeResize(BeforeResizeEventArgs ea)
	{
	}
	+/
	
	
	///
	protected void onMouseEnter(MouseEventArgs mea)
	{
		mouseEnter(this, mea);
	}
	
	
	///
	protected void onMouseMove(MouseEventArgs mea)
	{
		mouseMove(this, mea);
	}
	
	
	///
	protected void onKeyDown(KeyEventArgs kea)
	{
		keyDown(this, kea);
	}
	
	
	///
	protected void onKeyPress(KeyPressEventArgs kea)
	{
		keyPress(this, kea);
	}
	
	
	///
	protected void onKeyUp(KeyEventArgs kea)
	{
		keyUp(this, kea);
	}
	
	
	///
	protected void onMouseWheel(MouseEventArgs mea)
	{
		mouseWheel(this, mea);
	}
	
	
	///
	protected void onMouseHover(MouseEventArgs mea)
	{
		mouseHover(this, mea);
	}
	
	
	///
	protected void onMouseLeave(MouseEventArgs mea)
	{
		mouseLeave(this, mea);
	}
	
	
	///
	protected void onMouseDown(MouseEventArgs mea)
	{
		mouseDown(this, mea);
	}
	
	
	///
	protected void onMouseUp(MouseEventArgs mea)
	{
		mouseUp(this, mea);
	}
	
	
	///
	protected void onClick(EventArgs ea)
	{
		click(this, ea);
	}
	
	
	///
	protected void onDoubleClick(EventArgs ea)
	{
		doubleClick(this, ea);
	}
	
	
	///
	protected void onGotFocus(EventArgs ea)
	{
		gotFocus(this, ea);
	}
	
	
	/+
	deprecated protected void onEnter(EventArgs ea)
	{
		//enter(this, ea);
	}
	
	
	deprecated protected void onLeave(EventArgs ea)
	{
		//leave(this, ea);
	}
	
	
	deprecated protected void onValidated(EventArgs ea)
	{
		//validated(this, ea);
	}
	
	
	deprecated protected void onValidating(CancelEventArgs cea)
	{
		/+
		foreach(CancelEventHandler.Handler handler; validating.handlers())
		{
			handler(this, cea);
			
			if(cea.cancel)
				return; // Not validated.
		}
		
		onValidated(EventArgs.empty);
		+/
	}
	+/
	
	
	///
	protected void onLostFocus(EventArgs ea)
	{
		lostFocus(this, ea);
	}
	
	
	///
	protected void onEnabledChanged(EventArgs ea)
	{
		enabledChanged(this, ea);
	}
	
	
	///
	protected void onTextChanged(EventArgs ea)
	{
		textChanged(this, ea);
	}
	
	
	private void _propagateFontAmbience()
	{
		Font fon;
		fon = font;
		
		
		void pa(Control pc)
		{
			foreach(Control ctrl; pc.ccollection)
			{
				if(!ctrl.wfont) // If default.
				{
					if(fon is ctrl.font) // If same default.
					{
						if(ctrl.isHandleCreated)
							SendMessageA(ctrl.hwnd, WM_SETFONT, cast(WPARAM)fon.handle, MAKELPARAM(true, 0));
						ctrl.onFontChanged(EventArgs.empty);
						
						pa(ctrl); // Recursive.
					}
				}
			}
		}
		
		
		pa(this);
	}
	
	
	///
	protected void onFontChanged(EventArgs ea)
	{
		debug(EVENT_PRINT)
		{
			cprintf("{ Event: onFontChanged - Control %.*s }\n", name);
		}
		
		fontChanged(this, ea);
	}
	
	
	///
	protected void onRightToLeftChanged(EventArgs ea)
	{
		debug(EVENT_PRINT)
		{
			cprintf("{ Event: onRightToLeftChanged - Control %.*s }\n", name);
		}
		
		rightToLeftChanged(this, ea);
	}
	
	
	///
	protected void onVisibleChanged(EventArgs ea)
	{
		if(wparent)
		{
			wparent.vchanged();
			suspendLayout(); // Note: exception could cause failure to restore.
			wparent.alayout(this);
			resumeLayout(false);
		}
		if(visible)
			alayout(this);
		
		visibleChanged(this, ea);
		
		if(visible)
		{
			// If no focus or the focused control is hidden, try to select something...
			HWND hwfocus = GetFocus();
			if(!hwfocus
				|| (hwfocus == hwnd && !getStyle(ControlStyles.SELECTABLE))
				|| !IsWindowVisible(hwfocus))
			{
				selectNextControl(null, true, true, true, false);
			}
		}
	}
	
	
	///
	protected void onHelpRequested(HelpEventArgs hea)
	{
		debug(EVENT_PRINT)
		{
			cprintf("{ Event: onHelpRequested - Control %.*s }\n", name);
		}
		
		helpRequested(this, hea);
	}
	
	
	///
	protected void onSystemColorsChanged(EventArgs ea)
	{
		debug(EVENT_PRINT)
		{
			cprintf("{ Event: onSystemColorsChanged - Control %.*s }\n", name);
		}
		
		systemColorsChanged(this, ea);
	}
	
	
	///
	protected void onHandleCreated(EventArgs ea)
	{
		if(!(cbits & CBits.VSTYLE))
			_disableVisualStyle();
		
		Font fon;
		fon = font;
		if(fon)
			SendMessageA(hwnd, WM_SETFONT, cast(WPARAM)fon.handle, 0);
		
		if(wregion)
		{
			// Need to make a copy of the region.
			SetWindowRgn(hwnd, dupHrgn(wregion.handle), true);
		}
		
		version(DFL_NO_DRAG_DROP) {} else
		{
			// Need to do "allowDrop = true/false" after created handle.
			// When do "allowDrop = true" in MyForm.this(),
			// Now is _allowDrop == true and droptarget is null.
			// Therefore call here allowDropImplement() without change _allowDrop value.
			allowDropImplement(_allowDrop);
		}
		
		debug
		{
			_handlecreated = true;
		}
	}
	
	
	///
	protected void onHandleDestroyed(EventArgs ea)
	{
		handleDestroyed(this, ea);
	}
	
	
	///
	protected void onPaintBackground(PaintEventArgs pea)
	{
		RECT rect;
		pea.clipRectangle.getRect(&rect);
		FillRect(pea.graphics.handle, &rect, hbrBg);
	}
	
	
	private static MouseButtons wparamMouseButtons(WPARAM wparam)
	{
		MouseButtons result;
		if(wparam & MK_LBUTTON)
			result |= MouseButtons.LEFT;
		if(wparam & MK_RBUTTON)
			result |= MouseButtons.RIGHT;
		if(wparam & MK_MBUTTON)
			result |= MouseButtons.MIDDLE;
		return result;
	}
	
	
	package final void prepareDc(HDC hdc)
	{
		//SetBkMode(hdc, TRANSPARENT); // ?
		//SetBkMode(hdc, OPAQUE); // ?
		SetBkColor(hdc, backColor.toRgb());
		SetTextColor(hdc, foreColor.toRgb());
	}
	
	
	// Message copy so it cannot be modified.
	deprecated protected void onNotifyMessage(Message msg)
	{
	}
	
	
	/+
	/+package+/ LRESULT customMsg(ref CustomMsg msg) // package
	{
		return 0;
	}
	+/
	
	
	///
	protected void onReflectedMessage(ref Message m)
	{
		switch(m.msg)
		{
			case WM_CTLCOLORSTATIC:
			case WM_CTLCOLORLISTBOX:
			case WM_CTLCOLOREDIT:
			case WM_CTLCOLORSCROLLBAR:
			case WM_CTLCOLORBTN:
			//case WM_CTLCOLORDLG: // ?
			//case 0x0019: //WM_CTLCOLOR; obsolete.
				prepareDc(cast(HDC)m.wParam);
				//assert(GetObjectA(hbrBg, 0, null));
				m.result = cast(LRESULT)hbrBg;
				break;
			
			default:
		}
	}
	
	
	// ChildWindowFromPoint includes both hidden and disabled.
	// This includes disabled windows, but not hidden.
	// Here is a point in this control, see if it's over a visible child.
	// Returns null if not even in this control's client.
	final HWND pointOverVisibleChild(Point pt) // package
	{
		if(pt.x < 0 || pt.y < 0)
			return HWND.init;
		if(pt.x > wclientsz.width || pt.y > wclientsz.height)
			return HWND.init;
		
		// Note: doesn't include non-DFL windows... TO-DO: fix.
		foreach(Control ctrl; ccollection)
		{
			if(!ctrl.visible)
				continue;
			if(!ctrl.isHandleCreated) // Shouldn't..
				continue;
			if(ctrl.bounds.contains(pt))
				return ctrl.hwnd;
		}
		
		return hwnd; // Just over this control.
	}
	
	
	version(_DFL_WINDOWS_HUNG_WORKAROUND)
	{
		DWORD ldlgcode = 0;
	}
	

	///
	protected bool processKeyPreview(ref Message m)
	{
		return parent && parent.processKeyPreview(m);
	}


	///
	// Returns true in order to break in wndProc(), because processed.
	protected final bool processKeyMessage(ref Message m)
	{
		if (parent && parent.processKeyPreview(m))
		{
			return true;
		}
		return processKeyEventArgs(m);
	}


	///
	protected void wndProc(ref Message msg)
	{
		//if(ctrlStyle & ControlStyles.ENABLE_NOTIFY_MESSAGE)
		//	onNotifyMessage(msg);
		
		switch(msg.msg)
		{
			case WM_PAINT:
				{
					// This can't be done in BeginPaint() becuase part might get
					// validated during this event ?
					//RECT uprect;
					//GetUpdateRect(hwnd, &uprect, true);
					//onInvalidated(new InvalidateEventArgs(Rect(&uprect)));
					
					PAINTSTRUCT ps;
					BeginPaint(msg.hWnd, &ps);
					try
					{
						//onInvalidated(new InvalidateEventArgs(Rect(&uprect)));
						
						scope PaintEventArgs pea = new PaintEventArgs(new Graphics(ps.hdc, false), Rect(&ps.rcPaint));
						
						// Probably because ControlStyles.ALL_PAINTING_IN_WM_PAINT.
						if(ps.fErase)
						{
							prepareDc(ps.hdc);
							onPaintBackground(pea);
						}
						
						prepareDc(ps.hdc);
						onPaint(pea);
					}
					finally
					{
						EndPaint(hwnd, &ps);
					}
				}
				return;
			
			case WM_ERASEBKGND:
				if(ctrlStyle & ControlStyles.OPAQUE)
				{
					msg.result = 1; // Erased.
				}
				else if(!(ctrlStyle & ControlStyles.ALL_PAINTING_IN_WM_PAINT))
				{
					RECT uprect;
					/+
					GetUpdateRect(hwnd, &uprect, false);
					+/
					uprect.left = 0;
					uprect.top = 0;
					uprect.right = clientSize.width;
					uprect.bottom = clientSize.height;
					
					prepareDc(cast(HDC)msg.wParam);
					scope PaintEventArgs pea = new PaintEventArgs(new Graphics(cast(HDC)msg.wParam, false), Rect(&uprect));
					onPaintBackground(pea);
					msg.result = 1; // Erased.
				}
				return;
			
			case WM_PRINTCLIENT:
				prepareDc(cast(HDC)msg.wParam);
				scope PaintEventArgs pea = new PaintEventArgs(new Graphics(cast(HDC)msg.wParam, false), Rect(Point(0, 0), wclientsz));
				onPaint(pea);
				return;
			
			case WM_CTLCOLORSTATIC:
			case WM_CTLCOLORLISTBOX:
			case WM_CTLCOLOREDIT:
			case WM_CTLCOLORSCROLLBAR:
			case WM_CTLCOLORBTN:
			//case WM_CTLCOLORDLG: // ?
			//case 0x0019: //WM_CTLCOLOR; obsolete.
				{
					Control ctrl = fromChildHandle(cast(HWND)msg.lParam);
					if(ctrl)
					{
						//ctrl.prepareDc(cast(HDC)msg.wParam);
						//msg.result = cast(LRESULT)ctrl.hbrBg;
						ctrl.onReflectedMessage(msg);
						return;
					}
				}
				break;
			
			case WM_WINDOWPOSCHANGED:
				{
					WINDOWPOS* wp = cast(WINDOWPOS*)msg.lParam;
					bool needLayout = false;
					
					//if(!wp.hwndInsertAfter)
					//	wp.flags |= SWP_NOZORDER; // ?
					
					bool didvis = false;
					if(wp.flags & (SWP_HIDEWINDOW | SWP_SHOWWINDOW))
					{
						needLayout = true; // Only if not didvis / if not recreating.
						if(!recreatingHandle) // Note: suppresses onVisibleChanged
						{
							if(wp.flags & SWP_HIDEWINDOW) // Hiding.
								_clicking = false;
							onVisibleChanged(EventArgs.empty);
							didvis = true;
							//break; // Showing min/max includes other flags.
						}
					}
					
					if(!(wp.flags & SWP_NOZORDER) /+ || (wp.flags & SWP_SHOWWINDOW) +/)
					{
						if(wparent)
							wparent.vchanged();
					}
					
					if(!(wp.flags & SWP_NOMOVE))
					{
						onMove(EventArgs.empty);
					}
					
					if(!(wp.flags & SWP_NOSIZE))
					{
						if(szdraw)
							invalidate(true);
						
						onResize(EventArgs.empty);
						
						needLayout = true;
					}
					
					// Frame change results in a new client size.
					if(wp.flags & SWP_FRAMECHANGED)
					{
						if(szdraw)
							invalidate(true);
						
						needLayout = true;
					}
					
					if(!didvis) // onVisibleChanged already triggers layout.
					{
						if(/+ (wp.flags & SWP_SHOWWINDOW) || +/ !(wp.flags & SWP_NOSIZE) ||
							!(wp.flags & SWP_NOZORDER)) // z-order determines what is positioned first.
						{
							suspendLayout(); // Note: exception could cause failure to restore.
							if(wparent)
								wparent.alayout(this);
							resumeLayout(false);
							needLayout = true;
						}
						
						if(needLayout)
						{
							alayout(this);
						}
					}
				}
				break;
			
			case WM_WINDOWPOSCHANGING:
				{
					WINDOWPOS* wp = cast(WINDOWPOS*)msg.lParam;
					
					if (!(wp.flags & SWP_NOMOVE)
					 && (location.x != wp.x || location.y != wp.y))
					{
						scope e = new MovingEventArgs(Point(wp.x, wp.y));
						onMoving(e);
						wp.x = e.x;
						wp.y = e.y;
					}
					if (!(wp.flags & SWP_NOSIZE)
					 && (width != wp.cx || height != wp.cy))
					{
						scope e = new SizingEventArgs(Size(wp.cx, wp.cy));
						onSizing(e);
						wp.cx = e.width;
						wp.cy = e.height;
					}
				}
				break;
			
			case WM_MOUSEMOVE:
				if(_clicking)
				{
					if(!(msg.wParam & MK_LBUTTON))
						_clicking = false;
				}
				
				if(trackMouseEvent) // Requires Windows 95 with IE 5.5, 98 or NT4.
				{
					if(!menter)
					{
						menter = true;
						
						POINT pt;
						GetCursorPos(&pt);
						MapWindowPoints(HWND.init, hwnd, &pt, 1);
						scope MouseEventArgs mea = new MouseEventArgs(wparamMouseButtons(msg.wParam), 0, pt.x, pt.y, 0);
						onMouseEnter(mea);
						
						TRACKMOUSEEVENT tme;
						tme.cbSize = TRACKMOUSEEVENT.sizeof;
						tme.dwFlags = TME_HOVER | TME_LEAVE;
						tme.hwndTrack = msg.hWnd;
						tme.dwHoverTime = HOVER_DEFAULT;
						trackMouseEvent(&tme);
					}
				}
				
				onMouseMove(new MouseEventArgs(wparamMouseButtons(msg.wParam), 0, GET_X_LPARAM(msg.lParam), GET_Y_LPARAM(msg.lParam), 0));
				break;
			
			case WM_SETCURSOR:
				// Just update it so that Control.defWndProc() can set it correctly.
				if(cast(HWND)msg.wParam == hwnd)
				{
					Cursor cur;
					cur = cursor;
					if(cur)
					{
						if(cast(HCURSOR)GetClassLongPtrA(hwnd, GCL_HCURSOR) != cur.handle)
							SetClassLongPtrA(hwnd, GCL_HCURSOR, cast(LONG_PTR)cur.handle);
					}
					else
					{
						if(cast(HCURSOR)GetClassLongPtrA(hwnd, GCL_HCURSOR) != HCURSOR.init)
							SetClassLongPtrA(hwnd, GCL_HCURSOR, cast(LONG_PTR)cast(HCURSOR)null);
					}
					Control.defWndProc(msg);
					return;
				}
				break;
			
			/+
			case WM_NEXTDLGCTL:
				if(!LOWORD(msg.lParam))
				{
					select(true, msg.wParam != 0);
					return;
				}
				break;
			+/
			
			case WM_KEYDOWN:
			case WM_KEYUP:
			case WM_CHAR:
			case WM_SYSKEYDOWN:
			case WM_SYSKEYUP: // TODO: Is it correct?
			case WM_SYSCHAR:
			//case WM_IMECHAR:
				/+
				if(processKeyEventArgs(msg))
				{
					// The key was processed.
					msg.result = 0;
					return;
				}
				msg.result = 1; // The key was not processed.
				break;
				+/
				if (processKeyMessage(msg))
				{
					//msg.result = 0;
					return;
				}
				defWndProc(msg);

				//msg.result = !processKeyEventArgs(msg);
				return;
			
			case WM_MOUSEWHEEL: // Requires Windows 98 or NT4.
				{
					scope MouseEventArgs mea = new MouseEventArgs(wparamMouseButtons(LOWORD(msg.wParam)), 0, GET_X_LPARAM(msg.lParam), GET_Y_LPARAM(msg.wParam), 0);
					onMouseWheel(mea);
				}
				break;
			
			case WM_MOUSEHOVER: // Requires Windows 95 with IE 5.5, 98 or NT4.
				{
					scope MouseEventArgs mea = new MouseEventArgs(wparamMouseButtons(msg.wParam), 0, GET_X_LPARAM(msg.lParam), GET_Y_LPARAM(msg.lParam), 0);
					onMouseHover(mea);
				}
				break;
			
			case WM_MOUSELEAVE: // Requires Windows 95 with IE 5.5, 98 or NT4.
				{
					menter = false;
					
					POINT pt;
					GetCursorPos(&pt);
					MapWindowPoints(HWND.init, hwnd, &pt, 1);
					scope MouseEventArgs mea = new MouseEventArgs(wparamMouseButtons(msg.wParam), 0, pt.x, pt.y, 0);
					onMouseLeave(mea);
				}
				break;
			
			case WM_LBUTTONDOWN:
				{
					_clicking = true;
					
					scope MouseEventArgs mea = new MouseEventArgs(MouseButtons.LEFT, 1, GET_X_LPARAM(msg.lParam), GET_Y_LPARAM(msg.lParam), 0);
					onMouseDown(mea);
					
					//if(ctrlStyle & ControlStyles.SELECTABLE)
					//	SetFocus(hwnd); // No, this goofs up stuff, including the ComboBox dropdown.
				}
				break;
			
			case WM_RBUTTONDOWN:
				{
					scope MouseEventArgs mea = new MouseEventArgs(MouseButtons.RIGHT, 1, GET_X_LPARAM(msg.lParam), GET_Y_LPARAM(msg.lParam), 0);
					onMouseDown(mea);
				}
				break;
			
			case WM_MBUTTONDOWN:
				{
					scope MouseEventArgs mea = new MouseEventArgs(MouseButtons.MIDDLE, 1, GET_X_LPARAM(msg.lParam), GET_Y_LPARAM(msg.lParam), 0);
					onMouseDown(mea);
				}
				break;
			
			case WM_LBUTTONUP:
				{
					if(msg.lParam == -1)
						break;
					
					// Use temp in case of exception.
					bool wasClicking = _clicking;
					_clicking = false;
					
					scope MouseEventArgs mea = new MouseEventArgs(MouseButtons.LEFT, 1, GET_X_LPARAM(msg.lParam), GET_Y_LPARAM(msg.lParam), 0);
					onMouseUp(mea);
					
					if(wasClicking && (ctrlStyle & ControlStyles.STANDARD_CLICK))
					{
						// See if the mouse up was over the control.
						if(Rect(0, 0, wclientsz.width, wclientsz.height).contains(mea.x, mea.y))
						{
							// Now make sure there's no child in the way.
							//if(ChildWindowFromPoint(hwnd, Point(mea.x, mea.y).point) == hwnd) // Includes hidden windows.
							if(pointOverVisibleChild(Point(mea.x, mea.y)) == hwnd)
								onClick(EventArgs.empty);
						}
					}
				}
				break;
			
			version(CUSTOM_MSG_HOOK)
			{}
			else
			{
				case WM_DRAWITEM:
					{
						Control ctrl;
						
						DRAWITEMSTRUCT* dis = cast(DRAWITEMSTRUCT*)msg.lParam;
						if(dis.CtlType == ODT_MENU)
						{
							// dis.hwndItem is the HMENU.
						}
						else
						{
							ctrl = Control.fromChildHandle(dis.hwndItem);
							if(ctrl)
							{
								//msg.result = ctrl.customMsg(*(cast(CustomMsg*)&msg));
								ctrl.onReflectedMessage(msg);
								return;
							}
						}
					}
					break;
				
				case WM_MEASUREITEM:
					{
						Control ctrl;
						
						MEASUREITEMSTRUCT* mis = cast(MEASUREITEMSTRUCT*)msg.lParam;
						if(!(mis.CtlType == ODT_MENU))
						{
							ctrl = Control.fromChildHandle(cast(HWND)mis.CtlID);
							if(ctrl)
							{
								//msg.result = ctrl.customMsg(*(cast(CustomMsg*)&msg));
								ctrl.onReflectedMessage(msg);
								return;
							}
						}
					}
					break;
				
				case WM_COMMAND:
					{
						HWND hwnd = cast(HWND)msg.lParam;
						Control ctrl = Control.fromChildHandle(hwnd);
						if(ctrl)
						{
							//msg.result = ctrl.customMsg(*(cast(CustomMsg*)&msg));
							ctrl.onReflectedMessage(msg);
							return;
						}
						else
						{
							version(DFL_NO_MENUS)
							{
							}
							else
							{
								int menuID = LOWORD(msg.wParam);
								MenuItem m = cast(MenuItem)Application.lookupMenuID(menuID);
								if(m)
								{
									//msg.result = m.customMsg(*(cast(CustomMsg*)&msg));
									m._reflectMenu(msg);
									//return; // ?
								}
							}
							return;
						}
					}
				
				case WM_NOTIFY:
					{
						NMHDR* nmh = cast(NMHDR*)msg.lParam;
						Control ctrl = Control.fromChildHandle(nmh.hwndFrom);
						if(ctrl)
						{
							//msg.result = ctrl.customMsg(*(cast(CustomMsg*)&msg));
							ctrl.onReflectedMessage(msg);
							return;
						}
					}
					break;
				
				case WM_HSCROLL:
				case WM_VSCROLL:
					{
						Control ctrl;
						HWND hWnd;

						hWnd = cast(HWND)msg.lParam;
						ctrl = Control.fromChildHandle(hWnd);
						if(ctrl)
						{
							ctrl.onReflectedMessage(msg);
							return;
						}
					}
					break;

				version(DFL_NO_MENUS)
				{
				}
				else
				{
					case WM_MENUSELECT:
						{
							UINT mflags;
							UINT uitem;
							int mid;
							MenuItem m;
							
							mflags = HIWORD(msg.wParam);
							uitem = LOWORD(msg.wParam); // Depends on the flags.
							
							if(mflags & MF_SYSMENU)
								break;
							
							if(mflags & MF_POPUP)
							{
								// -uitem- is an index.
								mid = GetMenuItemID(cast(HMENU)msg.lParam, uitem);
							}
							else
							{
								// -uitem- is the item identifier.
								mid = uitem;
							}
							
							m = cast(MenuItem)Application.lookupMenuID(mid);
							if(m)
							{
								//msg.result = m.customMsg(*(cast(CustomMsg*)&msg));
								m._reflectMenu(msg);
								//return;
							}
						}
						break;
					
					case WM_INITMENUPOPUP:
						if(HIWORD(msg.lParam))
						{
							// System menu.
						}
						else
						{
							MenuItem m;
							
							//m = cast(MenuItem)Application.lookupMenuID(GetMenuItemID(cast(HMENU)msg.wParam, LOWORD(msg.lParam)));
							m = cast(MenuItem)Application.lookupMenu(cast(HMENU)msg.wParam);
							if(m)
							{
								//msg.result = m.customMsg(*(cast(CustomMsg*)&msg));
								m._reflectMenu(msg);
								//return;
							}
						}
						break;
					
					case WM_INITMENU:
						{
							ContextMenu m;
							
							m = cast(ContextMenu)Application.lookupMenu(cast(HMENU)msg.wParam);
							if(m)
							{
								//msg.result = m.customMsg(*(cast(CustomMsg*)&msg));
								m._reflectMenu(msg);
								//return;
							}
						}
						break;
				}
			}
			
			case WM_RBUTTONUP:
				{
					scope MouseEventArgs mea = new MouseEventArgs(MouseButtons.RIGHT, 1, GET_X_LPARAM(msg.lParam), GET_Y_LPARAM(msg.lParam), 0);
					onMouseUp(mea);
				}
				break;
			
			case WM_MBUTTONUP:
				{
					scope MouseEventArgs mea = new MouseEventArgs(MouseButtons.MIDDLE, 1, GET_X_LPARAM(msg.lParam), GET_Y_LPARAM(msg.lParam), 0);
					onMouseUp(mea);
				}
				break;
			
			case WM_LBUTTONDBLCLK:
				{
					scope MouseEventArgs mea = new MouseEventArgs(MouseButtons.LEFT, 2, GET_X_LPARAM(msg.lParam), GET_Y_LPARAM(msg.lParam), 0);
					onMouseDown(mea);
					
					if((ctrlStyle & (ControlStyles.STANDARD_CLICK | ControlStyles.STANDARD_DOUBLE_CLICK))
						== (ControlStyles.STANDARD_CLICK | ControlStyles.STANDARD_DOUBLE_CLICK))
					{
						onDoubleClick(EventArgs.empty);
					}
				}
				break;
			
			case WM_RBUTTONDBLCLK:
				{
					scope MouseEventArgs mea = new MouseEventArgs(MouseButtons.RIGHT, 2, GET_X_LPARAM(msg.lParam), GET_Y_LPARAM(msg.lParam), 0);
					onMouseDown(mea);
				}
				break;
			
			case WM_MBUTTONDBLCLK:
				{
					scope MouseEventArgs mea = new MouseEventArgs(MouseButtons.MIDDLE, 2, GET_X_LPARAM(msg.lParam), GET_Y_LPARAM(msg.lParam), 0);
					onMouseDown(mea);
				}
				break;
			
			case WM_SETFOCUS:
				_wmSetFocus();
				// defWndProc* Form focuses a child.
				break;
			
			case WM_KILLFOCUS:
				_wmKillFocus();
				break;
			
			case WM_ENABLE:
				onEnabledChanged(EventArgs.empty);
				
				// defWndProc*
				break;
			
			/+
			case WM_NEXTDLGCTL:
				if(msg.wParam && !LOWORD(msg.lParam))
				{
					HWND hwf;
					hwf = GetFocus();
					if(hwf)
					{
						Control hwc;
						hwc = Control.fromHandle(hwf);
						if(hwc)
						{
							if(hwc._rtype() & 0x20) // TabControl
							{
								hwf = GetWindow(hwf, GW_CHILD);
								if(hwf)
								{
									// Can't do this because it could be modifying someone else's memory.
									//msg.wParam = cast(WPARAM)hwf;
									//msg.lParam = MAKELPARAM(1, 0);
									msg.result = DefWindowProcA(msg.hWnd, WM_NEXTDLGCTL, cast(WPARAM)hwf, MAKELPARAM(TRUE, 0));
									return;
								}
							}
						}
					}
				}
				break;
			+/
			
			case WM_SETTEXT:
				defWndProc(msg);
				
				// Need to fetch it because cast(char*)lparam isn't always accessible ?
				// Should this go in _wndProc()? Need to defWndProc() first ?
				if(ctrlStyle & ControlStyles.CACHE_TEXT)
					wtext = _fetchText();
				
				onTextChanged(EventArgs.empty);
				return;
			
			case WM_SETFONT:
				// Don't replace -wfont- if it's the same one, beacuse the old Font
				// object will get garbage collected and probably delete the HFONT.
				
				//onFontChanged(EventArgs.empty);
				
				// defWndProc*
				return;
			
			/+
			case WM_STYLECHANGED:
				{
					//defWndProc(msg);
					
					STYLESTRUCT* ss = cast(STYLESTRUCT*)msg.lParam;
					DWORD changed = ss.styleOld ^ ss.styleNew;
					
					if(msg.wParam == GWL_EXSTYLE)
					{
						//if(changed & WS_EX_RTLREADING)
						//	onRightToLeftChanged(EventArgs.empty);
					}
				}
				break;
			+/
			
			case WM_ACTIVATE:
				switch(LOWORD(msg.wParam))
				{
					case WA_INACTIVE:
						_clicking = false;
						break;
					
					default:
				}
				break;
			
			version(DFL_NO_MENUS)
			{
			}
			else
			{
				case WM_CONTEXTMENU:
					if(hwnd == cast(HWND)msg.wParam)
					{
						if(cmenu)
						{
							// Shift+F10 causes xPos and yPos to be -1.
							
							Point point;
							
							if(msg.lParam == -1)
								point = pointToScreen(Point(0, 0));
							else
								point = Point(GET_X_LPARAM(msg.lParam), GET_Y_LPARAM(msg.lParam));
							
							SetFocus(handle); // ?
							cmenu.show(this, point);
							
							return;
						}
					}
					break;
			}
			
			case WM_HELP:
				{
					HELPINFO* hi = cast(HELPINFO*)msg.lParam;
					
					scope HelpEventArgs hea = new HelpEventArgs(Point(hi.MousePos.x, hi.MousePos.y));
					onHelpRequested(hea);
					if(hea.handled)
					{
						msg.result = TRUE;
						return;
					}
				}
				break;
			
			case WM_SYSCOLORCHANGE:
				onSystemColorsChanged(EventArgs.empty);
				
				// Need to send the message to children for some common controls to update properly.
				foreach(Control ctrl; ccollection)
				{
					SendMessageA(ctrl.handle, WM_SYSCOLORCHANGE, msg.wParam, msg.lParam);
				}
				break;
			
			case WM_SETTINGCHANGE:
				// Send the message to children.
				foreach(Control ctrl; ccollection)
				{
					SendMessageA(ctrl.handle, WM_SETTINGCHANGE, msg.wParam, msg.lParam);
				}
				break;
			
			case WM_PALETTECHANGED:
				/+
				if(cast(HWND)msg.wParam != hwnd)
				{
					// Realize palette.
				}
				+/
				
				// Send the message to children.
				foreach(Control ctrl; ccollection)
				{
					SendMessageA(ctrl.handle, WM_PALETTECHANGED, msg.wParam, msg.lParam);
				}
				break;
			
			//case WM_QUERYNEWPALETTE: // Send this message to children ?
			
			/+
			// Moved this stuff to -parent-.
			case WM_PARENTNOTIFY:
				switch(LOWORD(msg.wParam))
				{
					case WM_DESTROY:
						Control ctrl = fromChildHandle(cast(HWND)msg.lParam);
						if(ctrl)
						{
							_ctrlremoved(new ControlEventArgs(ctrl));
							
							// ?
							vchanged();
							//alayout(ctrl); // This is already being called from somewhere else..
						}
						break;
					
					/+
					case WM_CREATE:
						initLayout();
						break;
					+/
					
					default:
				}
				break;
			+/
			
			case WM_CREATE:
				/+
				if(wparent)
					initLayout(); // ?
				+/
				if(cbits & CBits.NEED_INIT_LAYOUT)
				{
					if(visible)
					{
						if(wparent)
						{
							wparent.vchanged();
							suspendLayout(); // Note: exception could cause failure to restore.
							wparent.alayout(this);
							resumeLayout(false);
						}
						alayout(this);
					}
				}
				break;
			
			case WM_DESTROY:
				onHandleDestroyed(EventArgs.empty);
				break;
			
			case WM_GETDLGCODE:
				{
					version(_DFL_WINDOWS_HUNG_WORKAROUND)
					{
						/+
						if(ctrlStyle & ControlStyles.CONTAINER_CONTROL)
						{
							if(!(_exStyle & WS_EX_CONTROLPARENT))
								assert(0);
						}
						+/
						
						DWORD dw;
						dw = GetTickCount();
						if(ldlgcode < dw - 1020)
						{
							ldlgcode = dw - 1000;
						}
						else
						{
							ldlgcode += 50;
							if(ldlgcode > dw)
							{
								// Probably a problem with WS_EX_CONTROLPARENT and WS_TABSTOP.
								if(ldlgcode >= ldlgcode.max - 10_000)
								{
									ldlgcode = 0;
									throw new WindowsHungDflException("Windows hung");
								}
								//msg.result |= 0x0004 | 0x0002 | 0x0001; //DLGC_WANTALLKEYS | DLGC_WANTTAB | DLGC_WANTARROWS;
								ldlgcode = ldlgcode.max - 10_000;
								return;
							}
						}
					}
					
					/+
					if(msg.lParam)
					{
						Message m;
						m._winMsg = *cast(MSG*)msg.lParam;
						if(processKeyEventArgs(m))
							return;
					}
					+/
					
					defWndProc(msg);
					
					// Only want chars if ALT isn't down, because it would break mnemonics.
					if(!(GetKeyState(VK_MENU) & 0x8000))
						msg.result |= DLGC_WANTCHARS;
					
				}
				return;
			
			case WM_CLOSE:
				/+{
					if(parent)
					{
						Message mp;
						mp = msg;
						mp.hWnd = parent.handle;
						parent.wndProc(mp); // Pass to parent so it can decide what to do.
					}
				}+/
				return; // Prevent defWndProc from destroying the window!
			
			case 0: // WM_NULL
				// Don't confuse with failed RegisterWindowMessage().
				break;
			
			default:
				//defWndProc(msg);
				version(DFL_NO_WM_GETCONTROLNAME)
				{
				}
				else
				{
					if(msg.msg == wmGetControlName)
					{
						//cprintf("WM_GETCONTROLNAME: %.*s; wparam: %d\n", cast(uint)name.length, name.ptr, msg.wParam);
						if(msg.wParam && this.name.length)
						{
							OSVERSIONINFOA osver;
							osver.dwOSVersionInfoSize = OSVERSIONINFOA.sizeof;
							if(GetVersionExA(&osver))
							{
								try
								{
									if(osver.dwPlatformId <= VER_PLATFORM_WIN32_WINDOWS)
									{
										if(dfl.internal.utf.useUnicode)
										{
										}
										else
										{
											// ANSI.
											Dstring ansi;
											ansi = dfl.internal.utf.toAnsi(this.name);
											if(msg.wParam <= ansi.length)
												ansi = ansi[0 .. msg.wParam - 1];
											(cast(char*)msg.lParam)[0 .. ansi.length] = ansi[];
											(cast(char*)msg.lParam)[ansi.length] = 0;
											msg.result = ansi.length + 1;
										}
									}
									else
									{
										// Unicode.
										Dwstring uni;
										uni = dfl.internal.utf.toUnicode(this.name);
										if(msg.wParam <= uni.length)
											uni = uni[0 .. msg.wParam - 1];
										(cast(wchar*)msg.lParam)[0 .. uni.length] = uni[];
										(cast(wchar*)msg.lParam)[uni.length] = 0;
										msg.result = uni.length + 1;
									}
								}
								catch(Exception)
								{
								}
								return;
							}
						}
					}
				}
		}
		
		defWndProc(msg);
		
		if(msg.msg == WM_CREATE)
		{
			EventArgs ea;
			ea = EventArgs.empty;
			onHandleCreated(ea);
			
			debug
			{
				assert(_handlecreated, "If overriding onHandleCreated(), be sure to call super.onHandleCreated() first!");
			}
			handleCreated(this, ea);
			debug
			{
				_handlecreated = false; // Reset.
			}
		}
	}
	
	
	package final void _wmSetFocus()
	{
		//onEnter(EventArgs.empty);
		
		onGotFocus(EventArgs.empty);
		
		// defWndProc* Form focuses a child.
	}
	
	
	package final void _wmKillFocus()
	{
		_clicking = false;
		
		//onLeave(EventArgs.empty);
		
		//if(cvalidation)
		//	onValidating(new CancelEventArgs);
		
		onLostFocus(EventArgs.empty);
	}
	
	
	///
	protected void defWndProc(ref Message msg)
	{
		//msg.result = DefWindowProcA(msg.hWnd, msg.msg, msg.wParam, msg.lParam);
		msg.result = dfl.internal.utf.defWindowProc(msg.hWnd, msg.msg, msg.wParam, msg.lParam);
	}
	
	
	// Always called right when destroyed, before doing anything else.
	// hwnd is cleared after this step.
	void _destroying() // package
	{
		//wparent = null; // ?
	}
	
	
	// This function must be called FIRST for EVERY message to this
	// window in order to keep the correct window state.
	// This function must not throw exceptions.
	package final void mustWndProc(ref Message msg)
	{
		if(needCalcSize)
		{
			needCalcSize = false;
			RECT crect;
			GetClientRect(msg.hWnd, &crect);
			wclientsz.width = crect.right;
			wclientsz.height = crect.bottom;
		}
		
		switch(msg.msg)
		{
			case WM_NCCALCSIZE:
				needCalcSize = true;
				break;
			
			case WM_WINDOWPOSCHANGED:
				{
					WINDOWPOS* wp = cast(WINDOWPOS*)msg.lParam;
					
					if(!recreatingHandle)
					{
						//wstyle = GetWindowLongPtrA(hwnd, GWL_STYLE); // ..WM_SHOWWINDOW.
						if(wp.flags & (SWP_HIDEWINDOW | SWP_SHOWWINDOW))
						{
							//wstyle = GetWindowLongPtrA(hwnd, GWL_STYLE);
							cbits |= CBits.VISIBLE;
							wstyle |= WS_VISIBLE;
							if(wp.flags & SWP_HIDEWINDOW) // Hiding.
							{
								cbits &= ~CBits.VISIBLE;
								wstyle &= ~WS_VISIBLE;
							}
							//break; // Showing min/max includes other flags.
						}
					}
					
					//if(!(wp.flags & SWP_NOMOVE))
					//	wrect.location = Point(wp.x, wp.y);
					if(!(wp.flags & SWP_NOSIZE) || !(wp.flags & SWP_NOMOVE) || (wp.flags & SWP_FRAMECHANGED))
					{
						//wrect = _fetchBounds();
						wrect = Rect(wp.x, wp.y, wp.cx, wp.cy);
						wclientsz = _fetchClientSize();
					}
					
					if((wp.flags & (SWP_SHOWWINDOW | SWP_HIDEWINDOW)) || !(wp.flags & SWP_NOSIZE))
					{
						DWORD rstyle;
						rstyle = GetWindowLongPtrA(msg.hWnd, GWL_STYLE).toI32;
						rstyle &= WS_MAXIMIZE | WS_MINIMIZE;
						wstyle &= ~(WS_MAXIMIZE | WS_MINIMIZE);
						wstyle |= rstyle;
					}
				}
				break;
			
			/+
			case WM_WINDOWPOSCHANGING:
				//oldwrect = wrect;
				break;
			+/
			
			/+
			case WM_SETFONT:
				//wfont = _fetchFont();
				break;
			+/
			
			case WM_STYLECHANGED:
				{
					STYLESTRUCT* ss = cast(STYLESTRUCT*)msg.lParam;
					
					if(msg.wParam == GWL_STYLE)
						wstyle = ss.styleNew;
					else if(msg.wParam == GWL_EXSTYLE)
						wexstyle = ss.styleNew;
					
					/+
					wrect = _fetchBounds();
					wclientsz = _fetchClientSize();
					+/
				}
				break;
			
			/+
			// NOTE: this is sent even if the parent is shown.
			case WM_SHOWWINDOW:
				if(!msg.lParam)
				{
					/+
					{
						cbits &= ~(CBits.SW_SHOWN | CBits.SW_HIDDEN);
						DWORD rstyle;
						rstyle = GetWindowLongPtrA(msg.hWnd, GWL_STYLE);
						if(cast(BOOL)msg.wParam)
						{
							//wstyle |= WS_VISIBLE;
							if(!(WS_VISIBLE & wstyle) && (WS_VISIBLE & rstyle))
							{
								wstyle = rstyle;
								cbits |= CBits.SW_SHOWN;
								
								try
								{
									createChildren(); // Might throw.
								}
								catch(DThrowable e)
								{
									Application.onThreadException(e);
								}
							}
							wstyle = rstyle;
						}
						else
						{
							//wstyle &= ~WS_VISIBLE;
							if((WS_VISIBLE & wstyle) && !(WS_VISIBLE & rstyle))
							{
								wstyle = rstyle;
								cbits |= CBits.SW_HIDDEN;
							}
							wstyle = rstyle;
						}
					}
					+/
					wstyle = GetWindowLongPtrA(msg.hWnd, GWL_STYLE);
					//if(cbits & CBits.FVISIBLE)
					//	wstyle |= WS_VISIBLE;
				}
				break;
			+/
			
			case WM_ENABLE:
				/+
				//if(IsWindowEnabled(hwnd))
				if(cast(BOOL)msg.wParam)
					wstyle &= ~WS_DISABLED;
				else
					wstyle |= WS_DISABLED;
				+/
				wstyle = GetWindowLongPtrA(hwnd, GWL_STYLE).toI32;
				break;
			
			/+
			case WM_PARENTNOTIFY:
				switch(LOWORD(msg.wParam))
				{
					case WM_DESTROY:
						// ...
						break;
					
					default:
				}
				break;
			+/
			
			case WM_NCCREATE:
				{
					//hwnd = msg.hWnd;
					
					/+
					// Not using CREATESTRUCT for window bounds because it can contain
					// CW_USEDEFAULT and other magic values.
					
					CREATESTRUCTA* cs;
					cs = cast(CREATESTRUCTA*)msg.lParam;
					
					//wrect = Rect(cs.x, cs.y, cs.cx, cs.cy);
					+/
					
					wrect = _fetchBounds();
					//oldwrect = wrect;
					wclientsz = _fetchClientSize();
				}
				break;
			
			case WM_CREATE:
				try
				{
					cbits |= CBits.CREATED;
					
					//hwnd = msg.hWnd;
					
					CREATESTRUCTA* cs;
					cs = cast(CREATESTRUCTA*)msg.lParam;
					/+
					// Done in WM_NCCREATE now.
					//wrect = _fetchBounds();
					wrect = Rect(cs.x, cs.y, cs.cx, cs.cy);
					wclientsz = _fetchClientSize();
					+/
					
					// If class style was changed, update.
					if(_fetchClassLongPtr() != wclassStyle)
						SetClassLongPtrA(hwnd, GCL_STYLE, wclassStyle);
					
					// Need to update clientSize in case of styles in createParams().
					wclientsz = _fetchClientSize();
					
					//finishCreating(msg.hWnd);
					
					if(!(ctrlStyle & ControlStyles.CACHE_TEXT))
						wtext = null;
					
					/+
					// Gets created on demand instead.
					if(Color.empty != backc)
					{
						hbrBg = backc.createBrush();
					}
					+/
					
					/+
					// ?
					wstyle = cs.style;
					wexstyle = cs.dwExStyle;
					+/
					
					createChildren(); // Might throw. Used to be commented-out.
					
					if(recreatingHandle)
					{
						// After existing messages and functions are done.
						delayInvoke(function(Control cthis, size_t[] params){ cthis.cbits &= ~CBits.RECREATING; });
					}
				}
				catch(DThrowable e)
				{
					Application.onThreadException(e);
				}
				break;
			
			case WM_DESTROY:
				cbits &= ~CBits.CREATED;
				if(!recreatingHandle)
					cbits &= ~CBits.FORMLOADED;
				_destroying();
				//if(!killing)
				if(recreatingHandle)
					fillRecreationData();
				break;
			
			case WM_NCDESTROY:
				Application.removeHwnd(hwnd);
				hwnd = HWND.init;
				break;
			
			default:
				/+
				if(msg.msg == wmDfl)
				{
					switch(msg.wParam)
					{
						case WPARAM_DFL_:
						
						default:
					}
				}
				+/
		}
	}
	
	
	package final void _wndProc(ref Message msg)
	{
		//mustWndProc(msg); // Done in dflWndProc() now.
		wndProc(msg);
	}
	
	
	package final void _defWndProc(ref Message msg)
	{
		defWndProc(msg);
	}
	
	
	package final void doShow()
	{
		if(wparent) // Exclude owner.
		{
			SetWindowPos(hwnd, HWND.init, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_SHOWWINDOW | SWP_NOACTIVATE | SWP_NOZORDER);
		}
		else
		{
			SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_SHOWWINDOW);
		}
	}
	
	
	package final void doHide()
	{
		SetWindowPos(hwnd, HWND.init, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE | SWP_HIDEWINDOW | SWP_NOZORDER);
	}
	
	
	//EventHandler backColorChanged;
	Event!(Control, EventArgs) backColorChanged; ///
	// EventHandler backgroundImageChanged;
	/+
	deprecated EventHandler causesValidationChanged;
	deprecated InvalidateEventHandler invalidated;
	deprecated EventHandler validated;
	deprecated CancelEventHandler validating; // Once cancel is true, remaining events are suppressed (including validated).
	deprecated EventHandler enter; // Cascades up. TODO: fix implementation.
	deprecated EventHandler leave; // Cascades down. TODO: fix implementation.
	deprecated UICuesEventHandler changeUICues; // TODO: properly fire.
	+/
	//EventHandler click;
	Event!(Control, EventArgs) click; ///
	version(DFL_NO_MENUS)
	{
	}
	else
	{
		//EventHandler contextMenuChanged;
		Event!(Control, EventArgs) contextMenuChanged; ///
	}
	//ControlEventHandler controlAdded;
	Event!(Control, ControlEventArgs) controlAdded; ///
	//ControlEventHandler controlRemoved;
	Event!(Control, ControlEventArgs) controlRemoved; ///
	//EventHandler cursorChanged;
	Event!(Control, EventArgs) cursorChanged; ///
	//EventHandler disposed;
	Event!(Control, EventArgs) disposed; ///
	//EventHandler dockChanged;
	//Event!(Control, EventArgs) dockChanged; ///
	Event!(Control, EventArgs) hasLayoutChanged; ///
	alias dockChanged = hasLayoutChanged;
	//EventHandler doubleClick;
	Event!(Control, EventArgs) doubleClick; ///
	//EventHandler enabledChanged;
	Event!(Control, EventArgs) enabledChanged; ///
	//EventHandler fontChanged;
	Event!(Control, EventArgs) fontChanged; ///
	//EventHandler foreColorChanged;
	Event!(Control, EventArgs) foreColorChanged; ///
	//EventHandler gotFocus; // After enter.
	Event!(Control, EventArgs) gotFocus; ///
	//EventHandler handleCreated;
	Event!(Control, EventArgs) handleCreated; ///
	//EventHandler handleDestroyed;
	Event!(Control, EventArgs) handleDestroyed; ///
	//HelpEventHandler helpRequested;
	Event!(Control, HelpEventArgs) helpRequested; ///
	//KeyEventHandler keyDown;
	Event!(Control, KeyEventArgs) keyDown; ///
	//KeyEventHandler keyPress;
	Event!(Control, KeyPressEventArgs) keyPress; ///
	//KeyEventHandler keyUp;
	Event!(Control, KeyEventArgs) keyUp; ///
	//LayoutEventHandler layout;
	Event!(Control, LayoutEventArgs) layout; ///
	//EventHandler lostFocus;
	Event!(Control, EventArgs) lostFocus; ///
	//MouseEventHandler mouseDown;
	Event!(Control, MouseEventArgs) mouseDown; ///
	//MouseEventHandler mouseEnter;
	Event!(Control, MouseEventArgs) mouseEnter; ///
	//MouseEventHandler mouseHover;
	Event!(Control, MouseEventArgs) mouseHover; ///
	//MouseEventHandler mouseLeave;
	Event!(Control, MouseEventArgs) mouseLeave; ///
	//MouseEventHandler mouseMove;
	Event!(Control, MouseEventArgs) mouseMove; ///
	//MouseEventHandler mouseUp;
	Event!(Control, MouseEventArgs) mouseUp; ///
	//MouseEventHandler mouseWheel;
	Event!(Control, MouseEventArgs) mouseWheel; ///
	//EventHandler moving;
	Event!(Control, MovingEventArgs) moving; ///
	//EventHandler move;
	Event!(Control, EventArgs) move; ///
	//EventHandler locationChanged;
	alias locationChanged = move;
	//PaintEventHandler paint;
	Event!(Control, PaintEventArgs) paint; ///
	//EventHandler parentChanged;
	Event!(Control, EventArgs) parentChanged; ///
	//EventHandler sizing;
	Event!(Control, SizingEventArgs) sizing; ///
	//EventHandler resize;
	Event!(Control, EventArgs) resize; ///
	//EventHandler sizeChanged;
	alias sizeChanged = resize;
	//EventHandler rightToLeftChanged;
	Event!(Control, EventArgs) rightToLeftChanged; ///
	// EventHandler styleChanged;
	//EventHandler systemColorsChanged;
	Event!(Control, EventArgs) systemColorsChanged; ///
	// EventHandler tabIndexChanged;
	// EventHandler tabStopChanged;
	//EventHandler textChanged;
	Event!(Control, EventArgs) textChanged; ///
	//EventHandler visibleChanged;
	Event!(Control, EventArgs) visibleChanged; ///
	
	version(DFL_NO_DRAG_DROP) {} else
	{
		//DragEventHandler dragDrop;
		Event!(Control, DragEventArgs) dragDrop; ///
		//DragEventHandler dragEnter;
		Event!(Control, DragEventArgs) dragEnter; ///
		//EventHandler dragLeave;
		Event!(Control, EventArgs) dragLeave; ///
		//DragEventHandler dragOver;
		Event!(Control, DragEventArgs) dragOver; ///
		//GiveFeedbackEventHandler giveFeedback;
		Event!(Control, GiveFeedbackEventArgs) giveFeedback; ///
		//QueryContinueDragEventHandler queryContinueDrag;
		Event!(Control, QueryContinueDragEventArgs) queryContinueDrag; ///
	}
	
	
	/// Construct a new Control instance.
	this()
	{
		//name = DObject.toString(); // ?
		
		wrect.size = defaultSize;
		//oldwrect = wrect;
		
		/+
		backc = defaultBackColor;
		forec = defaultForeColor;
		wfont = defaultFont;
		wcurs = new Cursor(LoadCursorA(HINSTANCE.init, IDC_ARROW), false);
		+/
		backc = Color.empty;
		forec = Color.empty;
		wfont = null;
		wcurs = null;
		
		ccollection = createControlsInstance();
	}
	
	/// ditto
	this(Dstring text)
	{
		this();
		wtext = text;
		
		ccollection = createControlsInstance();
	}
	
	/// ditto
	this(Control cparent, Dstring text)
	{
		this();
		wtext = text;
		parent = cparent;
		
		ccollection = createControlsInstance();
	}
	
	/// ditto
	this(Dstring text, int left, int top, int width, int height)
	{
		this();
		wtext = text;
		wrect = Rect(left, top, width, height);
		
		ccollection = createControlsInstance();
	}
	
	/// ditto
	this(Control cparent, Dstring text, int left, int top, int width, int height)
	{
		this();
		wtext = text;
		wrect = Rect(left, top, width, height);
		parent = cparent;
		
		ccollection = createControlsInstance();
	}
	
	
	/+
	// Used internally.
	this(HWND hwnd)
	in
	{
		assert(hwnd);
	}
	do
	{
		this.hwnd = hwnd;
		owned = false;
		
		ccollection = new ControlCollection(this);
	}
	+/
	
	
	~this()
	{
		debug(APP_PRINT)
			cprintf("~Control %p\n", cast(void*)this);
		
		version(DFL_NO_ZOMBIE_FORM)
		{
		}
		else
		{
			Application.zombieKill(this); // Does nothing if not zombie.
		}
		
		//dispose(false);
		destroyHandle();
		deleteThisBackgroundBrush();
	}
	
	
	/+ package +/ /+ protected +/ int _rtype() // package
	{
		return 0;
	}
	
	
	///
	void dispose()
	{
		dispose(true);
	}
	
	/// ditto
	protected void dispose(bool disposing)
	{
		if(disposing)
		{
			killing = true;
			
			version(DFL_NO_MENUS)
			{
			}
			else
			{
				cmenu = cmenu.init;
			}
			_ctrlname = _ctrlname.init;
			otag = otag.init;
			wcurs = wcurs.init;
			wfont = wfont.init;
			wparent = wparent.init;
			wregion = wregion.init;
			wtext = wtext.init;
			deleteThisBackgroundBrush();
			//ccollection.children = null; // Not GC-safe in dtor.
			//ccollection = null; // ? Causes bad things. Leaving it will do just fine.
		}
		
		if(!isHandleCreated)
			return;
		
		destroyHandle();
		/+
		//assert(hwnd == HWND.init); // Zombie trips this. (Not anymore with the hwnd-prop)
		if(hwnd)
		{
			assert(!IsWindow(hwnd));
			hwnd = HWND.init;
		}
		+/
		assert(hwnd == HWND.init);
		
		onDisposed(EventArgs.empty);
	}
	
	
	protected:
	
	///
	@property Size defaultSize() // getter
	{
		return Size(0, 0);
	}
	
	
	/+
	// TODO: implement.
	@property EventHandlerList events() // getter
	{
	}
	+/
	
	
	/+
	// TODO: implement. Is this worth implementing?
	
	// Set to -1 to reset cache.
	final @property void fontHeight(int fh) // setter
	{
		
	}
	
	
	final @property int fontHeight() // getter
	{
		return fonth;
	}
	+/
	
	
	///
	//final void resizeRedraw(bool byes) // setter
	public final @property void resizeRedraw(bool byes) // setter
	{
		/+
		// These class styles get lost sometimes so don't rely on them.
		LONG cl = _classStyle();
		if(byes)
			cl |= CS_HREDRAW | CS_VREDRAW;
		else
			cl &= ~(CS_HREDRAW | CS_VREDRAW);
		
		_classStyle(cl);
		+/
		szdraw = byes;
	}
	
	/// ditto
	final @property bool resizeRedraw() // getter
	{
		//return (_classStyle() & (CS_HREDRAW | CS_VREDRAW)) != 0;
		return szdraw;
	}
	
	
	/+
	// ///
	// I don't think this is reliable.
	final bool hasVisualStyle() // getter
	{
		bool result = false;
		HWND hw = handle; // Always reference handle.
		HMODULE huxtheme = GetModuleHandleA("uxtheme.dll");
		//HMODULE huxtheme = LoadLibraryA("uxtheme.dll");
		if(huxtheme)
		{
			auto getwintheme = cast(typeof(&GetWindowTheme))GetProcAddress(huxtheme, "GetWindowTheme");
			if(getwintheme)
			{
				result = getwintheme(hw) != null;
			}
			//FreeLibrary(huxtheme);
		}
		return result;
	}
	+/
	
	
	package final void _disableVisualStyle()
	{
		assert(isHandleCreated);
		
		HMODULE hmuxt;
		hmuxt = GetModuleHandleA("uxtheme.dll");
		if(hmuxt)
		{
			auto setWinTheme = cast(typeof(&SetWindowTheme))GetProcAddress(hmuxt, "SetWindowTheme");
			if(setWinTheme)
			{
				setWinTheme(hwnd, " "w.ptr, " "w.ptr); // Clear the theme.
			}
		}
	}
	
	
	///
	public final void disableVisualStyle(bool byes = true)
	{
		if(!byes)
		{
			if(cbits & CBits.VSTYLE)
				return;
			cbits |= CBits.VSTYLE;
			
			if(isHandleCreated)
			{
				_crecreate();
			}
		}
		else
		{
			if(!(cbits & CBits.VSTYLE))
				return;
			cbits &= ~CBits.VSTYLE;
			
			if(isHandleCreated)
				_disableVisualStyle();
		}
	}
	
	deprecated public final void enableVisualStyle(bool byes = true)
	{
		return disableVisualStyle(!byes);
	}
	
	
	///
	ControlCollection createControlsInstance()
	{
		return new ControlCollection(this);
	}
	
	
	deprecated package final void createClassHandle(Dstring className)
	{
		if(!wparent || !wparent.handle || killing)
		{
			create_err:
			throw new DflException("Control creation failure");
		}
		
		// This is here because referencing wparent.handle might create me.
		//if(created)
		if(isHandleCreated)
			return;
		
		Application.creatingControl(this);
		hwnd = dfl.internal.utf.createWindowEx(wexstyle, className, wtext, wstyle, wrect.x, wrect.y,
			wrect.width, wrect.height, wparent.handle, HMENU.init, Application.getInstance(), null);
		if(!hwnd)
			goto create_err;
	}
	
	
	///
	// Override to change the creation parameters.
	// Be sure to call super.createParams() or all the create params will need to be filled.
	protected void createParams(ref CreateParams cp)
	{
		with(cp)
		{
			className = CONTROL_CLASSNAME;
			caption = wtext;
			param = null;
			//parent = wparent.handle;
			parent = wparent ? wparent.handle : HWND.init;
			menu = HMENU.init;
			inst = Application.getInstance();
			x = wrect.x;
			y = wrect.y;
			width = wrect.width;
			height = wrect.height;
			classStyle = wclassStyle;
			exStyle = wexstyle;
			wstyle |= WS_VISIBLE;
			if(!(cbits & CBits.VISIBLE))
				wstyle &= ~WS_VISIBLE;
			style = wstyle;
		}
	}
	
	
	///
	protected void createHandle()
	{
		// Note: if modified, Form.createHandle() should be modified as well.
		
		if(isHandleCreated)
			return;
		
		//createClassHandle(CONTROL_CLASSNAME);
		
		/+
		if(!wparent || !wparent.handle || killing)
		{
			create_err:
			//throw new DflException("Control creation failure");
			throw new DflException(Object.toString() ~ " creation failure"); // ?
		}
		+/
		
		debug
		{
			Dstring er;
		}
		if(killing)
		{
			debug
			{
				er = "the control is being disposed";
			}
			
			debug(APP_PRINT)
			{
				cprintf("Creating Control handle while disposing.\n");
			}
			
			create_err:
			Dstring kmsg = "Control creation failure";
			if(name.length)
				kmsg ~= " (" ~ name ~ ")";
			debug
			{
				if(er.length)
					kmsg ~= " - " ~ er;
			}
			throw new DflException(kmsg);
			//throw new DflException(Object.toString() ~ " creation failure"); // ?
		}
		
		// Need the parent's handle to exist.
		if(wparent)
			wparent.createHandle();
		
		// This is here because wparent.createHandle() might create me.
		//if(created)
		if(isHandleCreated)
			return;
		
		CreateParams cp;
		/+
		DWORD prevClassStyle;
		prevClassStyle = wclassStyle;
		+/
		
		createParams(cp);
		assert(!isHandleCreated); // Make sure the handle wasn't created in createParams().
		
		with(cp)
		{
			wtext = caption;
			//wrect = Rect(x, y, width, height); // This gets updated in WM_CREATE.
			wclassStyle = classStyle;
			wexstyle = exStyle;
			wstyle = style;
			
			//if(style & WS_CHILD) // Breaks context-help.
			if((ctrlStyle & ControlStyles.CONTAINER_CONTROL) && (style & WS_CHILD))
			{
				exStyle |= WS_EX_CONTROLPARENT;
			}
			
			bool vis = (style & WS_VISIBLE) != 0;
			
			Application.creatingControl(this);
			hwnd = dfl.internal.utf.createWindowEx(exStyle, className, caption, (style & ~WS_VISIBLE), x, y,
				width, height, parent, menu, inst, param);
			if(!hwnd)
			{
				debug(APP_PRINT)
				{
					cprintf("CreateWindowEx failed."
						~" (exStyle=0x%X, className=`%.*s`, caption=`%.*s`, style=0x%X, x=%d, y=%d, width=%d, height=%d,"
						~" parent=0x%X, menu=0x%X, inst=0x%X, param=0x%X)\n",
						exStyle, className.ptr, caption.ptr, style, x, y, width, height,
						parent, menu, inst, param);
				}
				
				debug
				{
					er = std.string.format("CreateWindowEx failed {className=%s;exStyle=0x%X;style=0x%X;parent=0x%X;menu=0x%X;inst=0x%X;}",
						className, exStyle, style, cast(void*)parent, cast(void*)menu, cast(void*)inst);
				}
				
				goto create_err;
			}
			
			if(vis)
				doShow(); // Properly fires onVisibleChanged.
		}
		
		//onHandleCreated(EventArgs.empty); // Called in WM_CREATE now.
	}
	
	
	package final void _createHandle()
	{
		createHandle();
	}
	
	
	///
	public final @property bool recreatingHandle() // getter
	{
		if(cbits & CBits.RECREATING)
			return true;
		return false;
	}
	
	
	private void _setAllRecreating()
	{
		cbits |= CBits.RECREATING;
		foreach(Control cc; controls)
		{
			cc._setAllRecreating();
		}
	}
	
	
	///
	protected void recreateHandle()
	in
	{
		assert(!recreatingHandle);
	}
	do
	{
		if(!isHandleCreated)
			return;
		
		if(recreatingHandle)
			return;
		
		bool hfocus = focused;
		HWND prevHwnd = GetWindow(hwnd, GW_HWNDPREV);
		
		_setAllRecreating();
		//scope(exit)
		//	cbits &= ~CBits.RECREATING; // Now done from WM_CREATE.
		
		destroyHandle();
		createHandle();
		
		if(prevHwnd)
			SetWindowPos(hwnd, prevHwnd, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
		else
			SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
		if(hfocus)
			select();
	}
	
	
	///
	void destroyHandle()
	{
		if(!isHandleCreated)
			return;
		
		DestroyWindow(hwnd);
		
		// This stuff is done in WM_DESTROY because DestroyWindow() could be called elsewhere..
		//hwnd = HWND.init; // Done in WM_DESTROY.
		//onHandleDestroyed(EventArgs.empty); // Done in WM_DESTROY.
	}
	
	
	private final void fillRecreationData()
	{
		//cprintf(" { fillRecreationData %.*s }\n", name);
		
		if(!(ctrlStyle & ControlStyles.CACHE_TEXT))
			wtext = _fetchText();
		
		//wclassStyle = _fetchClassLongPtr(); // ?
		
		// Fetch children.
		Control[] ccs;
		foreach(Control cc; controls)
		{
			ccs ~= cc;
		}
		ccollection.children = ccs;
	}
	
	
	///
	protected void onDisposed(EventArgs ea)
	{
		disposed(this, ea);
	}
	
	
	///
	protected final bool getStyle(ControlStyles flag)
	{
		return (ctrlStyle & flag) != 0;
	}
	
	/// ditto
	protected final void setStyle(ControlStyles flag, bool value)
	{
		if(flag & ControlStyles.CACHE_TEXT)
		{
			if(value)
				wtext = _fetchText();
			else
				wtext = null;
		}
		
		if(value)
			ctrlStyle |= flag;
		else
			ctrlStyle &= ~flag;
	}
	
	
	///
	// Only for setStyle() styles that are part of hwnd and wndclass styles.
	protected final void updateStyles()
	{
		LONG newClassStyles = _classStyle();
		LONG newWndStyles = _style();
		
		if(ctrlStyle & ControlStyles.STANDARD_DOUBLE_CLICK)
			newClassStyles |= CS_DBLCLKS;
		else
			newClassStyles &= ~CS_DBLCLKS;
		
		/+
		if(ctrlStyle & ControlStyles.RESIZE_REDRAW)
			newClassStyles |= CS_HREDRAW | CS_VREDRAW;
		else
			newClassStyles &= ~(CS_HREDRAW | CS_VREDRAW);
		+/
		
		/+
		if(ctrlStyle & ControlStyles.SELECTABLE)
			newWndStyles |= WS_TABSTOP;
		else
			newWndStyles &= ~WS_TABSTOP;
		+/
		
		_classStyle(newClassStyles);
		_style(newWndStyles);
	}
	
	
	///
	final bool getTopLevel()
	{
		// return GetParent(hwnd) == HWND.init;
		return wparent is null;
	}
	
	
	package final void alayout(Control ctrl, bool vcheck = true)
	{
		if(vcheck && !visible)
			return;
		
		if(cbits & CBits.IN_LAYOUT)
			return;
		
		//if(_allowLayout)
		if(!_disallowLayout)
		{
			//cprintf("alayout\n");
			scope LayoutEventArgs lea = new LayoutEventArgs(ctrl);
			onLayout(lea);
		}
	}
	
	
	// Z-order of controls has changed.
	package final void vchanged()
	{
		// Z-order can't change if it's not created or invisible.
		//if(!isHandleCreated || !visible)
		//	return;
		
		version(RADIO_GROUP_LAYOUT)
		{
			//cprintf("vchanged\n");
			
			bool foundRadio = false;
			
			foreach(Control ctrl; ccollection)
			{
				if(!ctrl.visible)
					continue;
				
				if(ctrl._rtype() & 1) // Radio type.
				{
					LONG wlg;
					wlg = ctrl._style();
					if(foundRadio)
					{
						if(wlg & WS_GROUP)
							//ctrl._style(wlg & ~WS_GROUP);
							ctrl._style(wlg & ~(WS_GROUP | WS_TABSTOP));
					}
					else
					{
						foundRadio = true;
						
						if(!(wlg & WS_GROUP))
							//ctrl._style(wlg | WS_GROUP);
							ctrl._style(wlg | WS_GROUP | WS_TABSTOP);
					}
				}
				else
				{
					// Found non-radio so reset group.
					// Update: only reset group if found ctrl with WS_EX_CONTROLPARENT.
					// TODO: check if correct implementation.
					if(ctrl._exStyle() & WS_EX_CONTROLPARENT)
						foundRadio = false;
				}
			}
		}
	}
	
	
	///
	// Called after adding the control to a container.
	protected void initLayout()
	{
		assert(wparent !is null);
		if(visible && created) // ?
		{
			wparent.vchanged();
			wparent.alayout(this);
		}
	}
	
	
	///
	protected void onLayout(LayoutEventArgs lea)
	{
		// Note: exception could cause failure to restore.
		//suspendLayout();
		cbits |= CBits.IN_LAYOUT;
		
		debug(EVENT_PRINT)
		{
			cprintf("{ Event: onLayout - Control %.*s }\n", name);
		}
		
		Rect area;
		area = displayRectangle;
		
		foreach(Control ctrl; ccollection)
		{
			if(!ctrl.visible || !ctrl.created)
				continue;
			if(ctrl._rtype() & (2 | 4)) // Mdichild | Tabpage
				continue;
			
			//Rect prevctrlbounds;
			//prevctrlbounds = ctrl.bounds;
			//ctrl.suspendLayout(); // Note: exception could cause failure to restore.
			switch(ctrl.sdock)
			{
				case DockStyle.NONE:
					/+
					if(ctrl.anch & (AnchorStyles.RIGHT | AnchorStyles.BOTTOM)) // If none of these are set, no point in doing any anchor code.
					{
						Rect newb;
						newb = ctrl.bounds;
						if(ctrl.anch & AnchorStyles.RIGHT)
						{
							if(ctrl.anch & AnchorStyles.LEFT)
								newb.width += bounds.width - originalBounds.width;
							else
								newb.x += bounds.width - originalBounds.width;
						}
						if(ctrl.anch & AnchorStyles.BOTTOM)
						{
							if(ctrl.anch & AnchorStyles.LEFT)
								newb.height += bounds.height - originalBounds.height;
							else
								newb.y += bounds.height - originalBounds.height;
						}
						if(newb != ctrl.bounds)
							ctrl.bounds = newb;
					}
					+/
					break;
				
				case DockStyle.LEFT:
					ctrl.setBoundsCore(area.x, area.y, 0, area.height, cast(BoundsSpecified)(BoundsSpecified.LOCATION | BoundsSpecified.HEIGHT));
					area.x = area.x + ctrl.width;
					area.width = area.width - ctrl.width;
					break;
				
				case DockStyle.TOP:
					ctrl.setBoundsCore(area.x, area.y, area.width, 0, cast(BoundsSpecified)(BoundsSpecified.LOCATION | BoundsSpecified.WIDTH));
					area.y = area.y + ctrl.height;
					area.height = area.height - ctrl.height;
					break;
				
				case DockStyle.FILL:
					//ctrl.bounds(Rect(area.x, area.y, area.width, area.height));
					ctrl.bounds = area;
					// area = ?
					break;
				
				case DockStyle.BOTTOM:
					ctrl.setBoundsCore(area.x, area.bottom - ctrl.height, area.width, 0, cast(BoundsSpecified)(BoundsSpecified.LOCATION | BoundsSpecified.WIDTH));
					area.height = area.height - ctrl.height;
					break;
				
				case DockStyle.RIGHT:
					ctrl.setBoundsCore(area.right - ctrl.width, area.y, 0, area.height, cast(BoundsSpecified)(BoundsSpecified.LOCATION | BoundsSpecified.HEIGHT));
					area.width = area.width - ctrl.width;
					break;
				
				default:
					assert(0);
			}
			//ctrl.resumeLayout(true);
			//ctrl.resumeLayout(prevctrlbounds != ctrl.bounds);
		}
		
		layout(this, lea);
		
		//resumeLayout(false);
		cbits &= ~CBits.IN_LAYOUT;
	}
	
	/+
	// Not sure what to do here.
	deprecated bool isInputChar(char charCode)
	{
		return false;
	}
	+/
	protected bool isInputChar(char charCode)
	{
		int mask = 0;
		if (charCode == Keys.TAB)
		{
			mask = (DLGC_WANTCHARS | DLGC_WANTALLKEYS | DLGC_WANTTAB);
		}
		else
		{
			mask = (DLGC_WANTCHARS | DLGC_WANTALLKEYS);
		}

		return (SendMessage(hwnd, WM_GETDLGCODE, 0, 0) & mask) != 0;
	}


	/// isInputKey returns true when keyData is a regular input key.
	// If keyData is input key, then window message is sended to wndProc()
	// such as WM_KEYDOWN, WM_KEYUP, WM_CHAR and so on.
	protected bool isInputKey(Keys keyData)
	{
		if ((keyData & Keys.ALT) == Keys.ALT)
		{
			return false;
		}

		auto mask = DLGC_WANTALLKEYS;
		switch (keyData & Keys.KEY_CODE)
		{
			case Keys.TAB:
				mask = DLGC_WANTALLKEYS | DLGC_WANTTAB;
				break;
			case Keys.LEFT:
			case Keys.RIGHT:
			case Keys.UP:
			case Keys.DOWN:
				mask = DLGC_WANTALLKEYS | DLGC_WANTARROWS;
				break;
			default:
				// Nothing
		}

		// return isHandleCreated
		// 	&& (SendMessage(hwnd, WM_GETDLGCODE, 0, 0) & mask) != 0;
		if (isHandleCreated)
		{
			LRESULT r = SendMessage(hwnd, WM_GETDLGCODE, 0, 0);
			return (r & mask) != 0;
		}
		return false;
	}	

	///
	void setVisibleCore(bool byes)
	{
		if(isHandleCreated)
		{
			//wstyle = GetWindowLongPtrA(hwnd, GWL_STYLE);
			if(visible == byes)
				return;
			
			//ShowWindow(hwnd, byes ? SW_SHOW : SW_HIDE);
			if(byes)
				doShow();
			else
				doHide();
		}
		else
		{
			if(byes)
			{
				cbits |= CBits.VISIBLE;
				wstyle |= WS_VISIBLE;
				createControl();
			}
			else
			{
				cbits &= ~CBits.VISIBLE;
				wstyle &= ~WS_VISIBLE;
				return; // Not created and being hidden..
			}
		}
	}
	
	
	package final bool _wantTabKey()
	{
		// if(ctrlStyle & ControlStyles.WANT_TAB_KEY)
		if((DLGC_WANTTAB & sendMessage(hwnd, WM_GETDLGCODE, 0, 0)) != 0)
			return true;
		else
			return false;
	}
	
	
	///
	// Return true if processed.
	protected bool processKeyEventArgs(ref Message msg)
	{
		// TODO: implement more (IME etc...)

		switch(msg.msg)
		{
			case WM_CHAR:
			case WM_SYSCHAR:
				{
					scope KeyPressEventArgs kpea = new KeyPressEventArgs(cast(dchar)msg.wParam, modifierKeys);
					onKeyPress(kpea);
					return kpea.handled;
				}

			case WM_KEYDOWN:
			case WM_SYSKEYDOWN:
				{
					scope KeyEventArgs kea = new KeyEventArgs(cast(Keys)(msg.wParam | modifierKeys));
					ushort repeat = msg.lParam & 0xFFFF; // First 16 bits.
					for(; repeat; repeat--)
					{
						//kea.handled = false;
						onKeyDown(kea);
					}
					
					return kea.handled;
				}
			
			case WM_KEYUP:
			case WM_SYSKEYUP: // TODO: Is it correct?
				{
					// Repeat count is always 1 for key up.
					scope KeyEventArgs kea = new KeyEventArgs(cast(Keys)(msg.wParam | modifierKeys));
					onKeyUp(kea);
					return kea.handled;
				}
			
			default:
				assert(0); // Does not reached here, if WM is traped in Control.wndProc() correctly.
		}
	}
	
	
	package final bool _processKeyEventArgs(ref Message msg)
	{
		return processKeyEventArgs(msg);
	}
	

	///
	protected bool processMnemonic(dchar charCode)
	{
		return false;
	}
	
	
	package bool _processMnemonic(dchar charCode)
	{
		return processMnemonic(charCode);
	}
	
	
	// Retain DFL 0.9.5 compatibility.
	public deprecated void setDFL095()
	{
		version(SET_DFL_095)
		{
			pragma(msg, "DFL: DFL 0.9.5 compatibility set at compile time");
		}
		else
		{
			//_compat = CCompat.DFL095;
			Application.setCompat(DflCompat.CONTROL_RECREATE_095);
		}
	}
	
	package enum CCompat: ubyte
	{
		NONE = 0,
		DFL095 = 1,
	}
	
	version(SET_DFL_095)
		package enum _compat = CCompat.DFL095;
	else version(DFL_NO_COMPAT)
		package enum _compat = CCompat.NONE;
	else
		package @property CCompat _compat() // getter
			{ if(Application._compat & DflCompat.CONTROL_RECREATE_095) return CCompat.DFL095; return CCompat.NONE; }
	
	
	package final void _crecreate()
	{
		if(CCompat.DFL095 != _compat)
		{
			if(!recreatingHandle)
				recreateHandle();
		}
	}
	
	
	package:
	HWND hwnd;
	//AnchorStyles anch = cast(AnchorStyles)(AnchorStyles.TOP | AnchorStyles.LEFT);
	//bool cvalidation = true;
	version(DFL_NO_MENUS)
	{
	}
	else
	{
		ContextMenu cmenu;
	}
	DockStyle sdock = DockStyle.NONE;
	Dstring _ctrlname;
	Object otag;
	Color backc, forec;
	Rect wrect;
	//Rect oldwrect;
	Size wclientsz;
	Cursor wcurs;
	Font wfont;
	Control wparent;
	Region wregion;
	ControlCollection ccollection;
	Dstring wtext; // After creation, this isn't used unless ControlStyles.CACHE_TEXT.
	ControlStyles ctrlStyle = ControlStyles.STANDARD_CLICK | ControlStyles.STANDARD_DOUBLE_CLICK /+ | ControlStyles.RESIZE_REDRAW +/ ;
	HBRUSH _hbrBg;
	RightToLeft rtol = RightToLeft.INHERIT;
	uint _disallowLayout = 0;
	
	version(DFL_NO_DRAG_DROP) {} else
	{
		DropTarget droptarget = null;
		bool _allowDrop = false;
	}
	
	// Note: WS_VISIBLE is not reliable.
	LONG wstyle = WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS; // Child, visible and enabled by default.
	LONG wexstyle;
	LONG wclassStyle = WNDCLASS_STYLE;
	
	
	enum CBits: uint
	{
		NONE = 0x0,
		MENTER = 0x1, // Is mouse entered? Only valid if -trackMouseEvent- is non-null.
		KILLING = 0x2,
		OWNED = 0x4,
		//ALLOW_LAYOUT = 0x8,
		CLICKING = 0x10,
		NEED_CALC_SIZE = 0x20,
		SZDRAW = 0x40,
		OWNEDBG = 0x80,
		HANDLE_CREATED = 0x100, // debug only
		SW_SHOWN = 0x200,
		SW_HIDDEN = 0x400,
		CREATED = 0x800,
		NEED_INIT_LAYOUT = 0x1000,
		IN_LAYOUT = 0x2000,
		FVISIBLE = 0x4000,
		VISIBLE = 0x8000,
		NOCLOSING = 0x10000,
		ASCROLL = 0x20000,
		ASCALE = 0x40000,
		FORM = 0x80000,
		RECREATING = 0x100000,
		HAS_LAYOUT = 0x200000,
		VSTYLE = 0x400000, // If not forced off.
		FORMLOADED = 0x800000, // If not forced off.
		ENABLED = 0x1000000, // Enabled state, not considering the parent.
	}
	
	//CBits cbits = CBits.ALLOW_LAYOUT;
	//CBits cbits = CBits.NONE;
	CBits cbits = CBits.VISIBLE | CBits.VSTYLE | CBits.ENABLED;
	
	
	final:
	
	@property void menter(bool byes) // setter
		{ if(byes) cbits |= CBits.MENTER; else cbits &= ~CBits.MENTER; }
	@property bool menter() // getter
		{ return (cbits & CBits.MENTER) != 0; }
	
	@property void killing(bool byes) // setter
		//{ if(byes) cbits |= CBits.KILLING; else cbits &= ~CBits.KILLING; }
		{ assert(byes); if(byes) cbits |= CBits.KILLING; }
	@property bool killing() // getter
		{ return (cbits & CBits.KILLING) != 0; }
	
	@property void owned(bool byes) // setter
		{ if(byes) cbits |= CBits.OWNED; else cbits &= ~CBits.OWNED; }
	@property bool owned() // getter
		{ return (cbits & CBits.OWNED) != 0; }
	
	/+
	void _allowLayout(bool byes) // setter
		{ if(byes) cbits |= CBits.ALLOW_LAYOUT; else cbits &= ~CBits.ALLOW_LAYOUT; }
	bool _allowLayout() // getter
		{ return (cbits & CBits.ALLOW_LAYOUT) != 0; }
	+/
	
	@property void _clicking(bool byes) // setter
		{ if(byes) cbits |= CBits.CLICKING; else cbits &= ~CBits.CLICKING; }
	@property bool _clicking() // getter
		{ return (cbits & CBits.CLICKING) != 0; }
	
	@property void needCalcSize(bool byes) // setter
		{ if(byes) cbits |= CBits.NEED_CALC_SIZE; else cbits &= ~CBits.NEED_CALC_SIZE; }
	@property bool needCalcSize() // getter
		{ return (cbits & CBits.NEED_CALC_SIZE) != 0; }
	
	@property void szdraw(bool byes) // setter
		{ if(byes) cbits |= CBits.SZDRAW; else cbits &= ~CBits.SZDRAW; }
	@property bool szdraw() // getter
		{ return (cbits & CBits.SZDRAW) != 0; }
	
	@property void ownedbg(bool byes) // setter
		{ if(byes) cbits |= CBits.OWNEDBG; else cbits &= ~CBits.OWNEDBG; }
	@property bool ownedbg() // getter
		{ return (cbits & CBits.OWNEDBG) != 0; }
	
	debug
	{
		@property void _handlecreated(bool byes) // setter
			{ if(byes) cbits |= CBits.HANDLE_CREATED; else cbits &= ~CBits.HANDLE_CREATED; }
		@property bool _handlecreated() // getter
			{ return (cbits & CBits.HANDLE_CREATED) != 0; }
	}
	
	
	@property LONG _exStyle()
	{
		// return GetWindowLongPtrA(hwnd, GWL_EXSTYLE);
		return wexstyle;
	}
	
	
	@property void _exStyle(LONG wl)
	{
		if(isHandleCreated)
		{
			SetWindowLongPtrA(hwnd, GWL_EXSTYLE, wl);
		}
		
		wexstyle = wl;
	}
	
	
	@property LONG _style()
	{
		// return GetWindowLongPtrA(hwnd, GWL_STYLE);
		return wstyle;
	}
	
	
	@property void _style(LONG wl)
	{
		if(isHandleCreated)
		{
			SetWindowLongPtrA(hwnd, GWL_STYLE, wl);
		}
		
		wstyle = wl;
	}
	
	
	@property HBRUSH hbrBg() // getter
	{
		if(_hbrBg)
			return _hbrBg;
		if(backc == Color.empty && parent && backColor == parent.backColor)
		{
			ownedbg = false;
			_hbrBg = parent.hbrBg;
			return _hbrBg;
		}
		hbrBg = backColor.createBrush(); // Call hbrBg's setter and set ownedbg.
		return _hbrBg;
	}
	
	
	@property void hbrBg(HBRUSH hbr) // setter
	in
	{
		if(hbr)
		{
			assert(!_hbrBg);
		}
	}
	do
	{
		_hbrBg = hbr;
		ownedbg = true;
	}
	
	
	void deleteThisBackgroundBrush()
	{
		if(_hbrBg)
		{
			if(ownedbg)
				DeleteObject(_hbrBg);
			_hbrBg = HBRUSH.init;
		}
	}
	
	
	LRESULT defwproc(UINT msg, WPARAM wparam, LPARAM lparam)
	{
		//return DefWindowProcA(hwnd, msg, wparam, lparam);
		return dfl.internal.utf.defWindowProc(hwnd, msg, wparam, lparam);
	}
	
	
	LONG_PTR _fetchClassLongPtr()
	{
		return GetClassLongPtrA(hwnd, GCL_STYLE);
	}
	
	
	LONG _classStyle()
	{
		// return GetClassLongPtrA(hwnd, GCL_STYLE);
		// return wclassStyle;
		
		if(isHandleCreated)
		{
			// Always fetch because it's not guaranteed to be accurate.
			wclassStyle = _fetchClassLongPtr().toI32;
		}
		
		return wclassStyle;
	}
	
	
	package void _classStyle(LONG cl)
	{
		if(isHandleCreated)
		{
			SetClassLongPtrA(hwnd, GCL_STYLE, cl);
		}
		
		wclassStyle = cl;
	}
}


package abstract class ControlSuperClass: Control // dapi.d
{
	// Call previous wndProc().
	abstract protected void prevWndProc(ref Message msg);
	
	
	protected override void wndProc(ref Message msg)
	{
		switch(msg.msg)
		{
			case WM_PAINT:
				{
					RECT uprect;
					//GetUpdateRect(hwnd, &uprect, true);
					//onInvalidated(new InvalidateEventArgs(Rect(&uprect)));
					
					//if(!msg.wParam)
						GetUpdateRect(hwnd, &uprect, false); // Preserve.
					
					prevWndProc(msg);
					
					// Now fake a normal paint event...
					
					scope Graphics gpx = new CommonGraphics(hwnd, GetDC(hwnd));
					//scope Graphics gpx = new CommonGraphics(hwnd, msg.wParam ? cast(HDC)msg.wParam : GetDC(hwnd), msg.wParam ? false : true);
					HRGN hrgn;
					
					hrgn = CreateRectRgnIndirect(&uprect);
					SelectClipRgn(gpx.handle, hrgn);
					DeleteObject(hrgn);
					
					scope PaintEventArgs pea = new PaintEventArgs(gpx, Rect(&uprect));
					
					// Can't erase the background now, Windows just painted..
					//if(ps.fErase)
					//{
					//	prepareDc(gpx.handle);
					//	onPaintBackground(pea);
					//}
					
					prepareDc(gpx.handle);
					onPaint(pea);
				}
				break;
			
			case WM_PRINTCLIENT:
				{
					prevWndProc(msg);
					
					scope Graphics gpx = new CommonGraphics(hwnd, GetDC(hwnd));
					scope PaintEventArgs pea = new PaintEventArgs(gpx,
						Rect(Point(0, 0), wclientsz));
					
					prepareDc(pea.graphics.handle);
					onPaint(pea);
				}
				break;
			
			case WM_PRINT:
				Control.defWndProc(msg);
				break;
			
			case WM_ERASEBKGND:
				Control.wndProc(msg);
				break;
			
			case WM_NCACTIVATE:
			case WM_NCCALCSIZE:
			case WM_NCCREATE:
			case WM_NCPAINT:
				prevWndProc(msg);
				break;
			
			case WM_KEYDOWN:
			case WM_KEYUP:
			case WM_CHAR:
			case WM_SYSKEYDOWN:
			case WM_SYSKEYUP:
			case WM_SYSCHAR:
			//case WM_IMECHAR:
				super.wndProc(msg);
				return;
			
			default:
				prevWndProc(msg);
				super.wndProc(msg);
		}
	}
	
	
	override void defWndProc(ref Message m)
	{
		switch(m.msg)
		{
			case WM_KEYDOWN:
			case WM_KEYUP:
			case WM_CHAR:
			case WM_SYSKEYDOWN:
			case WM_SYSKEYUP:
			case WM_SYSCHAR:
			//case WM_IMECHAR: // ?
				prevWndProc(m);
				break;
			
			default:
		}
	}
	
	
	protected override void onPaintBackground(PaintEventArgs pea)
	{
		Message msg;
		
		msg.hWnd = handle;
		msg.msg = WM_ERASEBKGND;
		msg.wParam = cast(WPARAM)pea.graphics.handle;
		
		prevWndProc(msg);
		
		// Don't paint the background twice.
		//super.onPaintBackground(pea);
		
		// Event ?
		//paintBackground(this, pea);
	}
}


///
class ScrollableControl: Control // docmain
{
	// ///
	deprecated void autoScroll(bool byes) // setter
	{
		if(byes)
			cbits |= CBits.ASCROLL;
		else
			cbits &= ~CBits.ASCROLL;
	}
	
	// /// ditto
	deprecated bool autoScroll() // getter
	{
		return (cbits & CBits.ASCROLL) == CBits.ASCROLL;
	}
	
	
	// ///
	deprecated final void autoScrollMargin(Size sz) // setter
	{
		//scrollmargin = sz;
	}
	
	// /// ditto
	deprecated final Size autoScrollMargin() // getter
	{
		//return scrollmargin;
		return Size(0, 0);
	}
	
	
	// ///
	deprecated final void autoScrollMinSize(Size sz) // setter
	{
		//scrollmin = sz;
	}
	
	// /// ditto
	deprecated final Size autoScrollMinSize() // getter
	{
		//return scrollmin;
		return Size(0, 0);
	}
	
	
	// ///
	deprecated final void autoScrollPosition(Point pt) // setter
	{
		//autoscrollpos = pt;
	}
	
	// /// ditto
	deprecated final Point autoScrollPosition() // getter
	{
		//return autoscrollpos;
		return Point(0, 0);
	}
	
	
	///
	final @property Size autoScaleBaseSize() // getter
	{
		return autossz;
	}
	
	/// ditto
	final @property void autoScaleBaseSize(Size newSize) // setter
	in
	{
		assert(newSize.width > 0);
		assert(newSize.height > 0);
	}
	do
	{
		autossz = newSize;
	}
	
	
	///
	final @property void autoScale(bool byes) // setter
	{
		if(byes)
			cbits |= CBits.ASCALE;
		else
			cbits &= ~CBits.ASCALE;
	}
	
	/// ditto
	final @property bool autoScale() // getter
	{
		return (cbits & CBits.ASCALE) == CBits.ASCALE;
	}
	
	
	final @property Point scrollPosition() // getter
	{
		return Point(xspos, yspos);
	}
	
	
	static Size calcScale(Size area, Size toScale, Size fromScale) // package
	in
	{
		assert(fromScale.width);
		assert(fromScale.height);
	}
	do
	{
		area.width = cast(int)(cast(float)area.width / cast(float)fromScale.width * cast(float)toScale.width);
		area.height = cast(int)(cast(float)area.height / cast(float)fromScale.height * cast(float)toScale.height);
		return area;
	}
	
	
	Size calcScale(Size area, Size toScale) // package
	{
		return calcScale(area, toScale, DEFAULT_SCALE);
	}
	
	
	final void _scale(Size toScale) // package
	{
		bool first = true;
		
		// Note: doesn't get to-scale for nested scrollable-controls.
		void xscale(Control c, Size fromScale)
		{
			c.suspendLayout();
			
			if(first)
			{
				first = false;
				c.size = calcScale(c.size, toScale, fromScale);
			}
			else
			{
				Point pt;
				Size sz;
				sz = calcScale(Size(c.left, c.top), toScale, fromScale);
				pt = Point(sz.width, sz.height);
				sz = calcScale(c.size, toScale, fromScale);
				c.bounds = Rect(pt, sz);
			}
			
			if(c.hasChildren)
			{
				ScrollableControl scc;
				foreach(Control cc; c.controls)
				{
					scc = cast(ScrollableControl)cc;
					if(scc)
					{
						if(scc.autoScale) // ?
						{
							xscale(scc, scc.autoScaleBaseSize);
							scc.autoScaleBaseSize = toScale;
						}
					}
					else
					{
						xscale(cc, fromScale);
					}
				}
			}
			
			//c.resumeLayout(true);
			c.resumeLayout(false); // Should still be perfectly proportionate if it was properly laid out before scaling.
		}
		
		
		xscale(this, autoScaleBaseSize);
		autoScaleBaseSize = toScale;
	}
	
	
	final void _scale() // package
	{
		return _scale(getAutoScaleSize());
	}
	
	
	protected override void onControlAdded(ControlEventArgs ea)
	{
		super.onControlAdded(ea);
		
		if(created) // ?
		if(isHandleCreated)
		{
			auto sc = cast(ScrollableControl)ea.control;
			if(sc)
			{
				if(sc.autoScale)
					sc._scale();
			}
			else
			{
				if(autoScale)
					_scale();
			}
		}
	}
	
	
	//override final Rect displayRectangle() // getter
	override @property Rect displayRectangle() // getter
	{
		Rect result = clientRectangle;
		
		// Subtract dock padding.
		result.x = result.x + dpad.left;
		result.width = result.width - dpad.right - dpad.left;
		result.y = result.y + dpad.top;
		result.height = result.height - dpad.bottom - dpad.top;
		
		// Add scroll width.
		if(scrollSize.width > clientSize.width)
			result.width = result.width + (scrollSize.width - clientSize.width);
		if(scrollSize.height > clientSize.height)
			result.height = result.height + (scrollSize.height - clientSize.height);
		
		// Adjust scroll position.
		result.location = Point(result.location.x - scrollPosition.x, result.location.y - scrollPosition.y);
		
		return result;
	}
	
	
	///
	final @property void scrollSize(Size sz) // setter
	{
		scrollsz = sz;
		
		_fixScrollBounds(); // Implies _adjustScrollSize().
	}
	
	/// ditto
	final @property Size scrollSize() // getter
	{
		return scrollsz;
	}
	
	
	///
	class DockPaddingEdges
	{
		private:
		
		int _left, _top, _right, _bottom;
		int _all;
		//package void delegate() changed;
		
		
		final:
		
		void changed()
		{
			dpadChanged();
		}
		
		
		public:
		
		///
		@property void all(int x) // setter
		{
			_bottom = _right = _top = _left = _all = x;
			
			changed();
		}
		
		/// ditto
		final @property int all() // getter
		{
			return _all;
		}
		
		/// ditto
		@property void left(int x) // setter
		{
			_left = x;
			
			changed();
		}
		
		/// ditto
		@property int left() // getter
		{
			return _left;
		}
		
		/// ditto
		@property void top(int x) // setter
		{
			_top = x;
			
			changed();
		}
		
		/// ditto
		@property int top() // getter
		{
			return _top;
		}
		
		/// ditto
		@property void right(int x) // setter
		{
			_right = x;
			
			changed();
		}
		
		/// ditto
		@property int right() // getter
		{
			return _right;
		}
		
		/// ditto
		@property void bottom(int x) // setter
		{
			_bottom = x;
			
			changed();
		}
		
		/// ditto
		@property int bottom() // getter
		{
			return _bottom;
		}
	}
	
	
	///
	final @property DockPaddingEdges dockPadding() // getter
	{
		return dpad;
	}
	
	
	deprecated final void setAutoScrollMargin(int x, int y)
	{
		//
	}
	
	
	this()
	{
		super();
		_init();
	}
	
	
	enum DEFAULT_SCALE = Size(5, 13);
	
	///
	final @property void hScroll(bool byes) // setter
	{
		LONG wl = _style();
		if(byes)
			wl |= WS_HSCROLL;
		else
			wl &= ~WS_HSCROLL;
		_style(wl);
		
		if(isHandleCreated)
			redrawEntire();
	}
	
	
	/// ditto
	final @property bool hScroll() // getter
	{
		return (_style() & WS_HSCROLL) != 0;
	}
	
	
	///
	final @property void vScroll(bool byes) // setter
	{
		LONG wl = _style();
		if(byes)
			wl |= WS_VSCROLL;
		else
			wl &= ~WS_VSCROLL;
		_style(wl);
		
		if(isHandleCreated)
			redrawEntire();
	}
	
	/// ditto
	final @property bool vScroll() // getter
	{
		return (_style() & WS_VSCROLL) != 0;
	}
	
	
	protected:
	
	
	/+
	override void onLayout(LayoutEventArgs lea)
	{
		// ...
		super.onLayout(lea);
	}
	+/
	
	
	/+
	override void scaleCore(float width, float height)
	{
		// Might not want to call super.scaleCore().
	}
	+/
	
	
	override void wndProc(ref Message m)
	{
		switch(m.msg)
		{
			case WM_VSCROLL:
				{
					SCROLLINFO si = void;
					si.cbSize = SCROLLINFO.sizeof;
					si.fMask = SIF_ALL;
					if(GetScrollInfo(m.hWnd, SB_VERT, &si))
					{
						int delta, maxp;
						maxp = scrollSize.height - clientSize.height;
						switch(LOWORD(m.wParam))
						{
							case SB_LINEDOWN:
								if(yspos >= maxp)
									return;
								delta = maxp - yspos;
								if(autossz.height < delta)
									delta = autossz.height;
								break;
							case SB_LINEUP:
								if(yspos <= 0)
									return;
								delta = yspos;
								if(autossz.height < delta)
									delta = autossz.height;
								delta = -delta;
								break;
							case SB_PAGEDOWN:
								if(yspos >= maxp)
									return;
								if(yspos >= maxp)
									return;
								delta = maxp - yspos;
								if(clientSize.height < delta)
									delta = clientSize.height;
								break;
							case SB_PAGEUP:
								if(yspos <= 0)
									return;
								delta = yspos;
								if(clientSize.height < delta)
									delta = clientSize.height;
								delta = -delta;
								break;
							case SB_THUMBTRACK:
							case SB_THUMBPOSITION:
								//delta = cast(int)HIWORD(m.wParam) - yspos; // Limited to 16-bits.
								delta = si.nTrackPos - yspos;
								break;
							case SB_BOTTOM:
								delta = maxp - yspos;
								break;
							case SB_TOP:
								delta = -yspos;
								break;
							default:
						}
						yspos += delta;
						SetScrollPos(m.hWnd, SB_VERT, yspos, TRUE);
						ScrollWindow(m.hWnd, 0, -delta, null, null);
					}
				}
				break;
			
			case WM_HSCROLL:
				{
					SCROLLINFO si = void;
					si.cbSize = SCROLLINFO.sizeof;
					si.fMask = SIF_ALL;
					if(GetScrollInfo(m.hWnd, SB_HORZ, &si))
					{
						int delta, maxp;
						maxp = scrollSize.width - clientSize.width;
						switch(LOWORD(m.wParam))
						{
							case SB_LINERIGHT:
								if(xspos >= maxp)
									return;
								delta = maxp - xspos;
								if(autossz.width < delta)
									delta = autossz.width;
								break;
							case SB_LINELEFT:
								if(xspos <= 0)
									return;
								delta = xspos;
								if(autossz.width < delta)
									delta = autossz.width;
								delta = -delta;
								break;
							case SB_PAGERIGHT:
								if(xspos >= maxp)
									return;
								if(xspos >= maxp)
									return;
								delta = maxp - xspos;
								if(clientSize.width < delta)
									delta = clientSize.width;
								break;
							case SB_PAGELEFT:
								if(xspos <= 0)
									return;
								delta = xspos;
								if(clientSize.width < delta)
									delta = clientSize.width;
								delta = -delta;
								break;
							case SB_THUMBTRACK:
							case SB_THUMBPOSITION:
								//delta = cast(int)HIWORD(m.wParam) - xspos; // Limited to 16-bits.
								delta = si.nTrackPos - xspos;
								break;
							case SB_RIGHT:
								delta = maxp - xspos;
								break;
							case SB_LEFT:
								delta = -xspos;
								break;
							default:
						}
						xspos += delta;
						SetScrollPos(m.hWnd, SB_HORZ, xspos, TRUE);
						ScrollWindow(m.hWnd, -delta, 0, null, null);
					}
				}
				break;
			
			default:
		}
		
		super.wndProc(m);
	}
	
	
	override void onMouseWheel(MouseEventArgs ea)
	{
		int maxp = scrollSize.height - clientSize.height;
		int delta;
		
		UINT wlines;
		if(!SystemParametersInfoA(SPI_GETWHEELSCROLLLINES, 0, &wlines, 0))
			wlines = 3;
		
		if(ea.delta < 0)
		{
			if(yspos < maxp)
			{
				delta = maxp - yspos;
				if(autossz.height * wlines < delta)
					delta = autossz.height * wlines;
				
				yspos += delta;
				SetScrollPos(hwnd, SB_VERT, yspos, TRUE);
				ScrollWindow(hwnd, 0, -delta, null, null);
			}
		}
		else
		{
			if(yspos > 0)
			{
				delta = yspos;
				if(autossz.height * wlines < delta)
					delta = autossz.height * wlines;
				delta = -delta;
				
				yspos += delta;
				SetScrollPos(hwnd, SB_VERT, yspos, TRUE);
				ScrollWindow(hwnd, 0, -delta, null, null);
			}
		}
		
		super.onMouseWheel(ea);
	}
	
	
	override void onHandleCreated(EventArgs ea)
	{
		xspos = 0;
		yspos = 0;
		
		super.onHandleCreated(ea);
		
		//_adjustScrollSize(FALSE);
		if(hScroll || vScroll)
		{
			_adjustScrollSize(FALSE);
			recalcEntire(); // Need to recalc frame.
		}
	}
	
	
	override void onVisibleChanged(EventArgs ea)
	{
		if(visible)
			_adjustScrollSize(FALSE);
		
		super.onVisibleChanged(ea);
	}
	
	
	private void _fixScrollBounds()
	{
		if(hScroll || vScroll)
		{
			int ydiff = 0, xdiff = 0;
			
			if(yspos > scrollSize.height - clientSize.height)
			{
				ydiff = (clientSize.height + yspos) - scrollSize.height;
				yspos -= ydiff;
				if(yspos < 0)
				{
					ydiff += yspos;
					yspos = 0;
				}
			}
			
			if(xspos > scrollSize.width - clientSize.width)
			{
				xdiff = (clientSize.width + xspos) - scrollSize.width;
				xspos -= xdiff;
				if(xspos < 0)
				{
					xdiff += xspos;
					xspos = 0;
				}
			}
			
			if(isHandleCreated)
			{
				if(xdiff || ydiff)
					ScrollWindow(hwnd, xdiff, ydiff, null, null);
				
				_adjustScrollSize();
			}
		}
	}
	
	
	override void onResize(EventArgs ea)
	{
		super.onResize(ea);
		
		_fixScrollBounds();
	}
	
	
	private:
	//Size scrollmargin, scrollmin;
	//Point autoscrollpos;
	DockPaddingEdges dpad;
	Size autossz = DEFAULT_SCALE;
	Size scrollsz = Size(0, 0);
	int xspos = 0, yspos = 0;
	
	
	void _init()
	{
		dpad = new DockPaddingEdges;
		//dpad.changed = &dpadChanged;
	}
	
	
	void dpadChanged()
	{
		alayout(this);
	}
	
	
	void _adjustScrollSize(BOOL fRedraw = TRUE)
	{
		assert(isHandleCreated);
		
		if(!hScroll && !vScroll)
			return;
		
		SCROLLINFO si;
		//if(vScroll)
		{
			si.cbSize = SCROLLINFO.sizeof;
			si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS;
			si.nPos = yspos;
			si.nMin = 0;
			si.nMax = clientSize.height;
			si.nPage = clientSize.height;
			if(scrollSize.height > clientSize.height)
				si.nMax = scrollSize.height;
			if(si.nMax)
				si.nMax--;
			SetScrollInfo(hwnd, SB_VERT, &si, fRedraw);
		}
		//if(hScroll)
		{
			si.cbSize = SCROLLINFO.sizeof;
			si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS;
			si.nPos = xspos;
			si.nMin = 0;
			si.nMax = clientSize.width;
			si.nPage = clientSize.width;
			if(scrollSize.width > clientSize.width)
				si.nMax = scrollSize.width;
			if(si.nMax)
				si.nMax--;
			SetScrollInfo(hwnd, SB_HORZ, &si, fRedraw);
		}
	}
}


///
interface IContainerControl // docmain
{
	///
	@property Control activeControl(); // getter
	
	deprecated void activeControl(Control); // setter
	
	deprecated bool activateControl(Control);
}


///
class ContainerControl: ScrollableControl, IContainerControl // docmain
{
	///
	@property Control activeControl() // getter
	{
		/+
		HWND hwfocus, hw;
		hw = hwfocus = GetFocus();
		while(hw)
		{
			if(hw == this.hwnd)
				return Control.fromChildHandle(hwfocus);
			hw = GetParent(hw);
		}
		return null;
		+/
		Control ctrlfocus, ctrl;
		ctrl = ctrlfocus = Control.fromChildHandle(GetFocus());
		while(ctrl)
		{
			if(ctrl is this)
				return ctrlfocus;
			ctrl = ctrl.parent;
		}
		return null;
	}
	
	/// ditto
	@property void activeControl(Control ctrl) // setter
	{
		if(!activateControl(ctrl))
			throw new DflException("Unable to activate control");
	}
	
	
	///
	// Returns true if successfully activated.
	final bool activateControl(Control ctrl)
	{
		// Not sure if this is correct.
		
		if(!ctrl.canSelect)
			return false;
		//if(!SetActiveWindow(ctrl.handle))
		//	return false;
		ctrl.select();
		return true;
	}
	
	
	///
	final @property Form parentForm() // getter
	{
		Control par;
		Form f;
		
		for(par = parent; par; par = par.parent)
		{
			f = cast(Form)par;
			if(f)
				return f;
		}
		
		return null;
	}
	
	
	/+
	final bool validate()
	{
		// ...
	}
	+/
	
	
	this()
	{
		super();
		_init();
	}
	
	
	/+
	// Used internally.
	this(HWND hwnd)
	{
		super(hwnd);
		_init();
	}
	+/
	
	
	private void _init()
	{
		//wexstyle |= WS_EX_CONTROLPARENT;
		ctrlStyle |= ControlStyles.CONTAINER_CONTROL;
	}
	
	
	protected bool processTabKey(bool forward)
	{
		if(isHandleCreated)
		{
            return selectNextControl(activeControl, forward, tabStop, true, false);
		}
		return false;
	}
}


private template hasLocalAliasing(T...)
{
	import std.traits, std.typecons;

	static if( !T.length )
		enum hasLocalAliasing = false;
	else
		enum hasLocalAliasing = std.traits.hasLocalAliasing!(T[0]) ||
			dfl.control.hasLocalAliasing!(T[1 .. $]);
}
