Skip to content

Commit

Permalink
Add support for setting content in place of the title in a TitleBar (#…
Browse files Browse the repository at this point in the history
…1030)

* Add support for setting content in place of the title in a TitleBar

* Use Header instead of HeaderLeft and TrailingContent instead of HeaderRight

Refactor TitleBar properties for clarity and usability

Renamed properties in the `TitleBar` class from `HeaderLeft` and `HeaderRight` to `Header` and `TrailingContent`. Updated XAML and C# files to reflect these changes, streamlining the structure of the `TitleBar`. Modified the constructor to set the `_titleBlock` to the new `Header` property. Adjusted hit testing logic in `WM.NCHITTEST` to accommodate the new property names, ensuring proper mouse interaction handling. These changes improve the overall clarity and usability of the `TitleBar` component.

---------

Co-authored-by: pomian <13592821+pomianowski@users.noreply.github.com>
  • Loading branch information
jonlipsky and pomianowski authored Feb 1, 2025
1 parent 0fc9208 commit 18ce2b8
Show file tree
Hide file tree
Showing 3 changed files with 114 additions and 17 deletions.
59 changes: 59 additions & 0 deletions src/Wpf.Ui.Gallery/Views/Windows/SandboxWindow.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,65 @@
<ui:TitleBar.Icon>
<ui:ImageIcon Source="pack://application:,,,/Assets/wpfui.png" />
</ui:TitleBar.Icon>
<ui:TitleBar.Header>
<Menu>
<MenuItem Header="File">
<MenuItem
Header="New" />
<MenuItem
Header="New window" />
<MenuItem
Header="Open..." />
<MenuItem
Header="Save" />
<MenuItem
Header="Save As..." />
<Separator />
<MenuItem
Header="Exit" />
</MenuItem>
<ui:MenuItem Header="Debug">
<MenuItem
Header="Undo" />
<Separator />
<MenuItem
Header="Cut" />
<MenuItem
Header="Copy" />
<MenuItem
Header="Paste" />
<MenuItem
Header="Delete"
IsEnabled="False" />
<Separator />
<MenuItem
Header="Search with browser" />
<MenuItem
Header="Find..." />
<MenuItem
Header="Find next" />
<Separator />
<MenuItem
Header="Select All" />
</ui:MenuItem>
</Menu>
</ui:TitleBar.Header>
<ui:TitleBar.TrailingContent>
<Menu>
<ui:MenuItem
Foreground="{DynamicResource PaletteDeepOrangeBrush}"
Icon="{ui:SymbolIcon Fire24, True}" />
<ui:MenuItem
Foreground="{DynamicResource PaletteGreenBrush}"
Icon="{ui:SymbolIcon Play24}" />
<ui:MenuItem
Foreground="{DynamicResource PaletteRedBrush}"
Icon="{ui:SymbolIcon Stop24}" />
<ui:MenuItem
Foreground="{DynamicResource PaletteLightBlueBrush}"
Icon="{ui:SymbolIcon ArrowClockwise24}" />
</Menu>
</ui:TitleBar.TrailingContent>
</ui:TitleBar>

<StackPanel Margin="24">
Expand Down
49 changes: 44 additions & 5 deletions src/Wpf.Ui/Controls/TitleBar/TitleBar.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
// All Rights Reserved.

using System.Diagnostics;
using System.Windows.Data;
using System.Windows.Input;
using Wpf.Ui.Designer;
using Wpf.Ui.Input;
Expand Down Expand Up @@ -52,13 +53,25 @@ public class TitleBar : System.Windows.Controls.Control, IThemeControl
new PropertyMetadata(null)
);

/// <summary>Identifies the <see cref="Header"/> dependency property.</summary>
/// <summary>
/// Property for <see cref="Header"/>.
/// </summary>
public static readonly DependencyProperty HeaderProperty = DependencyProperty.Register(
nameof(Header),
typeof(object),
typeof(TitleBar),
new PropertyMetadata(null)
);

/// <summary>
/// Property for <see cref="TrailingContent"/>.
/// </summary>
public static readonly DependencyProperty TrailingContentProperty = DependencyProperty.Register(
nameof(TrailingContent),
typeof(object),
typeof(TitleBar),
new PropertyMetadata(null)
);

