以前使用了微软自带的ChildWindow类,但是发现它并不能使用图标,而由于最近需要一个能使用图标的弹出式窗体,于是就自己编了一个,还不错,因此和大家分享一下。
它的用法和ChildWindow一模一样,只是多了一个功能。
效果:
1、窗体弹出、收缩效果类。PopWindowAutomationPeer.cs
/// <summary>
/// 窗体效果
/// </summary>
/// <QualityBand>Preview</QualityBand>
public class PopWindowAutomationPeer : FrameworkElementAutomationPeer, IWindowProvider, ITransformProvider
{
#region Data
/// <summary>
/// 指示该窗体是否是最顶部窗体
/// </summary>
private bool _isTopMost;
#endregion Data
#region 属性
/// <summary>
/// 获取和设置指示该窗体是否是最顶部窗体的值
/// true 则是,Flase不是
/// </summary>
private bool IsTopMostPrivate
{
get
{
return this._isTopMost;
}
set
{
if (this._isTopMost != value)
{
this._isTopMost = value;
this.RaisePropertyChangedEvent(WindowPatternIdentifiers.IsTopmostProperty, !this._isTopMost, this._isTopMost);
}
}
}
/// <summary>
/// 获取ChildWindow
/// </summary>
private PopWindow OwningChildWindow
{
get
{
return (PopWindow)Owner;
}
}
#endregion Properties
public PopWindowAutomationPeer(Wjq.SL.View.PopWindow owner)
: base(owner)
{
if (owner == null)
{
throw new ArgumentNullException("owner");
}
this.RefreshIsTopMostProperty();
}
#region 重载方法
/// <summary>
/// 获取此控件模式
/// <see cref="T:System.Windows.Automation.Peers.ChildWindowAutomationPeer" />.
/// </summary>
public override object GetPattern(PatternInterface patternInterface)
{
if (patternInterface == PatternInterface.Transform || patternInterface == PatternInterface.Window)
{
return this;
}
return base.GetPattern(patternInterface);
}
/// <summary>
/// 获取
/// <see cref="T:System.Windows.Automation.Peers.AutomationControlType" />
/// 与此相关的元素
/// <see cref="T:System.Windows.Automation.Peers.ChildWindowAutomationPeer" />.
/// Called by
/// <see cref="M:System.Windows.Automation.Peers.AutomationPeer.GetAutomationControlType" />.
/// </summary>
protected override AutomationControlType GetAutomationControlTypeCore()
{
return AutomationControlType.Window;
}
/// <summary>
/// 获取与此相关的对象类的名称
/// </summary>
/// <returns></returns>
protected override string GetClassNameCore()
{
return this.Owner.GetType().Name;
}
/// <summary>
/// 获取与此相关的文本标签
/// </summary>
/// <returns></returns>
protected override string GetNameCore()
{
string name = base.GetNameCore();
if (string.IsNullOrEmpty(name))
{
AutomationPeer labeledBy = GetLabeledByCore();
if (labeledBy != null)
{
name = labeledBy.GetName();
}
if (string.IsNullOrEmpty(name) && this.OwningChildWindow.Title != null)
{
name = this.OwningChildWindow.Title.ToString();
}
}
return name;
}
#endregion AutomationPeer overrides
#region IWindowProvider
/// <summary>
/// 获取窗口的交互状态。
/// </summary>
/// <value>
/// 该控件的交互状态,作为枚举值。
/// </value>
WindowInteractionState IWindowProvider.InteractionState
{
get
{
return this.OwningChildWindow.InteractionState;
}
}
/// <summary>
/// 获取一个值,指示窗体是否是模式窗体。
/// </summary>
/// <value>
/// true则是
/// </value>
bool IWindowProvider.IsModal
{
get { return true; }
}
/// <summary>
/// 获取一个值,指示是否是最顶层的窗口
/// </summary>
/// <value>
/// True 是; 否则 false.
/// </value>
bool IWindowProvider.IsTopmost
{
get
{
return this.IsTopMostPrivate;
}
}
/// <summary>
///获取一个值,指示是否窗口可以最大化。
/// </summary>
/// <value>False in all cases.</value>
bool IWindowProvider.Maximizable
{
get { return false; }
}
/// <summary>
/// 获取一个值,指示是否窗口可以最小化.
/// </summary>
/// <value>False in all cases.</value>
bool IWindowProvider.Minimizable
{
get { return false; }
}
/// <summary>
/// 获取窗口的可视状态。
/// </summary>
/// <value>
/// <see cref="F:System.Windows.Automation.WindowVisualState.Normal" />
/// in all cases.
/// </value>
WindowVisualState IWindowProvider.VisualState
{
get
{
return WindowVisualState.Normal;
}
}
/// <summary>
/// 关闭窗体
/// </summary>
void IWindowProvider.Close()
{
this.OwningChildWindow.Close();
}
/// <summary>
/// 改变了窗口(视觉状态,如减少或最大化它)。
/// </summary>
/// <param name="state">
/// The visual state of the window to change to, as a value of the
/// enumeration.
/// </param>
void IWindowProvider.SetVisualState(WindowVisualState state)
{
}
bool IWindowProvider.WaitForInputIdle(int milliseconds)
{
return false;
}
#endregion IWindowProvider
#region ITransformProvider
/// <summary>
/// 移除控件
/// </summary>
void ITransformProvider.Move(double x, double y)
{
if (x < 0)
{
x = 0;
}
if (y < 0)
{
y = 0;
}
if (x > this.OwningChildWindow.Width)
{
x = this.OwningChildWindow.Width;
}
if (y > this.OwningChildWindow.Height)
{
y = this.OwningChildWindow.Height;
}
FrameworkElement contentRoot = this.OwningChildWindow.ContentRoot;
if (contentRoot != null)
{
GeneralTransform gt = contentRoot.TransformToVisual(null);
if (gt != null)
{
Point p = gt.Transform(new Point(0, 0));
TransformGroup transformGroup = contentRoot.RenderTransform as TransformGroup;
if (transformGroup == null)
{
transformGroup = new TransformGroup();
transformGroup.Children.Add(contentRoot.RenderTransform);
}
TranslateTransform t = new TranslateTransform();
t.X = x - p.X;
t.Y = y - p.Y;
if (transformGroup != null)
{
transformGroup.Children.Add(t);
contentRoot.RenderTransform = transformGroup;
}
}
}
}
/// <summary>
/// 大小改变
/// </summary>
void ITransformProvider.Resize(double width, double height)
{
}
/// <summary>
/// 旋转控件
/// </summary>
void ITransformProvider.Rotate(double degrees)
{
}
/// <summary>
/// 获取一个值,指示元素是否可以移动。
/// </summary>
/// <value>True in all cases.</value>
bool ITransformProvider.CanMove
{
get { return true; }
}
/// <summary>
/// 获取一个值,指示元素是否可以调整大小
/// </summary>
/// <value>False in all cases.</value>
bool ITransformProvider.CanResize
{
get { return false; }
}
/// <summary>
/// 获取一个值,指示元素是否可以旋转。
/// </summary>
/// <value>False in all cases.</value>
bool ITransformProvider.CanRotate
{
get { return false; }
}
#endregion ITransformProvider
#region Methods
/// <summary>
/// 如果ChildWindow不是最上面的元素则返回。
/// </summary>
/// <returns>Bool value.</returns>
private bool GetIsTopMostCore()
{
return !(this.OwningChildWindow.InteractionState == WindowInteractionState.BlockedByModalWindow);
}
internal void RaiseInteractionStatePropertyChangedEvent(WindowInteractionState oldValue, WindowInteractionState newValue)
{
this.RaisePropertyChangedEvent(WindowPatternIdentifiers.WindowInteractionStateProperty, oldValue, newValue);
this.RefreshIsTopMostProperty();
}
private void RefreshIsTopMostProperty()
{
this.IsTopMostPrivate = this.GetIsTopMostCore();
}
#endregion Methods
}
2.控件类PopWindow.cs
/// <summary>
/// 提供一个可以在父窗口或模块显示的窗口
/// 与父窗口交互操作
/// </summary>
/// <QualityBand>预览</QualityBand>
[TemplatePart(Name = PART_Chrome, Type = typeof(FrameworkElement))]
[TemplatePart(Name = PART_CloseButton, Type = typeof(ButtonBase))]
[TemplatePart(Name = PART_ContentPresenter, Type = typeof(FrameworkElement))]
[TemplatePart(Name = PART_ContentRoot, Type = typeof(FrameworkElement))]
[TemplatePart(Name = PART_Overlay, Type = typeof(Panel))]
[TemplatePart(Name = PART_Root, Type = typeof(FrameworkElement))]
[TemplateVisualState(Name = VSMSTATE_StateClosed, GroupName = VSMGROUP_Window)]
[TemplateVisualState(Name = VSMSTATE_StateOpen, GroupName = VSMGROUP_Window)]
public class PopWindow : ContentControl
{
#region 静态字段和常量
/// <summary>
/// 浏览器模板的名称的一部分.
/// </summary>
private const string PART_Chrome = "Chrome";
/// <summary>
/// 关闭按钮
/// </summary>
private const string PART_CloseButton = "CloseButton";
/// <summary>
/// ContentPresenter
/// </summary>
private const string PART_ContentPresenter = "ContentPresenter";
/// <summary>
/// The name of the ContentRoot template part.
/// </summary>
private const string PART_ContentRoot = "ContentRoot";
/// <summary>
/// 覆盖,模式窗体
/// </summary>
private const string PART_Overlay = "Overlay";
/// <summary>
/// The name of the Root template part.
/// </summary>
private const string PART_Root = "Root";
/// <summary>
/// The name of the WindowStates VSM group.
/// </summary>
private const string VSMGROUP_Window = "WindowStates";
/// <summary>
/// The name of the Closing VSM state.
/// </summary>
private const string VSMSTATE_StateClosed = "Closed";
/// <summary>
/// The name of the Opening VSM state.
/// </summary>
private const string VSMSTATE_StateOpen = "Open";
/// <summary>
/// 保存RootVisual.IsEnabled以前的值
/// </summary>
private static bool RootVisual_PrevEnabledState = true;
/// <summary>
/// Stores a count of the number of open ChildWindow instances.
/// </summary>
private static int OpenChildWindowCount = 0;
#region 关闭按钮 CloseButton
/// <summary>
/// 获取或设置该控件是否有一个关闭按钮
/// </summary>
/// <value>
/// 有则为true,无则为false
/// 默认ture.
/// </value>
public bool HasCloseButton
{
get { return (bool)GetValue(HasCloseButtonProperty); }
set { SetValue(HasCloseButtonProperty, value); }
}
/// <summary>
/// 注入属性,标识控件是否有关闭按钮
/// Register:用于关联
/// PropertyMetadata:定义依赖项属性行为的某些方面,包括其注册条件
/// </summary>
public static readonly DependencyProperty HasCloseButtonProperty =
DependencyProperty.Register(
"HasCloseButton",
typeof(bool),
typeof(PopWindow),
new PropertyMetadata(true, OnHasCloseButtonPropertyChanged));
/// <summary>
/// HasCloseButton 属性改变后发生
/// </summary>
/// <param name="d"> HasCloseButton 属性改变后发生.</param>
/// <param name="e">DependencyPropertyChangedEventArgs which contains the old and new values.</param>
private static void OnHasCloseButtonPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
PopWindow pw = (PopWindow)d;
if (pw.CloseButton != null)
{
if ((bool)e.NewValue)
{
pw.CloseButton.Visibility = Visibility.Visible;
}
else
{
pw.CloseButton.Visibility = Visibility.Collapsed;
}
}
}
#endregion
#region public Brush OverlayBrush
/// <summary>
/// 获取或设置绘制区域的画笔,当该控件打开时,绘制父窗体的画笔
/// </summary>
/// <value>
/// 默认值是null
/// </value>
public Brush OverlayBrush
{
get { return (Brush)GetValue(OverlayBrushProperty); }
set { SetValue(OverlayBrushProperty, value); }
}
/// <summary>
/// OverlayBrush的注入属性,应用于绘制父窗体
/// </summary>
public static readonly DependencyProperty OverlayBrushProperty =
DependencyProperty.Register(
"OverlayBrush",
typeof(Brush),
typeof(PopWindow),
new PropertyMetadata(OnOverlayBrushPropertyChanged));
/// <summary>
/// 当OverlayBrush的值改变时发生
/// </summary>
/// <param name="d">ChildWindow object whose OverlayBrush property is changed.</param>
/// <param name="e">DependencyPropertyChangedEventArgs which contains the old and new values.</param>
private static void OnOverlayBrushPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
PopWindow pw = (PopWindow)d;
if (pw.Overlay != null)
{
pw.Overlay.Background = (Brush)e.NewValue;
}
}
#endregion public Brush OverlayBrush
#region public double OverlayOpacity
/// <summary>
///获取或设置绘制区域的画笔,当该控件打开时,绘制父窗体的画笔的透明度
/// </summary>
/// <value>
/// The opacity of the overlay brush that is used to cover the parent
/// window when the <see cref="T:System.Windows.Controls.ChildWindow" />
/// is open. The default is 1.0.
/// </value>
public double OverlayOpacity
{
get { return (double)GetValue(OverlayOpacityProperty); }
set { SetValue(OverlayOpacityProperty, value); }
}
/// <summary>
/// Identifies the
/// <see cref="P:System.Windows.Controls.ChildWindow.OverlayOpacity" />
/// dependency property.
/// </summary>
/// <value>
/// The identifier for the
/// <see cref="P:System.Windows.Controls.ChildWindow.OverlayOpacity" />
/// dependency property.
/// </value>
public static readonly DependencyProperty OverlayOpacityProperty =
DependencyProperty.Register(
"OverlayOpacity",
typeof(double),
typeof(PopWindow),
new PropertyMetadata(OnOverlayOpacityPropertyChanged));
/// <summary>
/// OverlayOpacityProperty PropertyChangedCallback call back static function.
/// </summary>
/// <param name="d">ChildWindow object whose OverlayOpacity property is changed.</param>
/// <param name="e">DependencyPropertyChangedEventArgs which contains the old and new values.</param>
private static void OnOverlayOpacityPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
PopWindow cw = (PopWindow)d;
if (cw.Overlay != null)
{
cw.Overlay.Opacity = (double)e.NewValue;
}
}
#endregion public double OverlayOpacity
#region private static Control RootVisual
/// <summary>
/// 是否显示改控件
/// </summary>
private static Control RootVisual
{
get
{
return Application.Current == null ? null : (Application.Current.RootVisual as Control);
}
}
#endregion private static Control RootVisual
#region 标题
/// <summary>
/// 获取或设置改控件显示的标题
/// </summary>
/// <value>
/// 默认值是null
/// </value>
public object Title
{
get { return GetValue(TitleProperty); }
set { SetValue(TitleProperty, value); }
}
/// <summary>
/// Identifies the
/// <see cref="P:System.Windows.Controls.ChildWindow.Title" />
/// dependency property.
/// </summary>
/// <value>
/// The identifier for the
/// <see cref="P:System.Windows.Controls.ChildWindow.Title" />
/// dependency property.
/// </value>
public static readonly DependencyProperty TitleProperty =
DependencyProperty.Register(
"Title",
typeof(object),
typeof(PopWindow),
null);
#endregion public object Title
#region 标题栏图片
/// <summary>
/// 获取或设置改控件显示的标题
/// </summary>
/// <value>
/// 默认值是null
/// </value>
public Image Icon
{
get
{
return (Image)GetValue(IconProperty);
}
set
{
base.SetValue(IconProperty, value);
}
}
/// <summary>
/// Identifies the
/// <see cref="P:System.Windows.Controls.ChildWindow.Title" />
/// dependency property.
/// </summary>
/// <value>
/// The identifier for the
/// <see cref="P:System.Windows.Controls.ChildWindow.Title" />
/// dependency property.
/// </value>
public static readonly DependencyProperty IconProperty =
DependencyProperty.Register(
"Icon",
typeof(Image),
typeof(PopWindow),
null);
#endregion public object Title
#endregion Static Fields and Constants
#region 成员
/// <summary>
/// Private accessor for the Chrome.
/// </summary>
private FrameworkElement _chrome;
/// <summary>
/// Private accessor for the click point on the chrome.
/// </summary>
private Point _clickPoint;
/// <summary>
/// 通过时间线控制动画,并为其子动画提供对象和属性目标信息。用于控制关闭控件时的动画效果
/// </summary>
private Storyboard _closed;
/// <summary>
/// Private accessor for the ContentPresenter.
/// </summary>
private FrameworkElement _contentPresenter;
/// <summary>
/// Private accessor for the translate transform that needs to be applied on to the ContentRoot.
/// </summary>
private TranslateTransform _contentRootTransform;
/// <summary>
/// Content area desired width.
/// </summary>
private double _desiredContentWidth;
/// <summary>
/// Content area desired height.
/// </summary>
private double _desiredContentHeight;
/// <summary>
/// Desired margin for the window.
/// </summary>
private Thickness _desiredMargin;
/// <summary>
/// 返回值
/// </summary>
private bool? _dialogresult;
/// <summary>
/// 窗体相互作用的状态
/// </summary>
private WindowInteractionState _interactionState;
/// <summary>
/// Boolean value that specifies whether the application is exit or not.
/// </summary>
private bool _isAppExit;
/// <summary>
/// Boolean value that specifies whether the window is in closing state or not.
/// </summary>
private bool _isClosing;
/// <summary>
/// Boolean value that specifies whether the window is opened.
/// </summary>
private bool _isOpen;
/// <summary>
/// Private accessor for the Opening storyboard.
/// </summary>
private Storyboard _opened;
/// <summary>
/// Boolean value that specifies whether the mouse is captured or not.
/// </summary>
private bool _isMouseCaptured;
/// <summary>
/// Boolean value that specifies whether we are listening to RootVisual.GotFocus.
/// </summary>
private bool _attachedRootVisualListener;
/// <summary>
/// Private accessor for the Root of the window.
/// </summary>
private FrameworkElement _root;
/// <summary>
/// Private accessor for the position of the window with respect to RootVisual.
/// </summary>
private Point _windowPosition;
#endregion Member Fields
#region 构造器
/// <summary>
/// 实例化
/// </summary>
public PopWindow()
{
this.DefaultStyleKey = typeof(PopWindow);
this.InteractionState = WindowInteractionState.NotResponding;
}
#endregion Constructors
#region 事件
/// <summary>
/// 控件关闭后发生
/// </summary>
public event EventHandler Closed;
/// <summary>
/// 控件关闭时发生
/// </summary>
public event EventHandler<CancelEventArgs> Closing;
#endregion Events
#region 属性
/// <summary>
/// Gets the internal accessor for the ContentRoot of the window.
/// </summary>
internal FrameworkElement ContentRoot
{
get;
private set;
}
/// <summary>
/// 获取或设置返回值
/// </summary>
[TypeConverter(typeof(NullableBoolConverter))]
public bool? DialogResult
{
get
{
return this._dialogresult;
}
set
{
if (this._dialogresult != value)
{
this._dialogresult = value;
this.Close();
}
}
}
/// <summary>
/// Gets the internal accessor for the PopUp of the window.
/// </summary>
internal Popup ChildWindowPopup
{
get;
private set;
}
/// <summary>
/// Gets the internal accessor for the close button of the window.
/// </summary>
internal ButtonBase CloseButton
{
get;
private set;
}
/// <summary>
/// Gets the InteractionState for the ChildWindow.
/// </summary>
internal WindowInteractionState InteractionState
{
get
{
return this._interactionState;
}
private set
{
if (this._interactionState != value)
{
WindowInteractionState oldValue = this._interactionState;
this._interactionState = value;
PopWindowAutomationPeer peer = PopWindowAutomationPeer.FromElement(this) as PopWindowAutomationPeer;
if (peer != null)
{
peer.RaiseInteractionStatePropertyChangedEvent(oldValue, this._interactionState);
}
}
}
}
/// <summary>
/// Gets a value indicating whether the PopUp is open or not.
/// </summary>
private bool IsOpen
{
get
{
return (this.ChildWindowPopup != null && this.ChildWindowPopup.IsOpen);
}
}
/// <summary>
/// Gets the internal accessor for the overlay of the window.
/// </summary>
internal Panel Overlay
{
get;
private set;
}
#endregion Properties
#region 静态方法
/// <summary>
/// 反转输入矩阵
/// </summary>
/// <param name="matrix">The matrix values that is to be inverted.</param>
/// <returns>Returns a value indicating whether the inversion was successful or not.</returns>
private static bool InvertMatrix(ref Matrix matrix)
{
double determinant = (matrix.M11 * matrix.M22) - (matrix.M12 * matrix.M21);
if (determinant == 0.0)
{
return false;
}
Matrix matCopy = matrix;
matrix.M11 = matCopy.M22 / determinant;
matrix.M12 = -1 * matCopy.M12 / determinant;
matrix.M21 = -1 * matCopy.M21 / determinant;
matrix.M22 = matCopy.M11 / determinant;
matrix.OffsetX = ((matCopy.OffsetY * matCopy.M21) - (matCopy.OffsetX * matCopy.M22)) / determinant;
matrix.OffsetY = ((matCopy.OffsetX * matCopy.M12) - (matCopy.OffsetY * matCopy.M11)) / determinant;
return true;
}
#endregion Static Methods
#region 方法
/// <summary>
/// Executed when the application is exited.
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">Event args.</param>
internal void Application_Exit(object sender, EventArgs e)
{
if (this.IsOpen)
{
this._isAppExit = true;
try
{
this.Close();
}
finally
{
this._isAppExit = false;
}
}
}
/// <summary>
/// Changes the visual state of the ChildWindow.
/// </summary>
private void ChangeVisualState()
{
if (this._isClosing)
{
VisualStateManager.GoToState(this, VSMSTATE_StateClosed, true);
}
else
{
VisualStateManager.GoToState(this, VSMSTATE_StateOpen, true);
}
}
/// <summary>
/// Executed when ChildWindow size is changed.
/// </summary>
/// <param name="sender">Sender object.</param>
/// <param name="e">Size changed event args.</param>
private void ChildWindow_SizeChanged(object sender, SizeChangedEventArgs e)
{
if (this.Overlay != null)
{
if (e.NewSize.Height != this.Overlay.ActualHeight)
{
this._desiredContentHeight = e.NewSize.Height;
}
if (e.NewSize.Width != this.Overlay.ActualWidth)
{
this._desiredContentWidth = e.NewSize.Width;
}
}
if (this.IsOpen)
{
this.UpdateOverlaySize();
}
}
/// <summary>
/// 关闭
/// </summary>
public void Close()
{
// AutomationPeer returns "Closing" when Close() is called
// but the window is not closed completely:
this.InteractionState = WindowInteractionState.Closing;
CancelEventArgs e = new CancelEventArgs();
this.OnClosing(e);
// On ApplicationExit, close() cannot be cancelled
if (!e.Cancel || this._isAppExit)
{
if (RootVisual != null && this.IsOpen)
{
--OpenChildWindowCount;
if (OpenChildWindowCount == 0)
{
// Restore the value saved when the first window was opened
RootVisual.IsEnabled = RootVisual_PrevEnabledState;
}
}
// Close Popup
if (this.IsOpen)
{
if (this._closed != null)
{
// Popup will be closed when the storyboard ends
this._isClosing = true;
try
{
this.ChangeVisualState();
}
finally
{
this._isClosing = false;
}
}
else
{
// If no closing storyboard is defined, close the Popup
this.ChildWindowPopup.IsOpen = false;
}
if (!this._dialogresult.HasValue)
{
// If close action is not happening because of DialogResult property change action,
// Dialogresult is always false:
this._dialogresult = false;
}
this.OnClosed(EventArgs.Empty);
this.UnSubscribeFromEvents();
this.UnsubscribeFromTemplatePartEvents();
if (Application.Current.RootVisual != null)
{
Application.Current.RootVisual.GotFocus -= new RoutedEventHandler(this.RootVisual_GotFocus);
_attachedRootVisualListener = false;
}
}
}
else
{
// If the Close is cancelled, DialogResult should always be NULL:
this._dialogresult = null;
this.InteractionState = WindowInteractionState.Running;
}
}
/// <summary>
/// Executed when the CloseButton is clicked.
/// </summary>
/// <param name="sender">Sender object.</param>
/// <param name="e">Routed event args.</param>
internal void CloseButton_Click(object sender, RoutedEventArgs e)
{
this.Close();
}
/// <summary>
/// Executed when the Closing storyboard ends.
/// </summary>
/// <param name="sender">Sender object.</param>
/// <param name="e">Event args.</param>
private void Closing_Completed(object sender, EventArgs e)
{
if (this.ChildWindowPopup != null)
{
this.ChildWindowPopup.IsOpen = false;
}
// AutomationPeer returns "NotResponding" when the ChildWindow is closed:
this.InteractionState = WindowInteractionState.NotResponding;
if (this._closed != null)
{
this._closed.Completed -= new EventHandler(this.Closing_Completed);
}
}
/// <summary>
/// Executed when the a key is presses when the window is open.
/// </summary>
/// <param name="sender">Sender object.</param>
/// <param name="e">Key event args.</param>
private void ChildWindow_KeyDown(object sender, KeyEventArgs e)
{
PopWindow ew = sender as PopWindow;
Debug.Assert(ew != null, "ChildWindow instance is null.");
//快捷键 Ctrl+Shift+F4 关闭窗体
if (e != null && !e.Handled && e.Key == Key.F4 &&
((Keyboard.Modifiers & ModifierKeys.Control) == ModifierKeys.Control) &&
((Keyboard.Modifiers & ModifierKeys.Shift) == ModifierKeys.Shift))
{
ew.Close();
e.Handled = true;
}
}
/// <summary>
/// 失去焦点时执行
/// </summary>
/// <param name="sender">Sender object.</param>
/// <param name="e">Routed event args.</param>
private void ChildWindow_LostFocus(object sender, RoutedEventArgs e)
{
if (this.IsOpen && Application.Current != null && Application.Current.RootVisual != null)
{
this.InteractionState = WindowInteractionState.BlockedByModalWindow;
if (!_attachedRootVisualListener)
{
Application.Current.RootVisual.GotFocus += new RoutedEventHandler(this.RootVisual_GotFocus);
_attachedRootVisualListener = true;
}
}
}
/// <summary>
/// Executed when mouse left button is down on the chrome.
/// </summary>
/// <param name="sender">Sender object.</param>
/// <param name="e">Mouse button event args.</param>
private void Chrome_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (this._chrome != null)
{
e.Handled = true;
if (this.CloseButton != null && !this.CloseButton.IsTabStop)
{
this.CloseButton.IsTabStop = true;
try
{
this.Focus();
}
finally
{
this.CloseButton.IsTabStop = false;
}
}
else
{
this.Focus();
}
this._chrome.CaptureMouse();
this._isMouseCaptured = true;
this._clickPoint = e.GetPosition(sender as UIElement);
}
}
/// <summary>
/// Executed when mouse left button is up on the chrome.
/// </summary>
/// <param name="sender">Sender object.</param>
/// <param name="e">Mouse button event args.</param>
private void Chrome_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if (this._chrome != null)
{
e.Handled = true;
this._chrome.ReleaseMouseCapture();
this._isMouseCaptured = false;
}
}
/// <summary>
/// Executed when mouse moves on the chrome.
/// </summary>
/// <param name="sender">Sender object.</param>
/// <param name="e">Mouse event args.</param>
private void Chrome_MouseMove(object sender, MouseEventArgs e)
{
if (this._isMouseCaptured && this.ContentRoot != null && Application.Current != null && Application.Current.RootVisual != null)
{
Point position = e.GetPosition(Application.Current.RootVisual);
GeneralTransform gt = this.ContentRoot.TransformToVisual(Application.Current.RootVisual);
if (gt != null)
{
Point p = gt.Transform(this._clickPoint);
this._windowPosition = gt.Transform(new Point(0, 0));
if (position.X < 0)
{
double Y = FindPositionY(p, position, 0);
position = new Point(0, Y);
}
if (position.X > this.Width)
{
double Y = FindPositionY(p, position, this.Width);
position = new Point(this.Width, Y);
}
if (position.Y < 0)
{
double X = FindPositionX(p, position, 0);
position = new Point(X, 0);
}
if (position.Y > this.Height)
{
double X = FindPositionX(p, position, this.Height);
position = new Point(X, this.Height);
}
double x = position.X - p.X;
double y = position.Y - p.Y;
// Take potential RightToLeft layout into account
FrameworkElement fe = Application.Current.RootVisual as FrameworkElement;
if (fe != null && fe.FlowDirection == FlowDirection.RightToLeft)
{
x = -x;
}
UpdateContentRootTransform(x, y);
}
}
}
/// <summary>
/// Executed when the ContentPresenter size changes.
/// </summary>
/// <param name="sender">Content Presenter object.</param>
/// <param name="e">SizeChanged event args.</param>
private void ContentPresenter_SizeChanged(object sender, SizeChangedEventArgs e)
{
if (this.ContentRoot != null && Application.Current != null && Application.Current.RootVisual != null && _isOpen)
{
GeneralTransform gt = this.ContentRoot.TransformToVisual(Application.Current.RootVisual);
if (gt != null)
{
Point p = gt.Transform(new Point(0, 0));
double x = this._windowPosition.X - p.X;
double y = this._windowPosition.Y - p.Y;
UpdateContentRootTransform(x, y);
}
}
RectangleGeometry rg = new RectangleGeometry();
rg.Rect = new Rect(0, 0, this._contentPresenter.ActualWidth, this._contentPresenter.ActualHeight);
this._contentPresenter.Clip = rg;
this.UpdatePosition();
}
/// <summary>
/// Finds the X coordinate of a point that is defined by a line.
/// </summary>
/// <param name="p1">Starting point of the line.</param>
/// <param name="p2">Ending point of the line.</param>
/// <param name="y">Y coordinate of the point.</param>
/// <returns>X coordinate of the point.</returns>
private static double FindPositionX(Point p1, Point p2, double y)
{
if (y == p1.Y || p1.X == p2.X)
{
return p2.X;
}
Debug.Assert(p1.Y != p2.Y, "Unexpected equal Y coordinates");
return (((y - p1.Y) * (p1.X - p2.X)) / (p1.Y - p2.Y)) + p1.X;
}
/// <summary>
/// Finds the Y coordinate of a point that is defined by a line.
/// </summary>
/// <param name="p1">Starting point of the line.</param>
/// <param name="p2">Ending point of the line.</param>
/// <param name="x">X coordinate of the point.</param>
/// <returns>Y coordinate of the point.</returns>
private static double FindPositionY(Point p1, Point p2, double x)
{
if (p1.Y == p2.Y || x == p1.X)
{
return p2.Y;
}
Debug.Assert(p1.X != p2.X, "Unexpected equal X coordinates");
return (((p1.Y - p2.Y) * (x - p1.X)) / (p1.X - p2.X)) + p1.Y;
}
/// <summary>
/// Builds the visual tree for the
/// <see cref="T:System.Windows.Controls.ChildWindow" /> control when a
/// new template is applied.
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "No need to split the code into two parts.")]
public override void OnApplyTemplate()
{
this.UnsubscribeFromTemplatePartEvents();
base.OnApplyTemplate();
this.CloseButton = GetTemplateChild(PART_CloseButton) as ButtonBase;
if (this.CloseButton != null)
{
if (this.HasCloseButton)
{
this.CloseButton.Visibility = Visibility.Visible;
}
else
{
this.CloseButton.Visibility = Visibility.Collapsed;
}
}
if (this._closed != null)
{
this._closed.Completed -= new EventHandler(this.Closing_Completed);
}
if (this._opened != null)
{
this._opened.Completed -= new EventHandler(this.Opening_Completed);
}
this._root = GetTemplateChild(PART_Root) as FrameworkElement;
if (this._root != null)
{
IList groups = VisualStateManager.GetVisualStateGroups(this._root);
if (groups != null)
{
IList states = null;
foreach (VisualStateGroup vsg in groups)
{
if (vsg.Name == PopWindow.VSMGROUP_Window)
{
states = vsg.States;
break;
}
}
if (states != null)
{
foreach (VisualState state in states)
{
if (state.Name == PopWindow.VSMSTATE_StateClosed)
{
this._closed = state.Storyboard;
}
if (state.Name == PopWindow.VSMSTATE_StateOpen)
{
this._opened = state.Storyboard;
}
}
}
}
}
this.ContentRoot = GetTemplateChild(PART_ContentRoot) as FrameworkElement;
this._chrome = GetTemplateChild(PART_Chrome) as FrameworkElement;
this.Overlay = GetTemplateChild(PART_Overlay) as Panel;
this._contentPresenter = GetTemplateChild(PART_ContentPresenter) as FrameworkElement;
this.SubscribeToTemplatePartEvents();
this.SubscribeToStoryBoardEvents();
this._desiredMargin = this.Margin;
this.Margin = new Thickness(0);
// Update overlay size
if (this.IsOpen)
{
this._desiredContentHeight = this.Height;
this._desiredContentWidth = this.Width;
this.UpdateOverlaySize();
this.UpdateRenderTransform();
this.ChangeVisualState();
}
}
/// <summary>
/// Raises the
/// <see cref="E:System.Windows.Controls.ChildWindow.Closed" /> event.
/// </summary>
/// <param name="e">The event data.</param>
protected virtual void OnClosed(EventArgs e)
{
EventHandler handler = this.Closed;
if (null != handler)
{
handler(this, e);
}
this._isOpen = false;
}
/// <summary>
/// Raises the
/// <see cref="E:System.Windows.Controls.ChildWindow.Closing" /> event.
/// </summary>
/// <param name="e">The event data.</param>
protected virtual void OnClosing(CancelEventArgs e)
{
EventHandler<CancelEventArgs> handler = this.Closing;
if (null != handler)
{
handler(this, e);
}
}
/// <summary>
/// Returns a
/// <see cref="T:System.Windows.Automation.Peers.ChildWindowAutomationPeer" />
/// for use by the Silverlight automation infrastructure.
/// </summary>
/// <returns>
/// <see cref="T:System.Windows.Automation.Peers.ChildWindowAutomationPeer" />
/// for the <see cref="T:System.Windows.Controls.ChildWindow" /> object.
/// </returns>
protected override AutomationPeer OnCreateAutomationPeer()
{
return new PopWindowAutomationPeer(this);
}
/// <summary>
/// This method is called every time a
/// <see cref="T:System.Windows.Controls.ChildWindow" /> is displayed.
/// </summary>
protected virtual void OnOpened()
{
this.UpdatePosition();
this._isOpen = true;
if (this.Overlay != null)
{
this.Overlay.Opacity = this.OverlayOpacity;
this.Overlay.Background = this.OverlayBrush;
}
if (!this.Focus())
{
// If the Focus() fails it means there is no focusable element in the
// ChildWindow. In this case we set IsTabStop to true to have the keyboard functionality
this.IsTabStop = true;
this.Focus();
}
}
/// <summary>
/// Executed when the opening storyboard finishes.
/// </summary>
/// <param name="sender">Sender object.</param>
/// <param name="e">Event args.</param>
private void Opening_Completed(object sender, EventArgs e)
{
if (this._opened != null)
{
this._opened.Completed -= new EventHandler(this.Opening_Completed);
}
// AutomationPeer returns "ReadyForUserInteraction" when the ChildWindow
// is open and all animations have been completed.
this.InteractionState = WindowInteractionState.ReadyForUserInteraction;
this.OnOpened();
}
/// <summary>
/// Executed when the page resizes.
/// </summary>
/// <param name="sender">Sender object.</param>
/// <param name="e">Event args.</param>
private void Page_Resized(object sender, EventArgs e)
{
if (this.ChildWindowPopup != null)
{
this.UpdateOverlaySize();
}
}
/// <summary>
/// Executed when the root visual gets focus.
/// </summary>
/// <param name="sender">Sender object.</param>
/// <param name="e">Routed event args.</param>
private void RootVisual_GotFocus(object sender, RoutedEventArgs e)
{
this.Focus();
this.InteractionState = WindowInteractionState.ReadyForUserInteraction;
}
/// <summary>
/// Opens a <see cref="T:System.Windows.Controls.ChildWindow" /> and
/// returns without waiting for the
/// <see cref="T:System.Windows.Controls.ChildWindow" /> to close.
/// </summary>
/// <exception cref="T:System.InvalidOperationException">
/// The child window is already in the visual tree.
/// </exception>
public void Show()
{
// AutomationPeer returns "Running" when Show() is called
// but the ChildWindow is not ready for user interaction:
this.InteractionState = WindowInteractionState.Running;
this.SubscribeToEvents();
this.SubscribeToTemplatePartEvents();
this.SubscribeToStoryBoardEvents();
if (this.ChildWindowPopup == null)
{
this.ChildWindowPopup = new Popup();
try
{
this.ChildWindowPopup.Child = this;
}
catch (ArgumentException)
{
// If the ChildWindow is already in the visualtree, we cannot set it to be the child of the popup
// we are throwing a friendlier exception for this case:
this.InteractionState = WindowInteractionState.NotResponding;
throw new InvalidOperationException("参数异常");
}
}
// MaxHeight and MinHeight properties should not be overwritten:
this.MaxHeight = double.PositiveInfinity;
this.MaxWidth = double.PositiveInfinity;
// disable the underlying UI
if (RootVisual != null && !this.IsOpen)
{
if (OpenChildWindowCount == 0)
{
// Save current value to restore it upon closing the last window
RootVisual_PrevEnabledState = RootVisual.IsEnabled;
}
++OpenChildWindowCount;
RootVisual.IsEnabled = false;
}
if (this.ChildWindowPopup != null && Application.Current.RootVisual != null)
{
this.ChildWindowPopup.IsOpen = true;
// while the ChildWindow is open, the DialogResult is always NULL:
this._dialogresult = null;
}
// if the template is already loaded, display loading visuals animation
if (this.ContentRoot != null)
{
this.ChangeVisualState();
}
}
/// <summary>
/// Subscribes to events when the ChildWindow is opened.
/// </summary>
private void SubscribeToEvents()
{
if (Application.Current != null && Application.Current.Host != null && Application.Current.Host.Content != null)
{
Application.Current.Exit += new EventHandler(this.Application_Exit);
Application.Current.Host.Content.Resized += new EventHandler(this.Page_Resized);
}
this.KeyDown += new KeyEventHandler(this.ChildWindow_KeyDown);
this.LostFocus += new RoutedEventHandler(this.ChildWindow_LostFocus);
this.SizeChanged += new SizeChangedEventHandler(this.ChildWindow_SizeChanged);
}
/// <summary>
/// Subscribes to events that are on the storyboards.
/// Unsubscribing from these events happen in the event handlers individually.
/// </summary>
private void SubscribeToStoryBoardEvents()
{
if (this._closed != null)
{
this._closed.Completed += new EventHandler(this.Closing_Completed);
}
if (this._opened != null)
{
this._opened.Completed += new EventHandler(this.Opening_Completed);
}
}
/// <summary>
/// Subscribes to events on the template parts.
/// </summary>
private void SubscribeToTemplatePartEvents()
{
if (this.CloseButton != null)
{
this.CloseButton.Click += new RoutedEventHandler(this.CloseButton_Click);
}
if (this._chrome != null)
{
this._chrome.MouseLeftButtonDown += new MouseButtonEventHandler(this.Chrome_MouseLeftButtonDown);
this._chrome.MouseLeftButtonUp += new MouseButtonEventHandler(this.Chrome_MouseLeftButtonUp);
this._chrome.MouseMove += new MouseEventHandler(this.Chrome_MouseMove);
}
if (this._contentPresenter != null)
{
this._contentPresenter.SizeChanged += new SizeChangedEventHandler(this.ContentPresenter_SizeChanged);
}
}
/// <summary>
/// Unsubscribe from events when the ChildWindow is closed.
/// </summary>
private void UnSubscribeFromEvents()
{
if (Application.Current != null && Application.Current.Host != null && Application.Current.Host.Content != null)
{
Application.Current.Exit -= new EventHandler(this.Application_Exit);
Application.Current.Host.Content.Resized -= new EventHandler(this.Page_Resized);
}
this.KeyDown -= new KeyEventHandler(this.ChildWindow_KeyDown);
this.LostFocus -= new RoutedEventHandler(this.ChildWindow_LostFocus);
this.SizeChanged -= new SizeChangedEventHandler(this.ChildWindow_SizeChanged);
}
/// <summary>
/// Unsubscribe from the events that are subscribed on the template part elements.
/// </summary>
private void UnsubscribeFromTemplatePartEvents()
{
if (this.CloseButton != null)
{
this.CloseButton.Click -= new RoutedEventHandler(this.CloseButton_Click);
}
if (this._chrome != null)
{
this._chrome.MouseLeftButtonDown -= new MouseButtonEventHandler(this.Chrome_MouseLeftButtonDown);
this._chrome.MouseLeftButtonUp -= new MouseButtonEventHandler(this.Chrome_MouseLeftButtonUp);
this._chrome.MouseMove -= new MouseEventHandler(this.Chrome_MouseMove);
}
if (this._contentPresenter != null)
{
this._contentPresenter.SizeChanged -= new SizeChangedEventHandler(this.ContentPresenter_SizeChanged);
}
}
/// <summary>
/// Updates the size of the overlay of the window.
/// </summary>
private void UpdateOverlaySize()
{
if (this.Overlay != null && Application.Current != null && Application.Current.Host != null && Application.Current.Host.Content != null)
{
this.Height = Application.Current.Host.Content.ActualHeight;
this.Width = Application.Current.Host.Content.ActualWidth;
if (Application.Current.Host.Settings.EnableAutoZoom)
{
double zoomFactor = Application.Current.Host.Content.ZoomFactor;
if (zoomFactor != 0)
{
this.Height /= zoomFactor;
this.Width /= zoomFactor;
}
}
this.Overlay.Height = this.Height;
this.Overlay.Width = this.Width;
if (this.ContentRoot != null)
{
this.ContentRoot.Width = this._desiredContentWidth;
this.ContentRoot.Height = this._desiredContentHeight;
this.ContentRoot.Margin = this._desiredMargin;
}
}
}
/// <summary>
/// Updates the position of the window in case the size of the content changes.
/// This allows ChildWindow only scale from right and bottom.
/// </summary>
private void UpdatePosition()
{
if (this.ContentRoot != null && Application.Current != null && Application.Current.RootVisual != null)
{
GeneralTransform gt = this.ContentRoot.TransformToVisual(Application.Current.RootVisual);
if (gt != null)
{
this._windowPosition = gt.Transform(new Point(0, 0));
}
}
}
/// <summary>
/// Updates the render transform applied on the overlay.
/// </summary>
private void UpdateRenderTransform()
{
if (this._root != null && this.ContentRoot != null)
{
// The Overlay part should not be affected by the render transform applied on the
// ChildWindow. In order to achieve this, we adjust an identity matrix to represent
// the _root's transformation, invert it, apply the inverted matrix on the _root, so that
// nothing is affected by the rendertransform, and apply the original transform only on the Content
GeneralTransform gt = this._root.TransformToVisual(null);
if (gt != null)
{
Point p10 = new Point(1, 0);
Point p01 = new Point(0, 1);
Point transform10 = gt.Transform(p10);
Point transform01 = gt.Transform(p01);
Matrix transformToRootMatrix = Matrix.Identity;
transformToRootMatrix.M11 = transform10.X;
transformToRootMatrix.M12 = transform10.Y;
transformToRootMatrix.M21 = transform01.X;
transformToRootMatrix.M22 = transform01.Y;
MatrixTransform original = new MatrixTransform();
original.Matrix = transformToRootMatrix;
InvertMatrix(ref transformToRootMatrix);
MatrixTransform mt = new MatrixTransform();
mt.Matrix = transformToRootMatrix;
TransformGroup tg = this._root.RenderTransform as TransformGroup;
if (tg != null)
{
tg.Children.Add(mt);
}
else
{
this._root.RenderTransform = mt;
}
tg = this.ContentRoot.RenderTransform as TransformGroup;
if (tg != null)
{
tg.Children.Add(original);
}
else
{
this.ContentRoot.RenderTransform = original;
}
}
}
}
/// <summary>
/// Updates the ContentRootTranslateTransform.
/// </summary>
/// <param name="X">X coordinate of the transform.</param>
/// <param name="Y">Y coordinate of the transform.</param>
private void UpdateContentRootTransform(double X, double Y)
{
if (this._contentRootTransform == null)
{
this._contentRootTransform = new TranslateTransform();
this._contentRootTransform.X = X;
this._contentRootTransform.Y = Y;
TransformGroup transformGroup = this.ContentRoot.RenderTransform as TransformGroup;
if (transformGroup == null)
{
transformGroup = new TransformGroup();
transformGroup.Children.Add(this.ContentRoot.RenderTransform);
}
transformGroup.Children.Add(this._contentRootTransform);
this.ContentRoot.RenderTransform = transformGroup;
}
else
{
this._contentRootTransform.X += X;
this._contentRootTransform.Y += Y;
}
}
#endregion Methods
}