git push Merge branch 'bugfix/esc-ticketdropdown-hang'

This commit is contained in:
Meik
2026-02-20 16:21:02 +01:00
9 changed files with 3523 additions and 3605 deletions

View File

@@ -3,16 +3,16 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:FasdDesktopUi.Basics.UserControls"
xmlns:ico="clr-namespace:FasdDesktopUi.Basics.UserControls.AdaptableIcon;assembly=F4SD-AdaptableIcon"
mc:Ignorable="d" mc:Ignorable="d"
d:DesignHeight="20" d:DesignHeight="20"
d:DesignWidth="40" d:DesignWidth="40"
x:Name="BadgeControl" x:Name="BadgeControl">
Loaded="BadgeControl_Loaded"
Unloaded="BadgeControl_Unloaded">
<Grid ClipToBounds="False">
<Border Background="{DynamicResource Color.SoftContrast}" <Border Background="{DynamicResource Color.SoftContrast}"
CornerRadius="5"> CornerRadius="5">
<StackPanel HorizontalAlignment="Center" <StackPanel HorizontalAlignment="Center"
VerticalAlignment="Center" VerticalAlignment="Center"
Orientation="Horizontal" Orientation="Horizontal"
@@ -22,51 +22,6 @@
Foreground="{DynamicResource FontColor.DetailsPage.DataHistory.Value}" Foreground="{DynamicResource FontColor.DetailsPage.DataHistory.Value}"
Text="{Binding ElementName=BadgeControl, Path=Text}" /> Text="{Binding ElementName=BadgeControl, Path=Text}" />
</StackPanel> </StackPanel>
</Border> </Border>
<Canvas x:Name="SparkleCanvas"
IsHitTestVisible="False"
ClipToBounds="False"
Visibility="Collapsed">
<Grid x:Name="SparkleTopRight"
Canvas.Right="-3"
Canvas.Top="-4"
Opacity="0"
RenderTransformOrigin="0.5,0.5">
<Grid.RenderTransform>
<ScaleTransform ScaleX="0.6" ScaleY="0.6" />
</Grid.RenderTransform>
<Rectangle Width="2"
Height="8"
RadiusX="1"
RadiusY="1"
Fill="{DynamicResource FontColor.DetailsPage.DataHistory.Value}" />
<Rectangle Width="8"
Height="2"
RadiusX="1"
RadiusY="1"
Fill="{DynamicResource FontColor.DetailsPage.DataHistory.Value}" />
</Grid>
<Grid x:Name="SparkleBottomLeft"
Canvas.Left="-3"
Canvas.Bottom="-4"
Opacity="0"
RenderTransformOrigin="0.5,0.5">
<Grid.RenderTransform>
<ScaleTransform ScaleX="0.6" ScaleY="0.6" />
</Grid.RenderTransform>
<Rectangle Width="2"
Height="8"
RadiusX="1"
RadiusY="1"
Fill="{DynamicResource FontColor.DetailsPage.DataHistory.Value}" />
<Rectangle Width="8"
Height="2"
RadiusX="1"
RadiusY="1"
Fill="{DynamicResource FontColor.DetailsPage.DataHistory.Value}" />
</Grid>
</Canvas>
</Grid>
</UserControl> </UserControl>

View File

