Files
Shrlalgo.RvKits/AntDesignWPF/Controls/AntdWindow.cs
2025-07-31 20:12:01 +08:00

719 lines
27 KiB
C#

using System;
using System.Collections;
using System.Reflection;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Media;
using AntDesign.WPF.Contracts;
using AntDesign.WPF.Microsoft.Windows.Shell;
using AntDesign.WPF.Win32;
using Standard;
namespace AntDesign.WPF.Controls
{
[TemplatePart(Name = PART_TitleBarThumb, Type = typeof(Thumb))]
[TemplatePart(Name = PART_TitleBar, Type = typeof(UIElement))]
[TemplatePart(Name = PART_Icon, Type = typeof(FrameworkElement))]
[TemplatePart(Name = PART_LeftWindowCommands, Type = typeof(WindowCommands))]
[TemplatePart(Name = PART_Title, Type = typeof(UIElement))]
[TemplatePart(Name = PART_RightWindowCommands, Type = typeof(WindowCommands))]
[TemplatePart(Name = PART_WindowButtons, Type = typeof(WindowButtons))]
public class AntdWindow : Window
{
#region Fields
private const string PART_TitleBarThumb = "PART_TitleBarThumb";
private const string PART_TitleBar = "PART_TitleBar";
private const string PART_Icon = "PART_Icon";
private const string PART_LeftWindowCommands = "PART_LeftWindowCommands";
private const string PART_Title = "PART_Title";
private const string PART_RightWindowCommands = "PART_RightWindowCommands";
private const string PART_WindowButtons = "PART_WindowButtons";
private Thumb titleBarThumb;
private UIElement titleBar;
private FrameworkElement icon;
internal ContentPresenter leftWindowCommands;
private UIElement title;
internal ContentPresenter rightWindowCommands;
internal ContentPresenter windowButtons;
#endregion
#region Properties
public static readonly DependencyProperty IgnoreTaskbarProperty =
DependencyProperty.Register("IgnoreTaskbar", typeof(bool), typeof(AntdWindow), new PropertyMetadata(false));
/// <summary>
/// Gets/sets whether the window will ignore (and overlap) the taskbar when maximized.
/// </summary>
public bool IgnoreTaskbar
{
get { return (bool)GetValue(IgnoreTaskbarProperty); }
set { SetValue(IgnoreTaskbarProperty, value); }
}
public static readonly DependencyProperty ShowSystemMenuProperty =
DependencyProperty.Register("ShowSystemMenu", typeof(bool), typeof(AntdWindow), new PropertyMetadata(false));
/// <summary>
/// Gets/sets if the the system menu should popup on right click.
/// </summary>
public bool ShowSystemMenu
{
get { return (bool)GetValue(ShowSystemMenuProperty); }
set { SetValue(ShowSystemMenuProperty, value); }
}
public static readonly DependencyProperty IsDraggableProperty =
DependencyProperty.Register("IsDraggable", typeof(bool), typeof(AntdWindow), new PropertyMetadata(true));
/// <summary>
/// Gets/sets if the the allow drag window
/// </summary>
public bool IsDraggable
{
get { return (bool)GetValue(IsDraggableProperty); }
set { SetValue(IsDraggableProperty, value); }
}
public static readonly DependencyProperty UseNoneWindowStyleProperty =
DependencyProperty.Register("UseNoneWindowStyle", typeof(bool), typeof(AntdWindow), new PropertyMetadata(false, OnUseNoneWindowStyleChanged));
/// <summary>
/// Gets/sets whether the WindowStyle is None or not.
/// </summary>
public bool UseNoneWindowStyle
{
get { return (bool)GetValue(UseNoneWindowStyleProperty); }
set { SetValue(UseNoneWindowStyleProperty, value); }
}
private static void OnUseNoneWindowStyleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (e.NewValue != e.OldValue)
{
(d as AntdWindow).ToggleNoneWindowStyle((bool)e.NewValue);
}
}
private void ToggleNoneWindowStyle(bool useNoneWindowStyle)
{
SetVisibiltyForTitleBarElements(!useNoneWindowStyle && TitleBarHeight > 0);
}
protected IntPtr CriticalHandle
{
get
{
var value = typeof(Window).GetProperty("CriticalHandle", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(this, new object[0]);
return (IntPtr)value;
}
}
public static readonly DependencyProperty TitleBarHeightProperty =
DependencyProperty.Register("TitleBarHeight", typeof(double), typeof(AntdWindow), new PropertyMetadata(30d, OnTitleBarHeightChanged));
/// <summary>
/// Gets/sets the TitleBar height.
/// </summary>
public double TitleBarHeight
{
get { return (double)GetValue(TitleBarHeightProperty); }
set { SetValue(TitleBarHeightProperty, value); }
}
private static void OnTitleBarHeightChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
{
var window = (AntdWindow)dependencyObject;
if (e.NewValue != e.OldValue)
{
window.SetVisibiltyForTitleBarElements((int)e.NewValue > 0);
}
}
public static readonly DependencyProperty TitleBarForegroundProperty =
DependencyProperty.Register("TitleBarForeground", typeof(Brush), typeof(AntdWindow));
/// <summary>
/// Gets/sets the foreground brush of the title bar.
/// </summary>
public Brush TitleBarForeground
{
get { return (Brush)GetValue(TitleBarForegroundProperty); }
set { SetValue(TitleBarForegroundProperty, value); }
}
public static readonly DependencyProperty TitleBarBackgroundProperty =
DependencyProperty.Register("TitleBarBackground", typeof(Brush), typeof(AntdWindow), new PropertyMetadata(Brushes.Transparent));
/// <summary>
/// Gets/sets the background brush of the title bar.
/// </summary>
public Brush TitleBarBackground
{
get { return (Brush)GetValue(TitleBarBackgroundProperty); }
set { SetValue(TitleBarBackgroundProperty, value); }
}
public static readonly DependencyProperty ShowIconProperty =
DependencyProperty.Register("ShowIcon", typeof(bool), typeof(AntdWindow), new PropertyMetadata(true, OnShowIconChanged));
/// <summary>
/// Gets/sets whether the titlebar icon is visible or not.
/// </summary>
public bool ShowIcon
{
get { return (bool)GetValue(ShowIconProperty); }
set { SetValue(ShowIconProperty, value); }
}
private static void OnShowIconChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (e.NewValue != e.OldValue)
{
(d as AntdWindow).SetVisibiltyForIcon();
}
}
public static readonly DependencyProperty IconTemplateProperty =
DependencyProperty.Register("IconTemplate", typeof(DataTemplate), typeof(AntdWindow), new PropertyMetadata(null));
/// <summary>
/// Gets/sets the icon content template to show a custom icon.
/// </summary>
public DataTemplate IconTemplate
{
get { return (DataTemplate)GetValue(IconTemplateProperty); }
set { SetValue(IconTemplateProperty, value); }
}
public static readonly DependencyProperty TitleCharacterCasingProperty =
DependencyProperty.Register("TitleCharacterCasing", typeof(CharacterCasing), typeof(AntdWindow),
new FrameworkPropertyMetadata(CharacterCasing.Normal, FrameworkPropertyMetadataOptions.Inherits | FrameworkPropertyMetadataOptions.AffectsMeasure),
value => CharacterCasing.Normal <= (CharacterCasing)value && (CharacterCasing)value <= CharacterCasing.Upper);
/// <summary>
/// Character casing of the title
/// </summary>
public CharacterCasing TitleCharacterCasing
{
get { return (CharacterCasing)GetValue(TitleCharacterCasingProperty); }
set { SetValue(TitleCharacterCasingProperty, value); }
}
public static readonly DependencyProperty TitleAlignmentProperty =
DependencyProperty.Register("TitleAlignment", typeof(HorizontalAlignment), typeof(AntdWindow), new PropertyMetadata(HorizontalAlignment.Stretch, OnTitleAlignmentChanged));
public HorizontalAlignment TitleAlignment
{
get { return (HorizontalAlignment)GetValue(TitleAlignmentProperty); }
set { SetValue(TitleAlignmentProperty, value); }
}
private static void OnTitleAlignmentChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
{
if (dependencyObject is AntdWindow window)
{
window.SizeChanged -= window.OnSizeChanged;
if ((HorizontalAlignment)e.NewValue == HorizontalAlignment.Center)
{
window.SizeChanged += window.OnSizeChanged;
}
}
}
public static readonly DependencyProperty TitleTemplateProperty =
DependencyProperty.Register("TitleTemplate", typeof(DataTemplate), typeof(AntdWindow), new PropertyMetadata(null));
/// <summary>
/// Gets/sets the title content template to show a custom title.
/// </summary>
public DataTemplate TitleTemplate
{
get { return (DataTemplate)GetValue(TitleTemplateProperty); }
set { SetValue(TitleTemplateProperty, value); }
}
public static readonly DependencyProperty LeftWindowCommandsProperty =
DependencyProperty.Register("LeftWindowCommands", typeof(WindowCommands), typeof(AntdWindow), new PropertyMetadata(null, UpdateLogicalChilds));
/// <summary>
/// Gets/sets the left window commands that hosts the user commands.
/// </summary>
public WindowCommands LeftWindowCommands
{
get { return (WindowCommands)GetValue(LeftWindowCommandsProperty); }
set { SetValue(LeftWindowCommandsProperty, value); }
}
public static readonly DependencyProperty RightWindowCommandsProperty =
DependencyProperty.Register("RightWindowCommands", typeof(WindowCommands), typeof(AntdWindow), new PropertyMetadata(null, UpdateLogicalChilds));
/// <summary>
/// Gets/sets the right window commands that hosts the user commands.
/// </summary>
public WindowCommands RightWindowCommands
{
get { return (WindowCommands)GetValue(RightWindowCommandsProperty); }
set { SetValue(RightWindowCommandsProperty, value); }
}
public static readonly DependencyProperty WindowButtonsProperty =
DependencyProperty.Register("WindowButtons", typeof(WindowButtons), typeof(AntdWindow), new PropertyMetadata(null, UpdateLogicalChilds));
/// <summary>
/// Gets/sets the window button commands that hosts the min/max/close commands.
/// </summary>
public WindowButtons WindowButtons
{
get { return (WindowButtons)GetValue(WindowButtonsProperty); }
set { SetValue(WindowButtonsProperty, value); }
}
private static void UpdateLogicalChilds(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
{
if (!(dependencyObject is AntdWindow window))
{
return;
}
if (e.OldValue is FrameworkElement oldChild)
{
window.RemoveLogicalChild(oldChild);
}
if (e.NewValue is FrameworkElement newChild)
{
window.AddLogicalChild(newChild);
// Yes, that's crazy. But we must do this to enable all possible scenarios for setting DataContext
// in a Window. Without set the DataContext at this point it can happen that e.g. a Flyout
// doesn't get the same DataContext.
// So now we can type
//
// InitializeComponent();
// DataContext = new MainViewModel();
//
// or
//
// DataContext = new MainViewModel();
// InitializeComponent();
//
newChild.DataContext = window.DataContext;
}
}
#endregion
#region Constructors
static AntdWindow()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(AntdWindow), new FrameworkPropertyMetadata(typeof(AntdWindow)));
}
public AntdWindow()
{
//SetResourceReference(StyleProperty, typeof(AntdWindow));
DataContextChanged += OnDataContextChanged;
}
#endregion
#region LogicalTree
protected override IEnumerator LogicalChildren
{
get
{
// cheat, make a list with all logical content and return the enumerator
ArrayList children = new ArrayList { Content };
if (LeftWindowCommands != null)
{
children.Add(LeftWindowCommands);
}
if (RightWindowCommands != null)
{
children.Add(RightWindowCommands);
}
if (WindowButtons != null)
{
children.Add(WindowButtons);
}
return children.GetEnumerator();
}
}
#endregion
#region Overrides
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
titleBarThumb = GetTemplateChild(PART_TitleBarThumb) as Thumb;
titleBar = GetTemplateChild(PART_TitleBar) as UIElement;
icon = GetTemplateChild(PART_Icon) as FrameworkElement;
title = GetTemplateChild(PART_Title) as UIElement;
leftWindowCommands = GetTemplateChild(PART_LeftWindowCommands) as ContentPresenter;
rightWindowCommands = GetTemplateChild(PART_RightWindowCommands) as ContentPresenter;
windowButtons = GetTemplateChild(PART_WindowButtons) as ContentPresenter;
if (LeftWindowCommands == null)
LeftWindowCommands = new WindowCommands();
if (RightWindowCommands == null)
RightWindowCommands = new WindowCommands();
if (WindowButtons == null)
WindowButtons = new WindowButtons();
LeftWindowCommands.ParentWindow = this;
RightWindowCommands.ParentWindow = this;
WindowButtons.ParentWindow = this;
var windowChrome = GetValue(WindowChrome.WindowChromeProperty) as WindowChrome;
if (windowChrome != null)
{
BindingOperations.SetBinding(
windowChrome,
WindowChrome.IgnoreTaskbarProperty,
new Binding { Path = new PropertyPath(IgnoreTaskbarProperty), Source = this, Mode = BindingMode.OneWay }
);
}
ToggleNoneWindowStyle(UseNoneWindowStyle);
}
#endregion
#region Private Methods
private void OnDataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
// add these controls to the window with AddLogicalChild method.
// This has the side effect that the DataContext doesn't update, so do this now here.
if (LeftWindowCommands != null) LeftWindowCommands.DataContext = DataContext;
if (RightWindowCommands != null) RightWindowCommands.DataContext = DataContext;
if (WindowButtons != null) WindowButtons.DataContext = DataContext;
}
private void OnSizeChanged(object sender, RoutedEventArgs e)
{
// this all works only for centered title
if (TitleAlignment != HorizontalAlignment.Center) return;
// Half of this Window
var halfDistance = ActualWidth / 2;
// Distance between center and left/right
var distanceToCenter = title.DesiredSize.Width / 2;
// Distance between right edge from LeftWindowCommands to left window side
var distanceFromLeft = icon.ActualWidth + LeftWindowCommands.ActualWidth;
// Distance between left edge from RightWindowCommands to right window side
var distanceFromRight = WindowButtons.ActualWidth + RightWindowCommands.ActualWidth;
// Margin
const double horizontalMargin = 5.0;
var dLeft = distanceFromLeft + distanceToCenter + horizontalMargin;
var dRight = distanceFromRight + distanceToCenter + horizontalMargin;
// TODO They should also change when the layout changes.
if ((dLeft < halfDistance) && (dRight < halfDistance))
{
Grid.SetColumn(title, 0);
Grid.SetColumnSpan(title, 5);
}
else
{
Grid.SetColumn(title, 2);
Grid.SetColumnSpan(title, 1);
}
}
private void SetVisibiltyForTitleBarElements(bool visible)
{
var value = visible ? Visibility.Visible : Visibility.Collapsed;
titleBar?.SetCurrentValue(VisibilityProperty, value);
title?.SetCurrentValue(VisibilityProperty, value);
SetVisibiltyForIcon();
leftWindowCommands?.SetValue(VisibilityProperty, value);
rightWindowCommands?.SetValue(VisibilityProperty, value);
windowButtons?.SetCurrentValue(VisibilityProperty, value);
SetWindowEvents();
}
private void SetVisibiltyForIcon()
{
if (icon == null) return;
icon.Visibility = ShowIcon && !UseNoneWindowStyle && TitleBarHeight > 0 ? Visibility.Visible : Visibility.Collapsed;
}
private void SetWindowEvents()
{
// clear all event handlers first
ClearWindowEvents();
// set mouse down/up for icon
if (icon != null && icon.Visibility == Visibility.Visible)
{
icon.MouseLeftButtonDown += OnIconMouseLeftButtonDown;
icon.MouseRightButtonUp += OnThumbMouseRightButtonUp;
}
if (titleBarThumb != null)
{
titleBarThumb.PreviewMouseLeftButtonUp += OnThumbPreviewMouseLeftButtonUp;
titleBarThumb.DragDelta += OnThumbDragDelta;
titleBarThumb.MouseDoubleClick += OnThumbMouseDoubleClick;
titleBarThumb.MouseRightButtonUp += OnThumbMouseRightButtonUp;
}
var thumbContentControl = title as IThumb;
if (thumbContentControl != null)
{
thumbContentControl.PreviewMouseLeftButtonUp += OnThumbPreviewMouseLeftButtonUp;
thumbContentControl.DragDelta += OnThumbDragDelta;
thumbContentControl.MouseDoubleClick += OnThumbMouseDoubleClick;
thumbContentControl.MouseRightButtonUp += OnThumbMouseRightButtonUp;
}
// handle size if we have a Grid for the title (e.g. clean window have a centered title)
if (title != null && TitleAlignment == HorizontalAlignment.Center)
{
SizeChanged += OnSizeChanged;
}
}
private void ClearWindowEvents()
{
// clear all event handlers first:
if (titleBarThumb != null)
{
titleBarThumb.PreviewMouseLeftButtonUp -= OnThumbPreviewMouseLeftButtonUp;
titleBarThumb.DragDelta -= OnThumbDragDelta;
titleBarThumb.MouseDoubleClick -= OnThumbMouseDoubleClick;
titleBarThumb.MouseRightButtonUp -= OnThumbMouseRightButtonUp;
}
var thumbContentControl = title as IThumb;
if (thumbContentControl != null)
{
thumbContentControl.PreviewMouseLeftButtonUp -= OnThumbPreviewMouseLeftButtonUp;
thumbContentControl.DragDelta -= OnThumbDragDelta;
thumbContentControl.MouseDoubleClick -= OnThumbMouseDoubleClick;
thumbContentControl.MouseRightButtonUp -= OnThumbMouseRightButtonUp;
}
if (icon != null)
{
icon.MouseLeftButtonDown -= OnIconMouseLeftButtonDown;
icon.MouseRightButtonUp -= OnThumbMouseRightButtonUp;
}
SizeChanged -= OnSizeChanged;
}
private void OnIconMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (!ShowSystemMenu) return;
if (e.ClickCount == 2)
{
Close();
} else
{
System.Windows.SystemCommands.ShowSystemMenu(this, PointToScreen(new Point(0, TitleBarHeight)));
}
}
private void OnThumbPreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
DoThumbPreviewMouseLeftButtonUp(this, e);
}
private void OnThumbDragDelta(object sender, DragDeltaEventArgs e)
{
DoThumbDragDelta(sender as IThumb, this, e);
}
private void OnThumbMouseDoubleClick(object sender, MouseButtonEventArgs e)
{
DoThumbMouseDoubleClick(this, e);
}
private void OnThumbMouseRightButtonUp(object sender, MouseButtonEventArgs e)
{
DoThumbMouseRightButtonUp(this, e);
}
internal static void DoThumbPreviewMouseLeftButtonUp(AntdWindow window, MouseButtonEventArgs e)
{
if (e.Source == e.OriginalSource)
{
Mouse.Capture(null);
}
}
internal static void DoThumbDragDelta(IThumb thumb, AntdWindow window, DragDeltaEventArgs e)
{
if (thumb == null)
{
throw new ArgumentNullException(nameof(thumb));
}
if (window == null)
{
throw new ArgumentNullException(nameof(window));
}
// drag only if IsDraggable is set to true
if (!window.IsDraggable ||
(!(Math.Abs(e.HorizontalChange) > 2) && !(Math.Abs(e.VerticalChange) > 2)))
{
return;
}
// tage from DragMove internal code
window.VerifyAccess();
//var cursorPos = WinApiHelper.GetPhysicalCursorPos();
// if the window is maximized dragging is only allowed on title bar (also if not visible)
var maximized = window.WindowState == WindowState.Maximized;
var isMouseOnTitlebar = Mouse.GetPosition(thumb).Y <= window.TitleBarHeight && window.TitleBarHeight > 0;
if (!isMouseOnTitlebar && maximized) return;
#pragma warning disable 618
// for the touch usage
UnsafeNativeMethods.ReleaseCapture();
#pragma warning restore 618
if (maximized)
{
//var cursorXPos = cursorPos.x;
EventHandler windowOnStateChanged = null;
windowOnStateChanged = (sender, args) =>
{
//window.Top = 2;
//window.Left = Math.Max(cursorXPos - window.RestoreBounds.Width / 2, 0);
window.StateChanged -= windowOnStateChanged;
if (window.WindowState == WindowState.Normal)
{
Mouse.Capture(thumb, CaptureMode.Element);
}
};
window.StateChanged += windowOnStateChanged;
}
var criticalHandle = window.CriticalHandle;
// DragMove works too
// window.DragMove();
// instead this 2 lines
#pragma warning disable 618
NativeMethods.SendMessage(criticalHandle, WM.SYSCOMMAND, (IntPtr)SC.MOUSEMOVE, IntPtr.Zero);
NativeMethods.SendMessage(criticalHandle, WM.LBUTTONUP, IntPtr.Zero, IntPtr.Zero);
#pragma warning restore 618
}
/// <summary>
/// Change window state on MouseDoubleClick
/// </summary>
/// <param name="window"></param>
/// <param name="e"></param>
internal static void DoThumbMouseDoubleClick(AntdWindow window, MouseButtonEventArgs e)
{
// restore/maximize only with left button
if (e.ChangedButton == MouseButton.Left)
{
// we can maximize or restore the window if the title bar height is set (also if title bar is hidden)
var canResize = window.ResizeMode == ResizeMode.CanResizeWithGrip || window.ResizeMode == ResizeMode.CanResize;
var mousePos = Mouse.GetPosition(window);
var isMouseOnTitlebar = mousePos.Y <= window.TitleBarHeight && window.TitleBarHeight > 0;
if (canResize && isMouseOnTitlebar)
{
if (window.WindowState == WindowState.Normal)
{
System.Windows.SystemCommands.MaximizeWindow(window);
}
else
{
System.Windows.SystemCommands.RestoreWindow(window);
}
e.Handled = true;
}
}
}
/// <summary>
/// Show system menu on MouseRightButtonUp
/// </summary>
/// <param name="window"></param>
/// <param name="e"></param>
internal static void DoThumbMouseRightButtonUp(AntdWindow window, MouseButtonEventArgs e)
{
if (window.ShowSystemMenu)
{
// show menu only if mouse pos is on title bar or if we have a window with none style and no title bar
var mousePos = e.GetPosition(window);
if ((mousePos.Y <= window.TitleBarHeight && window.TitleBarHeight > 0) || (window.UseNoneWindowStyle && window.TitleBarHeight <= 0))
{
System.Windows.SystemCommands.ShowSystemMenu(window, window.PointToScreen(mousePos));
}
}
}
/// <summary>
/// Gets the template child with the given name.
/// </summary>
/// <typeparam name="T">The interface type inheirted from DependencyObject.</typeparam>
/// <param name="name">The name of the template child.</param>
internal T GetPart<T>(string name) where T : class
{
return GetTemplateChild(name) as T;
}
/// <summary>
/// Gets the template child with the given name.
/// </summary>
/// <param name="name">The name of the template child.</param>
internal DependencyObject GetPart(string name)
{
return GetTemplateChild(name);
}
#endregion
}
}