From 5768be69f46600c0a886e54972d73d228f67e12e Mon Sep 17 00:00:00 2001 From: zengwenjie <1663900244@qq.com> Date: Tue, 30 Sep 2025 11:08:16 +0800 Subject: [PATCH] =?UTF-8?q?=E5=87=86=E5=A4=87=E7=BC=96=E5=86=99=E3=80=8CWi?= =?UTF-8?q?ndowBorder=E3=80=8D=E6=8E=A7=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DeedyDesigner/Deedy.Wpf/Themes/Generic.xaml | 13 + DeedyDesigner/Deedy.Wpf/WindowBorder.cs | 258 ++++++++++++++++++++ DeedyDesigner/Deedy.Wpf/WindowHeader.cs | 3 + 3 files changed, 274 insertions(+) create mode 100644 DeedyDesigner/Deedy.Wpf/WindowBorder.cs diff --git a/DeedyDesigner/Deedy.Wpf/Themes/Generic.xaml b/DeedyDesigner/Deedy.Wpf/Themes/Generic.xaml index e692577..3c9fe18 100644 --- a/DeedyDesigner/Deedy.Wpf/Themes/Generic.xaml +++ b/DeedyDesigner/Deedy.Wpf/Themes/Generic.xaml @@ -58,4 +58,17 @@ + + diff --git a/DeedyDesigner/Deedy.Wpf/WindowBorder.cs b/DeedyDesigner/Deedy.Wpf/WindowBorder.cs new file mode 100644 index 0000000..95511d5 --- /dev/null +++ b/DeedyDesigner/Deedy.Wpf/WindowBorder.cs @@ -0,0 +1,258 @@ +using System.ComponentModel; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Markup; +using System.Windows.Media; +using Shell = System.Windows.Shell; + +namespace Deedy +{ + /// + /// 为自定义窗口提供标题栏与内容容器;可以作为一个窗口的直接子节点 + /// + [ContentProperty("Content")] + [TemplatePart(Name = "MainMenu", Type = typeof(Menu))] + [TemplatePart(Name = "Minimize", Type = typeof(Button))] + [TemplatePart(Name = "Maximize", Type = typeof(Button))] + [TemplatePart(Name = "CloseWin", Type = typeof(Button))] + [TemplatePart(Name = "TitleBar", Type = typeof(Control))] + [TemplatePart(Name = "Controller", Type = typeof(Panel))] + [TemplatePart(Name = "Container", Type = typeof(Decorator))] + public class WindowBorder : HeaderedContentControl + { + static WindowBorder() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(WindowBorder), new FrameworkPropertyMetadata(typeof(WindowBorder))); + BackgroundProperty.OverrideMetadata(typeof(WindowBorder), new FrameworkPropertyMetadata(Brushes.Gray)); + } + /// + /// 悬停画刷 + /// + public Brush HoverBrush + { + get { return (Brush)GetValue(HoverBrushProperty); } + set { SetValue(HoverBrushProperty, value); } + } + public static readonly DependencyProperty HoverBrushProperty = + DependencyProperty.Register("HoverBrush", typeof(Brush), typeof(WindowBorder), new PropertyMetadata(Brushes.Silver)); + /// + /// 标题画刷 + /// + public Brush Headground + { + get { return (Brush)GetValue(HeadgroundProperty); } + set { SetValue(HeadgroundProperty, value); } + } + public static readonly DependencyProperty HeadgroundProperty = + DependencyProperty.Register("Headground", typeof(Brush), typeof(WindowBorder), new PropertyMetadata(Brushes.DimGray)); + /// + /// 标题高度 + /// + public double HeaderHeight + { + get { return (double)GetValue(HeaderHeightProperty); } + set { SetValue(HeaderHeightProperty, value); } + } + public static readonly DependencyProperty HeaderHeightProperty = + DependencyProperty.Register("HeaderHeight", typeof(double), typeof(WindowBorder), new PropertyMetadata(40.0, + (d, e) => (d as WindowBorder)?.HeaderHeight_PropertyChangedCallback(e))); + /// + /// 处理「WindowBorder.HeaderHeight」属性变更 + /// + protected virtual void HeaderHeight_PropertyChangedCallback(DependencyPropertyChangedEventArgs e) + { + if ((double)e.NewValue < 32) this.HeaderHeight = 32; + } + /// + /// 菜单 + /// + public MenuItem Menu + { + get { return (MenuItem)GetValue(MenuProperty); } + set { SetValue(MenuProperty, value); } + } + public static readonly DependencyProperty MenuProperty = + DependencyProperty.Register("Menu", typeof(MenuItem), typeof(WindowBorder), new PropertyMetadata(null, + (d, e) => (d as WindowBorder)?.Menu_PropertyChangedCallback(e))); + /// + /// 处理「WindowBorder.Menu」属性变更 + /// + protected virtual void Menu_PropertyChangedCallback(DependencyPropertyChangedEventArgs e) + { + if (this.MainMenu != null) + { + this.MainMenu.Items.Clear(); + if (e.NewValue is MenuItem menu) + { + menu.Height = this.HeaderHeight; + menu.Background = Brushes.Transparent; + this.MainMenu.Items.Add(menu); + } + } + } + + private Panel? Controller; + private Decorator? Container; + private Button? Minimize; + private Button? Maximize; + private Button? CloseWin; + private Menu? MainMenu; + private Control? TitleBar; + + private Window? Target; + private Shell.WindowChrome? Chrome; + + private static bool IsNeedOverrideMetadata = true; + public WindowBorder() { } + public void OnAttached() + { + FrameworkElement? parent = this.Parent as FrameworkElement; + Window? target = parent as Window; + while (parent != null && target == null) + { + parent = parent.Parent as FrameworkElement; + target = parent as Window; + } + //这是第【1】步 + if (target == null && !DesignerProperties.GetIsInDesignMode(this)) throw new ArgumentNullException("窗体装饰器[WindowDecorator]对象不可以附加到一个空的Window对象上!"); + this.Target = target; + if (this.Target == null) return; + this.Target.StateChanged += Target_StateChanged; + this.Target.Loaded += Target_Loaded; + if (IsNeedOverrideMetadata) + { + Window.WindowStyleProperty.OverrideMetadata(this.Target.GetType(), new FrameworkPropertyMetadata((d, e) => this.AdjustVisual())); + Window.ResizeModeProperty.OverrideMetadata(this.Target.GetType(), new FrameworkPropertyMetadata((d, e) => this.AdjustVisual())); + IsNeedOverrideMetadata = false; + } + } + public override void OnApplyTemplate() + { + this.OnDetaching(); + base.OnApplyTemplate(); + this.OnAttached(); + //这是第【2】步 + this.Controller = GetTemplateChild("Controller") as Panel; + this.Container = GetTemplateChild("Container") as Decorator; + this.Minimize = GetTemplateChild("Minimize") as Button; + this.Maximize = GetTemplateChild("Maximize") as Button; + this.CloseWin = GetTemplateChild("CloseWin") as Button; + this.TitleBar = GetTemplateChild("TitleBar") as Control; + this.MainMenu = GetTemplateChild("MainMenu") as Menu; + + if (this.MainMenu != null) + { + this.MainMenu.Items.Clear(); + if (this.Menu != null) + { + this.Menu.Height = this.Height; + this.Menu.Background = Brushes.Transparent; + this.MainMenu.Items.Add(this.Menu); + } + } + } + private void Target_Loaded(object sender, RoutedEventArgs e) + { + if (this.Background == BackgroundProperty.DefaultMetadata.DefaultValue) this.SetBinding(BackgroundProperty, new Binding() { Source = this.Target, Path = new PropertyPath(BackgroundProperty.Name) }); + if (this.BorderBrush == BorderBrushProperty.DefaultMetadata.DefaultValue) this.SetBinding(BorderBrushProperty, new Binding() { Source = this.Target, Path = new PropertyPath(BorderBrushProperty.Name) }); + //这是第【3】步 + this.Chrome = new Shell.WindowChrome() { CaptionHeight = this.HeaderHeight }; + Shell.WindowChrome.SetWindowChrome(this.Target, this.Chrome); + Shell.WindowChrome.SetIsHitTestVisibleInChrome(this.Container, true); + Shell.WindowChrome.SetIsHitTestVisibleInChrome(this.Controller, true); + + if (this.Controller != null) + { + foreach (var c in this.Controller.Children) + { + if (c is Button button) button.AddHandler(Button.ClickEvent, new RoutedEventHandler(this.CommandButton_Click)); + } + } + if (this.Menu != null) this.Menu.Height = this.HeaderHeight; + this.AdjustVisual(); + this.AdjustMargin(); + } + public void OnDetaching() + { + if (this.Target != null) + { + this.Target.StateChanged -= this.Target_StateChanged; + this.Target.Loaded -= this.Target_Loaded; + } + } + private void AdjustVisual() + { + if (this.Target != null) + { + this.Visibility = Visibility.Visible; + if (this.Controller != null) this.Controller.Visibility = Visibility.Visible; + if (this.Minimize != null) this.Minimize.Visibility = Visibility.Visible; + if (this.Maximize != null) this.Maximize.Visibility = Visibility.Visible; + if (this.CloseWin != null) this.CloseWin.Visibility = Visibility.Visible; + if (this.TitleBar != null) this.TitleBar.Visibility = Visibility.Visible; + + if (this.Target.ResizeMode == ResizeMode.NoResize) + { + if (this.Minimize != null) this.Minimize.Visibility = Visibility.Collapsed; + if (this.Maximize != null) this.Maximize.Visibility = Visibility.Collapsed; + } + if (this.Target.ResizeMode == ResizeMode.CanMinimize) + { + if (this.Maximize != null) this.Maximize.Visibility = Visibility.Collapsed; + } + if (this.Target.WindowStyle == WindowStyle.None) + { + if (this.TitleBar != null) this.TitleBar.Visibility = Visibility.Collapsed; + } + if (this.Target.WindowStyle == WindowStyle.ToolWindow) + { + if (this.Controller != null) this.Controller.Visibility = Visibility.Collapsed; + } + } + } + private void AdjustMargin() + { + if (this.Target != null) + { + var winContent = this.Target.Content as FrameworkElement; + if (winContent == null) return; + if (this.Target.WindowState == WindowState.Maximized) + winContent.Margin = new Thickness(8); + else winContent.Margin = new Thickness(0); + } + } + private void Target_StateChanged(object? sender, EventArgs e) + { + this.AdjustMargin(); + } + private void CommandButton_Click(object sender, RoutedEventArgs e) + { + Button? button = sender as Button; + if (button != null) + { + switch (button.Name) + { + case "Minimize": + if (this.Target != null) this.Target.WindowState = WindowState.Minimized; + break; + case "Maximize": + { + if (this.Target != null) + { + if (this.Target.WindowState == WindowState.Maximized || this.Target.WindowState == WindowState.Minimized) + this.Target.WindowState = WindowState.Normal; + else this.Target.WindowState = WindowState.Maximized; + } + } + break; + case "CloseWin": + this.Target?.Close(); + break; + default: + break; + } + } + } + } +} diff --git a/DeedyDesigner/Deedy.Wpf/WindowHeader.cs b/DeedyDesigner/Deedy.Wpf/WindowHeader.cs index 97683cd..05ff5b3 100644 --- a/DeedyDesigner/Deedy.Wpf/WindowHeader.cs +++ b/DeedyDesigner/Deedy.Wpf/WindowHeader.cs @@ -8,6 +8,9 @@ using Shell = System.Windows.Shell; namespace Deedy { + /// + /// 为自定义窗口提供标题栏 + /// [ContentProperty("Header")] [TemplatePart(Name = "MainMenu", Type = typeof(Menu))] [TemplatePart(Name = "Minimize", Type = typeof(Button))]