@@ -1,16 +1,10 @@
using System; using System.Windows;
using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Animation;
namespace FasdDesktopUi.Basics.UserControls namespace FasdDesktopUi.Basics.UserControls
{ {
public partial class Badge : UserControl public partial class Badge : UserControl
{ {
private readonly Storyboard _sparkleStoryboard;
private const double SparkleBaseScale = 0.6;
public string Text public string Text
{ {
get { return (string)GetValue(TextProperty); } get { return (string)GetValue(TextProperty); }
@@ -20,118 +14,10 @@ namespace FasdDesktopUi.Basics.UserControls
public static readonly DependencyProperty TextProperty = public static readonly DependencyProperty TextProperty =
DependencyProperty.Register(nameof(Text), typeof(string), typeof(Badge), new PropertyMetadata("Beta")); DependencyProperty.Register(nameof(Text), typeof(string), typeof(Badge), new PropertyMetadata("Beta"));
public bool IsSparkleEnabled
{
get { return (bool)GetValue(IsSparkleEnabledProperty); }
set { SetValue(IsSparkleEnabledProperty, value); }
}
public static readonly DependencyProperty IsSparkleEnabledProperty =
DependencyProperty.Register(nameof(IsSparkleEnabled), typeof(bool), typeof(Badge), new PropertyMetadata(false, OnIsSparkleEnabledChanged));
public Badge() public Badge()
{ {
InitializeComponent(); InitializeComponent();
_sparkleStoryboard = CreateSparkleStoryboard();
UpdateSparkleState();
}
private static void OnIsSparkleEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var control = d as Badge;
control?.UpdateSparkleState();
}
private void BadgeControl_Loaded(object sender, RoutedEventArgs e)
{
UpdateSparkleState();
}
private void BadgeControl_Unloaded(object sender, RoutedEventArgs e)
{
StopSparkles();
}
private void UpdateSparkleState()
{
if (SparkleCanvas == null || _sparkleStoryboard == null)
{
return;
}
if (IsSparkleEnabled)
{
SparkleCanvas.Visibility = Visibility.Visible;
_sparkleStoryboard.Begin(this, true);
return;
}
SparkleCanvas.Visibility = Visibility.Collapsed;
StopSparkles();
}
private void StopSparkles()
{
_sparkleStoryboard?.Stop(this);
if (SparkleTopRight != null)
{
SparkleTopRight.Opacity = 0;
if (SparkleTopRight.RenderTransform is ScaleTransform topTransform)
{
topTransform.ScaleX = SparkleBaseScale;
topTransform.ScaleY = SparkleBaseScale;
}
}
if (SparkleBottomLeft != null)
{
SparkleBottomLeft.Opacity = 0;
if (SparkleBottomLeft.RenderTransform is ScaleTransform bottomTransform)
{
bottomTransform.ScaleX = SparkleBaseScale;
bottomTransform.ScaleY = SparkleBaseScale;
}
}
}
private Storyboard CreateSparkleStoryboard()
{
var storyboard = new Storyboard
{
RepeatBehavior = RepeatBehavior.Forever
};
AddKeyFrames(storyboard, SparkleTopRight, "Opacity",
(0.00, 0.0), (0.18, 1.0), (0.42, 0.0), (2.20, 0.0));
AddKeyFrames(storyboard, SparkleTopRight, "(UIElement.RenderTransform).(ScaleTransform.ScaleX)",
(0.00, SparkleBaseScale), (0.22, 1.15), (0.42, SparkleBaseScale), (2.20, SparkleBaseScale));
AddKeyFrames(storyboard, SparkleTopRight, "(UIElement.RenderTransform).(ScaleTransform.ScaleY)",
(0.00, SparkleBaseScale), (0.22, 1.15), (0.42, SparkleBaseScale), (2.20, SparkleBaseScale));
AddKeyFrames(storyboard, SparkleBottomLeft, "Opacity",
(0.00, 0.0), (1.00, 0.0), (1.18, 1.0), (1.42, 0.0), (2.20, 0.0));
AddKeyFrames(storyboard, SparkleBottomLeft, "(UIElement.RenderTransform).(ScaleTransform.ScaleX)",
(0.00, SparkleBaseScale), (1.00, SparkleBaseScale), (1.22, 1.15), (1.42, SparkleBaseScale), (2.20, SparkleBaseScale));
AddKeyFrames(storyboard, SparkleBottomLeft, "(UIElement.RenderTransform).(ScaleTransform.ScaleY)",
(0.00, SparkleBaseScale), (1.00, SparkleBaseScale), (1.22, 1.15), (1.42, SparkleBaseScale), (2.20, SparkleBaseScale));
return storyboard;
}
private static void AddKeyFrames(Storyboard storyboard, DependencyObject target, string targetProperty, params (double TimeSeconds, double Value)[] frames)
{
var animation = new DoubleAnimationUsingKeyFrames();
foreach (var frame in frames)
{
animation.KeyFrames.Add(new LinearDoubleKeyFrame(frame.Value, KeyTime.FromTimeSpan(TimeSpan.FromSeconds(frame.TimeSeconds))));
}
Storyboard.SetTarget(animation, target);
Storyboard.SetTargetProperty(animation, new PropertyPath(targetProperty));
storyboard.Children.Add(animation);
} }
} }
} }

View File

@@ -222,6 +222,15 @@ namespace FasdDesktopUi.Basics.UserControls
return null; return null;
} }
public bool CloseDropDownIfOpen()
{
if (ComboBoxControl?.IsDropDownOpen != true)
return false;
ComboBoxControl.IsDropDownOpen = false;
return true;
}
#region Paging Events #region Paging Events
#region PageBack #region PageBack
@@ -316,6 +325,15 @@ namespace FasdDesktopUi.Basics.UserControls
} }
} }
private void SearchTextBox_PreviewKeyDown(object sender, KeyEventArgs e)
{
if (e.Key != Key.Escape)
return;
if (CloseDropDownIfOpen())
e.Handled = true;
}
private void ComboBoxControl_DropDownClosed(object sender, EventArgs e) private void ComboBoxControl_DropDownClosed(object sender, EventArgs e)
{ {
timer.Stop(); timer.Stop();
@@ -384,6 +402,8 @@ namespace FasdDesktopUi.Basics.UserControls
TextBox searchTextBox = FindVisualChild<TextBox>(partPopup.Child, "SearchTextBox"); TextBox searchTextBox = FindVisualChild<TextBox>(partPopup.Child, "SearchTextBox");
if (searchTextBox != null) if (searchTextBox != null)
{ {
searchTextBox.PreviewKeyDown -= SearchTextBox_PreviewKeyDown;
searchTextBox.PreviewKeyDown += SearchTextBox_PreviewKeyDown;
// Setzen des Fokus auf TextBox // Setzen des Fokus auf TextBox
searchTextBox.Focus(); searchTextBox.Focus();
} }

View File

@@ -34,6 +34,15 @@ namespace FasdDesktopUi.Basics.UserControls
searchDelayTimer.Tick += SearchDelayTimer_Tick; searchDelayTimer.Tick += SearchDelayTimer_Tick;
} }
public bool CloseDropDownIfOpen()
{
if (ComboBoxControl?.IsDropDownOpen != true)
return false;
ComboBoxControl.IsDropDownOpen = false;
return true;
}
public override void OnApplyTemplate() public override void OnApplyTemplate()
{ {
base.OnApplyTemplate(); base.OnApplyTemplate();
@@ -345,6 +354,7 @@ namespace FasdDesktopUi.Basics.UserControls
if (treeViewControl != null) if (treeViewControl != null)
{ {
treeViewControl.SelectedItemChanged += TreeViewControl_SelectedItemChanged; treeViewControl.SelectedItemChanged += TreeViewControl_SelectedItemChanged;
treeViewControl.PreviewKeyDown += DropDownContent_PreviewKeyDown;
treeViewControl.ItemsSource = VisibleItems; treeViewControl.ItemsSource = VisibleItems;
} }
} }
@@ -353,7 +363,10 @@ namespace FasdDesktopUi.Basics.UserControls
{ {
searchTextBox = ComboBoxControl.Template.FindName("PART_SearchTextBox", ComboBoxControl) as TextBox; searchTextBox = ComboBoxControl.Template.FindName("PART_SearchTextBox", ComboBoxControl) as TextBox;
if (searchTextBox != null) if (searchTextBox != null)
{
searchTextBox.TextChanged += SearchTextBox_TextChanged; searchTextBox.TextChanged += SearchTextBox_TextChanged;
searchTextBox.PreviewKeyDown += DropDownContent_PreviewKeyDown;
}
} }
if (itemsScrollViewer == null) if (itemsScrollViewer == null)
@@ -399,6 +412,15 @@ namespace FasdDesktopUi.Basics.UserControls
e.Handled = true; e.Handled = true;
} }
private void DropDownContent_PreviewKeyDown(object sender, KeyEventArgs e)
{
if (e.Key != Key.Escape)
return;
if (CloseDropDownIfOpen())
e.Handled = true;
}
#endregion #endregion
#region Keyboard #region Keyboard

