@@ -0,0 +1,214 @@
using System ;
using System.Collections.Generic ;
using System.Diagnostics.CodeAnalysis ;
using System.Linq ;
using System.Text ;
using System.Threading.Tasks ;
using System.Windows ;
using System.Windows.Controls ;
using System.Windows.Controls.Primitives ;
using System.Windows.Documents ;
using System.Windows.Media ;
using System.Windows.Media.Animation ;
namespace Deedy
{
/// <summary>
/// 抽屉窗口装饰器
/// </summary>
public class DrawerViewer : Adorner
{
private readonly VisualCollection _visualChildren ;
private readonly Border _border ;
public Brush Background { get = > ( Brush ) GetValue ( BackgroundProperty ) ; set = > SetValue ( BackgroundProperty , value ) ; }
public static readonly DependencyProperty BackgroundProperty = Border . BackgroundProperty . AddOwner ( typeof ( DrawerViewer ) ) ;
public Brush BorderBrush { get = > ( Brush ) GetValue ( BorderBrushProperty ) ; set = > SetValue ( BorderBrushProperty , value ) ; }
public static readonly DependencyProperty BorderBrushProperty = Border . BorderBrushProperty . AddOwner ( typeof ( DrawerViewer ) ) ;
public Thickness BorderThickness { get = > ( Thickness ) GetValue ( BorderThicknessProperty ) ; set = > SetValue ( BorderThicknessProperty , value ) ; }
public static readonly DependencyProperty BorderThicknessProperty = Border . BorderThicknessProperty . AddOwner ( typeof ( DrawerViewer ) ) ;
public CornerRadius CornerRadius { get = > ( CornerRadius ) GetValue ( CornerRadiusProperty ) ; set = > SetValue ( CornerRadiusProperty , value ) ; }
public static readonly DependencyProperty CornerRadiusProperty = Border . CornerRadiusProperty . AddOwner ( typeof ( DrawerViewer ) ) ;
private readonly ContentPresenter _contentPresenter ;
public object Content { get = > GetValue ( ContentProperty ) ; set = > SetValue ( ContentProperty , value ) ; }
public static readonly DependencyProperty ContentProperty = ContentPresenter . ContentProperty . AddOwner ( typeof ( DrawerViewer ) ) ;
public DataTemplate ContentTemplate { get = > ( DataTemplate ) GetValue ( ContentTemplateProperty ) ; set = > SetValue ( ContentTemplateProperty , value ) ; }
public static readonly DependencyProperty ContentTemplateProperty = ContentPresenter . ContentTemplateProperty . AddOwner ( typeof ( DrawerViewer ) ) ;
public DataTemplateSelector ContentTemplateSelector { get = > ( DataTemplateSelector ) GetValue ( ContentTemplateSelectorProperty ) ; set = > SetValue ( ContentTemplateSelectorProperty , value ) ; }
public static readonly DependencyProperty ContentTemplateSelectorProperty = ContentPresenter . ContentTemplateSelectorProperty . AddOwner ( typeof ( DrawerViewer ) ) ;
/// <summary>
/// 动画方向: 如果为「null」则使用缩放动画
/// </summary>
private readonly Dock ? _direction ;
/// <summary>
/// 根据抽屉加载方向创建边距动画所需要的「Thickness」对象
/// </summary>
/// <returns>动画需要的「Thickness」对象</returns>
private Thickness GetMargin4Animation ( )
{
switch ( this . _direction )
{
case Dock . Left : return new Thickness ( 0 , 0 , this . AdornedElement . RenderSize . Width , 0 ) ;
case Dock . Right : return new Thickness ( this . AdornedElement . RenderSize . Width , 0 , 0 , 0 ) ;
case Dock . Top : return new Thickness ( 0 , 0 , 0 , this . AdornedElement . RenderSize . Height ) ;
case Dock . Bottom : return new Thickness ( 0 , AdornedElement . RenderSize . Height , 0 , 0 ) ;
default :
double horMargin = this . AdornedElement . RenderSize . Width / 2 ;
double verMargin = this . AdornedElement . RenderSize . Height / 2 ;
return new Thickness ( horMargin , verMargin , horMargin , verMargin ) ;
}
}
/// <summary>
/// 注册附属视图;注册前请确保布局属性被正确设置
/// </summary>
/// <param name="attachedView">要注册的附属视图;注册前请确保布局属性被正确设置</param>
/// <param name="dock">在注册的方向</param>
/// <returns>当前装饰器对象,方便连续操作;不会创建新的装饰器</returns>
public DrawerViewer RegisterAttachedView ( UIElement attachedView , Dock ? dock )
{
dock ? ? = ( Dock ) ( ( ( int ) ( this . _direction ? ? Dock . Left ) + 2 ) % 4 ) ;
return this ;
}
/// <summary>
/// 在特定「UIElement」上创建「WindowDrawer」蒙层
/// </summary>
/// <param name="adornedElement">目标「UIElement」元素</param>
/// <param name="direction">抽屉的加载方向,如果不设置则使用缩放动画</param>
public DrawerViewer ( UIElement adornedElement , Dock ? direction = null ) : base ( adornedElement )
{
this . Opacity = 0 ;
this . _direction = direction ;
_visualChildren = new VisualCollection ( this ) ;
// 创建边框并绑定属性
_border = new Border ( ) { } ;
var backgroundBinding = new System . Windows . Data . Binding ( BackgroundProperty . Name ) { Source = this } ;
var borderBrushBinding = new System . Windows . Data . Binding ( BorderBrushProperty . Name ) { Source = this } ;
var borderThicknessBinding = new System . Windows . Data . Binding ( BorderThicknessProperty . Name ) { Source = this } ;
var cornerRadiusBinding = new System . Windows . Data . Binding ( CornerRadiusProperty . Name ) { Source = this } ;
_border . SetBinding ( Border . BackgroundProperty , backgroundBinding ) ;
_border . SetBinding ( Border . BorderBrushProperty , borderBrushBinding ) ;
_border . SetBinding ( Border . BorderThicknessProperty , borderThicknessBinding ) ;
_border . SetBinding ( Border . CornerRadiusProperty , cornerRadiusBinding ) ;
_visualChildren . Add ( _border ) ;
// 创建内容并绑定属性
_contentPresenter = new ContentPresenter
{
HorizontalAlignment = HorizontalAlignment . Stretch ,
VerticalAlignment = VerticalAlignment . Stretch
} ;
var contentBinding = new System . Windows . Data . Binding ( ContentProperty . Name ) { Source = this } ;
var contentTemplateBinding = new System . Windows . Data . Binding ( ContentTemplateProperty . Name ) { Source = this } ;
var contentTemplateSelectorBinding = new System . Windows . Data . Binding ( ContentTemplateSelectorProperty . Name ) { Source = this } ;
_contentPresenter . SetBinding ( ContentPresenter . ContentProperty , contentBinding ) ;
_contentPresenter . SetBinding ( ContentPresenter . ContentTemplateProperty , contentTemplateBinding ) ;
_contentPresenter . SetBinding ( ContentPresenter . ContentTemplateSelectorProperty , contentTemplateSelectorBinding ) ;
_border . Child = _contentPresenter ;
}
[AllowNull]
private readonly RoutedEvent [ ] _routedEvents ;
private readonly RoutedEventHandler ? _routedEventHandler ;
/// <summary>
/// 在特定「UIElement」上创建「WindowDrawer」蒙层, 同时绑定特定路由事件
/// </summary>
/// <param name="adornedElement">目标「UIElement」元素</param>
/// <param name="routedEventHandler">路由事件处理程序</param>
/// <param name="direction">抽屉的加载方向,如果不设置则使用缩放动画</param>
/// <param name="routedEvents">要绑定的路由事件</param>
/// <remarks>
/// 如果不设置「routedEvents」参数则注册「ButtonBase.ClickEvent」事件
/// </remarks>
public DrawerViewer ( UIElement adornedElement , RoutedEventHandler routedEventHandler , Dock ? direction , params RoutedEvent [ ] routedEvents )
: this ( adornedElement , direction )
{
if ( routedEvents . Length = = 0 ) routedEvents = [ ButtonBase . ClickEvent ] ;
if ( routedEventHandler ! = null )
{
_routedEvents = routedEvents ;
_routedEventHandler = routedEventHandler ;
foreach ( var routedEvent in routedEvents )
this . AddHandler ( routedEvent , routedEventHandler ) ;
}
}
/// <summary>
/// 在特定「UIElement」上创建「WindowDrawer」蒙层, 同时绑定特定路由事件
/// </summary>
/// <param name="adornedElement">目标「UIElement」元素</param>
/// <param name="routedEventHandler">路由事件处理程序</param>
/// <param name="routedEvents">要绑定的路由事件</param>
/// <remarks>
/// <para>如果不设置「routedEvents」参数则注册「ButtonBase.ClickEvent」事件</para>
/// <para>抽屉采用缩放方式加载</para>
/// </remarks>
public DrawerViewer ( UIElement adornedElement , RoutedEventHandler routedEventHandler , params RoutedEvent [ ] routedEvents )
: this ( adornedElement , routedEventHandler , null , routedEvents ) { }
protected override int VisualChildrenCount = > _visualChildren . Count ;
protected override Visual GetVisualChild ( int index ) = > _visualChildren [ index ] ;
protected override Size MeasureOverride ( Size constraint )
{
_border . Measure ( constraint ) ;
return base . MeasureOverride ( constraint ) ;
}
protected override Size ArrangeOverride ( Size finalSize )
{
_border . Arrange ( new Rect ( 0 , 0 , finalSize . Width , finalSize . Height ) ) ;
return finalSize ;
}
public void ShowWithAnimation ( double seconds = 1 )
{
var doubleAnimation = new DoubleAnimation
{
From = 0 ,
To = 1 ,
Duration = TimeSpan . FromSeconds ( seconds ) ,
EasingFunction = new CubicEase { EasingMode = EasingMode . EaseIn }
} ;
BeginAnimation ( OpacityProperty , doubleAnimation ) ;
var marginAnimation = new ThicknessAnimation
{
From = this . GetMargin4Animation ( ) ,
To = new Thickness ( 0 ) ,
Duration = TimeSpan . FromSeconds ( seconds ) ,
EasingFunction = new CubicEase { EasingMode = EasingMode . EaseIn }
} ;
_border . BeginAnimation ( MarginProperty , marginAnimation ) ;
}
public void HideWithAnimation ( double seconds = 1 )
{
var doubleAnimation = new DoubleAnimation
{
From = 1 ,
To = 0 ,
Duration = TimeSpan . FromSeconds ( seconds ) ,
EasingFunction = new CubicEase { EasingMode = EasingMode . EaseOut }
} ;
doubleAnimation . Completed + = ( s , e ) = >
{
if ( this . _routedEventHandler ! = null )
foreach ( var routedEvent in this . _routedEvents )
this . RemoveHandler ( routedEvent , this . _routedEventHandler ) ;
var layer = AdornerLayer . GetAdornerLayer ( AdornedElement ) ;
layer ? . Remove ( this ) ;
} ;
BeginAnimation ( OpacityProperty , doubleAnimation ) ;
var marginAnimation = new ThicknessAnimation
{
From = new Thickness ( 0 ) ,
To = this . GetMargin4Animation ( ) ,
Duration = TimeSpan . FromSeconds ( seconds ) ,
EasingFunction = new CubicEase { EasingMode = EasingMode . EaseOut }
} ;
_border . BeginAnimation ( MarginProperty , marginAnimation ) ;
}
}
}