diff --git a/source/RevitLookup.Abstractions/Services/Application/IRevitLookupUiService.cs b/source/RevitLookup.Abstractions/Services/Application/IRevitLookupUiService.cs index d64830bd..06fb500d 100644 --- a/source/RevitLookup.Abstractions/Services/Application/IRevitLookupUiService.cs +++ b/source/RevitLookup.Abstractions/Services/Application/IRevitLookupUiService.cs @@ -1,23 +1,29 @@ using System.Collections; -using System.Windows; using System.Windows.Controls; using RevitLookup.Abstractions.Models.Decomposition; using RevitLookup.Abstractions.ObservableModels.Decomposition; namespace RevitLookup.Abstractions.Services.Application; -public interface IRevitLookupUiService : ILookupServiceDependsStage, ILookupServiceRunStage +public interface IRevitLookupUiService : ILookupServiceHistoryStage, ILookupServiceDecomposeStage, ILookupServiceShowStage; + +public interface ILookupServiceHistoryStage +{ + ILookupServiceParentStage AddParent(IServiceProvider serviceProvider); +} + +public interface ILookupServiceParentStage : ILookupServiceDecomposeStage { - ILookupServiceDependsStage Decompose(KnownDecompositionObject decompositionObject); - ILookupServiceDependsStage Decompose(object? obj); - ILookupServiceDependsStage Decompose(IEnumerable objects); - ILookupServiceDependsStage Decompose(ObservableDecomposedObject decomposedObject); - ILookupServiceDependsStage Decompose(List decomposedObjects); + ILookupServiceDecomposeStage AddStackHistory(ObservableDecomposedObject item); } -public interface ILookupServiceDependsStage : ILookupServiceShowStage +public interface ILookupServiceDecomposeStage { - ILookupServiceShowStage DependsOn(Window parent); + ILookupServiceShowStage Decompose(KnownDecompositionObject knownObject); + ILookupServiceShowStage Decompose(object? input); + ILookupServiceShowStage Decompose(IEnumerable input); + ILookupServiceShowStage Decompose(ObservableDecomposedObject decomposedObject); + ILookupServiceShowStage Decompose(List decomposedObjects); } public interface ILookupServiceShowStage diff --git a/source/RevitLookup.Abstractions/Services/Decomposition/IDecompositionService.cs b/source/RevitLookup.Abstractions/Services/Decomposition/IDecompositionService.cs index efddf22b..bbaf9af6 100644 --- a/source/RevitLookup.Abstractions/Services/Decomposition/IDecompositionService.cs +++ b/source/RevitLookup.Abstractions/Services/Decomposition/IDecompositionService.cs @@ -5,7 +5,8 @@ namespace RevitLookup.Abstractions.Services.Decomposition; public interface IDecompositionService { - Task DecomposeAsync(object obj); + List DecompositionStackHistory { get; } + Task DecomposeAsync(object? obj); Task> DecomposeAsync(IEnumerable objects); Task> DecomposeMembersAsync(ObservableDecomposedObject decomposedObject); } \ No newline at end of file diff --git a/source/RevitLookup.UI.Framework/Converters/ValueConverters/CombinedDescriptorLabelConverter.cs b/source/RevitLookup.UI.Framework/Converters/ValueConverters/CombinedDescriptorLabelConverter.cs index 26477a33..897d2994 100644 --- a/source/RevitLookup.UI.Framework/Converters/ValueConverters/CombinedDescriptorLabelConverter.cs +++ b/source/RevitLookup.UI.Framework/Converters/ValueConverters/CombinedDescriptorLabelConverter.cs @@ -1,4 +1,5 @@ -using System.Globalization; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; using System.Windows.Data; using System.Windows.Markup; using RevitLookup.Abstractions.ObservableModels.Decomposition; @@ -10,7 +11,7 @@ public sealed class SingleDescriptorLabelConverter : DescriptorLabelConverter public override object Convert(object? value, Type targetType, object? parameter, CultureInfo culture) { var member = (ObservableDecomposedObject) value!; - if (!TryConvertInvalidNames(member.Name, out var name)) + if (!TryConvertInvalidNames(member.RawValue, out var name)) { name = CreateSingleName(member.Name, member.Description); } @@ -29,7 +30,7 @@ public sealed class CombinedDescriptorLabelConverter : DescriptorLabelConverter public override object Convert(object? value, Type targetType, object? parameter, CultureInfo culture) { var member = (ObservableDecomposedMember) value!; - if (!TryConvertInvalidNames(member.Value.Name, out var name)) + if (!TryConvertInvalidNames(member.Value.RawValue, out var name)) { name = CreateCombinedName(member.Value.Name, member.Value.Description); } @@ -48,16 +49,16 @@ private static string CreateCombinedName(string name, string? description) public abstract class DescriptorLabelConverter : MarkupExtension, IValueConverter { - protected bool TryConvertInvalidNames(string text, out string result) + protected bool TryConvertInvalidNames(object? value, [MaybeNullWhen(false)] out string result) { - result = text switch + result = value switch { null => "", - "" => "", - _ => text + string {Length: 0} => "", + _ => null }; - return text != result; + return result is not null; } public abstract object Convert(object? value, Type targetType, object? parameter, CultureInfo culture); diff --git a/source/RevitLookup.UI.Playground/Host.cs b/source/RevitLookup.UI.Playground/Host.cs index 0e9b0b3f..05875b52 100644 --- a/source/RevitLookup.UI.Playground/Host.cs +++ b/source/RevitLookup.UI.Playground/Host.cs @@ -52,7 +52,7 @@ private static ServiceProvider RegisterServices() services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); - services.AddSingleton(); + services.AddScoped(); services.AddScoped(); services.AddTransient(); diff --git a/source/RevitLookup.UI.Playground/Mockups/Services/Application/MockRevitLookupUiService.cs b/source/RevitLookup.UI.Playground/Mockups/Services/Application/MockRevitLookupUiService.cs index 4524cfab..946b7b14 100644 --- a/source/RevitLookup.UI.Playground/Mockups/Services/Application/MockRevitLookupUiService.cs +++ b/source/RevitLookup.UI.Playground/Mockups/Services/Application/MockRevitLookupUiService.cs @@ -6,17 +6,19 @@ using RevitLookup.Abstractions.ObservableModels.Decomposition; using RevitLookup.Abstractions.Services.Application; using RevitLookup.Abstractions.Services.Decomposition; +using RevitLookup.Abstractions.Services.Presentation; using RevitLookup.UI.Framework.Views.Windows; using Wpf.Ui; namespace RevitLookup.UI.Playground.Mockups.Services.Application; -public sealed class MockRevitLookupUiService : IRevitLookupUiService +public sealed class MockRevitLookupUiService : IRevitLookupUiService, ILookupServiceParentStage, ILookupServiceRunStage { - private Window? _parent; + private IServiceProvider? _parentProvider; private readonly Task _activeTask = Task.CompletedTask; private readonly IServiceScope _scope; - private readonly IVisualDecompositionService _decompositionService; + private readonly IDecompositionService _decompositionService; + private readonly IVisualDecompositionService _visualDecompositionService; private readonly INavigationService _navigationService; private readonly Window _host; @@ -25,45 +27,55 @@ public MockRevitLookupUiService(IServiceScopeFactory scopeFactory) _scope = scopeFactory.CreateScope(); _host = _scope.ServiceProvider.GetRequiredService(); - _decompositionService = _scope.ServiceProvider.GetRequiredService(); + _decompositionService = _scope.ServiceProvider.GetRequiredService(); + _visualDecompositionService = _scope.ServiceProvider.GetRequiredService(); _navigationService = _scope.ServiceProvider.GetRequiredService(); _host.Closed += (_, _) => _scope.Dispose(); } - public ILookupServiceDependsStage Decompose(KnownDecompositionObject decompositionObject) + public ILookupServiceShowStage Decompose(KnownDecompositionObject decompositionObject) { - _activeTask.ContinueWith(_ => _decompositionService.VisualizeDecompositionAsync(decompositionObject), TaskScheduler.FromCurrentSynchronizationContext()); + _activeTask.ContinueWith(_ => _visualDecompositionService.VisualizeDecompositionAsync(decompositionObject), TaskScheduler.FromCurrentSynchronizationContext()); return this; } - public ILookupServiceDependsStage Decompose(object? obj) + public ILookupServiceShowStage Decompose(object? obj) { - _activeTask.ContinueWith(_ => _decompositionService.VisualizeDecompositionAsync(obj), TaskScheduler.FromCurrentSynchronizationContext()); + _activeTask.ContinueWith(_ => _visualDecompositionService.VisualizeDecompositionAsync(obj), TaskScheduler.FromCurrentSynchronizationContext()); return this; } - public ILookupServiceDependsStage Decompose(IEnumerable objects) + public ILookupServiceShowStage Decompose(IEnumerable objects) { - _activeTask.ContinueWith(_ => _decompositionService.VisualizeDecompositionAsync(objects), TaskScheduler.FromCurrentSynchronizationContext()); + _activeTask.ContinueWith(_ => _visualDecompositionService.VisualizeDecompositionAsync(objects), TaskScheduler.FromCurrentSynchronizationContext()); return this; } - public ILookupServiceDependsStage Decompose(ObservableDecomposedObject decomposedObject) + public ILookupServiceShowStage Decompose(ObservableDecomposedObject decomposedObject) { - _activeTask.ContinueWith(_ => _decompositionService.VisualizeDecompositionAsync(decomposedObject), TaskScheduler.FromCurrentSynchronizationContext()); + _activeTask.ContinueWith(_ => _visualDecompositionService.VisualizeDecompositionAsync(decomposedObject), TaskScheduler.FromCurrentSynchronizationContext()); return this; } - public ILookupServiceDependsStage Decompose(List decomposedObjects) + public ILookupServiceShowStage Decompose(List decomposedObjects) { - _activeTask.ContinueWith(_ => _decompositionService.VisualizeDecompositionAsync(decomposedObjects), TaskScheduler.FromCurrentSynchronizationContext()); + _activeTask.ContinueWith(_ => _visualDecompositionService.VisualizeDecompositionAsync(decomposedObjects), TaskScheduler.FromCurrentSynchronizationContext()); return this; } - public ILookupServiceShowStage DependsOn(Window parent) + public ILookupServiceParentStage AddParent(IServiceProvider parentProvider) { - _parent = parent; + _parentProvider = parentProvider; + + var decompositionService = parentProvider.GetRequiredService(); + _decompositionService.DecompositionStackHistory.AddRange(decompositionService.DecompositionStackHistory); + return this; + } + + public ILookupServiceDecomposeStage AddStackHistory(ObservableDecomposedObject item) + { + _decompositionService.DecompositionStackHistory.Add(item); return this; } @@ -91,15 +103,17 @@ private void InvokeService(Action handler) where T : class private void ShowHost(bool modal) { - if (_parent is null) + if (_parentProvider is null) { _host.WindowStartupLocation = WindowStartupLocation.CenterScreen; } else { + var parentHost = _parentProvider.GetRequiredService().GetHost(); + _host.WindowStartupLocation = WindowStartupLocation.Manual; - _host.Left = _parent.Left + 47; - _host.Top = _parent.Top + 49; + _host.Left = parentHost.Left + 47; + _host.Top = parentHost.Top + 49; } if (modal) diff --git a/source/RevitLookup.UI.Playground/Mockups/Services/Summary/MockDecompositionService.cs b/source/RevitLookup.UI.Playground/Mockups/Services/Summary/MockDecompositionService.cs index c2b228bf..6f4889d2 100644 --- a/source/RevitLookup.UI.Playground/Mockups/Services/Summary/MockDecompositionService.cs +++ b/source/RevitLookup.UI.Playground/Mockups/Services/Summary/MockDecompositionService.cs @@ -13,7 +13,9 @@ namespace RevitLookup.UI.Playground.Mockups.Services.Summary; [SuppressMessage("ReSharper", "ForeachCanBeConvertedToQueryUsingAnotherGetEnumerator")] public sealed class MockDecompositionService(ISettingsService settingsService) : IDecompositionService { - public async Task DecomposeAsync(object obj) + public List DecompositionStackHistory { get; } = []; + + public async Task DecomposeAsync(object? obj) { var options = CreateDecomposeMembersOptions(); return await Task.Run(() => diff --git a/source/RevitLookup.UI.Playground/Mockups/Services/Summary/MockVisualDecompositionService.cs b/source/RevitLookup.UI.Playground/Mockups/Services/Summary/MockVisualDecompositionService.cs index a34b1cdf..8b8ee4e6 100644 --- a/source/RevitLookup.UI.Playground/Mockups/Services/Summary/MockVisualDecompositionService.cs +++ b/source/RevitLookup.UI.Playground/Mockups/Services/Summary/MockVisualDecompositionService.cs @@ -53,14 +53,12 @@ public async Task VisualizeDecompositionAsync(KnownDecompositionObject decomposi public async Task VisualizeDecompositionAsync(object? obj) { - var objects = obj switch + summaryViewModel.DecomposedObjects = obj switch { - ObservableDecomposedValue {Descriptor: IDescriptorEnumerator} decomposedValue => (IEnumerable) decomposedValue.RawValue!, - ObservableDecomposedValue decomposedValue => new[] {decomposedValue.RawValue}, - _ => new[] {obj} + ObservableDecomposedValue {Descriptor: IDescriptorEnumerator} decomposedValue => await decompositionService.DecomposeAsync((IEnumerable) decomposedValue.RawValue!), + ObservableDecomposedValue decomposedValue => [await decompositionService.DecomposeAsync(decomposedValue.RawValue)], + _ => [await decompositionService.DecomposeAsync(obj)] }; - - summaryViewModel.DecomposedObjects = await decompositionService.DecomposeAsync(objects); } public async Task VisualizeDecompositionAsync(IEnumerable objects) diff --git a/source/RevitLookup.UI.Playground/Mockups/Styles/ComponentStyles/ObjectsTree/TreeGroupTemplates.xaml b/source/RevitLookup.UI.Playground/Mockups/Styles/ComponentStyles/ObjectsTree/TreeGroupTemplates.xaml index e0137e87..0b3f177a 100644 --- a/source/RevitLookup.UI.Playground/Mockups/Styles/ComponentStyles/ObjectsTree/TreeGroupTemplates.xaml +++ b/source/RevitLookup.UI.Playground/Mockups/Styles/ComponentStyles/ObjectsTree/TreeGroupTemplates.xaml @@ -30,9 +30,7 @@ DataType="{x:Type decomposition:ObservableDecomposedObject}"> + Text="{Binding Converter={valueConverters:SingleDescriptorLabelConverter}, Mode=OneTime}" /> + Text="{Binding Converter={valueConverters:SingleDescriptorLabelConverter}, Mode=OneTime}" /> + Color="{Binding RawValue, Converter={converters:ObjectColorConverter}, Mode=OneTime}"> diff --git a/source/RevitLookup.UI.Playground/Mockups/ViewModels/Decomposition/MockDecompositionSummaryViewModel.cs b/source/RevitLookup.UI.Playground/Mockups/ViewModels/Decomposition/MockDecompositionSummaryViewModel.cs index f92d31dd..dfc9e384 100644 --- a/source/RevitLookup.UI.Playground/Mockups/ViewModels/Decomposition/MockDecompositionSummaryViewModel.cs +++ b/source/RevitLookup.UI.Playground/Mockups/ViewModels/Decomposition/MockDecompositionSummaryViewModel.cs @@ -1,18 +1,14 @@ using System.Collections.ObjectModel; -using System.Diagnostics.CodeAnalysis; using CommunityToolkit.Mvvm.ComponentModel; using JetBrains.Annotations; -using LookupEngine; using Microsoft.Extensions.Logging; using RevitLookup.Abstractions.ObservableModels.Decomposition; using RevitLookup.Abstractions.Services.Application; +using RevitLookup.Abstractions.Services.Decomposition; using RevitLookup.Abstractions.Services.Presentation; -using RevitLookup.Abstractions.Services.Settings; using RevitLookup.Abstractions.ViewModels.Decomposition; using RevitLookup.UI.Framework.Extensions; using RevitLookup.UI.Framework.Views.Decomposition; -using RevitLookup.UI.Playground.Mockups.Core.Decomposition; -using RevitLookup.UI.Playground.Mockups.Mappers; #if NETFRAMEWORK using RevitLookup.UI.Framework.Extensions; #endif @@ -21,8 +17,8 @@ namespace RevitLookup.UI.Playground.Mockups.ViewModels.Decomposition; [UsedImplicitly] public sealed partial class MockDecompositionSummaryViewModel( - ISettingsService settingsService, - IWindowIntercomService intercomService, + IServiceProvider serviceProvider, + IDecompositionService decompositionService, INotificationService notificationService, ILogger logger) : ObservableObject, IDecompositionSummaryViewModel @@ -35,24 +31,25 @@ public sealed partial class MockDecompositionSummaryViewModel( public void Navigate(object? value) { Host.GetService() + .AddParent(serviceProvider) + .AddStackHistory(SelectedDecomposedObject!) .Decompose(value) - .DependsOn(intercomService.GetHost()) .Show(); } public void Navigate(ObservableDecomposedObject value) { Host.GetService() + .AddParent(serviceProvider) .Decompose(value) - .DependsOn(intercomService.GetHost()) .Show(); } public void Navigate(List values) { Host.GetService() + .AddParent(serviceProvider) .Decompose(values) - .DependsOn(intercomService.GetHost()) .Show(); } @@ -168,37 +165,7 @@ private async Task FetchMembersAsync(ObservableDecomposedObject? value) if (value is null) return; if (value.Members.Count > 0) return; - value.Members = await DecomposeMembersAsync(value); - } - - [SuppressMessage("ReSharper", "ForeachCanBeConvertedToQueryUsingAnotherGetEnumerator")] - private async Task> DecomposeMembersAsync(ObservableDecomposedObject decomposedObject) - { - var options = new DecomposeOptions - { - IncludeRoot = settingsService.GeneralSettings.IncludeRootHierarchy, - IncludeFields = settingsService.GeneralSettings.IncludeFields, - IncludeEvents = settingsService.GeneralSettings.IncludeEvents, - IncludeUnsupported = settingsService.GeneralSettings.IncludeUnsupported, - IncludePrivateMembers = settingsService.GeneralSettings.IncludePrivate, - IncludeStaticMembers = settingsService.GeneralSettings.IncludeStatic, - EnableExtensions = settingsService.GeneralSettings.IncludeExtensions, - EnableRedirection = true, - TypeResolver = DescriptorsMap.FindDescriptor - }; - - return await Task.Run(() => - { - var decomposedMembers = LookupComposer.DecomposeMembers(decomposedObject.RawValue, options); - var members = new List(decomposedMembers.Count); - - foreach (var decomposedMember in decomposedMembers) - { - members.Add(DecompositionResultMapper.Convert(decomposedMember)); - } - - return members; - }); + value.Members = await decompositionService.DecomposeMembersAsync(value); } private ObservableCollection ApplyGrouping(List objects) diff --git a/source/RevitLookup.UI.Playground/Mockups/ViewModels/Decomposition/MockEventsSummaryViewModel.cs b/source/RevitLookup.UI.Playground/Mockups/ViewModels/Decomposition/MockEventsSummaryViewModel.cs index 559ccd30..46695646 100644 --- a/source/RevitLookup.UI.Playground/Mockups/ViewModels/Decomposition/MockEventsSummaryViewModel.cs +++ b/source/RevitLookup.UI.Playground/Mockups/ViewModels/Decomposition/MockEventsSummaryViewModel.cs @@ -19,7 +19,7 @@ namespace RevitLookup.UI.Playground.Mockups.ViewModels.Decomposition; [UsedImplicitly] public sealed partial class MockEventsSummaryViewModel( - IWindowIntercomService intercomService, + IServiceProvider serviceProvider, INotificationService notificationService, IDecompositionService decompositionService, ILogger logger) @@ -35,24 +35,25 @@ public sealed partial class MockEventsSummaryViewModel( public void Navigate(object? value) { Host.GetService() + .AddParent(serviceProvider) + .AddStackHistory(SelectedDecomposedObject!) .Decompose(value) - .DependsOn(intercomService.GetHost()) .Show(); } public void Navigate(ObservableDecomposedObject value) { Host.GetService() + .AddParent(serviceProvider) .Decompose(value) - .DependsOn(intercomService.GetHost()) .Show(); } public void Navigate(List values) { Host.GetService() + .AddParent(serviceProvider) .Decompose(values) - .DependsOn(intercomService.GetHost()) .Show(); } diff --git a/source/RevitLookup/Core/Decomposition/ContextFinder.cs b/source/RevitLookup/Core/Decomposition/ContextFinder.cs deleted file mode 100644 index 1519ed8f..00000000 --- a/source/RevitLookup/Core/Decomposition/ContextFinder.cs +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2003-2024 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. - -namespace RevitLookup.Core.Decomposition; - -public static class ContextFinder -{ - public static Document FindRevitContext(object obj) - { - var context = GetKnownContext(obj); - if (context is not null) return context; - - context = Context.ActiveDocument; - if (context is null) - { - throw new InvalidOperationException("RevitLookup executing in the invalid context"); - } - - return context; - } - - public static Document FindRevitContext(object obj, Document context) - { - var actualContext = GetKnownContext(obj); - - if (actualContext is null) return context; - if (!actualContext.Equals(context)) return actualContext; - return context; - } - - private static Document? GetKnownContext(object obj) - { - return obj switch - { - Element element => element.Document, - Parameter {Element: not null} parameter => parameter.Element.Document, - Document document => document, - _ => null - }; - } -} \ No newline at end of file diff --git a/source/RevitLookup/Host.cs b/source/RevitLookup/Host.cs index a473c2a4..75287bd1 100644 --- a/source/RevitLookup/Host.cs +++ b/source/RevitLookup/Host.cs @@ -75,7 +75,7 @@ public static void Start() builder.Services.AddHostedService(); //Services - builder.Services.AddSingleton(); + builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddTransient(); builder.Services.AddTransient(); diff --git a/source/RevitLookup/Services/Application/RevitLookupUiService.cs b/source/RevitLookup/Services/Application/RevitLookupUiService.cs index 3dff5df6..f681c7c8 100644 --- a/source/RevitLookup/Services/Application/RevitLookupUiService.cs +++ b/source/RevitLookup/Services/Application/RevitLookupUiService.cs @@ -7,15 +7,16 @@ using RevitLookup.Abstractions.ObservableModels.Decomposition; using RevitLookup.Abstractions.Services.Application; using RevitLookup.Abstractions.Services.Decomposition; +using RevitLookup.Abstractions.Services.Presentation; using RevitLookup.UI.Framework.Views.Windows; using Wpf.Ui; namespace RevitLookup.Services.Application; -public sealed class RevitLookupUiService : IRevitLookupUiService +public sealed class RevitLookupUiService : IRevitLookupUiService, ILookupServiceParentStage, ILookupServiceRunStage { private static readonly Dispatcher Dispatcher; - private UiServiceImpl _uiService = null!; //Late init in constructor + private UiServiceImpl _uiService = null!; //Late init in the constructor static RevitLookupUiService() { @@ -38,7 +39,7 @@ public RevitLookupUiService(IServiceScopeFactory scopeFactory) } } - public ILookupServiceDependsStage Decompose(KnownDecompositionObject decompositionObject) + public ILookupServiceShowStage Decompose(KnownDecompositionObject decompositionObject) { if (Dispatcher.CheckAccess()) { @@ -52,7 +53,7 @@ public ILookupServiceDependsStage Decompose(KnownDecompositionObject decompositi return this; } - public ILookupServiceDependsStage Decompose(object? obj) + public ILookupServiceShowStage Decompose(object? obj) { if (Dispatcher.CheckAccess()) { @@ -66,7 +67,7 @@ public ILookupServiceDependsStage Decompose(object? obj) return this; } - public ILookupServiceDependsStage Decompose(IEnumerable objects) + public ILookupServiceShowStage Decompose(IEnumerable objects) { if (Dispatcher.CheckAccess()) { @@ -80,7 +81,7 @@ public ILookupServiceDependsStage Decompose(IEnumerable objects) return this; } - public ILookupServiceDependsStage Decompose(ObservableDecomposedObject decomposedObject) + public ILookupServiceShowStage Decompose(ObservableDecomposedObject decomposedObject) { if (Dispatcher.CheckAccess()) { @@ -94,7 +95,7 @@ public ILookupServiceDependsStage Decompose(ObservableDecomposedObject decompose return this; } - public ILookupServiceDependsStage Decompose(List decomposedObjects) + public ILookupServiceShowStage Decompose(List decomposedObjects) { if (Dispatcher.CheckAccess()) { @@ -108,15 +109,29 @@ public ILookupServiceDependsStage Decompose(List dec return this; } - public ILookupServiceShowStage DependsOn(Window parent) + public ILookupServiceParentStage AddParent(IServiceProvider parentProvider) { if (Dispatcher.CheckAccess()) { - _uiService.DependsOn(parent); + _uiService.AddParent(parentProvider); } else { - Dispatcher.Invoke(() => _uiService.DependsOn(parent)); + Dispatcher.Invoke(() => _uiService.AddParent(parentProvider)); + } + + return this; + } + + public ILookupServiceDecomposeStage AddStackHistory(ObservableDecomposedObject item) + { + if (Dispatcher.CheckAccess()) + { + _uiService.AddStackHistory(item); + } + else + { + Dispatcher.Invoke(() => _uiService.AddStackHistory(item)); } return this; @@ -167,10 +182,11 @@ private static Dispatcher EnsureDispatcherStart(Thread thread) private sealed class UiServiceImpl { - private Window? _parent; + private IServiceProvider? _parentProvider; private readonly Task _activeTask = Task.CompletedTask; private readonly IServiceScope _scope; - private readonly IVisualDecompositionService _decompositionService; + private readonly IDecompositionService _decompositionService; + private readonly IVisualDecompositionService _visualDecompositionService; private readonly INavigationService _navigationService; private readonly Window _host; @@ -179,7 +195,8 @@ public UiServiceImpl(IServiceScopeFactory scopeFactory) _scope = scopeFactory.CreateScope(); _host = _scope.ServiceProvider.GetRequiredService(); - _decompositionService = _scope.ServiceProvider.GetRequiredService(); + _decompositionService = _scope.ServiceProvider.GetRequiredService(); + _visualDecompositionService = _scope.ServiceProvider.GetRequiredService(); _navigationService = _scope.ServiceProvider.GetRequiredService(); _host.Closed += (_, _) => _scope.Dispose(); @@ -187,32 +204,40 @@ public UiServiceImpl(IServiceScopeFactory scopeFactory) public void Decompose(KnownDecompositionObject decompositionObject) { - _activeTask.ContinueWith(_ => _decompositionService.VisualizeDecompositionAsync(decompositionObject), TaskScheduler.FromCurrentSynchronizationContext()); + _activeTask.ContinueWith(_ => _visualDecompositionService.VisualizeDecompositionAsync(decompositionObject), TaskScheduler.FromCurrentSynchronizationContext()); } public void Decompose(object? obj) { - _activeTask.ContinueWith(_ => _decompositionService.VisualizeDecompositionAsync(obj), TaskScheduler.FromCurrentSynchronizationContext()); + _activeTask.ContinueWith(_ => _visualDecompositionService.VisualizeDecompositionAsync(obj), TaskScheduler.FromCurrentSynchronizationContext()); } public void Decompose(IEnumerable objects) { - _activeTask.ContinueWith(_ => _decompositionService.VisualizeDecompositionAsync(objects), TaskScheduler.FromCurrentSynchronizationContext()); + _activeTask.ContinueWith(_ => _visualDecompositionService.VisualizeDecompositionAsync(objects), TaskScheduler.FromCurrentSynchronizationContext()); } public void Decompose(ObservableDecomposedObject decomposedObject) { - _activeTask.ContinueWith(_ => _decompositionService.VisualizeDecompositionAsync(decomposedObject), TaskScheduler.FromCurrentSynchronizationContext()); + _activeTask.ContinueWith(_ => _visualDecompositionService.VisualizeDecompositionAsync(decomposedObject), TaskScheduler.FromCurrentSynchronizationContext()); } public void Decompose(List decomposedObjects) { - _activeTask.ContinueWith(_ => _decompositionService.VisualizeDecompositionAsync(decomposedObjects), TaskScheduler.FromCurrentSynchronizationContext()); + _activeTask.ContinueWith(_ => _visualDecompositionService.VisualizeDecompositionAsync(decomposedObjects), TaskScheduler.FromCurrentSynchronizationContext()); + } + + public void AddParent(IServiceProvider parentProvider) + { + _parentProvider = parentProvider; + + var decompositionService = parentProvider.GetRequiredService(); + _decompositionService.DecompositionStackHistory.AddRange(decompositionService.DecompositionStackHistory); } - public void DependsOn(Window parent) + public void AddStackHistory(ObservableDecomposedObject item) { - _parent = parent; + _decompositionService.DecompositionStackHistory.Add(item); } public void Show() where T : Page @@ -237,15 +262,17 @@ private void InvokeService(Action handler) where T : class private void ShowHost(bool modal) { - if (_parent is null) + if (_parentProvider is null) { _host.WindowStartupLocation = WindowStartupLocation.CenterScreen; } else { + var parentHost = _parentProvider.GetRequiredService().GetHost(); + _host.WindowStartupLocation = WindowStartupLocation.Manual; - _host.Left = _parent.Left + 47; - _host.Top = _parent.Top + 49; + _host.Left = parentHost.Left + 47; + _host.Top = parentHost.Top + 49; } if (modal) diff --git a/source/RevitLookup/Services/Summary/DecompositionService.cs b/source/RevitLookup/Services/Summary/DecompositionService.cs index 55e1a9e5..a27f24fd 100644 --- a/source/RevitLookup/Services/Summary/DecompositionService.cs +++ b/source/RevitLookup/Services/Summary/DecompositionService.cs @@ -15,11 +15,18 @@ namespace RevitLookup.Services.Summary; [SuppressMessage("ReSharper", "ForeachCanBeConvertedToQueryUsingAnotherGetEnumerator")] public sealed class DecompositionService(ISettingsService settingsService) : IDecompositionService { - public async Task DecomposeAsync(object obj) + public List DecompositionStackHistory { get; } = []; + + public async Task DecomposeAsync(object? obj) { var options = CreateDecomposeMembersOptions(); return await RevitShell.AsyncObjectHandler.RaiseAsync(_ => { + if (TryFindRevitContext(obj, out var context)) + { + options.Context = context; + } + var result = LookupComposer.Decompose(obj, options); return DecompositionResultMapper.Convert(result); }); @@ -27,13 +34,19 @@ public async Task DecomposeAsync(object obj) public async Task> DecomposeAsync(IEnumerable objects) { - var options = CreateDecomposeOptions(); return await RevitShell.AsyncObjectsHandler.RaiseAsync(_ => { + var options = CreateDecomposeOptions(); var capacity = objects is ICollection collection ? collection.Count : 4; var decomposedObjects = new List(capacity); + foreach (var obj in objects) { + if (TryFindRevitContext(obj, out var context)) + { + options.Context = context; + } + var decomposedObject = LookupComposer.DecomposeObject(obj, options); decomposedObjects.Add(DecompositionResultMapper.Convert(decomposedObject)); } @@ -47,6 +60,11 @@ public async Task> DecomposeMembersAsync(Observ var options = CreateDecomposeMembersOptions(); return await RevitShell.AsyncMembersHandler.RaiseAsync(_ => { + if (TryFindRevitContext(decomposedObject.RawValue, out var context)) + { + options.Context = context; + } + var decomposedMembers = LookupComposer.DecomposeMembers(decomposedObject.RawValue, options); var members = new List(decomposedMembers.Count); @@ -59,11 +77,38 @@ public async Task> DecomposeMembersAsync(Observ }); } + private bool TryFindRevitContext(object? obj, [MaybeNullWhen(false)] out Document context) + { + context = GetKnownContext(obj); + if (context is not null) return true; + if (DecompositionStackHistory.Count == 0) return false; + + for (var i = DecompositionStackHistory.Count - 1; i >= 0; i--) + { + var historyItem = DecompositionStackHistory[i]; + context = GetKnownContext(historyItem.RawValue); + if (context is not null) return true; + } + + return false; + } + + private static Document? GetKnownContext(object? obj) + { + return obj switch + { + Element element => element.Document, + Parameter {Element: not null} parameter => parameter.Element.Document, + Document document => document, + _ => null + }; + } + private static DecomposeOptions CreateDecomposeOptions() { return new DecomposeOptions { - Context = Context.ActiveDocument!, //TODO: replace + Context = Context.ActiveDocument!, EnableRedirection = true, TypeResolver = DescriptorsMap.FindDescriptor }; @@ -73,7 +118,7 @@ private DecomposeOptions CreateDecomposeMembersOptions() { return new DecomposeOptions { - Context = Context.ActiveDocument!, //TODO: replace + Context = Context.ActiveDocument!, IncludeRoot = settingsService.GeneralSettings.IncludeRootHierarchy, IncludeFields = settingsService.GeneralSettings.IncludeFields, IncludeEvents = settingsService.GeneralSettings.IncludeEvents, diff --git a/source/RevitLookup/Services/Summary/VisualDecompositionService.cs b/source/RevitLookup/Services/Summary/VisualDecompositionService.cs index 10d7c047..4e5bffa8 100644 --- a/source/RevitLookup/Services/Summary/VisualDecompositionService.cs +++ b/source/RevitLookup/Services/Summary/VisualDecompositionService.cs @@ -73,14 +73,12 @@ private void HideHost() public async Task VisualizeDecompositionAsync(object? obj) { - var values = obj switch + summaryViewModel.DecomposedObjects = obj switch { - ObservableDecomposedValue {Descriptor: IDescriptorEnumerator} decomposedValue => (IEnumerable) decomposedValue.RawValue!, - ObservableDecomposedValue decomposedValue => new[] {decomposedValue.RawValue}, - _ => new[] {obj} + ObservableDecomposedValue {Descriptor: IDescriptorEnumerator} decomposedValue => await decompositionService.DecomposeAsync((IEnumerable) decomposedValue.RawValue!), + ObservableDecomposedValue decomposedValue => [await decompositionService.DecomposeAsync(decomposedValue.RawValue)], + _ => [await decompositionService.DecomposeAsync(obj)] }; - - summaryViewModel.DecomposedObjects = await decompositionService.DecomposeAsync(values); } public async Task VisualizeDecompositionAsync(IEnumerable objects) diff --git a/source/RevitLookup/ViewModels/Decomposition/DecompositionSummaryViewModel.cs b/source/RevitLookup/ViewModels/Decomposition/DecompositionSummaryViewModel.cs index dcece8fa..5ccd576e 100644 --- a/source/RevitLookup/ViewModels/Decomposition/DecompositionSummaryViewModel.cs +++ b/source/RevitLookup/ViewModels/Decomposition/DecompositionSummaryViewModel.cs @@ -12,7 +12,7 @@ namespace RevitLookup.ViewModels.Decomposition; [UsedImplicitly] public sealed partial class DecompositionSummaryViewModel( - IWindowIntercomService intercomService, + IServiceProvider serviceProvider, IDecompositionService decompositionService, INotificationService notificationService, ILogger logger) @@ -26,24 +26,25 @@ public sealed partial class DecompositionSummaryViewModel( public void Navigate(object? value) { Host.GetService() + .AddParent(serviceProvider) + .AddStackHistory(SelectedDecomposedObject!) .Decompose(value) - .DependsOn(intercomService.GetHost()) .Show(); } public void Navigate(ObservableDecomposedObject value) { Host.GetService() + .AddParent(serviceProvider) .Decompose(value) - .DependsOn(intercomService.GetHost()) .Show(); } public void Navigate(List values) { Host.GetService() + .AddParent(serviceProvider) .Decompose(values) - .DependsOn(intercomService.GetHost()) .Show(); } diff --git a/source/RevitLookup/ViewModels/Decomposition/EventsSummaryViewModel.cs b/source/RevitLookup/ViewModels/Decomposition/EventsSummaryViewModel.cs index d109b931..3cfb2859 100644 --- a/source/RevitLookup/ViewModels/Decomposition/EventsSummaryViewModel.cs +++ b/source/RevitLookup/ViewModels/Decomposition/EventsSummaryViewModel.cs @@ -13,7 +13,7 @@ namespace RevitLookup.ViewModels.Decomposition; [UsedImplicitly] public sealed partial class EventsSummaryViewModel( - IWindowIntercomService intercomService, + IServiceProvider serviceProvider, INotificationService notificationService, IDecompositionService decompositionService, EventsMonitoringService monitoringService, @@ -30,24 +30,25 @@ public sealed partial class EventsSummaryViewModel( public void Navigate(object? value) { Host.GetService() + .AddParent(serviceProvider) + .AddStackHistory(SelectedDecomposedObject!) .Decompose(value) - .DependsOn(intercomService.GetHost()) .Show(); } public void Navigate(ObservableDecomposedObject value) { Host.GetService() + .AddParent(serviceProvider) .Decompose(value) - .DependsOn(intercomService.GetHost()) .Show(); } public void Navigate(List values) { Host.GetService() + .AddParent(serviceProvider) .Decompose(values) - .DependsOn(intercomService.GetHost()) .Show(); }