重写「自定义窗口标题栏」控件
This commit is contained in:
@@ -10,6 +10,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Deedy.Activity\Deedy.Activity.csproj" />
|
||||
<ProjectReference Include="..\Deedy.Wpf\Deedy.Wpf.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
<UniformGrid>
|
||||
<Button Content="打开抽屉" Click="Button_Click"/>
|
||||
<Button Content="自定义窗口标题测试" Click="Button_Click_1"/>
|
||||
<Button Content="自定义窗口标题栏" Click="Button_Click_2"/>
|
||||
</UniformGrid>
|
||||
</AdornerDecorator>
|
||||
</DockPanel>
|
||||
|
||||
@@ -37,5 +37,10 @@ namespace Deedy
|
||||
{
|
||||
new WindowAdornerTest().ShowDialog();
|
||||
}
|
||||
|
||||
private void Button_Click_2(object sender, RoutedEventArgs e)
|
||||
{
|
||||
new WindowHeaderTest().ShowDialog();
|
||||
}
|
||||
}
|
||||
}
|
||||
16
DeedyDesigner/Deedy.Testing/WindowHeaderTest.xaml
Normal file
16
DeedyDesigner/Deedy.Testing/WindowHeaderTest.xaml
Normal file
@@ -0,0 +1,16 @@
|
||||
<Window x:Class="Deedy.Testing.WindowHeaderTest"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:local="clr-namespace:Deedy.Testing"
|
||||
xmlns:deedy="clr-namespace:Deedy;assembly=Deedy.Wpf"
|
||||
mc:Ignorable="d"
|
||||
Title="WindowHeaderTest" Height="450" Width="800" Foreground="White">
|
||||
<DockPanel>
|
||||
<deedy:WindowHeader DockPanel.Dock="Top" FontSize="18" HoverBrush="Wheat">
|
||||
<Button Content="按钮"/>
|
||||
</deedy:WindowHeader>
|
||||
<Grid/>
|
||||
</DockPanel>
|
||||
</Window>
|
||||
27
DeedyDesigner/Deedy.Testing/WindowHeaderTest.xaml.cs
Normal file
27
DeedyDesigner/Deedy.Testing/WindowHeaderTest.xaml.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Shapes;
|
||||
|
||||
namespace Deedy.Testing
|
||||
{
|
||||
/// <summary>
|
||||
/// WindowHeaderTest.xaml 的交互逻辑
|
||||
/// </summary>
|
||||
public partial class WindowHeaderTest : Window
|
||||
{
|
||||
public WindowHeaderTest()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
||||
10
DeedyDesigner/Deedy.Wpf/AssemblyInfo.cs
Normal file
10
DeedyDesigner/Deedy.Wpf/AssemblyInfo.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using System.Windows;
|
||||
|
||||
[assembly: ThemeInfo(
|
||||
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
|
||||
//(used if a resource is not found in the page,
|
||||
// or application resource dictionaries)
|
||||
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
|
||||
//(used if a resource is not found in the page,
|
||||
// app, or any theme specific resource dictionaries)
|
||||
)]
|
||||
11
DeedyDesigner/Deedy.Wpf/Deedy.Wpf.csproj
Normal file
11
DeedyDesigner/Deedy.Wpf/Deedy.Wpf.csproj
Normal file
@@ -0,0 +1,11 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0-windows</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<UseWPF>true</UseWPF>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<RootNamespace>Deedy</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
69
DeedyDesigner/Deedy.Wpf/Themes/Generic.xaml
Normal file
69
DeedyDesigner/Deedy.Wpf/Themes/Generic.xaml
Normal file
@@ -0,0 +1,69 @@
|
||||
<ResourceDictionary
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="clr-namespace:Deedy">
|
||||
<Style TargetType="{x:Type local:WindowHeader}">
|
||||
<Setter Property="Height" Value="32"/>
|
||||
<Setter Property="HorizontalAlignment" Value="Stretch"/>
|
||||
<Setter Property="HorizontalContentAlignment" Value="Left"/>
|
||||
<Setter Property="Padding" Value="6"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type local:WindowHeader}">
|
||||
<ControlTemplate.Resources>
|
||||
<Style x:Key="controlButtonStyle" TargetType="{x:Type Button}">
|
||||
<Setter Property="Background" Value="Transparent"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type Button}">
|
||||
<Border Background="{TemplateBinding Background}">
|
||||
<ContentPresenter Margin="{TemplateBinding Padding}"/>
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</ControlTemplate.Resources>
|
||||
<Border Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}">
|
||||
<Grid x:Name="Root">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="auto"/>
|
||||
<ColumnDefinition Width="auto"/>
|
||||
<ColumnDefinition />
|
||||
<ColumnDefinition Width="auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Border x:Name="IconView" Width="{Binding ActualHeight, ElementName=Root}">
|
||||
<Image Margin="{TemplateBinding Padding}" VerticalAlignment="Center" HorizontalAlignment="Center"
|
||||
Source="{Binding Icon, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}"/>
|
||||
</Border>
|
||||
<TextBlock x:Name="TitleBar" Grid.Column="1" FontSize="{TemplateBinding FontSize}" Foreground="{TemplateBinding Foreground}"
|
||||
Margin="{TemplateBinding Padding}" VerticalAlignment="Center" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||
Text="{Binding Title, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}"/>
|
||||
<Border x:Name="Container" Grid.Column="2" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
|
||||
<StackPanel x:Name="Controller" Grid.Column="3" Orientation="Horizontal">
|
||||
<Button x:Name="Minimize" VerticalAlignment="Stretch" Style="{StaticResource controlButtonStyle}" Cursor="Hand"
|
||||
Padding="{TemplateBinding Padding}" Width="{Binding ActualHeight, ElementName=Root}">
|
||||
<Viewbox Stretch="Uniform">
|
||||
<Path Width="1024" Height="1024" Stroke="{TemplateBinding Foreground}" Fill="{TemplateBinding Foreground}" Data="M917.333333 554.666667H106.666667a21.333333 21.333333 0 0 1 0-42.666667h810.666666a21.333333 21.333333 0 0 1 0 42.666667z"/>
|
||||
</Viewbox>
|
||||
</Button>
|
||||
<Button x:Name="Maximize" VerticalAlignment="Stretch" Style="{StaticResource controlButtonStyle}" Cursor="Hand"
|
||||
Padding="{TemplateBinding Padding}" Width="{Binding ActualHeight, ElementName=Root}">
|
||||
<Viewbox Stretch="Uniform">
|
||||
<Path Width="1024" Height="1024" Stroke="{TemplateBinding Foreground}" Fill="{TemplateBinding Foreground}" Data="M714.666667 256H138.666667a53.393333 53.393333 0 0 0-53.333334 53.333333v576a53.393333 53.393333 0 0 0 53.333334 53.333334h576a53.393333 53.393333 0 0 0 53.333333-53.333334V309.333333a53.393333 53.393333 0 0 0-53.333333-53.333333z m10.666666 629.333333a10.666667 10.666667 0 0 1-10.666666 10.666667H138.666667a10.666667 10.666667 0 0 1-10.666667-10.666667V309.333333a10.666667 10.666667 0 0 1 10.666667-10.666666h576a10.666667 10.666667 0 0 1 10.666666 10.666666z m213.333334-746.666666v565.333333a21.333333 21.333333 0 0 1-42.666667 0V138.666667a10.666667 10.666667 0 0 0-10.666667-10.666667H320a21.333333 21.333333 0 0 1 0-42.666667h565.333333a53.393333 53.393333 0 0 1 53.333334 53.333334z"/>
|
||||
</Viewbox>
|
||||
</Button>
|
||||
<Button x:Name="CloseWin" VerticalAlignment="Stretch" Style="{StaticResource controlButtonStyle}" Cursor=""
|
||||
Padding="{TemplateBinding Padding}" Width="{Binding ActualHeight, ElementName=Root}">
|
||||
<Viewbox Stretch="Uniform">
|
||||
<Path Width="1024" Height="1024" Stroke="{TemplateBinding Foreground}" Fill="{TemplateBinding Foreground}" Data="M542.173333 512l347.58-347.58a21.333333 21.333333 0 1 0-30.173333-30.173333L512 481.826667 164.42 134.246667a21.333333 21.333333 0 0 0-30.173333 30.173333L481.826667 512l-347.58 347.58a21.333333 21.333333 0 0 0 30.173333 30.173333L512 542.173333l347.58 347.58a21.333333 21.333333 0 0 0 30.173333-30.173333z"/>
|
||||
</Viewbox>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</ResourceDictionary>
|
||||
214
DeedyDesigner/Deedy.Wpf/WindowHeader.cs
Normal file
214
DeedyDesigner/Deedy.Wpf/WindowHeader.cs
Normal file
@@ -0,0 +1,214 @@
|
||||
using System.ComponentModel;
|
||||
using System.Text;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Markup;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Navigation;
|
||||
using System.Windows.Shapes;
|
||||
using System.Windows.Shell;
|
||||
|
||||
namespace Deedy
|
||||
{
|
||||
[ContentProperty("Child")]
|
||||
[TemplatePart(Name = "IconView", Type = typeof(Border))]
|
||||
[TemplatePart(Name = "TitleBar", Type = typeof(TextBlock))]
|
||||
[TemplatePart(Name = "Minimize", Type = typeof(Button))]
|
||||
[TemplatePart(Name = "Maximize", Type = typeof(Button))]
|
||||
[TemplatePart(Name = "CloseWin", Type = typeof(Button))]
|
||||
[TemplatePart(Name = "Controller", Type = typeof(Panel))]
|
||||
[TemplatePart(Name = "Container", Type = typeof(Decorator))]
|
||||
public class WindowHeader : Control
|
||||
{
|
||||
static WindowHeader()
|
||||
{
|
||||
DefaultStyleKeyProperty.OverrideMetadata(typeof(WindowHeader), new FrameworkPropertyMetadata(typeof(WindowHeader)));
|
||||
BackgroundProperty.OverrideMetadata(typeof(WindowHeader), new FrameworkPropertyMetadata(Brushes.DimGray));
|
||||
}
|
||||
public Brush HoverBrush
|
||||
{
|
||||
get { return (Brush)GetValue(HoverBrushProperty); }
|
||||
set { SetValue(HoverBrushProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty HoverBrushProperty =
|
||||
DependencyProperty.Register("HoverBrush", typeof(Brush), typeof(WindowHeader), new PropertyMetadata(Brushes.Silver));
|
||||
|
||||
public UIElement Child
|
||||
{
|
||||
get { return (UIElement)GetValue(ChildProperty); }
|
||||
set { SetValue(ChildProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty ChildProperty =
|
||||
DependencyProperty.Register("Child", typeof(UIElement), typeof(WindowHeader), new PropertyMetadata(null, (d, e) =>
|
||||
{
|
||||
WindowHeader windowHeader = (WindowHeader)d;
|
||||
if (e.NewValue != null)
|
||||
{
|
||||
if (windowHeader.Container != null)
|
||||
windowHeader.Container.Child = e.NewValue as UIElement;
|
||||
}
|
||||
}));
|
||||
|
||||
private Panel? Controller;
|
||||
private Decorator? Container;
|
||||
private Button? Minimize;
|
||||
private Button? Maximize;
|
||||
private Button? CloseWin;
|
||||
private TextBlock? TitleBar;
|
||||
private Border? IconView;
|
||||
|
||||
private Window? Target;
|
||||
private WindowChrome? Chrome;
|
||||
|
||||
private static bool IsNeedOverrideMetadata = true;
|
||||
public WindowHeader() { }
|
||||
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.IconView = GetTemplateChild("IconView") as Border;
|
||||
this.TitleBar = GetTemplateChild("TitleBar") as TextBlock;
|
||||
if (this.Container != null) this.Container.Child = this.Child;
|
||||
}
|
||||
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 WindowChrome() { CaptionHeight = this.ActualHeight };
|
||||
WindowChrome.SetWindowChrome(this.Target, this.Chrome);
|
||||
WindowChrome.SetIsHitTestVisibleInChrome(this.Container, true);
|
||||
WindowChrome.SetIsHitTestVisibleInChrome(this.Controller, true);
|
||||
if (this.Controller != null)
|
||||
{
|
||||
foreach (var c in this.Controller.Children)
|
||||
{
|
||||
Button? button = c as Button;
|
||||
button?.AddHandler(Button.ClickEvent, new RoutedEventHandler(this.CommandButton_Click));
|
||||
button?.AddHandler(Button.MouseEnterEvent, new RoutedEventHandler((s, e) => ((Button)e.Source).Background = this.HoverBrush));
|
||||
button?.AddHandler(Button.MouseLeaveEvent, new RoutedEventHandler((s, e) => ((Button)e.Source).Background = Brushes.Transparent));
|
||||
}
|
||||
}
|
||||
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)
|
||||
{
|
||||
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.IconView != null) this.IconView.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.IconView != null) this.IconView.Visibility = Visibility.Collapsed;
|
||||
if (this.TitleBar != null) this.TitleBar.Visibility = Visibility.Collapsed;
|
||||
if (this.Controller != null) this.Controller.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Deedy.Activity", "Deedy.Act
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Deedy.Testing", "Deedy.Testing\Deedy.Testing.csproj", "{550109E5-E39E-497A-84EB-08B075C6E766}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Deedy.Wpf", "Deedy.Wpf\Deedy.Wpf.csproj", "{C250C7CB-276E-4A50-9B79-5CC866274265}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@@ -21,6 +23,10 @@ Global
|
||||
{550109E5-E39E-497A-84EB-08B075C6E766}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{550109E5-E39E-497A-84EB-08B075C6E766}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{550109E5-E39E-497A-84EB-08B075C6E766}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{C250C7CB-276E-4A50-9B79-5CC866274265}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{C250C7CB-276E-4A50-9B79-5CC866274265}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C250C7CB-276E-4A50-9B79-5CC866274265}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{C250C7CB-276E-4A50-9B79-5CC866274265}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
||||
Reference in New Issue
Block a user