diff --git a/src/DynamoCoreWpf/ViewModels/PackageManager/PackageManagerSearchViewModel.cs b/src/DynamoCoreWpf/ViewModels/PackageManager/PackageManagerSearchViewModel.cs index 2da23f68084..da7a300324d 100644 --- a/src/DynamoCoreWpf/ViewModels/PackageManager/PackageManagerSearchViewModel.cs +++ b/src/DynamoCoreWpf/ViewModels/PackageManager/PackageManagerSearchViewModel.cs @@ -304,8 +304,12 @@ public string SearchText get { return _SearchText; } set { - _SearchText = value; - RaisePropertyChanged("SearchText"); + if(_SearchText != value) + { + _SearchText = value; + SearchAndUpdateResults(); + RaisePropertyChanged("SearchText"); + } } } @@ -1073,19 +1077,24 @@ public bool TimedOut } } + private System.Timers.Timer aTimer; + private void StartTimer() { - var aTimer = new System.Timers.Timer(); - aTimer.Elapsed += new ElapsedEventHandler(OnTimedEvent); + if(aTimer == null) + aTimer = new System.Timers.Timer(); + + aTimer.Elapsed += OnTimedEvent; aTimer.Interval = MAX_LOAD_TIME; aTimer.AutoReset = false; aTimer.Enabled = true; + aTimer.Start(); } private void OnTimedEvent(object sender, ElapsedEventArgs e) { var aTimer = (System.Timers.Timer)sender; - aTimer.Dispose(); + aTimer.Stop(); // If we have managed to get all the results // Simply dispose of the timer @@ -1577,12 +1586,23 @@ public void DisableSearchTextBox() /// /// Clear after closing down /// - internal void Close() + internal void PackageManagerViewClose() + { + SearchAndUpdateResults(String.Empty); // reset the search text property + InitialResultsLoaded = false; + TimedOut = false; + } + + /// + /// Remove PackageManagerSearchViewModel resources + /// + internal void Dispose() { + nonHostFilter?.ForEach(f => f.PropertyChanged -= filter_PropertyChanged); + aTimer.Elapsed -= OnTimedEvent; + TimedOut = false; // reset the timedout screen InitialResultsLoaded = false; // reset the loading screen settings - RequestShowFileDialog -= OnRequestShowFileDialog; - nonHostFilter.ForEach(f => f.PropertyChanged -= filter_PropertyChanged); ClearMySearchResults(); } diff --git a/src/DynamoCoreWpf/Views/Core/DynamoView.xaml.cs b/src/DynamoCoreWpf/Views/Core/DynamoView.xaml.cs index 571626f7efa..ccdd8a491ce 100644 --- a/src/DynamoCoreWpf/Views/Core/DynamoView.xaml.cs +++ b/src/DynamoCoreWpf/Views/Core/DynamoView.xaml.cs @@ -2014,6 +2014,7 @@ private void WindowClosed(object sender, EventArgs e) this.Dispose(); sharedViewExtensionLoadedParams?.Dispose(); + this._pkgSearchVM?.Dispose(); } // the key press event is being intercepted before it can get to @@ -2274,9 +2275,11 @@ private void DynamoViewModelRequestShowPackageManager(object s, EventArgs e) { packageManagerWindow.Navigate((e as OpenPackageManagerEventArgs).Tab); } + _pkgSearchVM.RefreshAndSearchAsync(); } + internal void EnableEnvironment(bool isEnabled) { this.mainGrid.IsEnabled = isEnabled; diff --git a/src/DynamoCoreWpf/Views/PackageManager/Controls/PackageManagerSearchControl.xaml b/src/DynamoCoreWpf/Views/PackageManager/Controls/PackageManagerSearchControl.xaml index c4b7ced2d6b..93968bc06db 100644 --- a/src/DynamoCoreWpf/Views/PackageManager/Controls/PackageManagerSearchControl.xaml +++ b/src/DynamoCoreWpf/Views/PackageManager/Controls/PackageManagerSearchControl.xaml @@ -335,7 +335,8 @@ + Margin="0 5 0 0" + IsEnabled="{Binding Path=InitialResultsLoaded, UpdateSourceTrigger=PropertyChanged}" /> @@ -387,18 +388,16 @@ Visibility="{Binding IsAnyFilterOn, Converter={StaticResource BoolToVisibilityCollapsedConverter}, UpdateSourceTrigger=PropertyChanged}" /> - - + Visibility="Visible" /> - + diff --git a/src/DynamoCoreWpf/Views/PackageManager/Controls/PackageManagerSearchControl.xaml.cs b/src/DynamoCoreWpf/Views/PackageManager/Controls/PackageManagerSearchControl.xaml.cs index 4e9422310d3..44101cca15d 100644 --- a/src/DynamoCoreWpf/Views/PackageManager/Controls/PackageManagerSearchControl.xaml.cs +++ b/src/DynamoCoreWpf/Views/PackageManager/Controls/PackageManagerSearchControl.xaml.cs @@ -2,6 +2,7 @@ using System.Windows.Controls; using System.Windows.Controls.Primitives; using System.Windows.Data; +using Dynamo.Controls; using Dynamo.PackageManager.ViewModels; namespace Dynamo.PackageManager.UI @@ -23,6 +24,18 @@ public PackageManagerSearchControl() private void InitializeContext(object sender, RoutedEventArgs e) { PkgSearchVM = this.DataContext as PackageManagerSearchViewModel; + + if (PkgSearchVM != null) + { + // Create the binding once the DataContext is available + var binding = new Binding(nameof(PackageManagerSearchViewModel.InitialResultsLoaded)) + { + Source = PkgSearchVM, + Converter = new InverseBooleanToVisibilityCollapsedConverter() + }; + + this.loadingAnimationSearchControlScreen.SetBinding(UIElement.VisibilityProperty, binding); + } } diff --git a/src/DynamoCoreWpf/Views/PackageManager/Controls/SearchBoxControl.xaml b/src/DynamoCoreWpf/Views/PackageManager/Controls/SearchBoxControl.xaml index f1e3f9110a1..155ec0f6c20 100644 --- a/src/DynamoCoreWpf/Views/PackageManager/Controls/SearchBoxControl.xaml +++ b/src/DynamoCoreWpf/Views/PackageManager/Controls/SearchBoxControl.xaml @@ -115,9 +115,8 @@ Cursor="IBeam" HorizontalAlignment="Stretch" IsEnabled="{Binding RelativeSource={RelativeSource AncestorType=UserControl}, Path=IsEnabled}" - TextChanged="SearchTextBox_OnTextChanged" - IsKeyboardFocusWithinChanged="SearchTextBox_OnKeyboardFocusWithinChanged" - Text="{Binding SearchText, UpdateSourceTrigger=PropertyChanged}"> + Text="{Binding SearchText, Delay=100, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" + IsKeyboardFocusWithinChanged="SearchTextBox_OnKeyboardFocusWithinChanged"> diff --git a/src/DynamoCoreWpf/Views/PackageManager/Controls/SearchBoxControl.xaml.cs b/src/DynamoCoreWpf/Views/PackageManager/Controls/SearchBoxControl.xaml.cs index 051d87833ea..96cb033f590 100644 --- a/src/DynamoCoreWpf/Views/PackageManager/Controls/SearchBoxControl.xaml.cs +++ b/src/DynamoCoreWpf/Views/PackageManager/Controls/SearchBoxControl.xaml.cs @@ -1,11 +1,8 @@ -using Dynamo.PackageManager; -using HelixToolkit.Wpf.SharpDX; using System; using System.Globalization; using System.Windows; using System.Windows.Controls; using System.Windows.Data; -using System.Windows.Threading; namespace Dynamo.PackageManager.UI { @@ -15,10 +12,6 @@ namespace Dynamo.PackageManager.UI /// public partial class SearchBoxControl : UserControl { - private DispatcherTimer delayTimer; - - // set delay for event 500ms - private static int delayTime = 500; /// /// Constructor /// @@ -26,37 +19,7 @@ public SearchBoxControl() { InitializeComponent(); } - private static DispatcherTimer Debounce(DispatcherTimer dispatcher, TimeSpan interval, Action action) - { - dispatcher?.Stop(); - dispatcher = null; - dispatcher = new DispatcherTimer(interval, DispatcherPriority.ApplicationIdle, (s, e) => - { - dispatcher?.Stop(); - action.Invoke(); - }, Dispatcher.CurrentDispatcher); - dispatcher?.Start(); - - return dispatcher; - } - - /// - /// Handles Search Box Text Changed Event - /// - /// - /// - /// - private void SearchTextBox_OnTextChanged(object sender, TextChangedEventArgs e) - { - Debounce(delayTimer, TimeSpan.FromMilliseconds(delayTime), () => - { - var textBox = sender as TextBox; - if (textBox == null) return; - (this.DataContext as PackageManagerSearchViewModel)?.SearchAndUpdateResults(textBox.Text); - textBox.Focus(); - }); - } private void OnSearchClearButtonClicked(object sender, System.Windows.Input.MouseButtonEventArgs e) { this.SearchTextBox.Clear(); @@ -78,86 +41,6 @@ private void SearchTextBox_OnKeyboardFocusWithinChanged(object sender, Dependenc } } - /// - /// A multivalue visibilty converter - /// value 0 - boolean - /// value 1 - text - /// - public class MultiBooleanToVisibilityConverter : IMultiValueConverter - { - public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) - { - bool focusVisible = true; - bool textVisible = true; - - foreach (object value in values) - { - if (value is bool) - { - if ((bool)value) - { - focusVisible = false; // If the textbox has the focus, don't show the Control.. - } - else - { - focusVisible = true; // If the textbox don't have focus, we can show the Control.. - } - } - else - { - if (value != null && string.IsNullOrWhiteSpace(value.ToString())) - { - textVisible = true; // If the textbox has no text, we can show the Control.. - } - else - { - textVisible = false; // If the textbox has some text, don't show the Control.. - } - } - } - - // Only of both conditions are true, show the Control.. - if (focusVisible && textVisible) - return Visibility.Visible; - else - return Visibility.Collapsed; - } - - public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) - { - throw new NotImplementedException(); - } - } - - /// - /// Converts null or empty string to Visibility Collapsed - /// - public class NonEmptyStringToCollapsedConverter : IValueConverter - { - enum Parameters - { - Normal, Inverted - } - - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) - { - parameter = parameter ?? "Normal"; - - var boolValue = value is string s && !string.IsNullOrEmpty(s); - var direction = (Parameters)Enum.Parse(typeof(Parameters), (string)parameter); - - if (direction == Parameters.Inverted) - return !boolValue ? Visibility.Visible : Visibility.Collapsed; - - return boolValue ? Visibility.Visible : Visibility.Collapsed; - } - - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) - { - return null; - } - } - /// /// Converts null or empty string to Visibility Collapsed /// diff --git a/src/DynamoCoreWpf/Views/PackageManager/PackageManagerView.xaml.cs b/src/DynamoCoreWpf/Views/PackageManager/PackageManagerView.xaml.cs index 3ce63bbd82f..1e6dab884ac 100644 --- a/src/DynamoCoreWpf/Views/PackageManager/PackageManagerView.xaml.cs +++ b/src/DynamoCoreWpf/Views/PackageManager/PackageManagerView.xaml.cs @@ -6,6 +6,7 @@ using Dynamo.Controls; using Dynamo.Logging; using Dynamo.UI; +using Dynamo.Utilities; using Dynamo.ViewModels; using Dynamo.Wpf.Utilities; using DynamoUtilities; @@ -124,7 +125,7 @@ private void WindowClosed(object sender, EventArgs e) { this.packageManagerPublish.Dispose(); this.PackageManagerViewModel.PackageSearchViewModel.RequestShowFileDialog -= OnRequestShowFileDialog; - this.PackageManagerViewModel.PackageSearchViewModel.Close(); + this.PackageManagerViewModel.PackageSearchViewModel.PackageManagerViewClose(); } private void SearchForPackagesButton_Click(object sender, RoutedEventArgs e) diff --git a/test/DynamoCoreWpfTests/PackageManager/PackageManagerUITests.cs b/test/DynamoCoreWpfTests/PackageManager/PackageManagerUITests.cs index 987a2ecc1fd..64bd688ff23 100644 --- a/test/DynamoCoreWpfTests/PackageManager/PackageManagerUITests.cs +++ b/test/DynamoCoreWpfTests/PackageManager/PackageManagerUITests.cs @@ -1613,6 +1613,33 @@ public void PackageManagerClosesWithDynamo() AssertWindowClosedWithDynamoView(); } + [Test] + public void SearchBoxInactiveOnWindowOpened() + { + ViewModel.OnRequestPackageManagerDialog(null, null); + + var windows = GetWindowEnumerable(View.OwnedWindows); + var packageManagerView = windows.First(x => x is PackageManagerView) as PackageManagerView; + + Assert.IsNotNull(packageManagerView); + + var searchBox = LogicalTreeHelper.FindLogicalNode(packageManagerView, "SearchBox") as UserControl; + Assert.IsNotNull(searchBox); + Assert.IsFalse(searchBox.IsEnabled); + + packageManagerView.PackageManagerViewModel.PackageSearchViewModel.InitialResultsLoaded = true; + Assert.IsTrue(searchBox.IsEnabled); + } + + [Test] + public void PackageManagerDialogDoesNotThrowExceptions() + { + Assert.DoesNotThrow(() => ViewModel.OnRequestPackageManagerDialog(null, null), "Package Manager View did not open without exceptions"); + + AssertWindowOwnedByDynamoView(); + } + + /// /// Asserts that the filter context menu will stay open while the user interacts with it ///