/// <summary>Identifies the <see cref="ButtonsForeground"/> dependency property.</summary>
public static readonly DependencyProperty ButtonsForegroundProperty = DependencyProperty.Register(
Expand Down Expand Up @@ -209,13 +222,22 @@ public string? Title
}

/// <summary>
/// Gets or sets the content displayed in the <see cref="TitleBar"/>.
/// Gets or sets the content displayed in the left side of the <see cref="TitleBar"/>.
/// </summary>
public object? Header
public object Header
{
get => GetValue(HeaderProperty);
set => SetValue(HeaderProperty, value);
}

/// <summary>
/// Gets or sets the content displayed in right side of the <see cref="TitleBar"/>.
/// </summary>
public object TrailingContent
{
get => GetValue(TrailingContentProperty);
set => SetValue(TrailingContentProperty, value);
}

/// <summary>
/// Gets or sets the foreground of the navigation buttons.
Expand Down Expand Up @@ -376,6 +398,7 @@ public event TypedEventHandler<TitleBar, RoutedEventArgs> HelpClicked

/*private System.Windows.Controls.Grid _mainGrid = null!;*/
private System.Windows.Controls.ContentPresenter _icon = null!;
private readonly TextBlock _titleBlock;

/// <summary>
/// Initializes a new instance of the <see cref="TitleBar"/> class and sets the default <see cref="FrameworkElement.Loaded"/> event.
Expand All @@ -386,6 +409,12 @@ public TitleBar()

dpiScale ??= VisualTreeHelper.GetDpi(this);

_titleBlock = new TextBlock();
_titleBlock.VerticalAlignment = VerticalAlignment.Center;
_titleBlock.SetBinding(System.Windows.Controls.TextBlock.TextProperty, new Binding(nameof(Title)) { Source = this });
_titleBlock.SetBinding(System.Windows.Controls.TextBlock.FontSizeProperty, new Binding(nameof(FontSize)) { Source = this });
Header = _titleBlock;

Loaded += OnLoaded;
Unloaded += OnUnloaded;
}
Expand Down Expand Up @@ -617,9 +646,19 @@ or User32.WM.NCLBUTTONUP

bool isMouseOverHeaderContent = false;

if (message == User32.WM.NCHITTEST && Header is UIElement headerUiElement)
if (message == User32.WM.NCHITTEST && (TrailingContent is UIElement || Header is UIElement))
{
isMouseOverHeaderContent = headerUiElement.IsMouseOverElement(lParam);
UIElement? headerLeftUIElement = Header as UIElement;
UIElement? headerRightUiElement = TrailingContent as UIElement;

if (headerLeftUIElement is not null && headerLeftUIElement != _titleBlock)
{
isMouseOverHeaderContent = headerLeftUIElement.IsMouseOverElement(lParam) || (headerRightUiElement?.IsMouseOverElement(lParam) ?? false);
}
else
{
isMouseOverHeaderContent = headerRightUiElement?.IsMouseOverElement(lParam) ?? false;
}
}

switch (message)
Expand Down
23 changes: 11 additions & 12 deletions src/Wpf.Ui/Controls/TitleBar/TitleBar.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -118,42 +118,41 @@
VerticalAlignment="Center">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>

<!-- Custom application icon -->
<ContentPresenter
x:Name="PART_Icon"
Grid.Column="0"
Height="16"
Margin="0,0,12,0"
VerticalAlignment="Center"
Content="{TemplateBinding Icon}"
Focusable="False"
RenderOptions.BitmapScalingMode="HighQuality" />

<!-- Main application title -->
<TextBlock
Grid.Column="1"
VerticalAlignment="Center"
FontSize="{TemplateBinding FontSize}"
Text="{TemplateBinding Title}" />
</Grid>

<Grid Grid.Column="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>

<!-- Additional header content -->
<!-- Title text or other header content -->
<ContentPresenter
Grid.Column="0"
HorizontalAlignment="Right"
HorizontalAlignment="Left"
Content="{TemplateBinding Header}" />

<!-- Additional header content -->
<ContentPresenter
Grid.Column="1"
HorizontalAlignment="Right"
Content="{TemplateBinding TrailingContent}" />

<!-- Navigation buttons - Close, Restore, Maximize, Minimize -->
<Grid
Grid.Column="1"
Grid.Column="2"
HorizontalAlignment="Right"
VerticalAlignment="Top">
<Grid.ColumnDefinitions>
Expand Down

0 comments on commit 18ce2b8

Please sign in to comment.