From 36f70555c712664cec7b769a80edfae8621b29cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Standa=20Luke=C5=A1?= Date: Wed, 14 Sep 2022 12:37:30 +0200 Subject: [PATCH] Improve error handling of CommandResolver: missing markup control --- .../Controls/DotvvmControlProperties.cs | 73 ++++++++++++++++++- .../DefaultViewModelSerializer.cs | 8 +- 2 files changed, 78 insertions(+), 3 deletions(-) diff --git a/src/Framework/Framework/Controls/DotvvmControlProperties.cs b/src/Framework/Framework/Controls/DotvvmControlProperties.cs index 468cb79e57..5a254b4b10 100644 --- a/src/Framework/Framework/Controls/DotvvmControlProperties.cs +++ b/src/Framework/Framework/Controls/DotvvmControlProperties.cs @@ -388,9 +388,11 @@ public DotvvmPropertyDictionary(DotvvmBindableObject control) public object? this[DotvvmProperty key] { get => control.properties.GetOrThrow(key); set => control.properties.Set(key, value); } - public ICollection Keys => throw new NotImplementedException(); + public KeysCollection Keys => new KeysCollection(control); + ICollection IDictionary.Keys => Keys; - public ICollection Values => throw new NotImplementedException(); + public ValuesCollection Values => new ValuesCollection(control); + ICollection IDictionary.Values => Values; public int Count => control.properties.Count(); public bool IsReadOnly => false; @@ -442,5 +444,72 @@ public bool TryGetValue(DotvvmProperty key, out object? value) => IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator(); IEnumerator> IEnumerable>.GetEnumerator() => this.GetEnumerator(); + + + public readonly struct KeysCollection : ICollection, IReadOnlyCollection + { + private readonly DotvvmBindableObject control; + + public KeysCollection(DotvvmBindableObject control) { this.control = control; } + public int Count => control.properties.Count(); + + public bool IsReadOnly => true; + public void Add(DotvvmProperty item) => throw new NotSupportedException("Adding a property without value doesn't make sense"); + public void Clear() => throw new NotSupportedException("Explicitly use control.Properties.Clear() instead."); + public bool Contains(DotvvmProperty item) => control.properties.Contains(item); + public void CopyTo(DotvvmProperty[] array, int arrayIndex) + { + foreach (var x in control.properties) + { + array[arrayIndex++] = x.Key; + } + } + public IEnumerator GetEnumerator() + { + foreach (var x in control.properties) + { + yield return x.Key; + } + } + public bool Remove(DotvvmProperty item) => throw new NotSupportedException("Explicitly use control.Properties.Remove() instead."); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } + + public struct ValuesCollection : ICollection + { + private readonly DotvvmBindableObject control; + + public ValuesCollection(DotvvmBindableObject control) { this.control = control; } + public int Count => control.properties.Count(); + + public bool IsReadOnly => true; + public void Add(object? item) => throw new NotSupportedException("Adding a value without property doesn't make sense"); + public void Clear() => throw new NotSupportedException("Explicitly use control.Properties.Clear() instead."); + public bool Contains(object? item) + { + foreach (var x in control.properties) + { + if (Object.Equals(x.Value, item)) + return true; + } + return false; + } + public void CopyTo(object?[] array, int arrayIndex) + { + foreach (var x in control.properties) + { + array[arrayIndex++] = x.Value; + } + } + public IEnumerator GetEnumerator() + { + foreach (var x in control.properties) + { + yield return x.Value; + } + } + public bool Remove(object? item) => throw new NotSupportedException("Explicitly use control.Properties.Remove() instead."); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } } } diff --git a/src/Framework/Framework/ViewModel/Serialization/DefaultViewModelSerializer.cs b/src/Framework/Framework/ViewModel/Serialization/DefaultViewModelSerializer.cs index 54bb4a235f..baa539e194 100644 --- a/src/Framework/Framework/ViewModel/Serialization/DefaultViewModelSerializer.cs +++ b/src/Framework/Framework/ViewModel/Serialization/DefaultViewModelSerializer.cs @@ -503,7 +503,13 @@ public ActionInfo ResolveCommand(IDotvvmRequestContext context, DotvvmView view) var target = view.FindControlByUniqueId(controlUniqueId); if (target == null) { - throw new Exception(string.Format("The control with ID '{0}' was not found!", controlUniqueId)); + var markupControls = + view.GetAllDescendants() + .OfType() + .Where(c => c.GetAllDescendants(cc => cc is not DotvvmMarkupControl) + .Any(cc => cc.Properties.Values + .Any(value => value is Binding.Expressions.CommandBindingExpression cb && cb.BindingId == command))); + throw new Exception($"The control with ID '{controlUniqueId}' was not found! Existing markup controls with this command are: {string.Join(", ", markupControls.Select(c => c.GetDotvvmUniqueId().ToString()).OrderBy(s => s, StringComparer.Ordinal))}"); } return commandResolver.GetFunction(target, view, context, path!, command, args); }