Skip to content

Commit

Permalink
Added AppBar
Browse files Browse the repository at this point in the history
Added MessageBox - incomplete
  • Loading branch information
ChrisPulman committed Jan 15, 2024
1 parent f99bce5 commit 89f7f2f
Show file tree
Hide file tree
Showing 13 changed files with 850 additions and 24 deletions.
2 changes: 1 addition & 1 deletion src/CrissCross.WPF.UI.Test/AssemblyInfo.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) Chris Pulman. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

[assembly: ThemeInfo(ResourceDictionaryLocation.None, ResourceDictionaryLocation.SourceAssembly)]
[assembly: ThemeInfo(ResourceDictionaryLocation.SourceAssembly, ResourceDictionaryLocation.SourceAssembly)]
8 changes: 6 additions & 2 deletions src/CrissCross.WPF.UI.Test/ViewModels/DashboardViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ namespace CrissCross.WPF.UI.Test.ViewModels;
/// DashboardViewModel.
/// </summary>
/// <seealso cref="RxObject" />
public class DashboardViewModel : RxObject
public class DashboardViewModel : RxObject, IControlAppBar
{
private int _counter;

Expand Down Expand Up @@ -39,5 +39,9 @@ public int Counter
/// </value>
public ReactiveCommand<Unit, Unit> CounterIncrementCommand { get; }

private void OnCounterIncrement() => Counter++;
private void OnCounterIncrement()
{
Counter++;
this.ShowAppBar();
}
}
9 changes: 9 additions & 0 deletions src/CrissCross.WPF.UI.Test/Views/MainWindow.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:CrissCross.WPF.UI.Test.Views"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:rxNav="clr-namespace:CrissCross.WPF.UI;assembly=CrissCross.WPF.UI"
xmlns:tray="http://schemas.lepo.co/wpfui/2022/xaml/tray"
xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"
Title="{Binding ViewModel.ApplicationTitle, Mode=OneWay}"
Expand Down Expand Up @@ -74,5 +75,13 @@
<ContextMenu ItemsSource="{Binding ViewModel.TrayMenuItems, Mode=OneWay}" />
</tray:NotifyIcon.Menu>
</tray:NotifyIcon>
<rxNav:AppBar
Grid.RowSpan="2"
Height="88"
VerticalAlignment="Bottom">
<rxNav:AppBar.AppBarLeft>
<ui:Button Content="Hello" />
</rxNav:AppBar.AppBarLeft>
</rxNav:AppBar>
</Grid>
</ui:FluentWindow>
2 changes: 1 addition & 1 deletion src/CrissCross.WPF.UI/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@

[assembly: XmlnsDefinition("https://github.com/ChrisPulman/CrissCross", "CrissCross.WPF.UI")]
[assembly: XmlnsPrefix("https://github.com/ChrisPulman/CrissCross", "rxNav")]
[assembly: ThemeInfo(ResourceDictionaryLocation.None, ResourceDictionaryLocation.SourceAssembly)]
[assembly: ThemeInfo(ResourceDictionaryLocation.SourceAssembly, ResourceDictionaryLocation.SourceAssembly)]
79 changes: 79 additions & 0 deletions src/CrissCross.WPF.UI/Controls/AppBar.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<UserControl
x:Class="CrissCross.WPF.UI.AppBar"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ab="clr-namespace:CrissCross.WPF.UI"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"
d:DesignHeight="88"
d:DesignWidth="800"
mc:Ignorable="d">
<UserControl.Resources>
<ui:ThemesDictionary Theme="Dark" />
</UserControl.Resources>
<!-- APP BAR START -->
<Grid
x:Name="BottomAppBar"
Height="0"
HorizontalAlignment="Stretch"
VerticalAlignment="Bottom"
Background="{DynamicResource SystemAccentColorBrush}">
<Grid.Resources>
<Storyboard x:Key="Hide">
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="BottomAppBar" Storyboard.TargetProperty="(FrameworkElement.Height)">
<EasingDoubleKeyFrame KeyTime="0:0:0.5" Value="0" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
<Storyboard x:Key="Show">
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="BottomAppBar" Storyboard.TargetProperty="(FrameworkElement.Height)">
<EasingDoubleKeyFrame KeyTime="0" Value="0" />
<EasingDoubleKeyFrame KeyTime="0:0:0.5" Value="88">
<EasingDoubleKeyFrame.EasingFunction>
<BounceEase
Bounces="1"
Bounciness="5"
EasingMode="EaseOut" />
</EasingDoubleKeyFrame.EasingFunction>
</EasingDoubleKeyFrame>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</Grid.Resources>

