// Copyright (c) The Avalonia Project. All rights reserved. // Licensed under the MIT license. See licence.md file in the project root for full license information. using System; using System.Collections.Generic; using System.Linq; using System.Reactive.Linq; using Avalonia.Controls.Templates; using Avalonia.Data; namespace Avalonia.Controls { /// /// A control which displays an error notifier when there is a DataValidationError. /// Provides attached properties to track errors on a control /// /// /// You will probably only want to create instances inside of control templates. /// public class DataValidationErrors : ContentControl { /// /// Defines the DataValidationErrors.Errors attached property. /// public static readonly AttachedProperty> ErrorsProperty = AvaloniaProperty.RegisterAttached>("Errors"); /// /// Defines the DataValidationErrors.HasErrors attached property. /// public static readonly AttachedProperty HasErrorsProperty = AvaloniaProperty.RegisterAttached("HasErrors"); public static readonly StyledProperty ErrorTemplateProperty = AvaloniaProperty.Register(nameof(ErrorTemplate)); private Control _owner; public static readonly DirectProperty OwnerProperty = AvaloniaProperty.RegisterDirect( nameof(Owner), o => o.Owner, (o, v) => o.Owner = v); public Control Owner { get { return _owner; } set { SetAndRaise(OwnerProperty, ref _owner, value); } } /// /// Initializes static members of the class. /// static DataValidationErrors() { ErrorsProperty.Changed.Subscribe(ErrorsChanged); HasErrorsProperty.Changed.Subscribe(HasErrorsChanged); TemplatedParentProperty.Changed.AddClassHandler(x => x.OnTemplatedParentChange); } private void OnTemplatedParentChange(AvaloniaPropertyChangedEventArgs e) { if (Owner == null) { Owner = (e.NewValue as Control); } } public IDataTemplate ErrorTemplate { get { return GetValue(ErrorTemplateProperty); } set { SetValue(ErrorTemplateProperty, value); } } private static void ErrorsChanged(AvaloniaPropertyChangedEventArgs e) { var control = (Control)e.Sender; var errors = (IEnumerable)e.NewValue; var hasErrors = false; if (errors != null && errors.Any()) hasErrors = true; control.SetValue(HasErrorsProperty, hasErrors); } private static void HasErrorsChanged(AvaloniaPropertyChangedEventArgs e) { var control = (Control)e.Sender; var classes = (IPseudoClasses)control.Classes; classes.Set(":error", (bool)e.NewValue); } public static IEnumerable GetErrors(Control control) { return control.GetValue(ErrorsProperty); } public static void SetErrors(Control control, IEnumerable errors) { control.SetValue(ErrorsProperty, errors); } public static void SetError(Control control, Exception error) { SetErrors(control, UnpackException(error)); } public static void ClearErrors(Control control) { SetErrors(control, null); } public static bool GetHasErrors(Control control) { return control.GetValue(HasErrorsProperty); } private static IEnumerable UnpackException(Exception exception) { if (exception != null) { var aggregate = exception as AggregateException; var exceptions = aggregate == null ? (IEnumerable)new[] { exception } : aggregate.InnerExceptions; var filtered = exceptions.Where(x => !(x is BindingChainException)).ToList(); if (filtered.Count > 0) { return filtered; } } return null; } } }