/************************************************************************************* Extended WPF Toolkit Copyright (C) 2007-2013 Xceed Software Inc. This program is provided to you under the terms of the Microsoft Public License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license For more features, controls, and fast professional support, pick up the Plus Edition at http://xceed.com/wpf_toolkit Stay informed: follow @datagrid on Twitter or Like http://facebook.com/datagrids ***********************************************************************************/ using System; using System.ComponentModel; using System.Windows; using System.Windows.Controls; using System.Windows.Controls.Primitives; using System.Windows.Input; using System.Windows.Media; using System.Windows.Shapes; using System.Windows.Threading; using Xceed.Wpf.Toolkit.Primitives; using System.Diagnostics; using Xceed.Wpf.Toolkit.Core; using Xceed.Wpf.Toolkit.Core.Utilities; namespace Xceed.Wpf.Toolkit { #pragma warning disable 0809 #pragma warning disable 0618 [TemplatePart( Name = PART_WindowRoot, Type = typeof( Grid ) )] [TemplatePart( Name = PART_Root, Type = typeof( Grid ) )] [TemplatePart( Name = PART_WindowControl, Type = typeof( WindowControl ) )] public class ChildWindow : WindowControl { private const string PART_WindowRoot = "PART_WindowRoot"; private const string PART_Root = "PART_Root"; private const string PART_WindowControl = "PART_WindowControl"; private const int _horizontalOffset = 3; private const int _verticalOffset = 3; #region Private Members private Grid _root; private TranslateTransform _moveTransform = new TranslateTransform(); private bool _startupPositionInitialized; private FrameworkElement _parentContainer; private Rectangle _modalLayer = new Rectangle(); private Canvas _modalLayerPanel = new Canvas(); private Grid _windowRoot; private WindowControl _windowControl; private bool _ignorePropertyChanged; private bool _hasChildren; private bool _hasWindowContainer; #endregion //Private Members #region Public Properties #region DialogResult private bool? _dialogResult; /// /// Gets or sets a value indicating whether the ChildWindow was accepted or canceled. /// /// /// True if the child window was accepted; false if the child window was /// canceled. The default is null. /// [TypeConverter( typeof( NullableBoolConverter ) )] public bool? DialogResult { get { return _dialogResult; } set { if( _dialogResult != value ) { _dialogResult = value; this.Close(); } } } #endregion //DialogResult #region DesignerWindowState public static readonly DependencyProperty DesignerWindowStateProperty = DependencyProperty.Register( "DesignerWindowState", typeof( WindowState ), typeof( ChildWindow ), new PropertyMetadata( WindowState.Closed, OnDesignerWindowStatePropertyChanged ) ); public WindowState DesignerWindowState { get { return ( WindowState )GetValue( DesignerWindowStateProperty ); } set { SetValue( DesignerWindowStateProperty, value ); } } private static void OnDesignerWindowStatePropertyChanged( DependencyObject d, DependencyPropertyChangedEventArgs e ) { ChildWindow childWindow = d as ChildWindow; if( childWindow != null ) childWindow.OnDesignerWindowStatePropertyChanged( ( WindowState )e.OldValue, ( WindowState )e.NewValue ); } protected virtual void OnDesignerWindowStatePropertyChanged( WindowState oldValue, WindowState newValue ) { if( DesignerProperties.GetIsInDesignMode( this ) ) { Visibility = newValue == Xceed.Wpf.Toolkit.WindowState.Open ? Visibility.Visible : Visibility.Collapsed; } } #endregion //DesignerWindowState #region FocusedElement public static readonly DependencyProperty FocusedElementProperty = DependencyProperty.Register( "FocusedElement", typeof( FrameworkElement ), typeof( ChildWindow ), new UIPropertyMetadata( null ) ); public FrameworkElement FocusedElement { get { return ( FrameworkElement )GetValue( FocusedElementProperty ); } set { SetValue( FocusedElementProperty, value ); } } #endregion #region IsModal public static readonly DependencyProperty IsModalProperty = DependencyProperty.Register( "IsModal", typeof( bool ), typeof( ChildWindow ), new UIPropertyMetadata( false, new PropertyChangedCallback( OnIsModalPropertyChanged ) ) ); public bool IsModal { get { return ( bool )GetValue( IsModalProperty ); } set { SetValue( IsModalProperty, value ); } } private static void OnIsModalPropertyChanged( DependencyObject d, DependencyPropertyChangedEventArgs e ) { ChildWindow childWindow = d as ChildWindow; if( childWindow != null ) childWindow.OnIsModalChanged( ( bool )e.OldValue, ( bool )e.NewValue ); } internal event EventHandler IsModalChanged; private void OnIsModalChanged( bool oldValue, bool newValue ) { EventHandler handler = IsModalChanged; if( handler != null ) { handler( this, EventArgs.Empty ); } if( !_hasWindowContainer ) { if( newValue ) { KeyboardNavigation.SetTabNavigation( this, KeyboardNavigationMode.Cycle ); ShowModalLayer(); } else { KeyboardNavigation.SetTabNavigation( this, KeyboardNavigationMode.Continue ); HideModalLayer(); } } } #endregion //IsModal #region OverlayBrush (Obsolete) [Obsolete( "This property is obsolete and should no longer be used. Use WindowContainer.ModalBackgroundBrushProperty instead." )] public static readonly DependencyProperty OverlayBrushProperty = DependencyProperty.Register( "OverlayBrush", typeof( Brush ), typeof( ChildWindow ), new PropertyMetadata( Brushes.Gray, OnOverlayBrushChanged ) ); [Obsolete( "This property is obsolete and should no longer be used. Use WindowContainer.ModalBackgroundBrushProperty instead." )] public Brush OverlayBrush { get { return ( Brush )GetValue( OverlayBrushProperty ); } set { SetValue( OverlayBrushProperty, value ); } } private static void OnOverlayBrushChanged( DependencyObject d, DependencyPropertyChangedEventArgs e ) { ChildWindow childWindow = d as ChildWindow; if( childWindow != null ) childWindow.OnOverlayBrushChanged( ( Brush )e.OldValue, ( Brush )e.NewValue ); } [Obsolete( "This method is obsolete and should no longer be used. Use WindowContainer.ModalBackgroundBrushProperty instead." )] protected virtual void OnOverlayBrushChanged( Brush oldValue, Brush newValue ) { _modalLayer.Fill = newValue; } #endregion //OverlayBrush #region OverlayOpacity (Obsolete) [Obsolete( "This property is obsolete and should no longer be used. Use WindowContainer.ModalBackgroundBrushProperty instead." )] public static readonly DependencyProperty OverlayOpacityProperty = DependencyProperty.Register( "OverlayOpacity", typeof( double ), typeof( ChildWindow ), new PropertyMetadata( 0.5, OnOverlayOpacityChanged ) ); [Obsolete( "This property is obsolete and should no longer be used. Use WindowContainer.ModalBackgroundBrushProperty instead." )] public double OverlayOpacity { get { return ( double )GetValue( OverlayOpacityProperty ); } set { SetValue( OverlayOpacityProperty, value ); } } private static void OnOverlayOpacityChanged( DependencyObject d, DependencyPropertyChangedEventArgs e ) { ChildWindow childWindow = d as ChildWindow; if( childWindow != null ) childWindow.OnOverlayOpacityChanged( ( double )e.OldValue, ( double )e.NewValue ); } [Obsolete( "This method is obsolete and should no longer be used. Use WindowContainer.ModalBackgroundBrushProperty instead." )] protected virtual void OnOverlayOpacityChanged( double oldValue, double newValue ) { _modalLayer.Opacity = newValue; } #endregion //OverlayOpacity #region WindowStartupLocation public static readonly DependencyProperty WindowStartupLocationProperty = DependencyProperty.Register( "WindowStartupLocation", typeof( WindowStartupLocation ), typeof( ChildWindow ), new UIPropertyMetadata( WindowStartupLocation.Manual, OnWindowStartupLocationChanged ) ); public WindowStartupLocation WindowStartupLocation { get { return ( WindowStartupLocation )GetValue( WindowStartupLocationProperty ); } set { SetValue( WindowStartupLocationProperty, value ); } } private static void OnWindowStartupLocationChanged( DependencyObject o, DependencyPropertyChangedEventArgs e ) { ChildWindow childWindow = o as ChildWindow; if( childWindow != null ) childWindow.OnWindowStartupLocationChanged( ( WindowStartupLocation )e.OldValue, ( WindowStartupLocation )e.NewValue ); } protected virtual void OnWindowStartupLocationChanged( WindowStartupLocation oldValue, WindowStartupLocation newValue ) { // TODO: Add your property changed side-effects. Descendants can override as well. } #endregion //WindowStartupLocation #region WindowState public static readonly DependencyProperty WindowStateProperty = DependencyProperty.Register( "WindowState", typeof( WindowState ), typeof( ChildWindow ), new FrameworkPropertyMetadata( WindowState.Closed, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnWindowStatePropertyChanged ) ); public WindowState WindowState { get { return ( WindowState )GetValue( WindowStateProperty ); } set { SetValue( WindowStateProperty, value ); } } private static void OnWindowStatePropertyChanged( DependencyObject d, DependencyPropertyChangedEventArgs e ) { ChildWindow childWindow = d as ChildWindow; if( childWindow != null ) childWindow.OnWindowStatePropertyChanged( ( WindowState )e.OldValue, ( WindowState )e.NewValue ); } protected virtual void OnWindowStatePropertyChanged( WindowState oldValue, WindowState newValue ) { if( !DesignerProperties.GetIsInDesignMode( this ) ) { if( !_ignorePropertyChanged ) SetWindowState( newValue ); } else { Visibility = DesignerWindowState == Xceed.Wpf.Toolkit.WindowState.Open ? Visibility.Visible : System.Windows.Visibility.Collapsed; } } #endregion //WindowState #endregion //Public Properties #region Constructors static ChildWindow() { DefaultStyleKeyProperty.OverrideMetadata( typeof( ChildWindow ), new FrameworkPropertyMetadata( typeof( ChildWindow ) ) ); } public ChildWindow() { DesignerWindowState = Xceed.Wpf.Toolkit.WindowState.Open; _modalLayer.Fill = OverlayBrush; _modalLayer.Opacity = OverlayOpacity; this.IsVisibleChanged += this.ChildWindow_IsVisibleChanged; } #endregion //Constructors #region Base Class Overrides internal override bool AllowPublicIsActiveChange { get { return false; } } public override void OnApplyTemplate() { base.OnApplyTemplate(); if( _windowControl != null ) { _windowControl.HeaderDragDelta -= ( o, e ) => this.OnHeaderDragDelta( e ); _windowControl.HeaderIconDoubleClicked -= ( o, e ) => this.OnHeaderIconDoubleClick( e ); _windowControl.CloseButtonClicked -= ( o, e ) => this.OnCloseButtonClicked( e ); } _windowControl = this.GetTemplateChild( PART_WindowControl ) as WindowControl; if( _windowControl != null ) { _windowControl.HeaderDragDelta += ( o, e ) => this.OnHeaderDragDelta( e ); _windowControl.HeaderIconDoubleClicked += ( o, e ) => this.OnHeaderIconDoubleClick( e ); _windowControl.CloseButtonClicked += ( o, e ) => this.OnCloseButtonClicked( e ); } this.UpdateBlockMouseInputsPanel(); _windowRoot = this.GetTemplateChild( PART_WindowRoot ) as Grid; if( _windowRoot != null ) { _windowRoot.RenderTransform = _moveTransform; } _hasWindowContainer = ( VisualTreeHelper.GetParent( this ) as WindowContainer ) != null; if( !_hasWindowContainer ) { _parentContainer = VisualTreeHelper.GetParent( this ) as FrameworkElement; if( _parentContainer != null ) { _parentContainer.LayoutUpdated += ParentContainer_LayoutUpdated; _parentContainer.SizeChanged += ParentContainer_SizeChanged; //this is for XBAP applications only. When inside an XBAP the parent container has no height or width until it has loaded. Therefore //we need to handle the loaded event and reposition the window. if( System.Windows.Interop.BrowserInteropHelper.IsBrowserHosted ) { _parentContainer.Loaded += ( o, e ) => { ExecuteOpen(); }; } } this.Unloaded += new RoutedEventHandler( ChildWindow_Unloaded ); //initialize our modal background width/height _modalLayer.Height = _parentContainer.ActualHeight; _modalLayer.Width = _parentContainer.ActualWidth; _root = this.GetTemplateChild( PART_Root ) as Grid; #if VS2008 FocusVisualStyle = null; #else Style focusStyle = ( _root != null ) ? _root.Resources[ "FocusVisualStyle" ] as Style : null; if( focusStyle != null ) { Setter focusStyleDataContext = new Setter( Control.DataContextProperty, this ); focusStyle.Setters.Add( focusStyleDataContext ); FocusVisualStyle = focusStyle; } #endif if( _root != null ) { _root.Children.Add( _modalLayerPanel ); } } } protected override void OnGotFocus( RoutedEventArgs e ) { base.OnGotFocus( e ); Action action = () => { if( FocusedElement != null ) { _hasChildren = true; FocusedElement.Focus(); } else { //Focus first Focusable Child element of ChildWindow var focusableChild = TreeHelper.FindChild( this.Content as DependencyObject, x => x.Focusable ); if( focusableChild != null ) { _hasChildren = true; focusableChild.Focus(); } else { _hasChildren = false; } } }; Dispatcher.BeginInvoke( DispatcherPriority.ApplicationIdle, action ); } protected override void OnPreviewKeyDown( KeyEventArgs e ) { base.OnPreviewKeyDown( e ); if( this.IsModal ) { // Prevent MenuItem shortcuts while ChildWindow is modal. if( Keyboard.IsKeyDown( Key.LeftAlt ) || Keyboard.IsKeyDown( Key.RightAlt ) ) { e.Handled = true; } // Prevent Tab when no children else if( (e.Key == Key.Tab) && !_hasChildren ) { e.Handled = true; } } } protected override void OnKeyDown( KeyEventArgs e ) { base.OnKeyDown( e ); if( WindowState == WindowState.Open ) { switch( e.Key ) { case Key.Left: this.Left -= _horizontalOffset; e.Handled = true; break; case Key.Right: this.Left += _horizontalOffset; e.Handled = true; break; case Key.Down: this.Top += _verticalOffset; e.Handled = true; break; case Key.Up: this.Top -= _verticalOffset; e.Handled = true; break; } } } protected override void OnLeftPropertyChanged( double oldValue, double newValue ) { base.OnLeftPropertyChanged( oldValue, newValue ); _hasWindowContainer = ( VisualTreeHelper.GetParent( this ) as WindowContainer ) != null; if( !_hasWindowContainer ) { Left = GetRestrictedLeft(); ProcessMove( newValue - oldValue, 0 ); } } protected override void OnTopPropertyChanged( double oldValue, double newValue ) { base.OnTopPropertyChanged( oldValue, newValue ); _hasWindowContainer = ( VisualTreeHelper.GetParent( this ) as WindowContainer ) != null; if( !_hasWindowContainer ) { Top = GetRestrictedTop(); ProcessMove( 0, newValue - oldValue ); } } internal override void UpdateBlockMouseInputsPanel() { if( _windowControl != null ) { _windowControl.IsBlockMouseInputsPanelActive = this.IsBlockMouseInputsPanelActive; } } #endregion //Base Class Overrides #region Event Handlers protected virtual void OnHeaderDragDelta( DragDeltaEventArgs e ) { if( !this.IsCurrentWindow( e.OriginalSource ) ) return; e.Handled = true; DragDeltaEventArgs args = new DragDeltaEventArgs( e.HorizontalChange, e.VerticalChange ); args.RoutedEvent = HeaderDragDeltaEvent; args.Source = this; this.RaiseEvent( args ); if( !args.Handled ) { if( object.Equals( e.OriginalSource, _windowControl ) ) { double left = 0.0; if( this.FlowDirection == FlowDirection.RightToLeft ) left = this.Left - e.HorizontalChange; else left = this.Left + e.HorizontalChange; this.Left = left; this.Top += e.VerticalChange; } } } protected virtual void OnHeaderIconDoubleClick( MouseButtonEventArgs e ) { if( !this.IsCurrentWindow( e.OriginalSource ) ) return; e.Handled = true; MouseButtonEventArgs args = new MouseButtonEventArgs( Mouse.PrimaryDevice, 0, MouseButton.Left ); args.RoutedEvent = HeaderIconDoubleClickedEvent; args.Source = this; this.RaiseEvent( args ); if( !args.Handled ) { this.Close(); } } protected virtual void OnCloseButtonClicked( RoutedEventArgs e ) { if( !this.IsCurrentWindow( e.OriginalSource ) ) return; e.Handled = true; RoutedEventArgs args = new RoutedEventArgs( CloseButtonClickedEvent, this ); this.RaiseEvent( args ); if( !args.Handled ) { this.Close(); } } [Obsolete( "This method is obsolete and should no longer be used." )] private void ParentContainer_LayoutUpdated( object sender, EventArgs e ) { if( DesignerProperties.GetIsInDesignMode( this ) ) return; //we only want to set the start position if this is the first time the control has bee initialized if( !_startupPositionInitialized ) { ExecuteOpen(); _startupPositionInitialized = true; } } [Obsolete( "This method is obsolete and should no longer be used." )] private void ChildWindow_Unloaded( object sender, RoutedEventArgs e ) { if( _parentContainer != null ) { _parentContainer.LayoutUpdated -= ParentContainer_LayoutUpdated; _parentContainer.SizeChanged -= ParentContainer_SizeChanged; //this is for XBAP applications only. When inside an XBAP the parent container has no height or width until it has loaded. Therefore //we need to handle the loaded event and reposition the window. if( System.Windows.Interop.BrowserInteropHelper.IsBrowserHosted ) { _parentContainer.Loaded -= ( o, ev ) => { ExecuteOpen(); }; } } } [Obsolete( "This method is obsolete and should no longer be used." )] void ParentContainer_SizeChanged( object sender, SizeChangedEventArgs e ) { //resize our modal layer _modalLayer.Height = e.NewSize.Height; _modalLayer.Width = e.NewSize.Width; //reposition our window Left = GetRestrictedLeft(); Top = GetRestrictedTop(); } private void ChildWindow_IsVisibleChanged( object sender, DependencyPropertyChangedEventArgs e ) { if( (bool)e.NewValue && this.IsModal ) { this.Focus(); } } #endregion //Event Handlers #region Methods #region Private [Obsolete( "This method is obsolete and should no longer be used. Use WindowContainer.GetRestrictedLeft() instead." )] private double GetRestrictedLeft() { if( Left < 0 ) return 0; if( ( _parentContainer != null ) && (_windowRoot != null) ) { if( Left + _windowRoot.ActualWidth > _parentContainer.ActualWidth && _parentContainer.ActualWidth != 0 ) { double left = _parentContainer.ActualWidth - _windowRoot.ActualWidth; return left < 0 ? 0 : left; } } return Left; } [Obsolete( "This method is obsolete and should no longer be used. Use WindowContainer.GetRestrictedTop() instead." )] private double GetRestrictedTop() { if( Top < 0 ) return 0; if( ( _parentContainer != null ) && ( _windowRoot != null ) ) { if( Top + _windowRoot.ActualHeight > _parentContainer.ActualHeight && _parentContainer.ActualHeight != 0 ) { double top = _parentContainer.ActualHeight - _windowRoot.ActualHeight; return top < 0 ? 0 : top; } } return Top; } private void SetWindowState( WindowState state ) { switch( state ) { case WindowState.Closed: { ExecuteClose(); break; } case WindowState.Open: { ExecuteOpen(); break; } } } private void ExecuteClose() { CancelEventArgs e = new CancelEventArgs(); OnClosing( e ); if( !e.Cancel ) { if( !_dialogResult.HasValue ) _dialogResult = false; OnClosed( EventArgs.Empty ); } else { CancelClose(); } } private void CancelClose() { _dialogResult = null; //when the close is cancelled, DialogResult should be null _ignorePropertyChanged = true; WindowState = WindowState.Open; //now reset the window state to open because the close was cancelled _ignorePropertyChanged = false; } private void ExecuteOpen() { _dialogResult = null; //reset the dialogResult to null each time the window is opened if( !_hasWindowContainer ) if( WindowStartupLocation == Xceed.Wpf.Toolkit.WindowStartupLocation.Center ) CenterChildWindow(); if( !_hasWindowContainer ) BringToFront(); } private bool IsCurrentWindow( object windowtoTest ) { return object.Equals( _windowControl, windowtoTest ); } [Obsolete( "This method is obsolete and should no longer be used. Use WindowContainer.BringToFront() instead." )] private void BringToFront() { int index = 0; if( _parentContainer != null ) index = ( int )_parentContainer.GetValue( Canvas.ZIndexProperty ); SetValue( Canvas.ZIndexProperty, ++index ); if( IsModal ) Canvas.SetZIndex( _modalLayerPanel, index - 2 ); } [Obsolete( "This method is obsolete and should no longer be used. Use WindowContainer.CenterChild() instead." )] private void CenterChildWindow() { if( ( _parentContainer != null ) && ( _windowRoot != null ) ) { Left = ( _parentContainer.ActualWidth - _windowRoot.ActualWidth ) / 2.0; Top = ( _parentContainer.ActualHeight - _windowRoot.ActualHeight ) / 2.0; } } [Obsolete( "This method is obsolete and should no longer be used." )] private void ShowModalLayer() { if( !DesignerProperties.GetIsInDesignMode( this ) ) { if( !_modalLayerPanel.Children.Contains( _modalLayer ) ) _modalLayerPanel.Children.Add( _modalLayer ); _modalLayer.Visibility = System.Windows.Visibility.Visible; } } [Obsolete( "This method is obsolete and should no longer be used." )] private void HideModalLayer() { _modalLayer.Visibility = System.Windows.Visibility.Collapsed; } [Obsolete( "This method is obsolete and should no longer be used. Use the ChildWindow in a WindowContainer instead." )] private void ProcessMove( double x, double y ) { _moveTransform.X += x; _moveTransform.Y += y; InvalidateArrange(); } #endregion //Private #region Public public void Show() { WindowState = WindowState.Open; } public void Close() { WindowState = WindowState.Closed; } #endregion //Public #endregion //Methods #region Events /// /// Occurs when the ChildWindow is closed. /// public event EventHandler Closed; protected virtual void OnClosed( EventArgs e ) { if( Closed != null ) Closed( this, e ); } /// /// Occurs when the ChildWindow is closing. /// public event EventHandler Closing; protected virtual void OnClosing( CancelEventArgs e ) { if( Closing != null ) Closing( this, e ); } #endregion //Events } #pragma warning restore 0809 #pragma warning restore 0618 }