From 6174df310d7b1fb61ab301e51a1aef78d1368caf Mon Sep 17 00:00:00 2001 From: SergeyNefyodov Date: Tue, 3 Sep 2024 21:49:22 +0200 Subject: [PATCH] Add entity editing first version --- .../Descriptors/ElementDescriptor.cs | 21 ++++ .../ExtensibleStorage/EditEntityViewModel.cs | 112 ++++++++++++++++++ .../Dialogs/ExtensibleStorage/EntityDto.cs | 27 +++++ .../SelectEntityViewModel.cs | 57 +++++++++ .../ExtensibleStorage/SimpleFieldDto.cs | 29 +++++ .../Views/Dialogs/EditEntityValuesDialog.xaml | 36 ++++++ .../Dialogs/EditEntityValuesDialog.xaml.cs | 73 ++++++++++++ .../Views/Dialogs/SelectEntityDialog.xaml | 21 ++++ .../Views/Dialogs/SelectEntityDialog.xaml.cs | 72 +++++++++++ 9 files changed, 448 insertions(+) create mode 100644 source/RevitLookup/ViewModels/Dialogs/ExtensibleStorage/EditEntityViewModel.cs create mode 100644 source/RevitLookup/ViewModels/Dialogs/ExtensibleStorage/EntityDto.cs create mode 100644 source/RevitLookup/ViewModels/Dialogs/ExtensibleStorage/SelectEntityViewModel.cs create mode 100644 source/RevitLookup/ViewModels/Dialogs/ExtensibleStorage/SimpleFieldDto.cs create mode 100644 source/RevitLookup/Views/Dialogs/EditEntityValuesDialog.xaml create mode 100644 source/RevitLookup/Views/Dialogs/EditEntityValuesDialog.xaml.cs create mode 100644 source/RevitLookup/Views/Dialogs/SelectEntityDialog.xaml create mode 100644 source/RevitLookup/Views/Dialogs/SelectEntityDialog.xaml.cs diff --git a/source/RevitLookup/Core/ComponentModel/Descriptors/ElementDescriptor.cs b/source/RevitLookup/Core/ComponentModel/Descriptors/ElementDescriptor.cs index 713a0a52..6e13e3cd 100644 --- a/source/RevitLookup/Core/ComponentModel/Descriptors/ElementDescriptor.cs +++ b/source/RevitLookup/Core/ComponentModel/Descriptors/ElementDescriptor.cs @@ -24,8 +24,10 @@ using System.Windows.Input; using Autodesk.Revit.DB.ExtensibleStorage; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; using RevitLookup.Services; using RevitLookup.ViewModels.Contracts; +using RevitLookup.Views.Dialogs; using RevitLookup.Views.Extensions; namespace RevitLookup.Core.ComponentModel.Descriptors; @@ -362,5 +364,24 @@ await RevitShell.AsyncEventHandler.RaiseAsync(_ => } }) .SetShortcut(Key.Delete); + + contextMenu.AddMenuItem("EditMenuItem") + .SetHeader("Edit values") + .SetAvailability(_element.IsValidObject) + .SetCommand(_element, async _ => + { + var context = (ISnoopViewModel) contextMenu.DataContext; + try + { + var dialog = new SelectEntityDialog(context.ServiceProvider, _element); + await dialog.ShowAsync(); + } + catch (Exception exception) + { + var logger = context.ServiceProvider.GetRequiredService>(); + logger.LogError(exception, "Initialize EditParameterDialog error"); + } + }) + .SetShortcut(Key.F2); } } \ No newline at end of file diff --git a/source/RevitLookup/ViewModels/Dialogs/ExtensibleStorage/EditEntityViewModel.cs b/source/RevitLookup/ViewModels/Dialogs/ExtensibleStorage/EditEntityViewModel.cs new file mode 100644 index 00000000..7b6e4fca --- /dev/null +++ b/source/RevitLookup/ViewModels/Dialogs/ExtensibleStorage/EditEntityViewModel.cs @@ -0,0 +1,112 @@ +// 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. + +using System.Collections.ObjectModel; +using System.Reflection; +using Autodesk.Revit.DB.ExtensibleStorage; +using RevitLookup.Core; + +namespace RevitLookup.ViewModels.Dialogs.ExtensibleStorage; + +public sealed partial class EditEntityViewModel : ObservableObject +{ + private readonly Entity _entity; + private readonly Element _element; + [ObservableProperty] private ObservableCollection _descriptors = []; + + public EditEntityViewModel(Entity entity, Element element) + { + _entity = entity; + _element = element; + var fields = entity.Schema.ListFields(); + foreach (var field in fields) + { + if (field.ContainerType == ContainerType.Simple) + { + var method = entity.GetType().GetMethod(nameof(Entity.Get), [typeof(Field)])!; + var genericMethod = MakeGenericInvoker(field, method); + var value = genericMethod.Invoke(entity, [field]); + + Descriptors.Add(new SimpleFieldDto + { + FieldType = field.ValueType, + Name = field.FieldName, + Value = value, + Unit = field.GetSpecTypeId() + }); + } + } + } + + private static MethodInfo MakeGenericInvoker(Field field, MethodInfo invoker) + { + var containerType = field.ContainerType switch + { + ContainerType.Simple => field.ValueType, + ContainerType.Array => typeof(IList<>).MakeGenericType(field.ValueType), + ContainerType.Map => typeof(IDictionary<,>).MakeGenericType(field.KeyType, field.ValueType), + _ => throw new ArgumentOutOfRangeException() + }; + + return invoker.MakeGenericMethod(containerType); + } + + public async Task SaveValueAsync() + { + await RevitShell.AsyncEventHandler.RaiseAsync(_ => + { + var transaction = new Transaction(_element.Document); + transaction.Start("Edit entity values"); + + foreach (var descriptor in Descriptors) + { + var type = descriptor.FieldType; + if (type == typeof(string)) + { + _entity.Set(descriptor.Name, descriptor.Value.ToString()); + } + else if (type == typeof(double)) + { + if (double.TryParse(descriptor.Value.ToString(), out var value)) + { + _entity.Set(descriptor.Name, value); + } + } + else if (type == typeof(int)) + { + if (int.TryParse(descriptor.Value.ToString(), out var value)) + { + _entity.Set(descriptor.Name, value); + } + } + else if (type == typeof(short)) + { + if (short.TryParse(descriptor.Value.ToString(), out var value)) + { + _entity.Set(descriptor.Name, value); + } + } + _element.SetEntity(_entity); + } + + transaction.Commit(); + }); + } +} \ No newline at end of file diff --git a/source/RevitLookup/ViewModels/Dialogs/ExtensibleStorage/EntityDto.cs b/source/RevitLookup/ViewModels/Dialogs/ExtensibleStorage/EntityDto.cs new file mode 100644 index 00000000..acf3b37d --- /dev/null +++ b/source/RevitLookup/ViewModels/Dialogs/ExtensibleStorage/EntityDto.cs @@ -0,0 +1,27 @@ +// 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.ViewModels.Dialogs.ExtensibleStorage; + +public class EntityDto +{ + public required string EntityName { get; set; } + public required Guid Guid { get; set; } +} \ No newline at end of file diff --git a/source/RevitLookup/ViewModels/Dialogs/ExtensibleStorage/SelectEntityViewModel.cs b/source/RevitLookup/ViewModels/Dialogs/ExtensibleStorage/SelectEntityViewModel.cs new file mode 100644 index 00000000..f5cf54b4 --- /dev/null +++ b/source/RevitLookup/ViewModels/Dialogs/ExtensibleStorage/SelectEntityViewModel.cs @@ -0,0 +1,57 @@ +// 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. + +using Autodesk.Revit.DB.ExtensibleStorage; +using RevitLookup.Views.Dialogs; + +namespace RevitLookup.ViewModels.Dialogs.ExtensibleStorage; + +public partial class SelectEntityViewModel : ObservableObject +{ + private readonly Element _element; + [ObservableProperty] private EntityDto _selectedEntity; + public List ExistedEntities { get; } = []; + public SelectEntityViewModel(Element element) + { + _element = element; + var schemas = Schema.ListSchemas(); + foreach (var schema in schemas) + { + if (!schema.ReadAccessGranted()) return; + var entity = element.GetEntity(schema); + if (entity is not null && entity.Schema is not null) + { + ExistedEntities.Add(new EntityDto + { + EntityName = schema.SchemaName, + Guid = entity.SchemaGUID + }); + } + } + } + + public async Task ShowEditEntityDialogAsync(IServiceProvider serviceProvider) + { + var schema = Schema.Lookup(SelectedEntity.Guid); + var entity = _element.GetEntity(schema); + var dialog = new EditEntityValuesDialog(serviceProvider, _element, entity); + await dialog.ShowAsync(); + } +} \ No newline at end of file diff --git a/source/RevitLookup/ViewModels/Dialogs/ExtensibleStorage/SimpleFieldDto.cs b/source/RevitLookup/ViewModels/Dialogs/ExtensibleStorage/SimpleFieldDto.cs new file mode 100644 index 00000000..c107b511 --- /dev/null +++ b/source/RevitLookup/ViewModels/Dialogs/ExtensibleStorage/SimpleFieldDto.cs @@ -0,0 +1,29 @@ +// 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.ViewModels.Dialogs.ExtensibleStorage; + +public partial class SimpleFieldDto : ObservableObject +{ + [ObservableProperty] private object _value; + public string Name { get; set; } + public Type FieldType { get; set; } + public ForgeTypeId Unit { get; set; } +} \ No newline at end of file diff --git a/source/RevitLookup/Views/Dialogs/EditEntityValuesDialog.xaml b/source/RevitLookup/Views/Dialogs/EditEntityValuesDialog.xaml new file mode 100644 index 00000000..420815da --- /dev/null +++ b/source/RevitLookup/Views/Dialogs/EditEntityValuesDialog.xaml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/source/RevitLookup/Views/Dialogs/EditEntityValuesDialog.xaml.cs b/source/RevitLookup/Views/Dialogs/EditEntityValuesDialog.xaml.cs new file mode 100644 index 00000000..0100732b --- /dev/null +++ b/source/RevitLookup/Views/Dialogs/EditEntityValuesDialog.xaml.cs @@ -0,0 +1,73 @@ +// 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. + +using System.Windows; +using System.Windows.Controls; +using Autodesk.Revit.DB.ExtensibleStorage; +using Microsoft.Extensions.DependencyInjection; +using RevitLookup.Services; +using RevitLookup.ViewModels.Dialogs.ExtensibleStorage; +using Wpf.Ui; +using Wpf.Ui.Controls; + +namespace RevitLookup.Views.Dialogs; + +public sealed partial class EditEntityValuesDialog +{ + private readonly IServiceProvider _serviceProvider; + private readonly EditEntityViewModel _viewModel; + + public EditEntityValuesDialog(IServiceProvider serviceProvider, Element element, Entity entity) + { + _serviceProvider = serviceProvider; + _viewModel = new EditEntityViewModel(entity, element); + + DataContext = _viewModel; + InitializeComponent(); + } + + public async Task ShowAsync() + { + var dialogOptions = new SimpleContentDialogCreateOptions + { + Title = "Edit entity field values", + Content = this, + PrimaryButtonText = "Save", + CloseButtonText = "Close", + DialogVerticalAlignment = VerticalAlignment.Center, + DialogHorizontalAlignment = HorizontalAlignment.Center, + HorizontalScrollVisibility = ScrollBarVisibility.Disabled, + VerticalScrollVisibility = ScrollBarVisibility.Disabled + }; + + var dialogResult = await _serviceProvider.GetRequiredService().ShowSimpleDialogAsync(dialogOptions); + if (dialogResult != ContentDialogResult.Primary) return; + + try + { + await _viewModel.SaveValueAsync(); + } + catch (Exception exception) + { + var notificationService = _serviceProvider.GetRequiredService(); + notificationService.ShowWarning("Invalid data", exception.Message); + } + } +} \ No newline at end of file diff --git a/source/RevitLookup/Views/Dialogs/SelectEntityDialog.xaml b/source/RevitLookup/Views/Dialogs/SelectEntityDialog.xaml new file mode 100644 index 00000000..f03fea48 --- /dev/null +++ b/source/RevitLookup/Views/Dialogs/SelectEntityDialog.xaml @@ -0,0 +1,21 @@ + + + + + + + + + \ No newline at end of file diff --git a/source/RevitLookup/Views/Dialogs/SelectEntityDialog.xaml.cs b/source/RevitLookup/Views/Dialogs/SelectEntityDialog.xaml.cs new file mode 100644 index 00000000..eb8a7266 --- /dev/null +++ b/source/RevitLookup/Views/Dialogs/SelectEntityDialog.xaml.cs @@ -0,0 +1,72 @@ +// 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. + +using System.Windows; +using System.Windows.Controls; +using Microsoft.Extensions.DependencyInjection; +using RevitLookup.Services; +using RevitLookup.ViewModels.Dialogs.ExtensibleStorage; +using Wpf.Ui; +using Wpf.Ui.Controls; + +namespace RevitLookup.Views.Dialogs; + +public sealed partial class SelectEntityDialog +{ + private readonly IServiceProvider _serviceProvider; + private readonly SelectEntityViewModel _viewModel; + + public SelectEntityDialog(IServiceProvider serviceProvider, Element element) + { + _serviceProvider = serviceProvider; + _viewModel = new SelectEntityViewModel(element); + + DataContext = _viewModel; + InitializeComponent(); + } + + public async Task ShowAsync() + { + var dialogOptions = new SimpleContentDialogCreateOptions + { + Title = "Select entity to edit", + Content = this, + PrimaryButtonText = "Edit", + CloseButtonText = "Close", + DialogVerticalAlignment = VerticalAlignment.Center, + DialogHorizontalAlignment = HorizontalAlignment.Center, + HorizontalScrollVisibility = ScrollBarVisibility.Disabled, + VerticalScrollVisibility = ScrollBarVisibility.Disabled + }; + + var dialogResult = await _serviceProvider.GetRequiredService().ShowSimpleDialogAsync(dialogOptions); + if (dialogResult != ContentDialogResult.Primary) return; + + try + { + await _viewModel.ShowEditEntityDialogAsync(_serviceProvider); + } + catch (Exception exception) + { + var notificationService = _serviceProvider.GetRequiredService(); + notificationService.ShowWarning("Invalid data", exception.Message); + } + } +} \ No newline at end of file