<Grid MinHeight="88">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<ItemsControl x:Name="AppBarLeftControl" HorizontalAlignment="Left">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel
x:Name="AppBarPanelLeft"
HorizontalAlignment="Left"
ab:MarginSetter.Margin="5,0"
CanHorizontallyScroll="True"
Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
<ItemsControl
x:Name="AppBarRightControl"
Grid.Column="1"
HorizontalAlignment="Right">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel
x:Name="AppBarPanelRight"
Grid.Column="1"
HorizontalAlignment="Right"
ab:MarginSetter.Margin="5,0"
CanHorizontallyScroll="True"
Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</Grid>
</Grid>
<!-- APP BAR END -->
</UserControl>
217 changes: 217 additions & 0 deletions src/CrissCross.WPF.UI/Controls/AppBar.xaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
// Copyright (c) Chris Pulman. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media.Animation;
using ReactiveMarbles.ObservableEvents;

namespace CrissCross.WPF.UI
{
/// <summary>
/// Interaction logic for AppBar.xaml.
/// </summary>
public partial class AppBar : IHaveAppBar
{
/// <summary>
/// The application bar enabled property.
/// </summary>
public static readonly DependencyProperty AppBarEnabledProperty = DependencyProperty.Register(nameof(AppBarEnabled), typeof(bool), typeof(ModernWindow), new PropertyMetadata(true));

/// <summary>
/// Holds AppBar open until explicitly closed.
/// </summary>
public static readonly DependencyProperty AppBarIsStickyProperty = DependencyProperty.Register(nameof(AppBarIsSticky), typeof(bool), typeof(ModernWindow), new PropertyMetadata(false));

/// <summary>
/// Recommended Height 88.
/// </summary>
public static readonly DependencyProperty AppBarLeftProperty = DependencyProperty.Register(nameof(AppBarLeft), typeof(ObservableCollection<FrameworkElement>), typeof(ModernWindow));

/// <summary>
/// Recommended Height 88.
/// </summary>
public static readonly DependencyProperty AppBarRightProperty = DependencyProperty.Register(nameof(AppBarRight), typeof(ObservableCollection<FrameworkElement>), typeof(ModernWindow));

private readonly Storyboard? _hide;
private readonly Storyboard? _show;
private bool _appBarVisible;
private bool _mouseIsOverAppBar;

/// <summary>
/// Initializes a new instance of the <see cref="AppBar"/> class.
/// </summary>
public AppBar()
{
InitializeComponent();
AppBarLeft = [];
AppBarRight = [];
AppBarLeftControl.ItemsSource = AppBarLeft;
AppBarRightControl.ItemsSource = AppBarRight;
_hide = BottomAppBar.Resources["Hide"] as Storyboard;
_show = BottomAppBar.Resources["Show"] as Storyboard;
BottomAppBar.MouseEnter += AppBar_MouseEnter;
BottomAppBar.MouseLeave += AppBar_MouseLeave;

HideAppBar();
this.Events().Loaded.Subscribe(_ =>
{
// Find the parent window
var parentWindow = Window.GetWindow(this);
if (parentWindow != null)
{
parentWindow.PreviewMouseDown += ModernWindow_PreviewMouseDown;
}
this.AppBarIsStickyListener(() => AppBarIsSticky, isSticky => AppBarIsSticky = isSticky);
this.AppBarLeftListener(() => AppBarLeft);
this.AppBarRightListener(() => AppBarRight);
this.ShowAppBarListener(ShowAppBar);
this.HideAppBarListener(HideAppBar);
});
}

/// <summary>
/// Gets or sets a value indicating whether [application bar enabled].
/// </summary>
/// <value><c>true</c> if [application bar enabled]; otherwise, <c>false</c> .</value>
[Description("Gets or Sets the Visibility of the Nav Bar")]
[Category("CrissCross")]
public bool AppBarEnabled
{
get => (bool)GetValue(AppBarEnabledProperty);

set
{
SetValue(AppBarEnabledProperty, value);
if (!value)
{
HideAppBar();
}
}
}

/// <summary>
/// Gets a value indicating whether [application bar is sticky].
/// </summary>
/// <value><c>true</c> if [application bar is sticky]; otherwise, <c>false</c> .</value>
[Description("Gets the AppBar Sticky state.")]
[Category("CrissCross")]
public bool AppBarIsSticky
{
get => (bool)GetValue(AppBarIsStickyProperty);

private set => SetValue(AppBarIsStickyProperty, value);
}

/// <summary>
/// Gets or sets the AppBarLeft content.
/// </summary>
[Description("Gets or sets the AppBarLeft content.")]
[Category("CrissCross")]
public ObservableCollection<FrameworkElement> AppBarLeft
{
get => (ObservableCollection<FrameworkElement>)GetValue(AppBarLeftProperty);
set => SetValue(AppBarLeftProperty, value);
}

/// <summary>
/// Gets or sets the AppBarRight content.
/// </summary>
[Description("Gets or sets the AppBarRight content.")]
[Category("CrissCross")]
public ObservableCollection<FrameworkElement> AppBarRight
{
get => (ObservableCollection<FrameworkElement>)GetValue(AppBarRightProperty);
set => SetValue(AppBarRightProperty, value);
}

/// <summary>
/// Gets the template child.
/// </summary>
/// <typeparam name="T">The element type.</typeparam>
/// <param name="name">The name.</param>
/// <returns>The Instance if exists.</returns>
/// <exception cref="ArgumentNullException">name.</exception>
protected T GetTemplateChild<T>(string name)
where T : DependencyObject
{
if (GetTemplateChild(name) is not T dependencyObject)
{
throw new ArgumentNullException(name);
}

return dependencyObject;
}

/// <summary>
/// Shows the application bar.
/// </summary>
/// <param name="isSticky">if set to <c>true</c> [is sticky].</param>
private void ShowAppBar(bool isSticky = false)
{
if (!AppBarEnabled)
{
HideAppBar();
return;
}

AppBarIsSticky = isSticky;
if (_show != null && !_appBarVisible)
{
_show.Begin();
_appBarVisible = true;
}
}

/// <summary>
/// Hides the application bar.
/// </summary>
private void HideAppBar()
{
if (_hide != null && _appBarVisible)
{
_hide.Begin();
_appBarVisible = false;
}
}

/// <summary>
/// Handles the MouseEnter event of the _AppBar control.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The <see cref="MouseEventArgs"/> instance containing the event data.</param>
private void AppBar_MouseEnter(object sender, MouseEventArgs e) => _mouseIsOverAppBar = true;

/// <summary>
/// Handles the MouseLeave event of the _AppBar control.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The <see cref="MouseEventArgs"/> instance containing the event data.</param>
private void AppBar_MouseLeave(object sender, MouseEventArgs e) => _mouseIsOverAppBar = false;

/// <summary>
/// Handles the PreviewMouseDown event of the ModernWindow control.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">
/// The <see cref="MouseButtonEventArgs"/> instance containing the event data.
/// </param>
private void ModernWindow_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
if (!_mouseIsOverAppBar)
{
if (!_appBarVisible && e.ButtonState == MouseButtonState.Pressed && e.ChangedButton == MouseButton.Right)
{
ShowAppBar();
}
else if (_appBarVisible && e.ChangedButton != MouseButton.Right && !AppBarIsSticky)
{
HideAppBar();
}
}
}
}
}
Loading

0 comments on commit 89f7f2f

Please sign in to comment.