Mercurial > silverbladetech
diff Chronosv2/source/Presentation/Windows/Controls/WindowElement.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/WindowElement.cs Tue Feb 21 17:25:44 2012 +0700 @@ -0,0 +1,801 @@ +/* +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.ComponentModel; +using System.Diagnostics; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Controls.Primitives; +using System.Windows.Data; +using System.Windows.Input; +using System.Windows.Interop; +using System.Windows.Media; +using System.Windows.Threading; +using Chronos.Extensions.Windows; +using Chronos.Presentation.Core.Windows; +using nRoute.Components; + +namespace Chronos.Presentation.Windows.Controls +{ + /// <summary> + /// Provides the ability to create, configure, show, and manage the lifetime of windows + /// </summary> + [TemplatePart(Name = WindowElement.PART_CloseButton, Type = typeof(ButtonBase))] + [TemplatePart(Name = WindowElement.PART_ContentPresenter, Type = typeof(ContentPresenter))] + [TemplateVisualState(Name = NormalVisualState, GroupName = WindowStateGroup)] + [TemplateVisualState(Name = MinimizedVisualState, GroupName = WindowStateGroup)] + [TemplateVisualState(Name = MaximizedVisualState, GroupName = WindowStateGroup)] + public class WindowElement + : DesktopElement, IWindow, IModalVindow + { + #region · Constants · + + #region · Template Parts · + + private const string PART_ContentPresenter = "PART_ContentPresenter"; + private const string PART_Root = "PART_Root"; + private const string PART_MaximizeButton = "PART_MaximizeButton"; + private const string PART_MinimizeButton = "PART_MinimizeButton"; + private const string PART_CloseButton = "PART_CloseButton"; + + #endregion + + #region · Visual States · + + private const string WindowStateGroup = "WindowState"; + private const string NormalVisualState = "Normal"; + private const string MinimizedVisualState = "Minimized"; + private const string MaximizedVisualState = "Maximized"; + + #endregion + + #region · Misc · + + private const int MaximizeMargin = 20; + + #endregion + + #endregion + + #region · Dependency Properties · + + /// <summary> + /// Identifies the Title dependency property. + /// </summary> + public static readonly DependencyProperty TitleProperty = + DependencyProperty.Register("Title", typeof(String), typeof(WindowElement), + new FrameworkPropertyMetadata(String.Empty)); + + /// <summary> + /// Identifies the WindowState dependency property. + /// </summary> + public static readonly DependencyProperty WindowStateProperty = + DependencyProperty.Register("WindowState", typeof(WindowState), typeof(WindowElement), + new FrameworkPropertyMetadata(WindowState.Normal)); + + /// <summary> + /// Identifies the ShowCloseButton dependency property. + /// </summary> + public static readonly DependencyProperty ShowCloseButtonProperty = + DependencyProperty.Register("ShowCloseButton", typeof(bool), typeof(WindowElement), + new FrameworkPropertyMetadata(true)); + + /// <summary> + /// Identifies the ShowMaximizeButton dependency property. + /// </summary> + public static readonly DependencyProperty ShowMaximizeButtonProperty = + DependencyProperty.Register("ShowMaximizeButton", typeof(bool), typeof(WindowElement), + new FrameworkPropertyMetadata(true)); + + /// <summary> + /// Identifies the ShowMinimizeButton dependency property. + /// </summary> + public static readonly DependencyProperty ShowMinimizeButtonProperty = + DependencyProperty.Register("ShowMinimizeButton", typeof(bool), typeof(WindowElement), + new FrameworkPropertyMetadata(true)); + + /// <summary> + /// Identifies the DialogResult dependency property. + /// </summary> + public static readonly DependencyProperty DialogResultProperty = + DependencyProperty.Register("DialogResult", typeof(DialogResult), typeof(WindowElement), + new FrameworkPropertyMetadata(DialogResult.None)); + + /// <summary> + /// Identifies the ViewMode dependency property. + /// </summary> + public static readonly DependencyProperty ViewModeProperty = + DependencyProperty.Register("ViewMode", typeof(ViewModeType), typeof(WindowElement), + new FrameworkPropertyMetadata(ViewModeType.Default, + new PropertyChangedCallback(OnViewModeChanged))); + + #endregion + + #region · Dependency Properties Callback Handlers · + + private static void OnViewModeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + if (d != null) + { + WindowElement window = d as WindowElement; + + if (window.IsActive) + { + ViewModeType oldViewModel = (ViewModeType)e.OldValue; + + if (oldViewModel == ViewModeType.Add + || oldViewModel == ViewModeType.Edit) + { + window.UpdateActiveElementBindings(); + } + else + { + window.MoveFocus(FocusNavigationDirection.Next); + } + } + } + } + + #endregion + + #region · Static members · + + /// <summary> + /// Container panel for modal windows + /// </summary> + public static Panel ModalContainerPanel; + + #endregion + + #region · Static Constructor · + + /// <summary> + /// Initializes the <see cref="WindowElement"/> class. + /// </summary> + static WindowElement() + { + WindowElement.DefaultStyleKeyProperty.OverrideMetadata(typeof(WindowElement), + new FrameworkPropertyMetadata(typeof(WindowElement))); + + KeyboardNavigation.IsTabStopProperty.OverrideMetadata(typeof(WindowElement), + new FrameworkPropertyMetadata(false)); + + KeyboardNavigation.DirectionalNavigationProperty.OverrideMetadata( + typeof(WindowElement), new FrameworkPropertyMetadata(KeyboardNavigationMode.Local)); + + KeyboardNavigation.TabNavigationProperty.OverrideMetadata( + typeof(WindowElement), new FrameworkPropertyMetadata(KeyboardNavigationMode.Cycle)); + + KeyboardNavigation.ControlTabNavigationProperty.OverrideMetadata( + typeof(WindowElement), new FrameworkPropertyMetadata(KeyboardNavigationMode.Once)); + + if (!DesignMode.IsInDesignMode + && Application.Current.GetRenderTier() != RenderTier.Tier2) + { + WindowElement.CacheModeProperty.OverrideMetadata(typeof(WindowElement), + new FrameworkPropertyMetadata(new BitmapCache { EnableClearType = true, RenderAtScale = 1, SnapsToDevicePixels = true })); + } + } + + #endregion + + #region · Events · + + /// <summary> + /// Occurs when the window is about to close. + /// </summary> + public event EventHandler Closed; + + /// <summary> + /// Occurs directly after System.Windows.Window.Close() is called, and can be + /// handled to cancel window closure. + /// </summary> + public event CancelEventHandler Closing; + + /// <summary> + /// Occurs when the window's System.Windows.Window.WindowState property changes. + /// </summary> + public event EventHandler WindowStateChanged; + + #endregion + + #region · Fields · + + private ContentPresenter contentPresenter; + private DispatcherFrame dispatcherFrame; + private WindowState oldWindowState; + private bool isShowed; + private bool isModal; + + #region · Commands · + + private ICommand minimizeCommand; + private ICommand maximizeCommand; + private ICommand closeCommand; + + #endregion + + #endregion + + #region · IWindow Commands · + + /// <summary> + /// Gets the maximize window command + /// </summary> + /// <value></value> + public ICommand MaximizeCommand + { + get + { + if (this.maximizeCommand == null) + { + this.maximizeCommand = new ActionCommand(() => OnMaximizeWindow()); + } + + return this.maximizeCommand; + } + } + + /// <summary> + /// Gets the minimize window command + /// </summary> + /// <value></value> + public ICommand MinimizeCommand + { + get + { + if (this.minimizeCommand == null) + { + this.minimizeCommand = new ActionCommand(() => OnMinimizeWindow()); + } + + return this.minimizeCommand; + } + } + + /// <summary> + /// Gets the close window command + /// </summary> + /// <value></value> + public ICommand CloseCommand + { + get + { + if (this.closeCommand == null) + { + this.closeCommand = new ActionCommand(() => OnCloseWindow()); + } + + return this.closeCommand; + } + } + + #endregion + + #region · Properties · + + /// <summary> + /// Gets or sets a window's title. This is a dependency property. + /// </summary> + public String Title + { + get { return (String)base.GetValue(WindowElement.TitleProperty); } + set { base.SetValue(WindowElement.TitleProperty, value); } + } + + /// <summary> + /// Gets the dialog result + /// </summary> + public DialogResult DialogResult + { + get { return (DialogResult)base.GetValue(DialogResultProperty); } + set { base.SetValue(DialogResultProperty, value); } + } + + /// <summary> + /// Gets or sets a value that indicates whether a window is restored, minimized, or maximized. + /// This is a dependency property. + /// </summary> + /// <value>A <see cref="WindowState"/> that determines whether a window is restored, minimized, or maximized. The default is Normal (restored).</value> + public WindowState WindowState + { + get { return (WindowState)base.GetValue(WindowStateProperty); } + set + { + if ((WindowState)this.GetValue(WindowStateProperty) != value) + { + if (this.oldWindowState == System.Windows.WindowState.Maximized + && this.WindowState == System.Windows.WindowState.Minimized + && value == System.Windows.WindowState.Normal) + { + this.UpdateWindowState(this.WindowState, this.oldWindowState); + + base.SetValue(WindowStateProperty, this.oldWindowState); + } + else + { + this.UpdateWindowState(this.WindowState, value); + + base.SetValue(WindowStateProperty, value); + } + + if (this.WindowStateChanged != null) + { + this.WindowStateChanged(this, EventArgs.Empty); + } + } + } + } + + /// <summary> + /// Gets a value that indicates whether the close button is visible. + /// This is a dependency property. + /// </summary> + public bool ShowCloseButton + { + get { return (bool)base.GetValue(ShowCloseButtonProperty); } + set { base.SetValue(ShowCloseButtonProperty, value); } + } + + /// <summary> + /// Gets a value that indicates whether the maximize button is visible. + /// This is a dependency property. + /// </summary> + public bool ShowMaximizeButton + { + get { return (bool)base.GetValue(ShowMaximizeButtonProperty); } + set { base.SetValue(ShowMaximizeButtonProperty, value); } + } + + /// <summary> + /// Gets a value that indicates whether the minimize button is visible. + /// This is a dependency property. + /// </summary> + public bool ShowMinimizeButton + { + get { return (bool)base.GetValue(ShowMinimizeButtonProperty); } + set { base.SetValue(ShowMinimizeButtonProperty, 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 override bool CanDrag + { + get { return (bool)base.GetValue(CanDragProperty) && this.WindowState == System.Windows.WindowState.Normal; } + set { base.SetValue(CanDragProperty, value); } + } + + /// <summary> + /// Gets the view mode + /// </summary> + public ViewModeType ViewMode + { + get { return (ViewModeType)base.GetValue(ViewModeProperty); } + set + { + base.SetValue(ViewModeProperty, value); + + if (value == ViewModeType.Busy) + { + this.GiveFocus(); + Application.Current.DoEvents(); + } + } + } + + #endregion + + #region · Constructors · + + /// <summary> + /// Initializes a new instance of the <see cref="WindowElement"/> class. + /// </summary> + public WindowElement() + : base() + { + } + + #endregion + + #region · Methods · + + public override void OnApplyTemplate() + { + base.OnApplyTemplate(); + + this.contentPresenter = this.GetTemplateChild(WindowElement.PART_ContentPresenter) as ContentPresenter; + } + + /// <summary> + /// Shows the window + /// </summary> + public void Show() + { + if (!this.isShowed) + { + this.isShowed = true; + } + + this.OnActivated(); + } + + /// <summary> + /// Shows the window as a modal dialog + /// </summary> + public DialogResult ShowDialog() + { + this.Parent = WindowElement.ModalContainerPanel; + this.isModal = true; + + this.LockKeyboardNavigation(); + this.LockMouseOutside(); + + this.Show(); + + // Set DialogResult default value + this.DialogResult = DialogResult.None; + + try + { + // Push the current thread to a modal state + ComponentDispatcher.PushModal(); + + // Create a DispatcherFrame instance and use it to start a message loop + this.dispatcherFrame = new DispatcherFrame(); + Dispatcher.PushFrame(this.dispatcherFrame); + } + finally + { + // Pop the current thread from modal state + ComponentDispatcher.PopModal(); + } + + return this.DialogResult; + } + + /// <summary> + /// Manually closes a <see cref="WindowElement"/>. + /// </summary> + public override void Close() + { + CancelEventArgs e = new CancelEventArgs(); + + if (this.Closing != null) + { + this.Closing(this, e); + } + + if (!e.Cancel) + { + this.LockMouseOutside(false); + + // Clean up + this.maximizeCommand = null; + this.minimizeCommand = null; + this.closeCommand = null; + this.dispatcherFrame = null; + this.isModal = false; + this.isShowed = false; + + this.CommandBindings.Clear(); + + base.Close(); + + if (this.Closed != null) + { + this.Closed(this, EventArgs.Empty); + } + } + } + + /// <summary> + /// Hides the Window + /// </summary> + public void Hide() + { + this.Visibility = System.Windows.Visibility.Collapsed; + + if (this.isModal && this.dispatcherFrame != null) + { + this.dispatcherFrame.Continue = false; + this.dispatcherFrame = null; + } + } + + #endregion + + #region · Protected Methods · + + /// <summary> + /// Focuses the window + /// </summary> + protected override void GiveFocus() + { + this.MoveFocus(FocusNavigationDirection.Next); + } + + /// <summary> + /// Invoked when an unhandled <see cref="E:System.Windows.Input.Keyboard.GotKeyboardFocus"/> 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.KeyboardFocusChangedEventArgs"/> that contains the event data.</param> + protected override void OnGotKeyboardFocus(KeyboardFocusChangedEventArgs e) + { + base.OnGotKeyboardFocus(e); + this.OnActivated(); + } + + protected override void OnPreviewKeyDown(KeyEventArgs e) + { + if ((e.Key == Key.Enter || e.Key == Key.Return) && + Keyboard.Modifiers == ModifierKeys.None) + { + if (e.OriginalSource != this) + { + if (e.OriginalSource is DataGridCell || + e.OriginalSource is ComboBoxItem) + { + } + else + { + FocusNavigationDirection direction = FocusNavigationDirection.Next; + UIElement element = e.OriginalSource as UIElement; + + if (element != null) + { + element.MoveFocus(new TraversalRequest(direction)); + e.Handled = true; + } + } + } + } + + base.OnPreviewKeyDown(e); + } + + #endregion + + #region · Command Execution Methods · + + protected virtual void OnCloseWindow() + { + if (this.isModal) + { + this.Hide(); + } + else + { + this.Close(); + } + } + + protected virtual void OnMaximizeWindow() + { + if (this.WindowState != WindowState.Maximized) + { + this.WindowState = WindowState.Maximized; + } + else + { + this.WindowState = WindowState.Normal; + } + } + + protected virtual void OnMinimizeWindow() + { + if (this.WindowState != WindowState.Minimized) + { + this.WindowState = WindowState.Minimized; + } + else + { + this.WindowState = WindowState.Normal; + } + } + + #endregion + + #region · Private Methods · + + /// <summary> + /// http://blogs.msdn.com/b/ryantrem/archive/2010/04/09/globally-updating-binding-sources-in-wpf.aspx + /// </summary> + private void UpdateActiveElementBindings() + { + if (Keyboard.FocusedElement != null && + Keyboard.FocusedElement is DependencyObject) + { + DependencyObject element = (DependencyObject)Keyboard.FocusedElement; + LocalValueEnumerator localValueEnumerator = element.GetLocalValueEnumerator(); + + while (localValueEnumerator.MoveNext()) + { + BindingExpressionBase bindingExpression = BindingOperations.GetBindingExpressionBase(element, localValueEnumerator.Current.Property); + + if (bindingExpression != null) + { + bindingExpression.UpdateSource(); + bindingExpression.UpdateTarget(); + } + } + } + } + + private void LockKeyboardNavigation() + { + KeyboardNavigation.SetTabNavigation(this, KeyboardNavigationMode.Cycle); + KeyboardNavigation.SetDirectionalNavigation(this, KeyboardNavigationMode.None); + KeyboardNavigation.SetControlTabNavigation(this, KeyboardNavigationMode.None); + } + + private void LockMouseOutside() + { + this.LockMouseOutside(true); + } + + private void LockMouseOutside(bool doLock) + { + if (doLock) + { + Debug.Assert(WindowElement.ModalContainerPanel != null); + + Binding wBinding = new Binding("ActualWidth"); + Binding hBinding = new Binding("ActualHeight"); + Canvas fence = new Canvas(); + + wBinding.Source = WindowElement.ModalContainerPanel; + hBinding.Source = WindowElement.ModalContainerPanel; + + fence.SetBinding(Canvas.WidthProperty, wBinding); + fence.SetBinding(Canvas.HeightProperty, hBinding); + + fence.Background = new SolidColorBrush(Color.FromArgb(141, 162, 174, 255)); + fence.Opacity = 0.5; + + WindowElement.ModalContainerPanel.Children.Add(this); + WindowElement.ModalContainerPanel.Children.Add(fence); + WindowElement.ModalContainerPanel.BringToBottom(fence); + WindowElement.ModalContainerPanel.Visibility = System.Windows.Visibility.Visible; + } + else + { + Debug.Assert(WindowElement.ModalContainerPanel != null); + + for (int i = 0; i < WindowElement.ModalContainerPanel.Children.Count; i++) + { + UIElement target = WindowElement.ModalContainerPanel.Children[i]; + + if (target is Panel) + { + WindowElement.ModalContainerPanel.Children.Remove(target); + break; + } + } + + WindowElement.ModalContainerPanel.Children.Remove(this); + WindowElement.ModalContainerPanel.Visibility = System.Windows.Visibility.Hidden; + } + } + + private void UpdateWindowState(WindowState oldWindowState, WindowState newWindowState) + { + if (newWindowState == WindowState.Maximized) + { + if (oldWindowState == System.Windows.WindowState.Minimized) + { + this.Visibility = System.Windows.Visibility.Visible; + } + + VisualStateManager.GoToState(this, MaximizedVisualState, true); + + ScaleTransform st = this.contentPresenter.LayoutTransform as ScaleTransform; + + if (st != null) + { + this.OldPosition = this.GetPosition(); + + if (this.ConstraintToParent) + { + if (this.ActualWidth > this.ActualHeight) + { + st.ScaleX = Math.Round((this.Parent.ActualWidth - MaximizeMargin) / this.OriginalSize.Width, 2); + } + else + { + st.ScaleY = Math.Round((this.Parent.ActualHeight - MaximizeMargin) / this.OriginalSize.Height, 2); + } + + Application.Current.DoEvents(); + + this.MoveElement + ( + (this.Parent.ActualWidth - this.ActualWidth) / 2, + (this.Parent.ActualHeight - this.ActualHeight) / 2 + ); + } + else + { + double scaleX = Math.Round(this.GetParent<Window>().ActualWidth / this.OriginalSize.Width, 2); + double scaleY = Math.Round(this.GetParent<Window>().ActualHeight / this.OriginalSize.Height, 2); + + if (scaleX < scaleY) + { + st.ScaleX = (scaleX > 2.5) ? 2.5 : scaleX; + } + else + { + st.ScaleY = (scaleY > 2.5) ? 2.5 : scaleY; + } + + Application.Current.DoEvents(); + + this.MoveElement + ( + (this.GetParent<Window>().ActualWidth- this.ActualWidth) / 2, + (this.GetParent<Window>().ActualHeight - this.ActualHeight) / 2 - (this.GetParent<Window>().ActualHeight - this.Parent.ActualHeight) + ); + } + } + + this.Parent.BringToFront(this); + } + else if (newWindowState == WindowState.Normal) + { + this.Visibility = System.Windows.Visibility.Visible; + + VisualStateManager.GoToState(this, NormalVisualState, true); + + if (oldWindowState == WindowState.Minimized) + { + this.Activate(); + } + else if (oldWindowState == WindowState.Maximized) + { + ScaleTransform st = this.contentPresenter.LayoutTransform as ScaleTransform; + + if (st != null) + { + st.ScaleX = 1.0; + } + } + + this.MoveElement(this.OldPosition.X, this.OldPosition.Y); + } + else if (newWindowState == WindowState.Minimized) + { + VisualStateManager.GoToState(this, MinimizedVisualState, true); + + this.Visibility = Visibility.Collapsed; + this.oldWindowState = oldWindowState; + this.OldPosition = this.GetPosition(); + + this.OnDeactivated(); + } + } + + #endregion + } +} \ No newline at end of file