View File

@@ -167,7 +167,9 @@
<Border Background="{DynamicResource BackgroundColor.DetailsPage.Widget.Title}" <Border Background="{DynamicResource BackgroundColor.DetailsPage.Widget.Title}"
CornerRadius="7.5" CornerRadius="7.5"
Width="{Binding ElementName=TicketSelectionBorder, Path=ActualWidth}"> Width="{Binding ElementName=TicketSelectionBorder, Path=ActualWidth}">
<StackPanel x:Name="TicketSelectionContainer" /> <StackPanel x:Name="TicketSelectionContainer"
Focusable="True"
PreviewKeyDown="TicketSelectionContainer_PreviewKeyDown" />
</Border> </Border>
</Popup> </Popup>
</StackPanel> </StackPanel>

View File

@@ -2541,6 +2541,14 @@ namespace FasdDesktopUi.Basics.UserControls
DropDownOpened(TicketSelectionBorder, EventArgs.Empty); DropDownOpened(TicketSelectionBorder, EventArgs.Empty);
TicketSelectionPopUp.IsOpen = !TicketSelectionPopUp.IsOpen; TicketSelectionPopUp.IsOpen = !TicketSelectionPopUp.IsOpen;
if (TicketSelectionPopUp.IsOpen)
{
_ = Dispatcher.BeginInvoke((Action)(() =>
{
TicketSelectionContainer?.Focus();
Keyboard.Focus(TicketSelectionContainer);
}), System.Windows.Threading.DispatcherPriority.Input);
}
}); });
} }
@@ -2554,6 +2562,15 @@ namespace FasdDesktopUi.Basics.UserControls
TicketSelectionBorder_Click(); TicketSelectionBorder_Click();
} }
private void TicketSelectionContainer_PreviewKeyDown(object sender, KeyEventArgs e)
{
if (e.Key != Key.Escape || TicketSelectionPopUp?.IsOpen != true)
return;
TicketSelectionPopUp.IsOpen = false;
e.Handled = true;
}
private void TicketSummaryTextBox_TextChanged(object sender, TextChangedEventArgs e) private void TicketSummaryTextBox_TextChanged(object sender, TextChangedEventArgs e)
{ {
try try
@@ -2792,10 +2809,26 @@ namespace FasdDesktopUi.Basics.UserControls
private void Combobox_PreviewKeyDown(object sender, KeyEventArgs e) private void Combobox_PreviewKeyDown(object sender, KeyEventArgs e)
{ {
if (e.Key == Key.Escape) if (e.Key != Key.Escape)
return;
if (sender is ComboBoxPageable pageable && pageable.CloseDropDownIfOpen())
{ {
if (sender is FrameworkElement fe && IsInsidePageable(fe)) return; e.Handled = true;
DropDownClosed(sender, e); return;
}
if (sender is HierarchicalSelectionControl hierarchicalSelectionControl && hierarchicalSelectionControl.CloseDropDownIfOpen())
{
e.Handled = true;
return;
}
if (sender is ComboBox comboBox && comboBox.IsDropDownOpen)
{
comboBox.IsDropDownOpen = false;
e.Handled = true;
return;
} }
} }
} }

