using System; using System.Timers; 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 SearchBar : UserControl { private readonly Timer _searchTimer = new Timer(); #region Dependency Properties public static readonly DependencyProperty SearchButtonSizeProperty = DependencyProperty.Register(nameof(SearchButtonSize), typeof(double), typeof(SearchBar), new PropertyMetadata(30.0)); public double SearchButtonSize { get => (double)GetValue(SearchButtonSizeProperty); set => SetValue(SearchButtonSizeProperty, value); } public static readonly DependencyProperty CloseButtonSizeProperty = DependencyProperty.Register(nameof(CloseButtonSize), typeof(double), typeof(SearchBar), new PropertyMetadata(30.0)); public double CloseButtonSize { get => (double)GetValue(CloseButtonSizeProperty); set => SetValue(CloseButtonSizeProperty, value); } public static readonly DependencyProperty ShowCloseButtonProperty = DependencyProperty.Register(nameof(ShowCloseButton), typeof(bool), typeof(SearchBar), new PropertyMetadata(true)); public bool ShowCloseButton { get => (bool)GetValue(ShowCloseButtonProperty); set => SetValue(ShowCloseButtonProperty, value); } public static readonly DependencyProperty ShowSearchButtonProperty = DependencyProperty.Register(nameof(ShowSearchButton), typeof(bool), typeof(SearchBar), new PropertyMetadata(true)); public bool ShowSearchButton { get => (bool)GetValue(ShowSearchButtonProperty); set => SetValue(ShowSearchButtonProperty, value); } public static readonly DependencyProperty PlaceholderTextProperty = DependencyProperty.Register(nameof(PlaceholderText), typeof(string), typeof(SearchBar), new PropertyMetadata(string.Empty)); public string PlaceholderText { get => (string)GetValue(PlaceholderTextProperty); set => SetValue(PlaceholderTextProperty, value); } public static readonly DependencyProperty DebounceIntervalProperty = DependencyProperty.Register(nameof(DebounceInterval), typeof(TimeSpan), typeof(SearchBar), new PropertyMetadata(TimeSpan.FromMilliseconds(250), OnDebounceIntervalChanged)); public TimeSpan DebounceInterval { get => (TimeSpan)GetValue(DebounceIntervalProperty); set => SetValue(DebounceIntervalProperty, value); } private static void OnDebounceIntervalChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { TimeSpan interval = (TimeSpan)e.NewValue; ((SearchBar)d)._searchTimer.Interval = Math.Max(interval.TotalMilliseconds, 10); } public static readonly DependencyProperty IsInputEnabledProperty = DependencyProperty.Register(nameof(IsInputEnabled), typeof(bool), typeof(SearchBar), new PropertyMetadata(true)); public bool IsInputEnabled { get => (bool)GetValue(IsInputEnabledProperty); set => SetValue(IsInputEnabledProperty, value); } public SolidColorBrush SearchBackground { get { return (SolidColorBrush)GetValue(SearchBackgroundProperty); } set { SetValue(SearchBackgroundProperty, value); } } public static readonly DependencyProperty SearchBackgroundProperty = DependencyProperty.Register(nameof(SearchBackground), typeof(SolidColorBrush), typeof(SearchBar), new PropertyMetadata(null)); #endregion #region SearchValue private string _searchValue = null; public string SearchValue { get => _searchValue; set { if (value != _searchValue) { _searchValue = value; if (IsInputEnabled) HandleSearchValueChanged(); } } } #endregion #region Events public event EventHandler SearchValueChanged; public static readonly RoutedEvent CancelledSearchEvent = EventManager.RegisterRoutedEvent(nameof(CancelledSearch), RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(SearchBar)); public event RoutedEventHandler CancelledSearch { add => AddHandler(CancelledSearchEvent, value); remove => RemoveHandler(CancelledSearchEvent, value); } #endregion public SearchBar() { InitializeComponent(); } public void Clear() { SearchTextBox.Text = null; } public void SetSearchText(string search) { SearchTextBox.Text = search; SearchTextBox.CaretIndex = search?.Length ?? 0; SearchTextBox.Focus(); Keyboard.Focus(SearchTextBox); } public void FocusInput() { SearchTextBox.Focus(); Keyboard.Focus(SearchTextBox); } internal void SetSpinnerVisibility(Visibility visibility) { Dispatcher.Invoke(() => SearchSpinner.Visibility = visibility); } private void HandleSearchValueChanged() { if (_searchTimer.Enabled) _searchTimer.Stop(); _searchTimer.Start(); } private void SearchTimerTick(object sender, EventArgs e) { _searchTimer.Stop(); SearchValueChanged?.Invoke(this, SearchValue); } private void SearchButton_Click(object sender, InputEventArgs e) { SearchTextBox.Focus(); } private void CloseButton_Click(object sender, InputEventArgs e) { RaiseEvent(new RoutedEventArgs(CancelledSearchEvent)); } private void SearchBar_Loaded(object sender, RoutedEventArgs e) { _searchTimer.Stop(); _searchTimer.Interval = Math.Max(DebounceInterval.TotalMilliseconds, 10); _searchTimer.Elapsed += SearchTimerTick; BackgroundBorder.CornerRadius = new CornerRadius(BackgroundBorder.ActualHeight / 2.0); } internal void ActivateManualSearch() { Search.Clear(); Search.IsInputEnabled = true; Dispatcher.BeginInvoke(new Action(() => { Search.FocusInput(); }), DispatcherPriority.Input); } } }