using C4IT.FASD.Base; using C4IT.MultiLanguage; using FasdDesktopUi.Basics.Helper; using FasdDesktopUi.Basics.Models; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Reflection; using System.Text; using System.Timers; using System.Windows; using System.Windows.Controls; using System.Windows.Controls.Primitives; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Navigation; using static C4IT.Logging.cLogManager; namespace FasdDesktopUi.Basics.UserControls { public partial class ComboBoxPageable : UserControl, IFocusInvoker { #region Fields & Properties private const int itemsPerPage = 10; private const double searchDelayInMilliseconds = 350; private int pageIndex = 0; private int pageCount = 1; private readonly Timer timer = new Timer(searchDelayInMilliseconds); private readonly cF4sdHealthSelectionDataRequest searchData = new cF4sdHealthSelectionDataRequest() { Search = string.Empty, Page = 0, PageSize = itemsPerPage }; public int? ParentIndex { get; set; } public UIElement ParentElement { get; set; } public string ResetFilterLabel { get { return (string)GetValue(ResetFilterLabelProperty); } set { SetValue(ResetFilterLabelProperty, value); } } public static readonly DependencyProperty ResetFilterLabelProperty = DependencyProperty.Register("ResetFilterLabel", typeof(string), typeof(ComboBoxPageable), new PropertyMetadata(cMultiLanguageSupport.GetItem("Dialog.CloseCase.ResetFilter"))); public bool ResetFilter { get { return (bool)GetValue(ResetFilterProperty); } set { SetValue(ResetFilterProperty, value); } } public static readonly DependencyProperty ResetFilterProperty = DependencyProperty.Register("ResetFilter", typeof(bool), typeof(ComboBoxPageable), new PropertyMetadata(false, new PropertyChangedCallback(ResetFilterChanged))); private static void ResetFilterChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { try { if (!(d is ComboBoxPageable me)) return; me.searchData.ResetFilter = me.ResetFilter; me.InvokeSearchValueChanged(); } catch (Exception E) { LogException(E); } } #region TotalItemCount public int TotalItemCount { get { return (int)GetValue(TotalItemCountProperty); } set { SetValue(TotalItemCountProperty, value); } } public static readonly DependencyProperty TotalItemCountProperty = DependencyProperty.Register("TotalItemCount", typeof(int), typeof(ComboBoxPageable), new PropertyMetadata(0, new PropertyChangedCallback(TotalItemCountChanged))); private static void TotalItemCountChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { try { if (!(d is ComboBoxPageable me)) return; me.pageCount = me.TotalItemCount % itemsPerPage == 0 ? me.TotalItemCount / itemsPerPage : (me.TotalItemCount / itemsPerPage) + 1; me.UpdatePageTracker(); } catch (Exception E) { LogException(E); } } #endregion public static readonly DependencyProperty SelectedItemProperty = DependencyProperty.Register( "SelectedItem", typeof(object), typeof(ComboBoxPageable), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault)); public object SelectedItem { get { return GetValue(SelectedItemProperty); } set { SetValue(SelectedItemProperty, value); } } #region SearchDataChanged public EventHandler SearchDataChanged { get { return (EventHandler)GetValue(SearchDataChangedProperty); } set { SetValue(SearchDataChangedProperty, value); } } public static readonly DependencyProperty SearchDataChangedProperty = DependencyProperty.Register("SearchDataChanged", typeof(EventHandler), typeof(ComboBoxPageable), new PropertyMetadata(null)); #endregion #region RestoreParentScrollFocusOnDropDownClose public bool RestoreParentScrollFocusOnDropDownClose { get { return (bool)GetValue(RestoreParentScrollFocusOnDropDownCloseProperty); } set { SetValue(RestoreParentScrollFocusOnDropDownCloseProperty, value); } } public static readonly DependencyProperty RestoreParentScrollFocusOnDropDownCloseProperty = DependencyProperty.Register("RestoreParentScrollFocusOnDropDownClose", typeof(bool), typeof(ComboBoxPageable), new PropertyMetadata(false)); #endregion #region ItemData public ObservableCollection> ItemData { get { return (ObservableCollection>)GetValue(ItemDataProperty); } set { SetValue(ItemDataProperty, value); } } public static readonly DependencyProperty ItemDataProperty = DependencyProperty.Register("ItemData", typeof(ObservableCollection>), typeof(ComboBoxPageable), new PropertyMetadata(null)); #endregion #region Styles public Brush ComboBoxBackground { get { return (Brush)GetValue(ComboBoxBackgroundProperty); } set { SetValue(ComboBoxBackgroundProperty, value); } } public static readonly DependencyProperty ComboBoxBackgroundProperty = DependencyProperty.Register("ComboBoxBackground", typeof(Brush), typeof(ComboBoxPageable), new PropertyMetadata(Brushes.Transparent)); #endregion #endregion public ComboBoxPageable() { InitializeComponent(); timer.Elapsed += TimerElapsed; } private void TimerElapsed(object sender, ElapsedEventArgs e) { try { timer.Stop(); Dispatcher.Invoke(InvokeSearchValueChanged); } catch (Exception E) { LogException(E); } } private void InvokeSearchValueChanged() { try { searchData.Page = pageIndex; if (SearchDataChanged != null) SearchDataChanged.Invoke(this, searchData); } catch (Exception E) { LogException(E); } } public KeyValuePair? GetSelectedValue() { try { if (ComboBoxControl.SelectedItem is KeyValuePair selectedItem) return selectedItem; } catch (Exception E) { LogException(E); } return null; } #region Paging Events #region PageBack private void PageBackButton_Click(RoutedEventArgs e) { if (pageIndex <= 0) { e.Handled = true; return; } pageIndex--; InvokeSearchValueChanged(); UpdatePageTracker(); e.Handled = true; } private void PageBackButton_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) { PageBackButton_Click(e); } private void PageBackButton_TouchDown(object sender, TouchEventArgs e) { PageBackButton_Click(e); } #endregion #region PageFor private void PageForButton_Click(RoutedEventArgs e) { if (pageIndex + 1 >= pageCount) return; pageIndex++; InvokeSearchValueChanged(); UpdatePageTracker(); e.Handled = true; } private void PageForButton_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) { PageForButton_Click(e); } private void PageForButton_TouchDown(object sender, TouchEventArgs e) { PageForButton_Click(e); } #endregion #endregion private void UpdatePageTracker() { try { var pageTrackerElement = ComboBoxControl.Template.FindName("PageTrackerTextBlock", ComboBoxControl); if (!(pageTrackerElement is TextBlock pageTrackerTextBlock)) return; pageTrackerTextBlock.Text = $"{pageIndex + 1} / {pageCount}"; } catch (Exception E) { LogException(E); } } private void SearchTextBox_TextChanged(object sender, TextChangedEventArgs e) { try { if (!(sender is TextBox searchTextBox)) return; pageIndex = 0; searchData.Search = searchTextBox.Text; UpdatePageTracker(); timer.Stop(); timer.Start(); } catch (Exception E) { LogException(E); } } private void ComboBoxControl_DropDownClosed(object sender, EventArgs e) { timer.Stop(); cFocusInvoker.InvokeLostFocus(this, e); if (RestoreParentScrollFocusOnDropDownClose) { _ = Dispatcher.BeginInvoke((Action)(() => { try { var parentScrollViewer = cUiElementHelper.GetFirstParentOfType(this); Keyboard.ClearFocus(); if (parentScrollViewer != null) { parentScrollViewer.Focus(); Keyboard.Focus(parentScrollViewer); } } catch (Exception exception) { LogException(exception); } }), System.Windows.Threading.DispatcherPriority.Input); } } private void ComboBoxControl_DropDownOpened(object sender, EventArgs e) { try { // Elterninfo nur setzen, wenn (noch) nicht sinnvoll vorhanden bool invalidIndex = false; if (ParentElement is Panel p && (ParentIndex == null || ParentIndex < 0 || ParentIndex > p.Children.Count)) invalidIndex = true; if (ParentElement == null || invalidIndex) { ParentElement = this.Parent as UIElement; if (this.Parent is Panel panel) ParentIndex = panel.Children.IndexOf(this); else ParentIndex = null; } cFocusInvoker.InvokeGotFocus(this, e); InvokeSearchValueChanged(); #region Focus auf Textfeld setzen beim Öffnen der Combobox if (!(sender is ComboBox comboBox)) return; // Zugriff auf das Popup-Element Popup partPopup = comboBox.Template.FindName("PART_Popup", comboBox) as Popup; if (partPopup == null) return; // Verzögerter Fokus var _h = Dispatcher.BeginInvoke((Action)(() => { if (partPopup.IsOpen) { // Suchen des SearchTextBox-Element innerhalb des Popups TextBox searchTextBox = FindVisualChild(partPopup.Child, "SearchTextBox"); if (searchTextBox != null) { // Setzen des Fokus auf TextBox searchTextBox.Focus(); } } }), System.Windows.Threading.DispatcherPriority.Loaded); #endregion } catch (Exception E) { LogException(E); } } private static T FindVisualChild(DependencyObject parent, string childName) where T : DependencyObject { if (parent == null) return null; T foundChild = null; int childrenCount = VisualTreeHelper.GetChildrenCount(parent); for (int i = 0; i < childrenCount; i++) { var child = VisualTreeHelper.GetChild(parent, i); var childType = child as T; if (childType == null) { foundChild = FindVisualChild(child, childName); if (foundChild != null) break; } else if (!string.IsNullOrEmpty(childName)) { var frameworkElement = child as FrameworkElement; if (frameworkElement != null && frameworkElement.Name == childName) { return (T)child; } } else { return (T)child; } } return foundChild; } } }