diff --git a/CDP4Composition.Tests/RuleVerification/RuleVerificationServiceTestFixture.cs b/CDP4Composition.Tests/RuleVerification/RuleVerificationServiceTestFixture.cs index b281f4270..a9d3dfb5c 100644 --- a/CDP4Composition.Tests/RuleVerification/RuleVerificationServiceTestFixture.cs +++ b/CDP4Composition.Tests/RuleVerification/RuleVerificationServiceTestFixture.cs @@ -198,6 +198,46 @@ public void VerifyThatBuiltInRulesCanBeExecutedAndMessageBusMessagesAreReceived( Assert.AreEqual(2, messageReceivedCounter); } + [Test] + public void VerifyThatBuiltInRuleVerificationStatusIsSetToFailedAndPersistedWhenViolationsAreFound() + { + OperationContainer writtenOperationContainer = null; + + this.session.Setup(s => s.Write(It.IsAny())) + .Returns(Task.CompletedTask) + .Callback(oc => writtenOperationContainer = oc); + + var service = new RuleVerificationService(this.builtInRules); + + var ruleVerificationList = new RuleVerificationList(Guid.NewGuid(), this.cache, this.uri); + this.iteration.RuleVerificationList.Add(ruleVerificationList); + + var builtInRuleVerification = new BuiltInRuleVerification(Guid.NewGuid(), this.cache, this.uri) + { + Name = this.builtInRuleName, + IsActive = true + }; + + ruleVerificationList.RuleVerification.Add(builtInRuleVerification); + + Assert.AreEqual(RuleVerificationStatusKind.NONE, builtInRuleVerification.Status); + + service.Execute(this.session.Object, ruleVerificationList); + + Assert.IsTrue(builtInRuleVerification.Violation.Any()); + Assert.AreEqual(RuleVerificationStatusKind.FAILED, builtInRuleVerification.Status); + + // the status must be part of the write transaction, otherwise the data-source round-trip resets it to its stored value + Assert.IsNotNull(writtenOperationContainer); + + var writtenVerification = writtenOperationContainer.Operations + .Select(operation => operation.ModifiedThing) + .OfType() + .Single(); + + Assert.AreEqual(RuleVerificationStatusKind.FAILED, writtenVerification.Status); + } + [Test] public async Task VerifyThatUserRuleVerificationCanBeExecutedAndMessageBusMessagesAreReceived() { diff --git a/CDP4Composition/Services/RuleVerification/RuleVerificationService.cs b/CDP4Composition/Services/RuleVerification/RuleVerificationService.cs index 31ca501b7..03a227a63 100644 --- a/CDP4Composition/Services/RuleVerification/RuleVerificationService.cs +++ b/CDP4Composition/Services/RuleVerification/RuleVerificationService.cs @@ -28,6 +28,7 @@ namespace CDP4Composition.Services using System; using System.Collections.Generic; using System.ComponentModel.Composition; + using System.Linq; using System.Reactive.Linq; using System.Threading.Tasks; @@ -151,7 +152,7 @@ public async Task Execute(ISession session, RuleVerificationList verificationLis if (builtInRuleVerification != null && builtInRuleVerification.IsActive) { - this.Execute(session, builtInRuleVerification, verificationList); + await this.ExecuteAsync(session, builtInRuleVerification, verificationList); } var userRuleVerification = ruleVerification as UserRuleVerification; @@ -175,7 +176,7 @@ public async Task Execute(ISession session, RuleVerificationList verificationLis /// /// The container of the /// - private void Execute(ISession session, BuiltInRuleVerification builtInRuleVerification, RuleVerificationList container) + private async Task ExecuteAsync(ISession session, BuiltInRuleVerification builtInRuleVerification, RuleVerificationList container) { var iteration = (Iteration)container.Container; @@ -200,9 +201,11 @@ private void Execute(ISession session, BuiltInRuleVerification builtInRuleVerifi return; } - var violations = builtInRule.Verify(iteration); + var violations = builtInRule.Verify(iteration).ToList(); - this.UpdateExecutedOn(session, builtInRuleVerification); + var status = violations.Any() ? RuleVerificationStatusKind.FAILED : RuleVerificationStatusKind.PASSED; + + await this.UpdateExecutedOnAsync(session, builtInRuleVerification, status); builtInRuleVerification.Violation.AddRange(violations); @@ -242,10 +245,10 @@ private async Task Execute(ISession session, UserRuleVerification userRuleVerifi } userRuleVerification.Violation.Clear(); - userRuleVerification.Status = RuleVerificationStatusKind.PASSED; session.CDPMessageBus.SendObjectChangeEvent(userRuleVerification, EventKind.Updated); + var status = RuleVerificationStatusKind.PASSED; IEnumerable violations = null; switch (userRuleVerification.Rule.ClassKind) @@ -274,12 +277,14 @@ private async Task Execute(ISession session, UserRuleVerification userRuleVerifi if (violations is not null) { - userRuleVerification.Status = RuleVerificationStatusKind.FAILED; + var violationList = violations.ToList(); + + status = violationList.Any() ? RuleVerificationStatusKind.FAILED : RuleVerificationStatusKind.PASSED; IDisposable subscription = null; - //Listen for changes to the verification rule that will happen after UpdateExecutedOn in order to get the updated version. - //The violations must be added lastly as they are not persistent + //Listen for changes to the verification rule that will happen after UpdateExecutedOnAsync in order to get the updated version. + //The violations must be added lastly as they are not persistent subscription = session.CDPMessageBus.Listen(userRuleVerification) .Where(objectChange => objectChange.EventKind == EventKind.Updated) .ObserveOn(RxApp.MainThreadScheduler) @@ -290,22 +295,23 @@ private async Task Execute(ISession session, UserRuleVerification userRuleVerifi subscription.Dispose(); var verification = updated.ChangedThing as UserRuleVerification; - verification.Violation.AddRange(violations); + verification.Violation.AddRange(violationList); session.CDPMessageBus.SendObjectChangeEvent(verification, EventKind.Updated); - foreach (var ruleViolation in violations) + foreach (var ruleViolation in violationList) { session.CDPMessageBus.SendObjectChangeEvent(ruleViolation, EventKind.Added); } }); } - await this.UpdateExecutedOn(session, userRuleVerification); + await this.UpdateExecutedOnAsync(session, userRuleVerification, status); } /// - /// Updates the Executed On property of the in the data-source + /// Updates the and properties of + /// the in the data-source. /// /// /// The instance used to update the contained instances on the data-source. @@ -313,8 +319,15 @@ private async Task Execute(ISession session, UserRuleVerification userRuleVerifi /// /// The that is to be updated. /// + /// + /// The that reflects the outcome of the verification and that is to be persisted. + /// /// An awaitable - private async Task UpdateExecutedOn(ISession session, RuleVerification ruleVerification) + /// + /// The is set on the clone so that it is part of the write transaction. If it were only + /// set on the cached instance, the data-source round-trip would echo the previously stored status and reset it. + /// + private async Task UpdateExecutedOnAsync(ISession session, RuleVerification ruleVerification, RuleVerificationStatusKind status) { try { @@ -323,8 +336,12 @@ private async Task UpdateExecutedOn(ISession session, RuleVerification ruleVerif var transactionContext = TransactionContextResolver.ResolveContext(ruleVerification); var transaction = new ThingTransaction(transactionContext, clone); clone.ExecutedOn = DateTime.UtcNow; + clone.Status = status; var operationContainer = transaction.FinalizeTransaction(); + + ruleVerification.Status = status; + await session.Write(operationContainer); } catch (Exception ex) diff --git a/EngineeringModel.Tests/ViewModels/RuleVerificationListBrowser/RuleVerificationListRowViewModelTestFixture.cs b/EngineeringModel.Tests/ViewModels/RuleVerificationListBrowser/RuleVerificationListRowViewModelTestFixture.cs index bd790404d..18cc98f6e 100644 --- a/EngineeringModel.Tests/ViewModels/RuleVerificationListBrowser/RuleVerificationListRowViewModelTestFixture.cs +++ b/EngineeringModel.Tests/ViewModels/RuleVerificationListBrowser/RuleVerificationListRowViewModelTestFixture.cs @@ -395,5 +395,64 @@ public void VerifyThatViolationAreAddedRemoved() this.messageBus.SendObjectChangeEvent(userRuleVerification, EventKind.Updated); Assert.IsEmpty(userRow.ContainedRows); } + + [Test] + public void VerifyThatViolatingThingsAreResolvedToRowsWithNamesAndShortNames() + { + var elementDefinition = new ElementDefinition(Guid.NewGuid(), this.cache, this.uri) + { + Name = "Battery", + ShortName = "bat", + Owner = this.domain + }; + + this.cache.TryAdd(new CacheKey(elementDefinition.Iid, this.iteration.Iid), new Lazy(() => elementDefinition)); + + var ruleVerificationList = new RuleVerificationList(Guid.NewGuid(), this.cache, this.uri) + { + Owner = this.domain + }; + + this.iteration.RuleVerificationList.Add(ruleVerificationList); + + var builtInRuleVerification = new BuiltInRuleVerification(Guid.NewGuid(), this.cache, this.uri) + { + Name = "BuiltIn", + Status = RuleVerificationStatusKind.INCONCLUSIVE, + IsActive = true + }; + + ruleVerificationList.RuleVerification.Add(builtInRuleVerification); + + var listRowViewModel = new RuleVerificationListRowViewModel(ruleVerificationList, this.session.Object, null); + + var builtInRow = listRowViewModel.ContainedRows.Single(x => x.Thing == builtInRuleVerification); + + var violation = new RuleViolation(Guid.NewGuid(), this.cache, this.uri) + { + Description = "The Element Definition does not contain the required parameters." + }; + + violation.ViolatingThing.Add(elementDefinition.Iid); + + builtInRuleVerification.Violation.Add(violation); + this.revision.SetValue(builtInRuleVerification, 10); + this.messageBus.SendObjectChangeEvent(builtInRuleVerification, EventKind.Updated); + + var violationRow = builtInRow.ContainedRows.OfType().Single(); + Assert.AreEqual(violation.Description, violationRow.Tooltip); + + var violatingThingRow = violationRow.ContainedRows.OfType().Single(); + Assert.AreEqual(elementDefinition, violatingThingRow.Thing); + Assert.AreEqual("Battery", violatingThingRow.Name); + Assert.AreEqual("bat", violatingThingRow.ShortName); + Assert.AreEqual(this.domain.ShortName, violatingThingRow.OwnerName); + + // the violation row (and its resolved violating thing) is removed when the violation is cleared + builtInRuleVerification.Violation.Clear(); + this.revision.SetValue(builtInRuleVerification, 20); + this.messageBus.SendObjectChangeEvent(builtInRuleVerification, EventKind.Updated); + Assert.IsEmpty(builtInRow.ContainedRows); + } } } diff --git a/EngineeringModel.Tests/ViewModels/RuleVerificationListBrowser/RuleVerificationListViewModelTestFixture.cs b/EngineeringModel.Tests/ViewModels/RuleVerificationListBrowser/RuleVerificationListViewModelTestFixture.cs index af57a6869..89fe246a3 100644 --- a/EngineeringModel.Tests/ViewModels/RuleVerificationListBrowser/RuleVerificationListViewModelTestFixture.cs +++ b/EngineeringModel.Tests/ViewModels/RuleVerificationListBrowser/RuleVerificationListViewModelTestFixture.cs @@ -41,6 +41,7 @@ namespace CDP4EngineeringModel.Tests.ViewModels.RuleVerificationListBrowser using CDP4Common.Types; using CDP4Composition.DragDrop; + using CDP4Composition.Events; using CDP4Composition.Navigation; using CDP4Composition.Navigation.Interfaces; using CDP4Composition.Services; @@ -392,5 +393,51 @@ public async Task VerifyThatIfRuleVerificationListIsSelecedTheRulesCanBeVerified this.ruleVerificationService.Verify(x => x.Execute(this.session.Object, ruleVerificationList)); } + + [Test] + public async Task VerifyThatHighlightCommandHighlightsAllViolatingThingsBelowSelectedRow() + { + var elementDefinition1 = new ElementDefinition(Guid.NewGuid(), this.cache, this.uri) { Name = "Battery", ShortName = "bat", Owner = this.domain }; + var elementDefinition2 = new ElementDefinition(Guid.NewGuid(), this.cache, this.uri) { Name = "Panel", ShortName = "pan", Owner = this.domain }; + + this.cache.TryAdd(new CacheKey(elementDefinition1.Iid, this.iteration.Iid), new Lazy(() => elementDefinition1)); + this.cache.TryAdd(new CacheKey(elementDefinition2.Iid, this.iteration.Iid), new Lazy(() => elementDefinition2)); + + var ruleVerificationList = new RuleVerificationList(Guid.NewGuid(), this.cache, this.uri) { Owner = this.domain }; + this.iteration.RuleVerificationList.Add(ruleVerificationList); + + var builtInRuleVerification = new BuiltInRuleVerification(Guid.NewGuid(), this.cache, this.uri) { Name = "BuiltIn", IsActive = true }; + ruleVerificationList.RuleVerification.Add(builtInRuleVerification); + + var violation1 = new RuleViolation(Guid.NewGuid(), this.cache, this.uri) { Description = "v1" }; + violation1.ViolatingThing.Add(elementDefinition1.Iid); + + var violation2 = new RuleViolation(Guid.NewGuid(), this.cache, this.uri) { Description = "v2" }; + violation2.ViolatingThing.Add(elementDefinition2.Iid); + + var vm = new RuleVerificationListBrowserViewModel(this.iteration, this.participant, this.session.Object, null, null, null, null); + + var listRow = vm.RuleVerificationListRowViewModels.Single(x => x.Thing == ruleVerificationList); + + builtInRuleVerification.Violation.Add(violation1); + builtInRuleVerification.Violation.Add(violation2); + this.revision.SetValue(builtInRuleVerification, 10); + this.messageBus.SendObjectChangeEvent(builtInRuleVerification, EventKind.Updated); + + var highlightedThings = new List(); + this.messageBus.Listen().Subscribe(x => highlightedThings.Add(x.HighlightedThing)); + + var highlightedElementUsageDefinitions = new List(); + this.messageBus.Listen().Subscribe(x => highlightedElementUsageDefinitions.Add(x.ElementDefinition)); + + // selecting the parent Rule Verification List row should highlight all violating things below it + vm.SelectedThing = listRow; + await vm.HighlightCommand.Execute(); + + CollectionAssert.AreEquivalent(new Thing[] { elementDefinition1, elementDefinition2 }, highlightedThings); + + // ElementDefinition violating things must also raise an ElementUsageHighlightEvent so the Product Tree highlights their usages + CollectionAssert.AreEquivalent(new[] { elementDefinition1, elementDefinition2 }, highlightedElementUsageDefinitions); + } } } diff --git a/EngineeringModel/ViewModels/RuleVerificationListBrowser/RuleVerificationListBrowserViewModel.cs b/EngineeringModel/ViewModels/RuleVerificationListBrowser/RuleVerificationListBrowserViewModel.cs index 0c49b03e6..da0695487 100644 --- a/EngineeringModel/ViewModels/RuleVerificationListBrowser/RuleVerificationListBrowserViewModel.cs +++ b/EngineeringModel/ViewModels/RuleVerificationListBrowser/RuleVerificationListBrowserViewModel.cs @@ -26,6 +26,7 @@ namespace CDP4EngineeringModel.ViewModels { using System; + using System.Collections.Generic; using System.Linq; using System.Reactive; using System.Reactive.Linq; @@ -38,6 +39,7 @@ namespace CDP4EngineeringModel.ViewModels using CDP4Composition; using CDP4Composition.DragDrop; + using CDP4Composition.Events; using CDP4Composition.Mvvm; using CDP4Composition.Mvvm.Types; using CDP4Composition.Navigation; @@ -200,6 +202,12 @@ public bool CanVerifyVerificationList /// public ReactiveCommand VerifyRuleVerificationList { get; private set; } + /// + /// Gets the used to highlight the selected violating + /// in the other open browsers + /// + public ReactiveCommand HighlightCommand { get; private set; } + /// /// Gets the active /// @@ -275,6 +283,8 @@ protected override void InitializeCommands() this.VerifyRuleVerificationList = ReactiveCommandCreator.CreateAsyncTask( this.ExecuteVerifyRuleVerificationList, this.WhenAnyValue(x => x.CanVerifyVerificationList)); + + this.HighlightCommand = ReactiveCommandCreator.Create(this.ExecuteHighlightCommand); } /// @@ -314,6 +324,68 @@ public override void PopulateContextMenu() this.ContextMenu.Add(new ContextMenuItemViewModel("Execute the Rule Verification List", "", this.VerifyRuleVerificationList, MenuItemKind.None, ClassKind.NotThing)); } + + if (this.SelectedThing != null && this.QueryViolatingThings(this.SelectedThing).Any()) + { + this.ContextMenu.Add(new ContextMenuItemViewModel("Highlight the violating Things in the open Browsers", "", this.HighlightCommand, MenuItemKind.Highlight, ClassKind.NotThing)); + } + } + + /// + /// Executes the by sending a for each violating + /// that is contained by the selected row, so that they are all highlighted in the other + /// open browsers. This works for a single violating row as well as for a parent + /// , or row. + /// + private void ExecuteHighlightCommand() + { + this.CDPMessageBus.SendMessage(new CancelHighlightEvent()); + + if (this.SelectedThing == null) + { + return; + } + + var violatingThings = this.QueryViolatingThings(this.SelectedThing).Distinct().ToList(); + + foreach (var violatingThing in violatingThings) + { + this.CDPMessageBus.SendMessage(new HighlightEvent(violatingThing), violatingThing); + this.CDPMessageBus.SendMessage(new HighlightEvent(violatingThing), null); + + if (violatingThing is ElementDefinition elementDefinition) + { + this.CDPMessageBus.SendMessage(new ElementUsageHighlightEvent(elementDefinition), elementDefinition); + this.CDPMessageBus.SendMessage(new ElementUsageHighlightEvent(elementDefinition), null); + } + } + } + + /// + /// Recursively queries the violating s that are represented by the + /// s contained by (or equal to) the provided row. + /// + /// The row to query. + /// The violating s found below the provided row. + private IEnumerable QueryViolatingThings(IRowViewModelBase row) + { + if (row is ViolatingThingRowViewModel violatingThingRow) + { + if (violatingThingRow.Thing != null) + { + yield return violatingThingRow.Thing; + } + + yield break; + } + + foreach (var containedRow in row.ContainedRows) + { + foreach (var violatingThing in this.QueryViolatingThings(containedRow)) + { + yield return violatingThing; + } + } } /// diff --git a/EngineeringModel/ViewModels/RuleVerificationListBrowser/RuleViolationRowViewModel.cs b/EngineeringModel/ViewModels/RuleVerificationListBrowser/RuleViolationRowViewModel.cs new file mode 100644 index 000000000..8959d5519 --- /dev/null +++ b/EngineeringModel/ViewModels/RuleVerificationListBrowser/RuleViolationRowViewModel.cs @@ -0,0 +1,136 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2015-2026 Starion Group S.A. +// +// Author: Sam Gerené, Alex Vorobiev, Alexander van Delft, Nathanael Smiechowski, Antoine Théate, Rowan de Voogt +// +// This file is part of CDP4-COMET IME Community Edition. +// The CDP4-COMET IME Community Edition is the Starion Concurrent Design Desktop Application and Excel Integration +// compliant with ECSS-E-TM-10-25 Annex A and Annex C. +// +// The CDP4-COMET IME Community Edition is free software; you can redistribute it and/or +// modify it under the terms of the GNU Affero General Public +// License as published by the Free Software Foundation; either +// version 3 of the License, or any later version. +// +// The CDP4-COMET IME Community Edition is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see http://www.gnu.org/licenses/. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace CDP4EngineeringModel.ViewModels +{ + using System.Linq; + + using CDP4Common.CommonData; + using CDP4Common.EngineeringModelData; + + using CDP4Composition.Mvvm; + + using CDP4Dal; + using CDP4Dal.Events; + + /// + /// A row representing a . + /// + /// + /// In addition to the textual , this row resolves the + /// identifiers to the actual s and exposes + /// them as child rows. This presents the offending model items with their name, short name, owner and type + /// in dedicated columns, rather than only as identifiers embedded in a single sentence. + /// + public class RuleViolationRowViewModel : CDP4CommonView.RuleViolationRowViewModel + { + /// + /// Initializes a new instance of the class. + /// + /// + /// The that is represented by the current row-view-model. + /// + /// + /// The current active + /// + /// + /// The view-model that is the container of the current row-view-model. + /// + public RuleViolationRowViewModel(RuleViolation ruleViolation, ISession session, IViewModelBase containerViewModel) + : base(ruleViolation, session, containerViewModel) + { + this.UpdateProperties(); + } + + /// + /// The handler + /// + /// The + protected override void ObjectChangeEventHandler(ObjectChangedEvent objectChange) + { + base.ObjectChangeEventHandler(objectChange); + this.UpdateProperties(); + } + + /// + /// Updates the properties of this row on the update of the current + /// + private void UpdateProperties() + { + this.Tooltip = this.Thing.Description; + this.PopulateViolatingThings(); + } + + /// + /// Populates the child rows that represent the s referenced by + /// . + /// + private void PopulateViolatingThings() + { + var resolvedThings = this.Thing.ViolatingThing + .Select(this.ResolveViolatingThing) + .Where(thing => thing != null) + .ToList(); + + var currentThings = this.ContainedRows.Select(x => x.Thing).ToList(); + + var newThings = resolvedThings.Except(currentThings).ToList(); + var oldThings = currentThings.Except(resolvedThings).ToList(); + + foreach (var violatingThing in newThings) + { + var row = new ViolatingThingRowViewModel(violatingThing, this.Session, this); + this.ContainedRows.Add(row); + } + + foreach (var violatingThing in oldThings) + { + var row = this.ContainedRows.SingleOrDefault(x => x.Thing == violatingThing); + + if (row != null) + { + this.ContainedRows.RemoveAndDispose(row); + } + } + } + + /// + /// Resolves a violating identifier to the cached . + /// + /// The identifier of the violating . + /// The resolved , or null when it cannot be found in the cache. + private Thing ResolveViolatingThing(System.Guid iid) + { + if (this.Thing.Cache == null) + { + return null; + } + + return this.Thing.Cache.Values + .Select(lazy => lazy.Value) + .FirstOrDefault(thing => thing.Iid == iid); + } + } +} diff --git a/EngineeringModel/ViewModels/RuleVerificationListBrowser/ViolatingThingRowViewModel.cs b/EngineeringModel/ViewModels/RuleVerificationListBrowser/ViolatingThingRowViewModel.cs new file mode 100644 index 000000000..336b0c761 --- /dev/null +++ b/EngineeringModel/ViewModels/RuleVerificationListBrowser/ViolatingThingRowViewModel.cs @@ -0,0 +1,130 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2015-2026 Starion Group S.A. +// +// Author: Sam Gerené, Alex Vorobiev, Alexander van Delft, Nathanael Smiechowski, Antoine Théate, Rowan de Voogt +// +// This file is part of CDP4-COMET IME Community Edition. +// The CDP4-COMET IME Community Edition is the Starion Concurrent Design Desktop Application and Excel Integration +// compliant with ECSS-E-TM-10-25 Annex A and Annex C. +// +// The CDP4-COMET IME Community Edition is free software; you can redistribute it and/or +// modify it under the terms of the GNU Affero General Public +// License as published by the Free Software Foundation; either +// version 3 of the License, or any later version. +// +// The CDP4-COMET IME Community Edition is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see http://www.gnu.org/licenses/. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace CDP4EngineeringModel.ViewModels +{ + using CDP4Common.CommonData; + using CDP4Common.EngineeringModelData; + + using CDP4Composition.Mvvm; + + using CDP4Dal; + using CDP4Dal.Events; + + using ReactiveUI; + + /// + /// A row that represents a that violates a . + /// + /// + /// A only carries the identifiers of the violating + /// s. This row resolves each identifier to the actual so that its + /// human friendly name, short name, owner and type can be presented in dedicated, sortable columns instead + /// of only an identifier buried in a sentence. + /// + public class ViolatingThingRowViewModel : RowViewModelBase + { + /// + /// Backing field for + /// + private string name; + + /// + /// Backing field for + /// + private string shortName; + + /// + /// Backing field for + /// + private string ownerName; + + /// + /// Initializes a new instance of the class. + /// + /// + /// The that violates a rule and that is represented by the current row-view-model. + /// + /// + /// The current active + /// + /// + /// The view-model that is the container of the current row-view-model. + /// + public ViolatingThingRowViewModel(Thing violatingThing, ISession session, IViewModelBase containerViewModel) + : base(violatingThing, session, containerViewModel) + { + this.UpdateProperties(); + } + + /// + /// Gets the human friendly name of the violating + /// + public string Name + { + get => this.name; + private set => this.RaiseAndSetIfChanged(ref this.name, value); + } + + /// + /// Gets the short name of the violating + /// + public string ShortName + { + get => this.shortName; + private set => this.RaiseAndSetIfChanged(ref this.shortName, value); + } + + /// + /// Gets the short name of the owning domain of expertise of the violating , if any + /// + public string OwnerName + { + get => this.ownerName; + private set => this.RaiseAndSetIfChanged(ref this.ownerName, value); + } + + /// + /// The handler + /// + /// The + protected override void ObjectChangeEventHandler(ObjectChangedEvent objectChange) + { + base.ObjectChangeEventHandler(objectChange); + this.UpdateProperties(); + } + + /// + /// Updates the properties of this row from the violating + /// + private void UpdateProperties() + { + this.Name = this.Thing is INamedThing namedThing ? namedThing.Name : this.Thing.UserFriendlyName; + this.ShortName = this.Thing is IShortNamedThing shortNamedThing ? shortNamedThing.ShortName : this.Thing.UserFriendlyShortName; + this.OwnerName = this.Thing is IOwnedThing ownedThing && ownedThing.Owner != null ? ownedThing.Owner.ShortName : string.Empty; + this.Tooltip = $"{this.RowType}: {this.Name} [{this.ShortName}]"; + } + } +} diff --git a/EngineeringModel/Views/RuleVerificationListBrowser/RuleVerificationListBrowser.xaml b/EngineeringModel/Views/RuleVerificationListBrowser/RuleVerificationListBrowser.xaml index 31c506d24..8b842585f 100644 --- a/EngineeringModel/Views/RuleVerificationListBrowser/RuleVerificationListBrowser.xaml +++ b/EngineeringModel/Views/RuleVerificationListBrowser/RuleVerificationListBrowser.xaml @@ -67,7 +67,27 @@ - + + + + + + + + + + + + + + + + + + @@ -153,6 +173,8 @@ + +