From fa51d90878b7097b5e76a1ae0b7d11a033c1565f Mon Sep 17 00:00:00 2001 From: Nice3point Date: Mon, 4 Dec 2023 02:44:44 +0300 Subject: [PATCH] Mouse new window support --- .../Services/MoqLookupService.cs | 8 +- .../Services/MoqSnoopVisualService.cs | 16 +- .../ViewModels/MoqSnoopViewModel.cs | 13 +- .../Services/Contracts/ILookupService.cs | 1 + .../Services/Contracts/ISnoopVisualService.cs | 1 + RevitLookup/Services/LookupService.cs | 6 + RevitLookup/Services/SnoopVisualService.cs | 17 +- .../ViewModels/Contracts/ISnoopViewModel.cs | 3 +- .../ViewModels/Pages/SnoopViewModelBase.cs | 13 +- .../Abstraction/SnoopViewBase.ContextMenu.cs | 153 +++++++ .../Abstraction/SnoopViewBase.Gestures.cs | 40 ++ .../Abstraction/SnoopViewBase.Navigation.cs | 121 ++++++ .../Abstraction/SnoopViewBase.ToolTips.cs | 78 ++++ .../Pages/Abstraction/SnoopViewBase.xaml.cs | 196 +++++++++ RevitLookup/Views/Pages/EventsView.xaml | 11 +- RevitLookup/Views/Pages/EventsView.xaml.cs | 6 +- RevitLookup/Views/Pages/SnoopView.xaml | 15 +- RevitLookup/Views/Pages/SnoopView.xaml.cs | 2 +- RevitLookup/Views/Pages/SnoopViewBase.xaml.cs | 404 ------------------ 19 files changed, 668 insertions(+), 436 deletions(-) create mode 100644 RevitLookup/Views/Pages/Abstraction/SnoopViewBase.ContextMenu.cs create mode 100644 RevitLookup/Views/Pages/Abstraction/SnoopViewBase.Gestures.cs create mode 100644 RevitLookup/Views/Pages/Abstraction/SnoopViewBase.Navigation.cs create mode 100644 RevitLookup/Views/Pages/Abstraction/SnoopViewBase.ToolTips.cs create mode 100644 RevitLookup/Views/Pages/Abstraction/SnoopViewBase.xaml.cs delete mode 100644 RevitLookup/Views/Pages/SnoopViewBase.xaml.cs diff --git a/RevitLookup.UI.Demo/Services/MoqLookupService.cs b/RevitLookup.UI.Demo/Services/MoqLookupService.cs index 57277394..71da62bc 100644 --- a/RevitLookup.UI.Demo/Services/MoqLookupService.cs +++ b/RevitLookup.UI.Demo/Services/MoqLookupService.cs @@ -45,7 +45,13 @@ public ILookupServiceDependsStage Snoop(SnoopableObject snoopableObject) _serviceProvider.GetService()!.Snoop(snoopableObject); return this; } - + + public ILookupServiceDependsStage Snoop(IReadOnlyCollection snoopableObjects) + { + _serviceProvider.GetService()!.Snoop(snoopableObjects); + return this; + } + public ILookupServiceShowStage DependsOn(IServiceProvider provider) { _owner = (Window) provider.GetService(); diff --git a/RevitLookup.UI.Demo/Services/MoqSnoopVisualService.cs b/RevitLookup.UI.Demo/Services/MoqSnoopVisualService.cs index d5d068bd..df1432ad 100644 --- a/RevitLookup.UI.Demo/Services/MoqSnoopVisualService.cs +++ b/RevitLookup.UI.Demo/Services/MoqSnoopVisualService.cs @@ -46,6 +46,8 @@ public void Snoop(SnoopableObject snoopableObject) { viewModel.SnoopableObjects = new[] {snoopableObject}; } + + viewModel.SnoopableData = Array.Empty(); } catch (Exception exception) { @@ -53,6 +55,12 @@ public void Snoop(SnoopableObject snoopableObject) } } + public void Snoop(IReadOnlyCollection snoopableObjects) + { + viewModel.SnoopableObjects = snoopableObjects; + viewModel.SnoopableData = Array.Empty(); + } + public async Task SnoopAsync(SnoopableType snoopableType) { switch (snoopableType) @@ -89,8 +97,8 @@ public async Task SnoopAsync(SnoopableType snoopableType) _ => throw new ArgumentOutOfRangeException(nameof(snoopableType), snoopableType, null) }; - viewModel.SnoopableObjects = await GenerateObjectsAsync(generationCount); - viewModel.SnoopableData = Array.Empty(); + var items = await GenerateObjectsAsync(generationCount); + Snoop(items); UpdateWindowVisibility(Visibility.Visible); } @@ -108,10 +116,10 @@ private static async Task> GenerateObjectsA return await Task.Run(() => new Faker() .CustomInstantiator(faker => { - if (faker.IndexFaker % 2000 == 0) return new SnoopableObject(null); + if (faker.IndexFaker % 2000 == 0) return new SnoopableObject((object) null); if (faker.IndexFaker % 1000 == 0) return new SnoopableObject(string.Empty); if (faker.IndexFaker % 700 == 0) return new SnoopableObject(faker.Make(150, () => faker.Internet.UserName())); - if (faker.IndexFaker % 500 == 0) return new SnoopableObject(typeof(Debug)); + if (faker.IndexFaker % 500 == 0) return new SnoopableObject(typeof(DateTime)); if (faker.IndexFaker % 200 == 0) return new SnoopableObject(faker.Lorem.Sentence()); if (faker.IndexFaker % 100 == 0) return new SnoopableObject(new Color(faker.Random.Byte(), faker.Random.Byte(), faker.Random.Byte())); if (faker.IndexFaker % 5 == 0) return new SnoopableObject(faker.Random.Int(0)); diff --git a/RevitLookup.UI.Demo/ViewModels/MoqSnoopViewModel.cs b/RevitLookup.UI.Demo/ViewModels/MoqSnoopViewModel.cs index a1901a43..e3f0dec4 100644 --- a/RevitLookup.UI.Demo/ViewModels/MoqSnoopViewModel.cs +++ b/RevitLookup.UI.Demo/ViewModels/MoqSnoopViewModel.cs @@ -41,13 +41,18 @@ public sealed partial class MoqSnoopViewModel(NotificationService notificationSe public SnoopableObject SelectedObject { get; set; } - public void Navigate(Descriptor selectedItem) + public void Navigate(SnoopableObject selectedItem) { - if (selectedItem.Value.Descriptor is not IDescriptorCollector) return; - if (selectedItem.Value.Descriptor is IDescriptorEnumerator {IsEmpty: true}) return; + Host.GetService() + .Snoop(selectedItem) + .DependsOn(provider) + .Show(); + } + public void Navigate(IReadOnlyCollection selectedItems) + { Host.GetService() - .Snoop(selectedItem.Value) + .Snoop(selectedItems) .DependsOn(provider) .Show(); } diff --git a/RevitLookup/Services/Contracts/ILookupService.cs b/RevitLookup/Services/Contracts/ILookupService.cs index 2d764eec..42f7b012 100644 --- a/RevitLookup/Services/Contracts/ILookupService.cs +++ b/RevitLookup/Services/Contracts/ILookupService.cs @@ -28,6 +28,7 @@ public interface ILookupService : ILookupServiceDependsStage, ILookupServiceShow { ILookupServiceDependsStage Snoop(SnoopableType snoopableType); ILookupServiceDependsStage Snoop(SnoopableObject snoopableObject); + ILookupServiceDependsStage Snoop(IReadOnlyCollection snoopableObjects); new ILookupServiceShowStage DependsOn(IServiceProvider provider); new ILookupServiceExecuteStage Show() where T : Page; } diff --git a/RevitLookup/Services/Contracts/ISnoopVisualService.cs b/RevitLookup/Services/Contracts/ISnoopVisualService.cs index f18ea483..adef81bb 100644 --- a/RevitLookup/Services/Contracts/ISnoopVisualService.cs +++ b/RevitLookup/Services/Contracts/ISnoopVisualService.cs @@ -26,5 +26,6 @@ namespace RevitLookup.Services.Contracts; public interface ISnoopVisualService { void Snoop(SnoopableObject snoopableObject); + void Snoop(IReadOnlyCollection snoopableObjects); Task SnoopAsync(SnoopableType snoopableType); } \ No newline at end of file diff --git a/RevitLookup/Services/LookupService.cs b/RevitLookup/Services/LookupService.cs index 1982aa9e..ebaaf807 100644 --- a/RevitLookup/Services/LookupService.cs +++ b/RevitLookup/Services/LookupService.cs @@ -47,6 +47,12 @@ public ILookupServiceDependsStage Snoop(SnoopableObject snoopableObject) return this; } + public ILookupServiceDependsStage Snoop(IReadOnlyCollection snoopableObjects) + { + _serviceProvider.GetService()!.Snoop(snoopableObjects); + return this; + } + public ILookupServiceShowStage DependsOn(IServiceProvider provider) { _owner = (Window) provider.GetService(); diff --git a/RevitLookup/Services/SnoopVisualService.cs b/RevitLookup/Services/SnoopVisualService.cs index 86889581..0a84c7a8 100644 --- a/RevitLookup/Services/SnoopVisualService.cs +++ b/RevitLookup/Services/SnoopVisualService.cs @@ -35,16 +35,17 @@ public void Snoop(SnoopableObject snoopableObject) { try { + IReadOnlyCollection snoopableObjects; if (snoopableObject.Descriptor is IDescriptorEnumerator {IsEmpty: false} descriptor) { - viewModel.SnoopableObjects = descriptor.ParseEnumerable(snoopableObject); + snoopableObjects = descriptor.ParseEnumerable(snoopableObject); } else { - viewModel.SnoopableObjects = new[] {snoopableObject}; + snoopableObjects = new[] {snoopableObject}; } - viewModel.SnoopableData = Array.Empty(); + Snoop(snoopableObjects); } catch (Exception exception) { @@ -52,6 +53,12 @@ public void Snoop(SnoopableObject snoopableObject) } } + public void Snoop(IReadOnlyCollection snoopableObjects) + { + viewModel.SnoopableObjects = snoopableObjects; + viewModel.SnoopableData = Array.Empty(); + } + public async Task SnoopAsync(SnoopableType snoopableType) { try @@ -67,8 +74,8 @@ public async Task SnoopAsync(SnoopableType snoopableType) break; } - viewModel.SnoopableObjects = await Application.ExternalElementHandler.RaiseAsync(_ => Selector.Snoop(snoopableType)); - viewModel.SnoopableData = Array.Empty(); + var snoopableObjects = await Application.ExternalElementHandler.RaiseAsync(_ => Selector.Snoop(snoopableType)); + Snoop(snoopableObjects); } catch (OperationCanceledException) { diff --git a/RevitLookup/ViewModels/Contracts/ISnoopViewModel.cs b/RevitLookup/ViewModels/Contracts/ISnoopViewModel.cs index b8773d41..5a9bc116 100644 --- a/RevitLookup/ViewModels/Contracts/ISnoopViewModel.cs +++ b/RevitLookup/ViewModels/Contracts/ISnoopViewModel.cs @@ -33,5 +33,6 @@ public interface ISnoopViewModel IAsyncRelayCommand RefreshMembersCommand { get; } public string SearchText { get; set; } public SnoopableObject SelectedObject { get; set; } - void Navigate(Descriptor selectedItem); + void Navigate(SnoopableObject selectedItem); + void Navigate(IReadOnlyCollection selectedItems); } \ No newline at end of file diff --git a/RevitLookup/ViewModels/Pages/SnoopViewModelBase.cs b/RevitLookup/ViewModels/Pages/SnoopViewModelBase.cs index 5c54a599..971f384c 100644 --- a/RevitLookup/ViewModels/Pages/SnoopViewModelBase.cs +++ b/RevitLookup/ViewModels/Pages/SnoopViewModelBase.cs @@ -43,13 +43,18 @@ public abstract partial class SnoopViewModelBase(NotificationService notificatio public SnoopableObject SelectedObject { get; set; } - public void Navigate(Descriptor selectedItem) + public void Navigate(SnoopableObject selectedItem) { - if (selectedItem.Value.Descriptor is not IDescriptorCollector) return; - if (selectedItem.Value.Descriptor is IDescriptorEnumerator {IsEmpty: true}) return; + Host.GetService() + .Snoop(selectedItem) + .DependsOn(provider) + .Show(); + } + public void Navigate(IReadOnlyCollection selectedItems) + { Host.GetService() - .Snoop(selectedItem.Value) + .Snoop(selectedItems) .DependsOn(provider) .Show(); } diff --git a/RevitLookup/Views/Pages/Abstraction/SnoopViewBase.ContextMenu.cs b/RevitLookup/Views/Pages/Abstraction/SnoopViewBase.ContextMenu.cs new file mode 100644 index 00000000..6b520148 --- /dev/null +++ b/RevitLookup/Views/Pages/Abstraction/SnoopViewBase.ContextMenu.cs @@ -0,0 +1,153 @@ +// Copyright 2003-2023 by Autodesk, Inc. +// +// Permission to use, copy, modify, and distribute this software in +// object code form for any purpose and without fee is hereby granted, +// provided that the above copyright notice appears in all copies and +// that both that copyright notice and the limited warranty and +// restricted rights notice below appear in all supporting +// documentation. +// +// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS. +// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF +// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC. +// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE +// UNINTERRUPTED OR ERROR FREE. +// +// Use, duplication, or disclosure by the U.S. Government is subject to +// restrictions set forth in FAR 52.227-19 (Commercial Computer +// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii) +// (Rights in Technical Data and Computer Software), as applicable. + +using System.Windows; +using System.Windows.Controls; +using System.Windows.Input; +using RevitLookup.Core.Contracts; +using RevitLookup.Core.Objects; +using RevitLookup.Views.Extensions; +using RevitLookup.Views.Utils; + +namespace RevitLookup.Views.Pages.Abstraction; + +public partial class SnoopViewBase +{ + private void CreateTreeContextMenu(Descriptor descriptor, FrameworkElement row) + { + var contextMenu = new ContextMenu + { + Resources = Resources + }; + + contextMenu.AddMenuItem("CopyMenuItem") + .SetCommand(descriptor, parameter => Clipboard.SetText(parameter.Name)) + .SetShortcut(row, ModifierKeys.Control, Key.C); + contextMenu.AddMenuItem("HelpMenuItem") + .SetCommand(descriptor, parameter => HelpUtils.ShowHelp(parameter.TypeFullName)) + .SetShortcut(row, Key.F1); + + if (descriptor is IDescriptorConnector connector) connector.RegisterMenu(contextMenu, row); + row.ContextMenu = contextMenu; + } + + private void CreateGridContextMenu(DataGrid dataGrid) + { + var contextMenu = new ContextMenu + { + Resources = Resources + }; + + contextMenu.AddMenuItem("RefreshMenuItem") + .SetCommand(ViewModel.RefreshMembersCommand) + .SetGestureText(Key.F5); + + contextMenu.AddSeparator(); + contextMenu.AddLabel("Columns"); + + contextMenu.AddMenuItem("CheckableMenuItem") + .SetHeader("Time") + .SetCommand(dataGrid.Columns[2].Visibility == Visibility.Visible, parameter => + { + dataGrid.Columns[2].Visibility = parameter ? Visibility.Collapsed : Visibility.Visible; + _settingsService.ShowTimeColumn = !parameter; + }); + + contextMenu.AddSeparator(); + contextMenu.AddLabel("Show"); + + contextMenu.AddMenuItem("CheckableMenuItem") + .SetHeader("Events") + .SetCommand(_settingsService.IncludeEvents, parameter => + { + _settingsService.IncludeEvents = !parameter; + return RefreshGridAsync(); + }); + contextMenu.AddMenuItem("CheckableMenuItem") + .SetHeader("Extensions") + .SetCommand(_settingsService.IncludeExtensions, parameter => + { + _settingsService.IncludeExtensions = !parameter; + return RefreshGridAsync(); + }); + contextMenu.AddMenuItem("CheckableMenuItem") + .SetHeader("Fields") + .SetCommand(_settingsService.IncludeFields, parameter => + { + _settingsService.IncludeFields = !parameter; + return RefreshGridAsync(); + }); + contextMenu.AddMenuItem("CheckableMenuItem") + .SetHeader("Non-public") + .SetCommand(_settingsService.IncludePrivate, parameter => + { + _settingsService.IncludePrivate = !parameter; + return RefreshGridAsync(); + }); + contextMenu.AddMenuItem("CheckableMenuItem") + .SetHeader("Root hierarchy") + .SetCommand(_settingsService.IncludeRootHierarchy, parameter => + { + _settingsService.IncludeRootHierarchy = !parameter; + return RefreshGridAsync(); + }); + contextMenu.AddMenuItem("CheckableMenuItem") + .SetHeader("Static") + .SetCommand(_settingsService.IncludeStatic, parameter => + { + _settingsService.IncludeStatic = !parameter; + return RefreshGridAsync(); + }); + contextMenu.AddMenuItem("CheckableMenuItem") + .SetHeader("Unsupported") + .SetCommand(_settingsService.IncludeUnsupported, parameter => + { + _settingsService.IncludeUnsupported = !parameter; + return RefreshGridAsync(); + }); + + dataGrid.ContextMenu = contextMenu; + } + + private void CreateGridRowContextMenu(Descriptor descriptor, FrameworkElement row) + { + var contextMenu = new ContextMenu + { + Resources = Resources + }; + + contextMenu.AddMenuItem("CopyMenuItem") + .SetCommand(descriptor, parameter => Clipboard.SetText($"{parameter.Name}: {parameter.Value.Descriptor.Name}")) + .SetShortcut(row, ModifierKeys.Control, Key.C); + + contextMenu.AddMenuItem("CopyMenuItem") + .SetHeader("Copy value") + .SetCommand(descriptor, parameter => Clipboard.SetText(parameter.Value.Descriptor.Name)) + .SetShortcut(row, ModifierKeys.Control | ModifierKeys.Shift, Key.C) + .SetAvailability(descriptor.Value.Descriptor.Name is not null); + + contextMenu.AddMenuItem("HelpMenuItem") + .SetCommand(descriptor, parameter => HelpUtils.ShowHelp(parameter.TypeFullName, parameter.Name)) + .SetShortcut(row, new KeyGesture(Key.F1)); + + if (descriptor.Value.Descriptor is IDescriptorConnector connector) connector.RegisterMenu(contextMenu, row); + row.ContextMenu = contextMenu; + } +} \ No newline at end of file diff --git a/RevitLookup/Views/Pages/Abstraction/SnoopViewBase.Gestures.cs b/RevitLookup/Views/Pages/Abstraction/SnoopViewBase.Gestures.cs new file mode 100644 index 00000000..9dbd5fbd --- /dev/null +++ b/RevitLookup/Views/Pages/Abstraction/SnoopViewBase.Gestures.cs @@ -0,0 +1,40 @@ +// Copyright 2003-2023 by Autodesk, Inc. +// +// Permission to use, copy, modify, and distribute this software in +// object code form for any purpose and without fee is hereby granted, +// provided that the above copyright notice appears in all copies and +// that both that copyright notice and the limited warranty and +// restricted rights notice below appear in all supporting +// documentation. +// +// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS. +// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF +// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC. +// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE +// UNINTERRUPTED OR ERROR FREE. +// +// Use, duplication, or disclosure by the U.S. Government is subject to +// restrictions set forth in FAR 52.227-19 (Commercial Computer +// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii) +// (Rights in Technical Data and Computer Software), as applicable. + +using System.Windows.Input; +using CommunityToolkit.Mvvm.Input; + +namespace RevitLookup.Views.Pages.Abstraction; + +public partial class SnoopViewBase +{ + private void AddShortcuts() + { + var command = new AsyncRelayCommand(() => ViewModel.RefreshMembersCommand.ExecuteAsync(null)); + InputBindings.Add(new KeyBinding(command, new KeyGesture(Key.F5))); + } + + private void OnPageKeyPressed(object sender, KeyEventArgs e) + { + if (SearchBoxControl.IsKeyboardFocused) return; + if (e.KeyboardDevice.Modifiers != ModifierKeys.None) return; + if (e.Key is >= Key.D0 and <= Key.Z or >= Key.NumPad0 and <= Key.NumPad9) SearchBoxControl.Focus(); + } +} \ No newline at end of file diff --git a/RevitLookup/Views/Pages/Abstraction/SnoopViewBase.Navigation.cs b/RevitLookup/Views/Pages/Abstraction/SnoopViewBase.Navigation.cs new file mode 100644 index 00000000..0704fa5e --- /dev/null +++ b/RevitLookup/Views/Pages/Abstraction/SnoopViewBase.Navigation.cs @@ -0,0 +1,121 @@ +// Copyright 2003-2023 by Autodesk, Inc. +// +// Permission to use, copy, modify, and distribute this software in +// object code form for any purpose and without fee is hereby granted, +// provided that the above copyright notice appears in all copies and +// that both that copyright notice and the limited warranty and +// restricted rights notice below appear in all supporting +// documentation. +// +// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS. +// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF +// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC. +// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE +// UNINTERRUPTED OR ERROR FREE. +// +// Use, duplication, or disclosure by the U.S. Government is subject to +// restrictions set forth in FAR 52.227-19 (Commercial Computer +// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii) +// (Rights in Technical Data and Computer Software), as applicable. + +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Input; +using RevitLookup.Core.Contracts; +using RevitLookup.Core.Objects; +using RevitLookup.Views.Utils; + +namespace RevitLookup.Views.Pages.Abstraction; + +public partial class SnoopViewBase +{ + /// + /// Execute collector for selection + /// + protected void OnTreeItemSelected(object sender, RoutedPropertyChangedEventArgs e) + { + switch (e.NewValue) + { + case SnoopableObject snoopableObject: + ViewModel.SelectedObject = snoopableObject; + break; + case CollectionViewGroup: + ViewModel.SelectedObject = null; + break; + default: + //Internal __Canon object + return; + } + + ViewModel.FetchMembersCommand.Execute(null); + } + + /// + /// Navigate selection in new window + /// + protected void OnGridRowClicked(object sender, RoutedEventArgs routedEventArgs) + { + var selectedItem = (Descriptor) DataGridControl.SelectedItem; + + if ((Keyboard.Modifiers & ModifierKeys.Control) == 0) + { + if (selectedItem.Value.Descriptor is not IDescriptorCollector) return; + if (selectedItem.Value.Descriptor is IDescriptorEnumerator {IsEmpty: true}) return; + } + + ViewModel.Navigate(selectedItem.Value); + } + + /// + /// Navigate selection in new window + /// + protected void OnTreeItemClicked(object sender, RoutedEventArgs args) + { + if ((Keyboard.Modifiers & ModifierKeys.Control) == 0) return; + args.Handled = true; + + switch (TreeViewControl.SelectedItem) + { + case SnoopableObject item: + ViewModel.Navigate(item); + break; + case CollectionViewGroup group: + ViewModel.Navigate(group.Items.Cast().ToArray()); + break; + } + } + + protected void OnPresenterCursorInteracted(object sender, MouseEventArgs args) + { + var presenter = (FrameworkElement) sender; + if ((Keyboard.Modifiers & ModifierKeys.Control) == 0) + { + presenter.Cursor = null; + return; + } + + FrameworkElement item = sender switch + { + DataGrid => VisualUtils.FindVisualParent((DependencyObject) args.OriginalSource), + TreeView => VisualUtils.FindVisualParent((DependencyObject) args.OriginalSource), + _ => throw new NotSupportedException() + }; + + if (item is null) + { + presenter.Cursor = null; + return; + } + + presenter.Cursor = Cursors.Hand; + presenter.PreviewKeyUp += OnPresenterCursorRestored; + } + + private void OnPresenterCursorRestored(object sender, KeyEventArgs e) + { + var presenter = (FrameworkElement) sender; + presenter.PreviewKeyUp -= OnPresenterCursorRestored; + presenter.Cursor = null; + } +} \ No newline at end of file diff --git a/RevitLookup/Views/Pages/Abstraction/SnoopViewBase.ToolTips.cs b/RevitLookup/Views/Pages/Abstraction/SnoopViewBase.ToolTips.cs new file mode 100644 index 00000000..75223ba2 --- /dev/null +++ b/RevitLookup/Views/Pages/Abstraction/SnoopViewBase.ToolTips.cs @@ -0,0 +1,78 @@ +// Copyright 2003-2023 by Autodesk, Inc. +// +// Permission to use, copy, modify, and distribute this software in +// object code form for any purpose and without fee is hereby granted, +// provided that the above copyright notice appears in all copies and +// that both that copyright notice and the limited warranty and +// restricted rights notice below appear in all supporting +// documentation. +// +// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS. +// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF +// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC. +// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE +// UNINTERRUPTED OR ERROR FREE. +// +// Use, duplication, or disclosure by the U.S. Government is subject to +// restrictions set forth in FAR 52.227-19 (Commercial Computer +// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii) +// (Rights in Technical Data and Computer Software), as applicable. + +using System.Text; +using System.Windows; +using System.Windows.Controls; +using RevitLookup.Core.Enums; +using RevitLookup.Core.Objects; + +namespace RevitLookup.Views.Pages.Abstraction; + +public partial class SnoopViewBase +{ + private void CreateTreeTooltip(Descriptor descriptor, FrameworkElement row) + { + row.ToolTip = new ToolTip + { + Content = new StringBuilder() + .Append("Type: ") + .AppendLine(descriptor.Type) + .Append("Value: ") + .Append(descriptor.Name) + .ToString() + }; + } + + private void CreateGridRowTooltip(Descriptor descriptor, FrameworkElement row) + { + var builder = new StringBuilder(); + + if ((descriptor.MemberAttributes & MemberAttributes.Private) != 0) builder.Append("Private "); + if ((descriptor.MemberAttributes & MemberAttributes.Static) != 0) builder.Append("Static "); + if ((descriptor.MemberAttributes & MemberAttributes.Property) != 0) builder.Append("Property: "); + if ((descriptor.MemberAttributes & MemberAttributes.Extension) != 0) builder.Append("Extension: "); + if ((descriptor.MemberAttributes & MemberAttributes.Method) != 0) builder.Append("Method: "); + if ((descriptor.MemberAttributes & MemberAttributes.Event) != 0) builder.Append("Event: "); + if ((descriptor.MemberAttributes & MemberAttributes.Field) != 0) builder.Append("Field: "); + + builder.AppendLine(descriptor.Name) + .Append("Type: ") + .AppendLine(descriptor.Value.Descriptor.Type) + .Append("Value: ") + .Append(descriptor.Value.Descriptor.Name); + + if (descriptor.Value.Descriptor.Description is not null) + builder.AppendLine() + .Append("Description: ") + .Append(descriptor.Value.Descriptor.Description); + + if (descriptor.ComputationTime > 0) + builder.AppendLine() + .Append("Time: ") + .Append(descriptor.ComputationTime) + .Append(" ms"); + + row.ToolTip = new ToolTip + { + Content = builder.ToString() + }; + } +} \ No newline at end of file diff --git a/RevitLookup/Views/Pages/Abstraction/SnoopViewBase.xaml.cs b/RevitLookup/Views/Pages/Abstraction/SnoopViewBase.xaml.cs new file mode 100644 index 00000000..bca63a9f --- /dev/null +++ b/RevitLookup/Views/Pages/Abstraction/SnoopViewBase.xaml.cs @@ -0,0 +1,196 @@ +// Copyright 2003-2023 by Autodesk, Inc. +// +// Permission to use, copy, modify, and distribute this software in +// object code form for any purpose and without fee is hereby granted, +// provided that the above copyright notice appears in all copies and +// that both that copyright notice and the limited warranty and +// restricted rights notice below appear in all supporting +// documentation. +// +// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS. +// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF +// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC. +// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE +// UNINTERRUPTED OR ERROR FREE. +// +// Use, duplication, or disclosure by the U.S. Government is subject to +// restrictions set forth in FAR 52.227-19 (Commercial Computer +// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii) +// (Rights in Technical Data and Computer Software), as applicable. + +using System.Collections; +using System.ComponentModel; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using RevitLookup.Core.Objects; +using RevitLookup.Services.Contracts; +using RevitLookup.ViewModels.Contracts; +using RevitLookup.Views.Utils; +using Wpf.Ui.Controls; +using DataGrid = Wpf.Ui.Controls.DataGrid; +using TreeView = Wpf.Ui.Controls.TreeView; +using TreeViewItem = System.Windows.Controls.TreeViewItem; + +namespace RevitLookup.Views.Pages.Abstraction; + +public partial class SnoopViewBase : Page, INavigableView, INavigationAware +{ + private readonly ISettingsService _settingsService; + private readonly DataGrid _dataGridControl; + + protected SnoopViewBase(ISettingsService settingsService) + { + _settingsService = settingsService; + AddShortcuts(); + } + + protected UIElement SearchBoxControl { get; init; } + protected TreeView TreeViewControl { get; init; } + + protected DataGrid DataGridControl + { + get => _dataGridControl; + init + { + _dataGridControl = value; + OnDataGridChanged(value); + } + } + + public ISnoopViewModel ViewModel { get; protected init; } + + protected void OnTreeSourceChanged(object sender, IEnumerable enumerable) + { + if (IsLoaded) + { + SetupTreeView(); + return; + } + + Loaded += OnLoaded; + return; + + void OnLoaded(object o, RoutedEventArgs args) + { + Loaded -= OnLoaded; + SetupTreeView(); + } + } + + private async void OnDataGridChanged(DataGrid control) + { + //Lazy init. 1 ms is enough to display data and start initialising components + await Task.Delay(1); + + ValidateTimeColumn(control); + CreateGridContextMenu(control); + control.ItemsSourceChanged += OnGridItemsSourceChanged; + } + + private void OnGridItemsSourceChanged(object sender, EventArgs _) + { + var dataGrid = (DataGrid) sender; + + //Clear shapingStorage for remove duplications. WpfBug? + dataGrid.Items.GroupDescriptions!.Clear(); + dataGrid.Items.SortDescriptions.Add(new SortDescription(nameof(Descriptor.Depth), ListSortDirection.Descending)); + dataGrid.Items.SortDescriptions.Add(new SortDescription(nameof(Descriptor.MemberAttributes), ListSortDirection.Ascending)); + dataGrid.Items.SortDescriptions.Add(new SortDescription(nameof(Descriptor.Name), ListSortDirection.Ascending)); + dataGrid.Items.GroupDescriptions.Add(new PropertyGroupDescription(nameof(Descriptor.Type))); + } + + public void OnNavigatedTo() + { + Wpf.Ui.Application.MainWindow.PreviewKeyDown += OnPageKeyPressed; + } + + public void OnNavigatedFrom() + { + Wpf.Ui.Application.MainWindow.PreviewKeyDown -= OnPageKeyPressed; + } + + /// + /// Enable navigation + /// + protected async void OnHeaderLoaded(object sender, RoutedEventArgs routedEventArgs) + { + //Lazy init. 1 ms is enough to display data and start initialising components + await Task.Delay(1); + + var treeItem = VisualUtils.FindVisualParent((DependencyObject) sender); + treeItem.PreviewMouseLeftButtonUp += OnTreeItemClicked; + } + + /// + /// Create tooltip, menu + /// + protected async void OnRowLoaded(object sender, RoutedEventArgs routedEventArgs) + { + //Lazy init. 1 ms is enough to display data and start initialising components + await Task.Delay(1); + + var element = (FrameworkElement) sender; + switch (element.DataContext) + { + case SnoopableObject context: + var treeItem = VisualUtils.FindVisualParent((DependencyObject) sender); + CreateTreeTooltip(context.Descriptor, treeItem); + CreateTreeContextMenu(context.Descriptor, treeItem); + break; + case Descriptor context: + CreateGridRowTooltip(context, element); + CreateGridRowContextMenu(context, element); + break; + default: + return; + } + } + + private async void SetupTreeView() + { + // Await Frame transition. GetMembers freezes the thread and breaks the animation + await Task.Delay(_settingsService.TransitionDuration); + + // if (TryRestoreSelection()) return; + + ExpandFirstGroup(); + } + + [Obsolete("Low performance")] + private bool TryRestoreSelection() + { + if (ViewModel.SelectedObject is null) return false; + + var treeViewItem = VisualUtils.GetTreeViewItem(TreeViewControl, ViewModel.SelectedObject); + if (treeViewItem is null || treeViewItem.IsSelected) return false; + + TreeViewControl.SelectedItemChanged -= OnTreeItemSelected; + treeViewItem.IsSelected = true; + TreeViewControl.SelectedItemChanged += OnTreeItemSelected; + return true; + } + + private void ExpandFirstGroup() + { + if (TreeViewControl.Items.Count > 3) return; + + var rootItem = VisualUtils.GetTreeViewItem(TreeViewControl, 0); + if (rootItem is null) return; + + var nestedItem = VisualUtils.GetTreeViewItem(rootItem, 0); + if (nestedItem is null) return; + + nestedItem.IsSelected = true; + } + + private void ValidateTimeColumn(System.Windows.Controls.DataGrid control) + { + control.Columns[2].Visibility = _settingsService.ShowTimeColumn ? Visibility.Visible : Visibility.Collapsed; + } + + private Task RefreshGridAsync() + { + return ViewModel.RefreshMembersCommand.ExecuteAsync(null); + } +} \ No newline at end of file diff --git a/RevitLookup/Views/Pages/EventsView.xaml b/RevitLookup/Views/Pages/EventsView.xaml index dec83d89..9434b752 100644 --- a/RevitLookup/Views/Pages/EventsView.xaml +++ b/RevitLookup/Views/Pages/EventsView.xaml @@ -1,4 +1,4 @@ - + ItemsSource="{Binding ViewModel.FilteredSnoopableData}" + MouseMove="OnPresenterCursorInteracted"> + Handler="OnGridRowClicked" /> @@ -240,4 +243,4 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/RevitLookup/Views/Pages/EventsView.xaml.cs b/RevitLookup/Views/Pages/EventsView.xaml.cs index 7cccebec..da3065a0 100644 --- a/RevitLookup/Views/Pages/EventsView.xaml.cs +++ b/RevitLookup/Views/Pages/EventsView.xaml.cs @@ -26,16 +26,16 @@ namespace RevitLookup.Views.Pages; public sealed partial class EventsView { - public EventsView(IServiceProvider serviceProvider, ISettingsService settingsService) : base(settingsService) + public EventsView(EventsViewModel viewModel, ISettingsService settingsService) : base(settingsService) { InitializeComponent(); DataGridControl = DataGrid; TreeViewControl = TreeView; SearchBoxControl = SearchBox; - TreeView.SelectedItemChanged += OnTreeSelectionChanged; + TreeView.SelectedItemChanged += OnTreeItemSelected; - ViewModel = (ISnoopViewModel) serviceProvider.GetService(typeof(EventsViewModel)); + ViewModel = viewModel; DataContext = this; } } \ No newline at end of file diff --git a/RevitLookup/Views/Pages/SnoopView.xaml b/RevitLookup/Views/Pages/SnoopView.xaml index 61dcc42f..f8748e4c 100644 --- a/RevitLookup/Views/Pages/SnoopView.xaml +++ b/RevitLookup/Views/Pages/SnoopView.xaml @@ -1,4 +1,4 @@ - + Converter={converters:TreeViewGroupConverter}}" + MouseMove="OnPresenterCursorInteracted"> @@ -99,13 +102,15 @@ ColumnHeaderHeight="30" HeadersVisibility="Column" AutoGenerateColumns="False" + SelectionMode="Single" CanUserAddRows="False" CanUserDeleteRows="False" CanUserResizeRows="False" CanUserSortColumns="False" CanUserReorderColumns="False" HorizontalScrollBarVisibility="Disabled" - ItemsSource="{Binding ViewModel.FilteredSnoopableData}"> + ItemsSource="{Binding ViewModel.FilteredSnoopableData}" + MouseMove="OnPresenterCursorInteracted"> + Handler="OnGridRowClicked" /> @@ -249,4 +254,4 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/RevitLookup/Views/Pages/SnoopView.xaml.cs b/RevitLookup/Views/Pages/SnoopView.xaml.cs index d10d4305..65aa8dd8 100644 --- a/RevitLookup/Views/Pages/SnoopView.xaml.cs +++ b/RevitLookup/Views/Pages/SnoopView.xaml.cs @@ -32,7 +32,7 @@ public SnoopView(ISettingsService settingsService, ISnoopViewModel viewModel) : DataGridControl = DataGrid; TreeViewControl = TreeView; SearchBoxControl = SearchBox; - TreeView.SelectedItemChanged += OnTreeSelectionChanged; + TreeView.SelectedItemChanged += OnTreeItemSelected; TreeView.ItemsSourceChanged += OnTreeSourceChanged; ViewModel = viewModel; diff --git a/RevitLookup/Views/Pages/SnoopViewBase.xaml.cs b/RevitLookup/Views/Pages/SnoopViewBase.xaml.cs deleted file mode 100644 index 81f49db7..00000000 --- a/RevitLookup/Views/Pages/SnoopViewBase.xaml.cs +++ /dev/null @@ -1,404 +0,0 @@ -// Copyright 2003-2023 by Autodesk, Inc. -// -// Permission to use, copy, modify, and distribute this software in -// object code form for any purpose and without fee is hereby granted, -// provided that the above copyright notice appears in all copies and -// that both that copyright notice and the limited warranty and -// restricted rights notice below appear in all supporting -// documentation. -// -// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS. -// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF -// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC. -// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE -// UNINTERRUPTED OR ERROR FREE. -// -// Use, duplication, or disclosure by the U.S. Government is subject to -// restrictions set forth in FAR 52.227-19 (Commercial Computer -// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii) -// (Rights in Technical Data and Computer Software), as applicable. - -using System.Collections; -using System.ComponentModel; -using System.Text; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Data; -using System.Windows.Input; -using CommunityToolkit.Mvvm.Input; -using RevitLookup.Core.Contracts; -using RevitLookup.Core.Enums; -using RevitLookup.Core.Objects; -using RevitLookup.Services.Contracts; -using RevitLookup.ViewModels.Contracts; -using RevitLookup.Views.Extensions; -using RevitLookup.Views.Utils; -using Wpf.Ui.Controls; -using DataGrid = Wpf.Ui.Controls.DataGrid; -using TreeView = Wpf.Ui.Controls.TreeView; -using TreeViewItem = System.Windows.Controls.TreeViewItem; - -namespace RevitLookup.Views.Pages; - -public class SnoopViewBase : Page, INavigableView, INavigationAware -{ - private readonly ISettingsService _settingsService; - private readonly DataGrid _dataGridControl; - - protected SnoopViewBase(ISettingsService settingsService) - { - _settingsService = settingsService; - AddShortcuts(); - } - - protected UIElement SearchBoxControl { get; init; } - protected TreeView TreeViewControl { get; init; } - - protected DataGrid DataGridControl - { - get => _dataGridControl; - init - { - _dataGridControl = value; - OnDataGridChanged(value); - } - } - - public ISnoopViewModel ViewModel { get; protected init; } - - protected void OnTreeSourceChanged(object sender, IEnumerable enumerable) - { - if (IsLoaded) - { - SetupTreeView(); - return; - } - - Loaded += OnLoaded; - - void OnLoaded(object o, RoutedEventArgs args) - { - Loaded -= OnLoaded; - SetupTreeView(); - } - } - - /// - /// Execute collector for selection - /// - protected void OnTreeSelectionChanged(object sender, RoutedPropertyChangedEventArgs e) - { - switch (e.NewValue) - { - case SnoopableObject snoopableObject: - ViewModel.SelectedObject = snoopableObject; - break; - case CollectionViewGroup: - ViewModel.SelectedObject = null; - break; - default: - //Internal __Canon object - return; - } - - ViewModel.FetchMembersCommand.Execute(null); - } - - private async void OnDataGridChanged(DataGrid control) - { - //Lazy init. 1 ms is enough to display data and start initialising components - await Task.Delay(1); - - ValidateTimeColumn(control); - CreateGridContextMenu(control); - control.ItemsSourceChanged += OnGridItemsSourceChanged; - } - - private void OnGridItemsSourceChanged(object sender, EventArgs _) - { - var dataGrid = (DataGrid) sender; - - //Clear shapingStorage for remove duplications. WpfBug? - dataGrid.Items.GroupDescriptions!.Clear(); - dataGrid.Items.SortDescriptions.Add(new SortDescription(nameof(Descriptor.Depth), ListSortDirection.Descending)); - dataGrid.Items.SortDescriptions.Add(new SortDescription(nameof(Descriptor.MemberAttributes), ListSortDirection.Ascending)); - dataGrid.Items.SortDescriptions.Add(new SortDescription(nameof(Descriptor.Name), ListSortDirection.Ascending)); - dataGrid.Items.GroupDescriptions.Add(new PropertyGroupDescription(nameof(Descriptor.Type))); - } - - /// - /// Navigate selection in new window - /// - protected void OnGridMouseLeftButtonUp(object sender, RoutedEventArgs routedEventArgs) - { - if (DataGridControl.SelectedItems.Count != 1) return; - ViewModel.Navigate((Descriptor) DataGridControl.SelectedItem); - } - - /// - /// Create tooltip, menu - /// - protected async void OnRowLoaded(object sender, RoutedEventArgs routedEventArgs) - { - //Lazy init. 1 ms is enough to display data and start initialising components - await Task.Delay(1); - - var element = (FrameworkElement) sender; - Descriptor descriptor; - switch (element.DataContext) - { - case SnoopableObject context: - descriptor = context.Descriptor; - var treeItem = VisualUtils.FindVisualParent((DependencyObject) sender); - CreateTreeTooltip(descriptor, treeItem); - CreateTreeContextMenu(descriptor, treeItem); - break; - case Descriptor context: - descriptor = context; - CreateGridRowTooltip(descriptor, element); - CreateGridRowContextMenu(descriptor, element); - break; - default: - return; - } - } - - public void OnNavigatedTo() - { - Wpf.Ui.Application.MainWindow.PreviewKeyDown += OnKeyPressed; - } - - public void OnNavigatedFrom() - { - Wpf.Ui.Application.MainWindow.PreviewKeyDown -= OnKeyPressed; - } - - private void OnKeyPressed(object sender, KeyEventArgs e) - { - if (SearchBoxControl.IsKeyboardFocused) return; - if (e.KeyboardDevice.Modifiers != ModifierKeys.None) return; - if (e.Key is >= Key.D0 and <= Key.Z or >= Key.NumPad0 and <= Key.NumPad9) SearchBoxControl.Focus(); - } - - private void CreateTreeTooltip(Descriptor descriptor, FrameworkElement row) - { - row.ToolTip = new ToolTip - { - Content = new StringBuilder() - .Append("Type: ") - .AppendLine(descriptor.Type) - .Append("Value: ") - .Append(descriptor.Name) - .ToString() - }; - } - - private void CreateGridRowTooltip(Descriptor descriptor, FrameworkElement row) - { - var builder = new StringBuilder(); - - if ((descriptor.MemberAttributes & MemberAttributes.Private) != 0) builder.Append("Private "); - if ((descriptor.MemberAttributes & MemberAttributes.Static) != 0) builder.Append("Static "); - if ((descriptor.MemberAttributes & MemberAttributes.Property) != 0) builder.Append("Property: "); - if ((descriptor.MemberAttributes & MemberAttributes.Extension) != 0) builder.Append("Extension: "); - if ((descriptor.MemberAttributes & MemberAttributes.Method) != 0) builder.Append("Method: "); - if ((descriptor.MemberAttributes & MemberAttributes.Event) != 0) builder.Append("Event: "); - if ((descriptor.MemberAttributes & MemberAttributes.Field) != 0) builder.Append("Field: "); - - builder.AppendLine(descriptor.Name) - .Append("Type: ") - .AppendLine(descriptor.Value.Descriptor.Type) - .Append("Value: ") - .Append(descriptor.Value.Descriptor.Name); - - if (descriptor.Value.Descriptor.Description is not null) - builder.AppendLine() - .Append("Description: ") - .Append(descriptor.Value.Descriptor.Description); - - if (descriptor.ComputationTime > 0) - builder.AppendLine() - .Append("Time: ") - .Append(descriptor.ComputationTime) - .Append(" ms"); - - row.ToolTip = new ToolTip - { - Content = builder.ToString() - }; - } - - private void CreateTreeContextMenu(Descriptor descriptor, FrameworkElement row) - { - var contextMenu = new ContextMenu - { - Resources = Resources - }; - - contextMenu.AddMenuItem("CopyMenuItem") - .SetCommand(descriptor, parameter => Clipboard.SetText(parameter.Name)) - .SetShortcut(row, ModifierKeys.Control, Key.C); - contextMenu.AddMenuItem("HelpMenuItem") - .SetCommand(descriptor, parameter => HelpUtils.ShowHelp(parameter.TypeFullName)) - .SetShortcut(row, Key.F1); - - if (descriptor is IDescriptorConnector connector) connector.RegisterMenu(contextMenu, row); - row.ContextMenu = contextMenu; - } - - private void CreateGridContextMenu(DataGrid dataGrid) - { - var contextMenu = new ContextMenu - { - Resources = Resources - }; - - contextMenu.AddMenuItem("RefreshMenuItem") - .SetCommand(ViewModel.RefreshMembersCommand) - .SetGestureText(Key.F5); - - contextMenu.AddSeparator(); - contextMenu.AddLabel("Columns"); - - contextMenu.AddMenuItem("CheckableMenuItem") - .SetHeader("Time") - .SetCommand(dataGrid.Columns[2].Visibility == Visibility.Visible, parameter => - { - dataGrid.Columns[2].Visibility = parameter ? Visibility.Collapsed : Visibility.Visible; - _settingsService.ShowTimeColumn = !parameter; - }); - - contextMenu.AddSeparator(); - contextMenu.AddLabel("Show"); - - contextMenu.AddMenuItem("CheckableMenuItem") - .SetHeader("Events") - .SetCommand(_settingsService.IncludeEvents, parameter => - { - _settingsService.IncludeEvents = !parameter; - return GetRefreshGridTask(); - }); - contextMenu.AddMenuItem("CheckableMenuItem") - .SetHeader("Extensions") - .SetCommand(_settingsService.IncludeExtensions, parameter => - { - _settingsService.IncludeExtensions = !parameter; - return GetRefreshGridTask(); - }); - contextMenu.AddMenuItem("CheckableMenuItem") - .SetHeader("Fields") - .SetCommand(_settingsService.IncludeFields, parameter => - { - _settingsService.IncludeFields = !parameter; - return GetRefreshGridTask(); - }); - contextMenu.AddMenuItem("CheckableMenuItem") - .SetHeader("Non-public") - .SetCommand(_settingsService.IncludePrivate, parameter => - { - _settingsService.IncludePrivate = !parameter; - return GetRefreshGridTask(); - }); - contextMenu.AddMenuItem("CheckableMenuItem") - .SetHeader("Root hierarchy") - .SetCommand(_settingsService.IncludeRootHierarchy, parameter => - { - _settingsService.IncludeRootHierarchy = !parameter; - return GetRefreshGridTask(); - }); - contextMenu.AddMenuItem("CheckableMenuItem") - .SetHeader("Static") - .SetCommand(_settingsService.IncludeStatic, parameter => - { - _settingsService.IncludeStatic = !parameter; - return GetRefreshGridTask(); - }); - contextMenu.AddMenuItem("CheckableMenuItem") - .SetHeader("Unsupported") - .SetCommand(_settingsService.IncludeUnsupported, parameter => - { - _settingsService.IncludeUnsupported = !parameter; - return GetRefreshGridTask(); - }); - - dataGrid.ContextMenu = contextMenu; - } - - private void CreateGridRowContextMenu(Descriptor descriptor, FrameworkElement row) - { - var contextMenu = new ContextMenu - { - Resources = Resources - }; - - contextMenu.AddMenuItem("CopyMenuItem") - .SetCommand(descriptor, parameter => Clipboard.SetText($"{parameter.Name}: {parameter.Value.Descriptor.Name}")) - .SetShortcut(row, ModifierKeys.Control, Key.C); - - contextMenu.AddMenuItem("CopyMenuItem") - .SetHeader("Copy value") - .SetCommand(descriptor, parameter => Clipboard.SetText(parameter.Value.Descriptor.Name)) - .SetShortcut(row, ModifierKeys.Control | ModifierKeys.Shift, Key.C) - .SetAvailability(descriptor.Value.Descriptor.Name is not null); - - contextMenu.AddMenuItem("HelpMenuItem") - .SetCommand(descriptor, parameter => HelpUtils.ShowHelp(parameter.TypeFullName, parameter.Name)) - .SetShortcut(row, new KeyGesture(Key.F1)); - - if (descriptor.Value.Descriptor is IDescriptorConnector connector) connector.RegisterMenu(contextMenu, row); - row.ContextMenu = contextMenu; - } - - private Task GetRefreshGridTask() - { - return ViewModel.RefreshMembersCommand.ExecuteAsync(null); - } - - private async void SetupTreeView() - { - // Await Frame transition. GetMembers freezes the thread and breaks the animation - await Task.Delay(_settingsService.TransitionDuration); - - // if (TryRestoreSelection()) return; - - ExpandFirstGroup(); - } - - [Obsolete("Low performance")] - private bool TryRestoreSelection() - { - if (ViewModel.SelectedObject is null) return false; - - var treeViewItem = VisualUtils.GetTreeViewItem(TreeViewControl, ViewModel.SelectedObject); - if (treeViewItem is null || treeViewItem.IsSelected) return false; - - TreeViewControl.SelectedItemChanged -= OnTreeSelectionChanged; - treeViewItem.IsSelected = true; - TreeViewControl.SelectedItemChanged += OnTreeSelectionChanged; - return true; - } - - private void ExpandFirstGroup() - { - if (TreeViewControl.Items.Count > 3) return; - - var rootItem = VisualUtils.GetTreeViewItem(TreeViewControl, 0); - if (rootItem is null) return; - - var nestedItem = VisualUtils.GetTreeViewItem(rootItem, 0); - if (nestedItem is null) return; - - nestedItem.IsSelected = true; - } - - private void ValidateTimeColumn(System.Windows.Controls.DataGrid control) - { - control.Columns[2].Visibility = _settingsService.ShowTimeColumn ? Visibility.Visible : Visibility.Collapsed; - } - - private void AddShortcuts() - { - var command = new AsyncRelayCommand(() => ViewModel.RefreshMembersCommand.ExecuteAsync(null)); - InputBindings.Add(new KeyBinding(command, new KeyGesture(Key.F5))); - } -} \ No newline at end of file