View File

@@ -120,7 +120,6 @@
FontWeight="Bold" FontWeight="Bold"
Visibility="Visible" /> Visibility="Visible" />
<buc:Badge Margin="6 0 0 0" <buc:Badge Margin="6 0 0 0"
IsSparkleEnabled="True"
VerticalAlignment="Center" VerticalAlignment="Center"
Text="Beta"> Text="Beta">
<buc:Badge.LayoutTransform> <buc:Badge.LayoutTransform>

View File

@@ -18,7 +18,7 @@
Background="Transparent" Background="Transparent"
WindowStartupLocation="CenterOwner" WindowStartupLocation="CenterOwner"
DataContext="{Binding RelativeSource={RelativeSource Self}}" DataContext="{Binding RelativeSource={RelativeSource Self}}"
PreviewKeyDown="CustomMessageBox_PreviewKeyDown" KeyDown="CustomMessageBox_KeyDown"
IsVisibleChanged="BlurInvoker_IsActiveChanged"> IsVisibleChanged="BlurInvoker_IsActiveChanged">
<WindowChrome.WindowChrome> <WindowChrome.WindowChrome>

View File

@@ -280,14 +280,15 @@ namespace FasdDesktopUi.Pages.TicketCompletion
#endregion #endregion
private void CustomMessageBox_PreviewKeyDown(object sender, KeyEventArgs e) private void CustomMessageBox_KeyDown(object sender, KeyEventArgs e)
{ {
switch (e.Key) if (e.Key != Key.Escape)
{ {
case Key.Escape: return;
Close_Click();
break;
} }
Close_Click();
e.Handled = true;
} }
private async Task DoCloseActionAsync() private async Task DoCloseActionAsync()