Kategorie
This commit is contained in:
@@ -0,0 +1,160 @@
|
||||
<UserControl x:Class="FasdDesktopUi.Basics.UserControls.HierarchicalSelectionControl"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:ico="clr-namespace:FasdDesktopUi.Basics.UserControls.AdaptableIcon;assembly=F4SD-AdaptableIcon"
|
||||
xmlns:vc="clr-namespace:FasdDesktopUi.Basics.Converter"
|
||||
xmlns:models="clr-namespace:FasdDesktopUi.Basics.Models"
|
||||
mc:Ignorable="d"
|
||||
x:Name="HierarchySelector"
|
||||
Focusable="True"
|
||||
d:DesignHeight="60"
|
||||
d:DesignWidth="350">
|
||||
|
||||
<UserControl.Resources>
|
||||
<vc:LanguageDefinitionsConverter x:Key="LanguageConverter" />
|
||||
<vc:NullValueToVisibilityConverter x:Key="NullToVisibility" />
|
||||
</UserControl.Resources>
|
||||
|
||||
<Grid>
|
||||
<Border x:Name="DisplayBorder"
|
||||
Height="28"
|
||||
Background="{Binding ElementName=HierarchySelector, Path=ComboBoxBackground}"
|
||||
BorderBrush="{Binding ElementName=HierarchySelector, Path=BorderBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="7.5"
|
||||
Padding="6 0"
|
||||
MouseLeftButtonUp="DisplayBorder_MouseLeftButtonUp">
|
||||
<Border.Style>
|
||||
<Style TargetType="Border">
|
||||
<Setter Property="Opacity" Value="1" />
|
||||
<Setter Property="Cursor" Value="Hand" />
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding IsOpen, ElementName=DropDownPopup}"
|
||||
Value="True">
|
||||
<Setter Property="BorderBrush" Value="{DynamicResource BackgroundColor.Menu.SubCategory.Hover}" />
|
||||
<Setter Property="Background" Value="{DynamicResource BackgroundColor.DetailsPage.DataHistory.ValueColumn}" />
|
||||
</DataTrigger>
|
||||
<Trigger Property="IsMouseOver"
|
||||
Value="True">
|
||||
<Setter Property="BorderBrush" Value="{DynamicResource BackgroundColor.Menu.SubCategory.Hover}" />
|
||||
</Trigger>
|
||||
<DataTrigger Binding="{Binding ElementName=HierarchySelector, Path=IsEnabled}"
|
||||
Value="False">
|
||||
<Setter Property="Opacity" Value="0.5" />
|
||||
<Setter Property="Cursor" Value="Arrow" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</Border.Style>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid Grid.Column="0"
|
||||
Margin="0 0 10 0"
|
||||
VerticalAlignment="Center">
|
||||
<TextBlock Text="{Binding ElementName=HierarchySelector, Path=SelectedItem.FullPath}"
|
||||
Foreground="{DynamicResource FontColor.Menu.Categories}"
|
||||
VerticalAlignment="Center"
|
||||
TextTrimming="CharacterEllipsis"
|
||||
ToolTip="{Binding ElementName=HierarchySelector, Path=SelectedItem.FullPath}" />
|
||||
</Grid>
|
||||
<Path Grid.Column="1"
|
||||
Margin="0 0 6 0"
|
||||
Data="F1 M 2.89067,4.99467C 2.89067,4.22933 3.724,3.74533 4.39067,4.13067L 10.8227,7.844L 17.26,4.13067C 18.412,3.48 19.4013,5.19333 18.26,5.86533L 11.3227,9.86533C 11.016,10.0413 10.636,10.0413 10.3227,9.86533L 3.39067,5.86533C 3.07867,5.688 2.89067,5.35467 2.89067,4.99467 Z"
|
||||
Fill="{DynamicResource Color.Menu.Icon}"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
Width="10"
|
||||
Height="6"
|
||||
Stretch="Uniform"
|
||||
IsHitTestVisible="False" />
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<Popup x:Name="DropDownPopup"
|
||||
PlacementTarget="{Binding ElementName=DisplayBorder}"
|
||||
Placement="Bottom"
|
||||
StaysOpen="False"
|
||||
AllowsTransparency="True"
|
||||
Width="{Binding ElementName=DisplayBorder, Path=ActualWidth}"
|
||||
Opened="DropDownPopup_Opened"
|
||||
Closed="DropDownPopup_Closed">
|
||||
<Border Background="{DynamicResource BackgroundColor.Menu.SubCategory}"
|
||||
CornerRadius="7.5"
|
||||
Padding="10">
|
||||
<Border.Effect>
|
||||
<DropShadowEffect BlurRadius="10"
|
||||
Color="{DynamicResource DropShadowColor.Menu}"
|
||||
Direction="55"
|
||||
ShadowDepth="10"
|
||||
Opacity="0.35" />
|
||||
</Border.Effect>
|
||||
<StackPanel>
|
||||
<Grid Margin="0 0 10 0">
|
||||
<TextBox x:Name="SearchTextBox"
|
||||
Style="{DynamicResource Customizable.Editable.TextBox.EditOnly}"
|
||||
Background="{Binding ElementName=HierarchySelector, Path=ComboBoxBackground}"
|
||||
Padding="27 5 5 5"
|
||||
VerticalContentAlignment="Center"
|
||||
TextChanged="SearchTextBox_TextChanged" />
|
||||
<ico:AdaptableIcon SelectedInternIcon="menuBar_search"
|
||||
Style="{DynamicResource Menu.MenuBar.PinnedIcon.Base}"
|
||||
BorderPadding="5 5 5 7"
|
||||
Margin="0"
|
||||
IconBackgroundColor="Transparent"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center" />
|
||||
<TextBlock Margin="30 0 0 0"
|
||||
Foreground="{DynamicResource FontColor.DetailsPage.DataHistory.Date}"
|
||||
IsHitTestVisible="False"
|
||||
VerticalAlignment="Center">
|
||||
<TextBlock.Style>
|
||||
<Style TargetType="TextBlock">
|
||||
<Setter Property="Visibility" Value="Collapsed" />
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding Text, ElementName=SearchTextBox}"
|
||||
Value="">
|
||||
<Setter Property="Text"
|
||||
Value="{Binding ElementName=HierarchySelector, Path=SearchPlaceholderText}" />
|
||||
<Setter Property="Visibility" Value="Visible" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</TextBlock.Style>
|
||||
</TextBlock>
|
||||
</Grid>
|
||||
|
||||
<Border Margin="0 10 0 0"
|
||||
Background="{DynamicResource BackgroundColor.Menu.SubCategory}"
|
||||
BorderThickness="0"
|
||||
CornerRadius="5">
|
||||
<ScrollViewer MaxHeight="300"
|
||||
VerticalScrollBarVisibility="Auto">
|
||||
<TreeView x:Name="TreeViewControl"
|
||||
Background="{Binding ElementName=HierarchySelector, Path=ComboBoxBackground}"
|
||||
ItemsSource="{Binding VisibleItems, ElementName=HierarchySelector}"
|
||||
SelectedItemChanged="TreeViewControl_SelectedItemChanged">
|
||||
<TreeView.Resources>
|
||||
<Style TargetType="TreeViewItem">
|
||||
<Setter Property="IsExpanded"
|
||||
Value="{Binding IsExpanded, Mode=TwoWay}" />
|
||||
</Style>
|
||||
<HierarchicalDataTemplate DataType="{x:Type models:HierarchicalSelectionItem}"
|
||||
ItemsSource="{Binding Children}">
|
||||
<TextBlock Text="{Binding DisplayName}"
|
||||
Foreground="{DynamicResource FontColor.Menu.Categories}"
|
||||
Margin="0 2 0 2" />
|
||||
</HierarchicalDataTemplate>
|
||||
</TreeView.Resources>
|
||||
</TreeView>
|
||||
</ScrollViewer>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</Popup>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@@ -0,0 +1,256 @@
|
||||
using FasdDesktopUi.Basics.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Threading;
|
||||
|
||||
namespace FasdDesktopUi.Basics.UserControls
|
||||
{
|
||||
public partial class HierarchicalSelectionControl : UserControl, IFocusInvoker
|
||||
{
|
||||
private readonly ObservableCollection<HierarchicalSelectionItem> visibleItems = new ObservableCollection<HierarchicalSelectionItem>();
|
||||
private readonly Dictionary<string, HierarchicalSelectionItem> itemLookup = new Dictionary<string, HierarchicalSelectionItem>();
|
||||
private readonly DispatcherTimer searchDelayTimer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(250) };
|
||||
private string lastSearchText = string.Empty;
|
||||
public ObservableCollection<HierarchicalSelectionItem> VisibleItems => visibleItems;
|
||||
|
||||
public int? ParentIndex { get; set; }
|
||||
public UIElement ParentElement { get; set; }
|
||||
|
||||
public HierarchicalSelectionControl()
|
||||
{
|
||||
InitializeComponent();
|
||||
TreeViewControl.ItemsSource = visibleItems;
|
||||
searchDelayTimer.Tick += SearchDelayTimer_Tick;
|
||||
}
|
||||
|
||||
#region DependencyProperties
|
||||
|
||||
public ObservableCollection<HierarchicalSelectionItem> ItemsSource
|
||||
{
|
||||
get => (ObservableCollection<HierarchicalSelectionItem>)GetValue(ItemsSourceProperty);
|
||||
set => SetValue(ItemsSourceProperty, value);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register(
|
||||
nameof(ItemsSource),
|
||||
typeof(ObservableCollection<HierarchicalSelectionItem>),
|
||||
typeof(HierarchicalSelectionControl),
|
||||
new PropertyMetadata(null, OnItemsSourceChanged));
|
||||
|
||||
private static void OnItemsSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (d is HierarchicalSelectionControl control)
|
||||
{
|
||||
control.RebuildLookup();
|
||||
control.ApplyFilter(control.lastSearchText);
|
||||
control.TryExpandToSelectedItem();
|
||||
}
|
||||
}
|
||||
|
||||
public HierarchicalSelectionItem SelectedItem
|
||||
{
|
||||
get => (HierarchicalSelectionItem)GetValue(SelectedItemProperty);
|
||||
set => SetValue(SelectedItemProperty, value);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty SelectedItemProperty = DependencyProperty.Register(
|
||||
nameof(SelectedItem),
|
||||
typeof(HierarchicalSelectionItem),
|
||||
typeof(HierarchicalSelectionControl),
|
||||
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnSelectedItemChanged));
|
||||
|
||||
private static void OnSelectedItemChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (d is HierarchicalSelectionControl control)
|
||||
{
|
||||
control.TryExpandToSelectedItem();
|
||||
}
|
||||
}
|
||||
|
||||
public Brush ComboBoxBackground
|
||||
{
|
||||
get => (Brush)GetValue(ComboBoxBackgroundProperty);
|
||||
set => SetValue(ComboBoxBackgroundProperty, value);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty ComboBoxBackgroundProperty =
|
||||
DependencyProperty.Register(nameof(ComboBoxBackground), typeof(Brush), typeof(HierarchicalSelectionControl), new PropertyMetadata(Brushes.Transparent));
|
||||
|
||||
public string SearchPlaceholderText
|
||||
{
|
||||
get => (string)GetValue(SearchPlaceholderTextProperty);
|
||||
set => SetValue(SearchPlaceholderTextProperty, value);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty SearchPlaceholderTextProperty =
|
||||
DependencyProperty.Register(nameof(SearchPlaceholderText), typeof(string), typeof(HierarchicalSelectionControl), new PropertyMetadata(string.Empty));
|
||||
|
||||
#endregion
|
||||
|
||||
private void DropDownPopup_Opened(object sender, EventArgs e)
|
||||
{
|
||||
SearchTextBox.Focus();
|
||||
SearchTextBox.SelectAll();
|
||||
EnsureParentPlacement();
|
||||
cFocusInvoker.InvokeGotFocus(this, e);
|
||||
}
|
||||
|
||||
private void DropDownPopup_Closed(object sender, EventArgs e)
|
||||
{
|
||||
searchDelayTimer.Stop();
|
||||
cFocusInvoker.InvokeLostFocus(this, e);
|
||||
}
|
||||
|
||||
private void EnsureParentPlacement()
|
||||
{
|
||||
if (ParentElement == null || ParentElement != this.Parent)
|
||||
{
|
||||
ParentElement = this.Parent as UIElement;
|
||||
|
||||
if (this.Parent is Panel panel)
|
||||
ParentIndex = panel.Children.IndexOf(this);
|
||||
else
|
||||
ParentIndex = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void SearchTextBox_TextChanged(object sender, TextChangedEventArgs e)
|
||||
{
|
||||
searchDelayTimer.Stop();
|
||||
searchDelayTimer.Start();
|
||||
}
|
||||
|
||||
private void SearchDelayTimer_Tick(object sender, EventArgs e)
|
||||
{
|
||||
searchDelayTimer.Stop();
|
||||
lastSearchText = SearchTextBox.Text ?? string.Empty;
|
||||
ApplyFilter(lastSearchText);
|
||||
}
|
||||
|
||||
private void TreeViewControl_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
|
||||
{
|
||||
if (e.NewValue is HierarchicalSelectionItem selected)
|
||||
{
|
||||
var original = ResolveOriginalItem(selected);
|
||||
if (original != null)
|
||||
SelectedItem = original;
|
||||
|
||||
DropDownPopup.IsOpen = false;
|
||||
}
|
||||
}
|
||||
|
||||
private HierarchicalSelectionItem ResolveOriginalItem(HierarchicalSelectionItem item)
|
||||
{
|
||||
if (item == null)
|
||||
return null;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(item.Id) && itemLookup.TryGetValue(item.Id, out var original))
|
||||
return original;
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
private void RebuildLookup()
|
||||
{
|
||||
itemLookup.Clear();
|
||||
|
||||
if (ItemsSource == null)
|
||||
return;
|
||||
|
||||
foreach (var entry in ItemsSource.SelectMany(item => item.SelfAndDescendants()))
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(entry.Id))
|
||||
continue;
|
||||
|
||||
itemLookup[entry.Id] = entry;
|
||||
}
|
||||
}
|
||||
|
||||
private void ApplyFilter(string searchText)
|
||||
{
|
||||
visibleItems.Clear();
|
||||
|
||||
if (ItemsSource == null)
|
||||
return;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(searchText))
|
||||
{
|
||||
foreach (var item in ItemsSource)
|
||||
{
|
||||
item.SetExpandedRecursive(false);
|
||||
visibleItems.Add(item);
|
||||
}
|
||||
|
||||
TryExpandToSelectedItem();
|
||||
return;
|
||||
}
|
||||
|
||||
var comparison = StringComparison.CurrentCultureIgnoreCase;
|
||||
|
||||
foreach (var root in ItemsSource)
|
||||
{
|
||||
var clone = root.CloneBranch(node =>
|
||||
node.FullPath?.IndexOf(searchText, comparison) >= 0 ||
|
||||
node.DisplayName?.IndexOf(searchText, comparison) >= 0);
|
||||
|
||||
if (clone == null)
|
||||
continue;
|
||||
|
||||
clone.SetExpandedRecursive(true);
|
||||
visibleItems.Add(clone);
|
||||
}
|
||||
}
|
||||
|
||||
private void TryExpandToSelectedItem()
|
||||
{
|
||||
if (SelectedItem == null || string.IsNullOrWhiteSpace(SelectedItem.Id))
|
||||
return;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(lastSearchText))
|
||||
return;
|
||||
|
||||
var chain = SelectedItem;
|
||||
while (chain != null)
|
||||
{
|
||||
chain.IsExpanded = true;
|
||||
chain = chain.Parent;
|
||||
}
|
||||
}
|
||||
|
||||
private void DisplayBorder_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
if (!IsEnabled)
|
||||
return;
|
||||
|
||||
e.Handled = true;
|
||||
DropDownPopup.IsOpen = !DropDownPopup.IsOpen;
|
||||
}
|
||||
|
||||
protected override void OnPreviewKeyDown(KeyEventArgs e)
|
||||
{
|
||||
base.OnPreviewKeyDown(e);
|
||||
|
||||
if (!IsEnabled)
|
||||
return;
|
||||
|
||||
if (!DropDownPopup.IsOpen && (e.Key == Key.Enter || e.Key == Key.Down || e.Key == Key.Space))
|
||||
{
|
||||
DropDownPopup.IsOpen = true;
|
||||
e.Handled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (DropDownPopup.IsOpen && e.Key == Key.Escape)
|
||||
{
|
||||
DropDownPopup.IsOpen = false;
|
||||
e.Handled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -160,13 +160,13 @@
|
||||
Margin="0 10 0 0"
|
||||
Foreground="{DynamicResource FontColor.Menu.Categories}" />
|
||||
</StackPanel>
|
||||
<local:ComboBoxPageable x:Name="CategorySelectionControl"
|
||||
Margin="0 5 0 0"
|
||||
SelectedItem="{Binding SelectedCategory, ElementName=CloseCaseDialogUc, Mode=TwoWay}"
|
||||
ComboBoxBackground="{DynamicResource BackgroundColor.DetailsPage.DataHistory.ValueColumn}"
|
||||
BorderBrush="{DynamicResource BackgroundColor.Menu.SubCategory.Hover}"
|
||||
PreviewKeyDown="Combobox_PreviewKeyDown"
|
||||
/>
|
||||
<local:HierarchicalSelectionControl x:Name="CategorySelectionControl"
|
||||
Margin="0 5 0 0"
|
||||
SelectedItem="{Binding SelectedCategory, ElementName=CloseCaseDialogUc, Mode=TwoWay}"
|
||||
ComboBoxBackground="{DynamicResource BackgroundColor.DetailsPage.DataHistory.ValueColumn}"
|
||||
BorderBrush="{DynamicResource BackgroundColor.Menu.SubCategory.Hover}"
|
||||
SearchPlaceholderText="{Binding Converter={StaticResource LanguageConverter}, ConverterParameter=Searchbar.Placeholder}"
|
||||
PreviewKeyDown="Combobox_PreviewKeyDown" />
|
||||
<StackPanel>
|
||||
<TextBlock Text="{Binding Converter={StaticResource LanguageConverter}, ConverterParameter=Dialog.CloseCase.Template}"
|
||||
FontWeight="Bold"
|
||||
|
||||
@@ -59,13 +59,21 @@ namespace FasdDesktopUi.Basics.UserControls
|
||||
Infos = new Dictionary<string, string> { ["Special"] = "NoTicket" }
|
||||
};
|
||||
|
||||
private readonly cF4sdApiSearchResultRelation _removeAffectedAssetEntry = new cF4sdApiSearchResultRelation()
|
||||
{
|
||||
DisplayName = cMultiLanguageSupport.GetItem("Dialog.CloseCase.RemoveAffectedAssetEntry"),
|
||||
Type = enumF4sdSearchResultClass.Computer,
|
||||
Identities = null,
|
||||
Infos = new Dictionary<string, string> { ["Special"] = "RemoveAffectedAssetEntry" }
|
||||
};
|
||||
private readonly cF4sdApiSearchResultRelation _removeAffectedAssetEntry = new cF4sdApiSearchResultRelation()
|
||||
{
|
||||
DisplayName = cMultiLanguageSupport.GetItem("Dialog.CloseCase.RemoveAffectedAssetEntry"),
|
||||
Type = enumF4sdSearchResultClass.Computer,
|
||||
Identities = null,
|
||||
Infos = new Dictionary<string, string> { ["Special"] = "RemoveAffectedAssetEntry" }
|
||||
};
|
||||
|
||||
private const string CategoryTableNamePrimary = "M42Wpm-Ticket-Categories";
|
||||
private const string CategoryTableNameLegacy = "M42Wpm-Ticket-CloseCase-Categories";
|
||||
private string activeCategoryTableName = CategoryTableNamePrimary;
|
||||
|
||||
private ObservableCollection<HierarchicalSelectionItem> categoryHierarchy = new ObservableCollection<HierarchicalSelectionItem>();
|
||||
private readonly Dictionary<string, HierarchicalSelectionItem> categoryLookup = new Dictionary<string, HierarchicalSelectionItem>(StringComparer.OrdinalIgnoreCase);
|
||||
private bool isCategoryLoading;
|
||||
|
||||
public bool IsTicket
|
||||
{
|
||||
@@ -139,17 +147,17 @@ namespace FasdDesktopUi.Basics.UserControls
|
||||
ShowAssetWarningTicketAction = IsTicket;
|
||||
ValidateProperty("SelectedTicket");
|
||||
EnableHoldTicketAction = _selectedTicket == null || SelectedTicket == _selectTicketRelation || selectedTicketIsNewTicket;
|
||||
if (_selectedTicket != null && _selectedTicket.Infos.TryGetValue("StatusId", out var statusIdString))
|
||||
{
|
||||
object parsedStatusId = Enum.Parse(typeof(enumTicketStatus), statusIdString);
|
||||
if (parsedStatusId is enumTicketStatus statusId)
|
||||
{
|
||||
EnableHoldTicketAction = statusId != enumTicketStatus.OnHold;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
if (_selectedTicket != null && _selectedTicket.Infos.TryGetValue("StatusId", out var statusIdString))
|
||||
{
|
||||
object parsedStatusId = Enum.Parse(typeof(enumTicketStatus), statusIdString);
|
||||
if (parsedStatusId is enumTicketStatus statusId)
|
||||
{
|
||||
EnableHoldTicketAction = statusId != enumTicketStatus.OnHold;
|
||||
}
|
||||
}
|
||||
TrySelectTicketCategoryFromTicketInfos();
|
||||
}
|
||||
}
|
||||
|
||||
private bool selectedTicketIsNewTicket
|
||||
{
|
||||
@@ -207,26 +215,22 @@ namespace FasdDesktopUi.Basics.UserControls
|
||||
|
||||
#region SelectedCategory DependencyProperty
|
||||
|
||||
public static readonly DependencyProperty SelectedCategoryProperty = DependencyProperty.Register(
|
||||
"SelectedCategory",
|
||||
typeof(KeyValuePair<string, object>),
|
||||
typeof(CloseCaseDialogWithTicket),
|
||||
new PropertyMetadata(default(KeyValuePair<string, object>), OnSelectedCategoryChanged));
|
||||
|
||||
public KeyValuePair<string, object> SelectedCategory
|
||||
{
|
||||
get { return (KeyValuePair<string, object>)GetValue(SelectedCategoryProperty); }
|
||||
set
|
||||
{
|
||||
SetValue(SelectedCategoryProperty, value);
|
||||
}
|
||||
}
|
||||
|
||||
private static void OnSelectedCategoryChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
var control = (CloseCaseDialogWithTicket)d;
|
||||
var newValue = (KeyValuePair<string, object>)e.NewValue;
|
||||
}
|
||||
public static readonly DependencyProperty SelectedCategoryProperty = DependencyProperty.Register(
|
||||
"SelectedCategory",
|
||||
typeof(HierarchicalSelectionItem),
|
||||
typeof(CloseCaseDialogWithTicket),
|
||||
new PropertyMetadata(null, OnSelectedCategoryChanged));
|
||||
|
||||
public HierarchicalSelectionItem SelectedCategory
|
||||
{
|
||||
get { return (HierarchicalSelectionItem)GetValue(SelectedCategoryProperty); }
|
||||
set { SetValue(SelectedCategoryProperty, value); }
|
||||
}
|
||||
|
||||
private static void OnSelectedCategoryChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
// Reserved for future use when additional reactions are required.
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -373,19 +377,15 @@ namespace FasdDesktopUi.Basics.UserControls
|
||||
me.UpdateAffectedServiceLabel();
|
||||
me.UpdateAffectedAssetComboBox();
|
||||
|
||||
me.ServiceSelectionControl.SearchDataChanged = me.HandleServiceSearchDataChanged;
|
||||
me.ServiceSelectionControl.ParentElement = me.MainStack;
|
||||
me.ServiceSelectionControl.ParentIndex = me.MainStack.Children.IndexOf(me.ServiceSelectionControl);
|
||||
|
||||
me.CategorySelectionControl.ParentElement = me.MainStack;
|
||||
me.CategorySelectionControl.ParentIndex = me.MainStack.Children.IndexOf(me.CategorySelectionControl);
|
||||
|
||||
me.CategorySelectionControl.SearchDataChanged = me.HandleCategorySearchDataChanged;
|
||||
|
||||
me.AddNewOrExistingOrNoneTicketSelection();
|
||||
me.UpdateCaseNotesPreview();
|
||||
me.UpdateTicketComponentVisibility();
|
||||
await me.UpdateQuickCallsComboBoxAsync();
|
||||
me.ServiceSelectionControl.SearchDataChanged = me.HandleServiceSearchDataChanged;
|
||||
me.ServiceSelectionControl.ParentElement = me.MainStack;
|
||||
me.ServiceSelectionControl.ParentIndex = me.MainStack.Children.IndexOf(me.ServiceSelectionControl);
|
||||
|
||||
me.AddNewOrExistingOrNoneTicketSelection();
|
||||
me.UpdateCaseNotesPreview();
|
||||
me.UpdateTicketComponentVisibility();
|
||||
await me.InitializeCategorySelectionAsync();
|
||||
await me.UpdateQuickCallsComboBoxAsync();
|
||||
}
|
||||
catch (Exception E)
|
||||
{
|
||||
@@ -412,13 +412,8 @@ namespace FasdDesktopUi.Basics.UserControls
|
||||
|
||||
dpd?.AddValueChanged(CaseNotesPreview, TicketCaseNotesTextBlock_TextChanged);
|
||||
|
||||
#if !IsNewFeature
|
||||
CategoryLabelPanel.Visibility = Visibility.Collapsed;
|
||||
CategorySelectionControl.Visibility = Visibility.Collapsed;
|
||||
#endif
|
||||
|
||||
SupportNotepad = true;
|
||||
}
|
||||
SupportNotepad = true;
|
||||
}
|
||||
|
||||
private void CloseCaseDialogWithTicket_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
@@ -886,95 +881,193 @@ namespace FasdDesktopUi.Basics.UserControls
|
||||
|
||||
return null;
|
||||
}
|
||||
private string lastCategorySearch;
|
||||
|
||||
private async void HandleCategorySearchDataChanged(object sender, cF4sdHealthSelectionDataRequest e)
|
||||
{
|
||||
await UpdateCategorySelectionControlAsync(e);
|
||||
}
|
||||
|
||||
private async Task UpdateCategorySelectionControlAsync(cF4sdHealthSelectionDataRequest requestData)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
requestData.Identities = DataProvider.Identities;
|
||||
requestData.Table = "M42Wpm-Ticket-CloseCase-Categories";
|
||||
requestData.ResetFilter = CategorySelectionControl.ResetFilter;
|
||||
|
||||
if (string.IsNullOrEmpty(lastCategorySearch) ||
|
||||
!lastCategorySearch.Equals(requestData.Search, StringComparison.CurrentCultureIgnoreCase))
|
||||
{
|
||||
var resCategoryCount = await cFasdCockpitCommunicationBase.Instance.GetPagedDataCount(requestData);
|
||||
CategorySelectionControl.TotalItemCount = resCategoryCount;
|
||||
lastCategorySearch = requestData.Search;
|
||||
}
|
||||
|
||||
var categoryData = await cFasdCockpitCommunicationBase.Instance.GetPagedData(requestData);
|
||||
var categoriesList = GetCategorySelectionData(categoryData) ?? new ObservableCollection<KeyValuePair<string, object>>();
|
||||
|
||||
// Aktuell am Ticket gesetzte Kategorie ggf. sicherstellen
|
||||
string categoryId = null;
|
||||
string categoryName = null;
|
||||
|
||||
if (SelectedTicket != null && SelectedTicket.Infos != null)
|
||||
{
|
||||
var catIdPair = SelectedTicket.Infos.FirstOrDefault(x => x.Key == "CategoryId");
|
||||
categoryId = catIdPair.Value;
|
||||
|
||||
var catNamePair = SelectedTicket.Infos.FirstOrDefault(x => x.Key == "CategoryName");
|
||||
categoryName = catNamePair.Value;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(categoryId) && Guid.Empty.ToString() != categoryId)
|
||||
{
|
||||
bool exists = categoriesList.Any(kvp => kvp.Value != null && kvp.Value.ToString() == categoryId);
|
||||
if (!exists)
|
||||
{
|
||||
categoriesList.Add(new KeyValuePair<string, object>(
|
||||
string.IsNullOrEmpty(categoryName) ? categoryId : categoryName,
|
||||
categoryId));
|
||||
}
|
||||
}
|
||||
|
||||
CategorySelectionControl.ItemData = categoriesList;
|
||||
}
|
||||
catch (Exception E)
|
||||
{
|
||||
LogException(E);
|
||||
}
|
||||
}
|
||||
|
||||
private ObservableCollection<KeyValuePair<string, object>> GetCategorySelectionData(cF4SDHealthCardRawData.cHealthCardTable dataTable)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (dataTable == null || dataTable.Name != "M42Wpm-Ticket-CloseCase-Categories")
|
||||
return null;
|
||||
|
||||
if (!dataTable.Columns.TryGetValue("id", out var idColumn))
|
||||
return null;
|
||||
|
||||
if (!dataTable.Columns.TryGetValue("Name", out var nameColumn))
|
||||
return null;
|
||||
|
||||
var zippedKeyValuePairs =
|
||||
idColumn.Values.Zip(nameColumn.Values, (id, display) =>
|
||||
new KeyValuePair<string, object>(display != null ? display.ToString() : string.Empty, id));
|
||||
|
||||
return new ObservableCollection<KeyValuePair<string, object>>(zippedKeyValuePairs);
|
||||
}
|
||||
catch (Exception E)
|
||||
{
|
||||
LogException(E);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
private async Task UpdateQuickCallsComboBoxAsync()
|
||||
{
|
||||
private async Task InitializeCategorySelectionAsync()
|
||||
{
|
||||
if (isCategoryLoading)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
if (DataProvider == null || CategorySelectionControl == null)
|
||||
return;
|
||||
|
||||
isCategoryLoading = true;
|
||||
|
||||
var tableCandidates = new[] { CategoryTableNamePrimary, CategoryTableNameLegacy };
|
||||
bool initialized = false;
|
||||
|
||||
foreach (var tableName in tableCandidates)
|
||||
{
|
||||
if (await TryPopulateCategoryHierarchyAsync(tableName))
|
||||
{
|
||||
activeCategoryTableName = tableName;
|
||||
initialized = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (initialized)
|
||||
{
|
||||
CategorySelectionControl.ItemsSource = categoryHierarchy;
|
||||
TrySelectTicketCategoryFromTicketInfos();
|
||||
}
|
||||
}
|
||||
catch (Exception E)
|
||||
{
|
||||
LogException(E);
|
||||
}
|
||||
finally
|
||||
{
|
||||
isCategoryLoading = false;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<bool> TryPopulateCategoryHierarchyAsync(string tableName)
|
||||
{
|
||||
try
|
||||
{
|
||||
const int defaultPageSize = 250;
|
||||
|
||||
var requestData = new cF4sdHealthSelectionDataRequest
|
||||
{
|
||||
Identities = DataProvider.Identities,
|
||||
Table = tableName,
|
||||
Page = 0,
|
||||
PageSize = defaultPageSize,
|
||||
Search = string.Empty
|
||||
};
|
||||
|
||||
var totalCount = await cFasdCockpitCommunicationBase.Instance.GetPagedDataCount(requestData);
|
||||
var effectivePageSize = requestData.PageSize > 0 ? requestData.PageSize : defaultPageSize;
|
||||
var pageCount = Math.Max(1, (int)Math.Ceiling((double)totalCount / Math.Max(1, effectivePageSize)));
|
||||
|
||||
var flatItems = new Dictionary<string, HierarchicalSelectionItem>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
for (int pageIndex = 0; pageIndex < pageCount; pageIndex++)
|
||||
{
|
||||
requestData.Page = pageIndex;
|
||||
requestData.PageSize = effectivePageSize;
|
||||
|
||||
var categoryTable = await cFasdCockpitCommunicationBase.Instance.GetPagedData(requestData);
|
||||
bool rowAdded = false;
|
||||
|
||||
foreach (var item in ConvertCategoryTable(categoryTable))
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(item.Id))
|
||||
continue;
|
||||
|
||||
flatItems[item.Id] = item;
|
||||
rowAdded = true;
|
||||
}
|
||||
|
||||
if (!rowAdded && categoryTable == null)
|
||||
return false;
|
||||
}
|
||||
|
||||
var hierarchy = HierarchicalSelectionItem.BuildTree(flatItems.Values);
|
||||
if (hierarchy == null || hierarchy.Count == 0)
|
||||
return false;
|
||||
|
||||
categoryHierarchy = hierarchy;
|
||||
UpdateCategoryLookup();
|
||||
return true;
|
||||
}
|
||||
catch (Exception E)
|
||||
{
|
||||
LogException(E);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerable<HierarchicalSelectionItem> ConvertCategoryTable(cF4SDHealthCardRawData.cHealthCardTable dataTable)
|
||||
{
|
||||
if (dataTable == null || dataTable.Columns == null)
|
||||
yield break;
|
||||
|
||||
if (!dataTable.Columns.TryGetValue("id", out var idColumn))
|
||||
yield break;
|
||||
|
||||
if (!dataTable.Columns.TryGetValue("Name", out var nameColumn))
|
||||
yield break;
|
||||
|
||||
cF4SDHealthCardRawData.cHealthCardTableColumn parentIdColumn = null;
|
||||
cF4SDHealthCardRawData.cHealthCardTableColumn parentNameColumn = null;
|
||||
if (!dataTable.Columns.TryGetValue("parentValue", out parentIdColumn))
|
||||
dataTable.Columns.TryGetValue("Parent_Value", out parentIdColumn);
|
||||
if (!dataTable.Columns.TryGetValue("parent", out parentNameColumn))
|
||||
dataTable.Columns.TryGetValue("Parent", out parentNameColumn);
|
||||
|
||||
for (int index = 0; index < idColumn.Values.Count; index++)
|
||||
{
|
||||
var id = idColumn.Values[index]?.ToString();
|
||||
if (string.IsNullOrWhiteSpace(id))
|
||||
continue;
|
||||
|
||||
var displayName = index < nameColumn.Values.Count ? nameColumn.Values[index]?.ToString() : string.Empty;
|
||||
|
||||
string parentId = null;
|
||||
if (parentIdColumn != null && index < parentIdColumn.Values.Count)
|
||||
parentId = parentIdColumn.Values[index]?.ToString();
|
||||
|
||||
if (Guid.TryParse(parentId, out var parsedParentId) && parsedParentId == Guid.Empty)
|
||||
parentId = null;
|
||||
|
||||
var parentDisplayName = parentNameColumn != null && index < parentNameColumn.Values.Count
|
||||
? parentNameColumn.Values[index]?.ToString()
|
||||
: null;
|
||||
|
||||
yield return new HierarchicalSelectionItem
|
||||
{
|
||||
Id = id,
|
||||
DisplayName = string.IsNullOrWhiteSpace(displayName) ? parentDisplayName ?? id : displayName,
|
||||
ParentId = parentId,
|
||||
ParentDisplayName = parentDisplayName
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateCategoryLookup()
|
||||
{
|
||||
categoryLookup.Clear();
|
||||
|
||||
if (categoryHierarchy == null)
|
||||
return;
|
||||
|
||||
foreach (var node in categoryHierarchy.SelectMany(item => item.SelfAndDescendants()))
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(node.Id))
|
||||
categoryLookup[node.Id] = node;
|
||||
}
|
||||
}
|
||||
|
||||
private void TrySelectTicketCategoryFromTicketInfos()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (SelectedTicket?.Infos == null)
|
||||
{
|
||||
SelectedCategory = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!SelectedTicket.Infos.TryGetValue("CategoryId", out var categoryId) || string.IsNullOrWhiteSpace(categoryId))
|
||||
{
|
||||
SelectedCategory = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (categoryLookup.TryGetValue(categoryId, out var categoryNode))
|
||||
SelectedCategory = categoryNode;
|
||||
}
|
||||
catch (Exception E)
|
||||
{
|
||||
LogException(E);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task UpdateQuickCallsComboBoxAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
cF4sdHealthSelectionDataRequest requestData = new cF4sdHealthSelectionDataRequest
|
||||
|
||||
Reference in New Issue
Block a user