Mercurial > silverbladetech
diff Chronosv2/source/Presentation/Windows/Controls/DesktopElement.cs @ 10:443821e55f06
Initial cleaned up add from Codeplex files
author | stevenh7776 stevenhollidge@hotmail.com |
---|---|
date | Tue, 21 Feb 2012 17:25:44 +0700 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Chronosv2/source/Presentation/Windows/Controls/DesktopElement.cs Tue Feb 21 17:25:44 2012 +0700 @@ -0,0 +1,865 @@ +/* +The MIT License + +Copyright (c) 2009-2010. Carlos Guzmán Álvarez. http://chronoswpf.codeplex.com/ + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +using System; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Input; +using Chronos.Extensions.Windows; +using Chronos.Presentation.Core.Windows; + +namespace Chronos.Presentation.Windows.Controls +{ + /// <summary> + /// Base class for <see cref="Desktop"/> elements (shortcuts, widgets,...) + /// </summary> + [TemplatePart(Name = DesktopElement.PART_Dragger, Type = typeof(FrameworkElement))] + public abstract class DesktopElement + : ContentControl, IDesktopElement, IActiveAware + { + #region · Constants · + + protected const string PART_Dragger = "PART_Dragger"; + protected const int ResizeSideThichness = 5; + protected const int ResizeCornerSize = 5; + + #endregion + + #region · Dependency Properties · + + /// <summary> + /// Identifies the Id dependency property. + /// </summary> + public static readonly DependencyProperty IdProperty = + DependencyProperty.Register("Id", typeof(Guid), typeof(DesktopElement), + new FrameworkPropertyMetadata(Guid.NewGuid())); + + /// <summary> + /// Identifies the WindowStartupLocation dependency property. + /// </summary> + public static readonly DependencyProperty StartupLocationProperty = + DependencyProperty.Register("StartupLocation", typeof(StartupPosition), typeof(WindowElement), + new FrameworkPropertyMetadata(StartupPosition.CenterParent, FrameworkPropertyMetadataOptions.AffectsArrange)); + + /// <summary> + /// Identifies the CanResize dependency property. + /// </summary> + public static readonly DependencyProperty CanResizeProperty = + DependencyProperty.Register("CanResize", typeof(bool), typeof(DesktopElement), + new FrameworkPropertyMetadata(true)); + + /// <summary> + /// Identifies the CanDrag dependency property. + /// </summary> + public static readonly DependencyProperty CanDragProperty = + DependencyProperty.Register("CanDrag", typeof(bool), typeof(DesktopElement), + new FrameworkPropertyMetadata(true)); + + /// <summary> + /// Identifies the CanClose dependency property. + /// </summary> + public static readonly DependencyProperty CanCloseProperty = + DependencyProperty.Register("CanClose", typeof(bool), typeof(DesktopElement), + new FrameworkPropertyMetadata(true)); + + /// <summary> + /// Identifies the ConstraintToParent dependency property. + /// </summary> + public static readonly DependencyProperty ConstraintToParentProperty = + DependencyProperty.Register("ConstraintToParent", typeof(bool), typeof(DesktopElement), + new FrameworkPropertyMetadata(false)); + + /// <summary> + /// Identifies the IsActive dependency property + /// </summary> + public static readonly DependencyProperty IsActiveProperty = + DependencyProperty.Register("IsActive", + typeof(bool), + typeof(DesktopElement), + new FrameworkPropertyMetadata(false)); + + #endregion + + #region · Static Constructors · + + /// <summary> + /// Initializes the <see cref="DesktopElement"/> class. + /// </summary> + static DesktopElement() + { + // set the key to reference the style for this control + FrameworkElement.DefaultStyleKeyProperty.OverrideMetadata( + typeof(DesktopElement), new FrameworkPropertyMetadata(typeof(DesktopElement))); + } + + #endregion + + #region · Static Routed Events · + + /// <summary> + /// Occurs when a desktop element is activated + /// </summary> + public static readonly RoutedEvent ActivatedEvent = EventManager. + RegisterRoutedEvent("Activated", RoutingStrategy.Bubble, + typeof(RoutedEventHandler), typeof(DesktopElement)); + + /// <summary> + /// Occurs when a desktop element is deactivated + /// </summary> + public static readonly RoutedEvent DeactivatedEvent = EventManager. + RegisterRoutedEvent("Deactivated", RoutingStrategy.Bubble, + typeof(RoutedEventHandler), typeof(DesktopElement)); + + #endregion + + #region · Events · + + /// <summary> + /// Occurs when the element becomes activated + /// </summary> + public event RoutedEventHandler Activated + { + add { base.AddHandler(DesktopElement.ActivatedEvent, value); } + remove { base.RemoveHandler(DesktopElement.ActivatedEvent, value); } + } + + /// <summary> + /// Occurs when the element becomes deactivated + /// </summary> + public event RoutedEventHandler Deactivated + { + add { base.AddHandler(DesktopElement.DeactivatedEvent, value); } + remove { base.RemoveHandler(DesktopElement.DeactivatedEvent, value); } + } + + #endregion + + #region · Fields · + + private Panel parent; + private FrameworkElement partDragger; + private DragOrResizeStatus previewDragOrResizeStatus; + private DragOrResizeStatus dragOrResizeStatus; + private Point startMousePosition; + private Point previousMousePosition; + private Point oldPosition; + private Size originalSize; + private Size previousSize; + private bool isInitialized; + private bool oldCanResize; + + #endregion + + #region · Properties · + + /// <summary> + /// Gets or sets the element id. + /// </summary> + /// <value>The id.</value> + public Guid Id + { + get { return (Guid)base.GetValue(IdProperty); } + set { base.SetValue(IdProperty, value); } + } + + /// <summary> + /// Gets the window parent control + /// </summary> + public new Panel Parent + { + get { return this.parent; } + set { this.parent = value; } + } + + /// <summary> + /// Gets or sets the position of the window when first shown. + /// </summary> + public StartupPosition StartupLocation + { + get { return (StartupPosition)base.GetValue(StartupLocationProperty); } + set { base.SetValue(StartupLocationProperty, value); } + } + + /// <summary> + /// Gets or sets a value indicating whether the element can be resized + /// </summary> + public bool CanResize + { + get { return (bool)base.GetValue(CanResizeProperty); } + set { base.SetValue(CanResizeProperty, value); } + } + + /// <summary> + /// Gets a value indicating whether the element can be dragged. + /// </summary> + /// <value><c>true</c> if this instance can drag; otherwise, <c>false</c>.</value> + public virtual bool CanDrag + { + get { return (bool)base.GetValue(CanDragProperty); } + set { base.SetValue(CanDragProperty, value); } + } + + /// <summary> + /// Gets a value indicating whether the element can be closed. + /// </summary> + /// <value><c>true</c> if this instance can be closed; otherwise, <c>false</c>.</value> + public virtual bool CanClose + { + get { return (bool)base.GetValue(CanCloseProperty); } + set { base.SetValue(CanCloseProperty, value); } + } + + /// <summary> + /// Gets a value indicating whether the element should be constrained to parent. + /// </summary> + /// <value><c>true</c> if this instance can drag; otherwise, <c>false</c>.</value> + public virtual bool ConstraintToParent + { + get { return (bool)base.GetValue(ConstraintToParentProperty); } + set { base.SetValue(ConstraintToParentProperty, value); } + } + + /// <summary> + /// Gets or sets a value indicating whether the element is active. + /// </summary> + /// <value> + /// <c>true</c> if this instance is active; otherwise, <c>false</c>. + /// </value> + public virtual bool IsActive + { + get { return (bool)base.GetValue(IsActiveProperty); } + } + + #endregion + + #region · Protected Properties · + + /// <summary> + /// Gets the current drag or resize status + /// </summary> + protected DragOrResizeStatus DragOrResizeStatus + { + get { return this.dragOrResizeStatus; } + } + + /// <summary> + /// Gets or sets the old element position. + /// </summary> + protected Point OldPosition + { + get { return this.oldPosition; } + set { this.oldPosition = value; } + } + + /// <summary> + /// Gets or sets the original element size + /// </summary> + protected Size OriginalSize + { + get { return this.originalSize; } + set { this.originalSize = value; } + } + + /// <summary> + /// Gets the parent <see cref="Desktop"/>. + /// </summary> + /// <value>The parent desktop.</value> + protected Desktop ParentDesktop + { + //get { return this.GetParent<Desktop>(); } + get { return this.parent as Desktop; } + } + + protected FrameworkElement PartDragger + { + get { return this.partDragger; } + set { this.partDragger = value; } + } + + #endregion + + #region · Constructors · + + /// <summary> + /// Initializes a new instance of the <see cref="DesktopElement"/> class. + /// </summary> + protected DesktopElement() + { + this.previewDragOrResizeStatus = DragOrResizeStatus.None; + this.dragOrResizeStatus = DragOrResizeStatus.None; + this.startMousePosition = new Point(); + this.oldCanResize = this.CanResize; + } + + #endregion + + #region · Methods · + + public override void OnApplyTemplate() + { + base.OnApplyTemplate(); + + this.PartDragger = this.GetTemplateChild(DesktopElement.PART_Dragger) as FrameworkElement; + } + + /// <summary> + /// Attempts to bring the element to the foreground and activates it. + /// </summary> + public void Activate() + { + this.OnActivated(); + } + + /// <summary> + /// Deactivates the element + /// </summary> + public void Deactivate() + { + this.OnDeactivated(); + } + + /// <summary> + /// Closes the desktop element + /// </summary> + public virtual void Close() + { + if (this.Parent != null) + { + this.Parent.Children.Remove(this); + } + + // Clean up + this.Id = Guid.Empty; + this.previewDragOrResizeStatus = DragOrResizeStatus.None; + this.dragOrResizeStatus = DragOrResizeStatus.None; + this.startMousePosition = new Point(); + this.previousMousePosition = new Point(); + this.oldPosition = new Point(); + this.previousSize = Size.Empty; + this.originalSize = Size.Empty; + this.isInitialized = false; + this.oldCanResize = false; + this.partDragger = null; + this.parent = null; + this.Content = null; + this.DataContext = null; + } + + #endregion + + #region · Protected Methods · + + /// <summary> + /// Raises the <see cref="E:System.Windows.FrameworkElement.SizeChanged"/> event, using the specified information as part of the eventual event data. + /// </summary> + /// <param name="sizeInfo">Details of the old and new size involved in the change.</param> + protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo) + { + base.OnRenderSizeChanged(sizeInfo); + + if (!this.isInitialized) + { + this.RefreshCalculatedVisualProperties(); + + this.OriginalSize = new Size(this.ActualWidth, this.ActualHeight); + this.isInitialized = true; + } + } + + protected void RefreshCalculatedVisualProperties() + { + if (!DesignMode.IsInDesignMode) + { + switch (this.StartupLocation) + { + case StartupPosition.CenterParent: + if (!this.ConstraintToParent && + this.GetParent<Window>() != null) + { + this.MoveElement + ( + (this.GetParent<Window>().ActualWidth - this.ActualWidth) / 2, + (this.GetParent<Window>().ActualHeight - this.ActualHeight) / 2 - (this.GetParent<Window>().ActualHeight - this.Parent.ActualHeight) + ); + } + else if (this.Parent != null) + { + this.MoveElement + ( + (this.Parent.ActualWidth - this.ActualWidth) / 2, + (this.Parent.ActualHeight - this.ActualHeight) / 2 + ); + } + break; + + case StartupPosition.Manual: + break; + + case StartupPosition.WindowsDefaultLocation: + this.MoveElement(5, 5); + break; + } + } + } + + /// <summary> + /// Raises the <see cref="DesktopElement.Activated"/> event. + /// </summary> + protected virtual void OnActivated() + { + if (!(bool)this.GetValue(DesktopElement.IsActiveProperty)) + { + if (this.ParentDesktop != null) + { + this.ParentDesktop.OnActivatedElement(this.Id); + this.ParentDesktop.BringToFront(this); + } + + this.GiveFocus(); + + base.SetValue(DesktopElement.IsActiveProperty, true); + + RoutedEventArgs e = new RoutedEventArgs(DesktopElement.ActivatedEvent, this); + base.RaiseEvent(e); + } + } + + /// <summary> + /// Raises the <see cref="DesktopElement.Deactivated"/> event. + /// </summary> + protected virtual void OnDeactivated() + { + if (this.IsActive) + { + base.SetValue(DesktopElement.IsActiveProperty, false); + + RoutedEventArgs e = new RoutedEventArgs(DesktopElement.DeactivatedEvent, this); + base.RaiseEvent(e); + } + } + + /// <summary> + /// Focuses the element + /// </summary> + protected virtual void GiveFocus() + { + this.SetFocus(); + } + + #endregion + + #region · Protected Element Move/Resize Methods · + + protected virtual void MoveElement(double x, double y) + { + this.Move(x, y); + } + + protected virtual void MoveElementLeft(double value) + { + this.MoveLeft(value); + } + + protected virtual void MoveElementTop(double value) + { + this.MoveTop(value); + } + + protected virtual void AdjustBounds(Point mousePosition) + { + Point position = this.GetPosition(); + + if (this.Parent != null) + { + Vector changeFromStart = Point.Subtract(mousePosition, this.startMousePosition); + + if (this.dragOrResizeStatus == DragOrResizeStatus.Drag) + { + if (this.CanDrag) + { + double x = position.X + changeFromStart.X; + double y = position.Y + changeFromStart.Y; + + if (this.ConstraintToParent) + { + if (x < 0) + { + x = 0; + } + if (y < 0) + { + y = 0; + } + if (y + this.ActualHeight > this.ParentDesktop.ActualHeight) + { + y = this.ParentDesktop.ActualHeight - this.ActualHeight; + } + if (x + this.ActualWidth > this.ParentDesktop.ActualWidth) + { + x = this.ParentDesktop.ActualWidth - this.ActualWidth; + } + } + + if (x != position.X || y != position.Y) + { + this.MoveElement(x, y); + } + } + } + else + { + Size size = this.RenderSize; + Vector changeFromPrevious = Point.Subtract(mousePosition, this.previousMousePosition); + + if (this.dragOrResizeStatus.IsOnRight) + { + if (size.Width + changeFromPrevious.X > this.MinWidth) + { + size.Width += changeFromPrevious.X; + } + } + else if (this.dragOrResizeStatus.IsOnLeft) + { + if (size.Width - changeFromStart.X > this.MinWidth) + { + this.MoveElementLeft(position.X + changeFromStart.X); + size.Width -= changeFromStart.X; + } + } + + if (this.dragOrResizeStatus.IsOnBottom) + { + if (size.Height + changeFromPrevious.Y > this.MinHeight) + { + size.Height += changeFromPrevious.Y; + } + } + else if (this.dragOrResizeStatus.IsOnTop) + { + if (size.Height - changeFromStart.Y > this.MinHeight) + { + this.MoveElementTop(position.Y + changeFromStart.Y); + size.Height -= changeFromStart.Y; + } + } + + this.Width = size.Width; + this.Height = size.Height; + } + } + } + + #endregion + + #region · Mouse Handling Methods · + + /// <summary> + /// Invoked when an unhandled <see cref="E:System.Windows.Input.Mouse.PreviewMouseDown"/> attached routed event + /// reaches an element in its route that is derived from this class. + /// Implement this method to add class handling for this event. + /// </summary> + /// <param name="e"> + /// The <see cref="T:System.Windows.Input.MouseButtonEventArgs"/> that contains the event data. + /// The event data reports that one or more mouse buttons were pressed. + /// </param> + protected override void OnPreviewMouseDown(MouseButtonEventArgs e) + { + if (!this.IsActive) + { + this.OnActivated(); + } + + base.OnPreviewMouseDown(e); + } + + /// <summary> + /// Invoked when an unhandled <see cref="E:System.Windows.UIElement.PreviewMouseLeftButtonDown"/> routed event + /// reaches an element in its route that is derived from this class. + /// Implement this method to add class handling for this event. + /// </summary> + /// <param name="e"> + /// The <see cref="T:System.Windows.Input.MouseButtonEventArgs"/> that contains the event data. + /// The event data reports that the left mouse button was pressed. + /// </param> + protected override void OnPreviewMouseLeftButtonDown(MouseButtonEventArgs e) + { + if (!e.Handled + && e.ClickCount == 1 + && e.Source == this) + { + if (this.dragOrResizeStatus == DragOrResizeStatus.None && + this.previewDragOrResizeStatus != DragOrResizeStatus.None) + { + e.Handled = true; + + this.dragOrResizeStatus = this.previewDragOrResizeStatus; + this.startMousePosition = this.previousMousePosition = e.GetPosition(this); + + this.CaptureMouse(); + } + } + + base.OnPreviewMouseLeftButtonDown(e); + } + + /// <summary> + /// Invoked when an unhandled <see cref="E:System.Windows.Input.Mouse.PreviewMouseMove"/> attached event + /// reaches an element in its route that is derived from this class. + /// Implement this method to add class handling for this event. + /// </summary> + /// <param name="e">The <see cref="T:System.Windows.Input.MouseEventArgs"/> that contains the event data.</param> + protected override void OnPreviewMouseMove(MouseEventArgs e) + { + if (this.dragOrResizeStatus == DragOrResizeStatus.None) + { + // http://www.switchonthecode.com/tutorials/wpf-snippet-reliably-getting-the-mouse-position + Point point = e.GetPosition(this); + + this.previewDragOrResizeStatus = this.GetDragOrResizeMode(point); + + if (!this.CanResize + && this.previewDragOrResizeStatus != DragOrResizeStatus.Drag + && this.previewDragOrResizeStatus != DragOrResizeStatus.None) + { + this.previewDragOrResizeStatus = DragOrResizeStatus.None; + } + + this.SetResizeCursor(this.previewDragOrResizeStatus); + } + else if (this.IsMouseCaptured) + { + if (e.MouseDevice.LeftButton == MouseButtonState.Pressed) + { + // http://www.switchonthecode.com/tutorials/wpf-snippet-reliably-getting-the-mouse-position + Point point = e.GetPosition(this); + + if (Math.Abs(point.X - this.previousMousePosition.X) > SystemParameters.MinimumHorizontalDragDistance || + Math.Abs(point.Y - this.previousMousePosition.Y) > SystemParameters.MinimumVerticalDragDistance) + { + e.Handled = true; + + this.AdjustBounds(point); + this.previousMousePosition = point; + } + } + else + { + this.CancelDragOrResize(); + } + } + else + { + this.CancelDragOrResize(); + } + + base.OnPreviewMouseMove(e); + } + + /// <summary> + /// Invoked when an unhandled <see cref="E:System.Windows.UIElement.MouseLeftButtonUp"/> routed event reaches an element + /// in its route that is derived from this class. Implement this method to add class handling for this event. + /// </summary> + /// <param name="e"> + /// The <see cref="T:System.Windows.Input.MouseButtonEventArgs"/> that contains the event data. The event data reports + /// that the left mouse button was released. + /// </param> + protected override void OnPreviewMouseLeftButtonUp(System.Windows.Input.MouseButtonEventArgs e) + { + if (this.IsMouseCaptured) + { + e.Handled = true; + this.CancelDragOrResize(); + } + + base.OnPreviewMouseLeftButtonUp(e); + } + + #endregion + + #region · Drag and Resize Methods · + + private DragOrResizeStatus GetDragOrResizeMode(Point position) + { + DragOrResizeStatus status = DragOrResizeStatus.None; + + if (this.CanDrag && + this.partDragger != null && + this.partDragger.IsMouseOver) + { + status = DragOrResizeStatus.Drag; + } + else if (this.CanResize) + { + if (position.X <= ResizeSideThichness) // left + { + status = this.GetLeftDragStatus(position); + } + else if (this.ActualWidth - position.X <= ResizeSideThichness) // right + { + status = this.GetRightDragStatus(position); + } + else if (position.Y <= ResizeSideThichness) // top + { + status = this.GetTopDragStatus(position); + } + else if (this.ActualHeight - position.Y <= ResizeSideThichness) // bottom + { + status = this.GetBottomDragStatus(position); + } + } + + return status; + } + + private DragOrResizeStatus GetBottomDragStatus(Point position) + { + DragOrResizeStatus status; + + if (position.X <= ResizeCornerSize) + { + status = DragOrResizeStatus.BottomLeft; + } + else if (this.ActualWidth - position.X <= ResizeCornerSize) + { + status = DragOrResizeStatus.BottomRight; + } + else + { + status = DragOrResizeStatus.BottomCenter; + } + + return status; + } + + private DragOrResizeStatus GetTopDragStatus(Point position) + { + DragOrResizeStatus status; + + if (position.X <= ResizeCornerSize) + { + status = DragOrResizeStatus.TopLeft; + } + else if (this.ActualWidth - position.X <= ResizeCornerSize) + { + status = DragOrResizeStatus.TopRight; + } + else + { + status = DragOrResizeStatus.TopCenter; + } + + return status; + } + + private DragOrResizeStatus GetRightDragStatus(Point position) + { + DragOrResizeStatus status; + + if (position.Y <= ResizeCornerSize) + { + status = DragOrResizeStatus.TopRight; + } + else if (this.ActualHeight - position.Y <= ResizeCornerSize) + { + status = DragOrResizeStatus.BottomRight; + } + else + { + status = DragOrResizeStatus.MiddleRight; + } + + return status; + } + + private DragOrResizeStatus GetLeftDragStatus(Point position) + { + DragOrResizeStatus status; + + if (position.Y <= ResizeCornerSize) + { + status = DragOrResizeStatus.TopLeft; + } + else if (this.ActualHeight - position.Y <= ResizeCornerSize) + { + status = DragOrResizeStatus.BottomLeft; + } + else + { + status = DragOrResizeStatus.MiddleLeft; + } + + return status; + } + + private void CancelDragOrResize() + { + this.Cursor = null; + this.dragOrResizeStatus = DragOrResizeStatus.None; + this.previewDragOrResizeStatus = DragOrResizeStatus.None; + + this.ReleaseMouseCapture(); + } + + private void SetResizeCursor(DragOrResizeStatus resizeStatus) + { + if (this.CanResize || this.CanDrag) + { + if (resizeStatus.IsDragging) + { + this.Cursor = null; + } + else if (resizeStatus.IsOnTopLeftOrBottomRight) + { + this.Cursor = Cursors.SizeNWSE; + } + else if (resizeStatus.IsOnTopRightOrBottomLeft) + { + this.Cursor = Cursors.SizeNESW; + } + else if (resizeStatus.IsOnTopRightOrBottomLeft) + { + this.Cursor = Cursors.SizeNESW; + } + else if (resizeStatus.IsOnTopCenterOrBottomCenter) + { + this.Cursor = Cursors.SizeNS; + } + else if (resizeStatus.IsOnMiddleLeftOrMiddleRight) + { + this.Cursor = Cursors.SizeWE; + } + else if (this.Cursor != null) + { + this.Cursor = null; + } + } + else if (this.Cursor != null) + { + this.Cursor = null; + } + } + + #endregion + } +}