committed by
GitHub
6 changed files with 223 additions and 76 deletions
@ -0,0 +1,135 @@ |
|||
// 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.Primitives; |
|||
using Avalonia.Controls.Templates; |
|||
using Avalonia.Data; |
|||
|
|||
namespace Avalonia.Controls |
|||
{ |
|||
/// <summary>
|
|||
/// A control which displays an error notifier when there is a DataValidationError.
|
|||
/// Provides attached properties to track errors on a control
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// You will probably only want to create instances inside of control templates.
|
|||
/// </remarks>
|
|||
public class DataValidationErrors : ContentControl |
|||
{ |
|||
/// <summary>
|
|||
/// Defines the DataValidationErrors.Errors attached property.
|
|||
/// </summary>
|
|||
public static readonly AttachedProperty<IEnumerable<Exception>> ErrorsProperty = |
|||
AvaloniaProperty.RegisterAttached<DataValidationErrors, Control, IEnumerable<Exception>>("Errors"); |
|||
|
|||
/// <summary>
|
|||
/// Defines the DataValidationErrors.HasErrors attached property.
|
|||
/// </summary>
|
|||
public static readonly AttachedProperty<bool> HasErrorsProperty = |
|||
AvaloniaProperty.RegisterAttached<DataValidationErrors, Control, bool>("HasErrors"); |
|||
|
|||
public static readonly StyledProperty<IDataTemplate> ErrorTemplateProperty = |
|||
AvaloniaProperty.Register<DataValidationErrors, IDataTemplate>(nameof(ErrorTemplate)); |
|||
|
|||
|
|||
private Control _owner; |
|||
|
|||
public static readonly DirectProperty<DataValidationErrors, Control> OwnerProperty = |
|||
AvaloniaProperty.RegisterDirect<DataValidationErrors, Control>( |
|||
nameof(Owner), |
|||
o => o.Owner, |
|||
(o, v) => o.Owner = v); |
|||
|
|||
public Control Owner |
|||
{ |
|||
get { return _owner; } |
|||
set { SetAndRaise(OwnerProperty, ref _owner, value); } |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes static members of the <see cref="DataValidationErrors"/> class.
|
|||
/// </summary>
|
|||
static DataValidationErrors() |
|||
{ |
|||
ErrorsProperty.Changed.Subscribe(ErrorsChanged); |
|||
HasErrorsProperty.Changed.Subscribe(HasErrorsChanged); |
|||
TemplatedParentProperty.Changed.AddClassHandler<DataValidationErrors>(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<Exception>)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<Exception> GetErrors(Control control) |
|||
{ |
|||
return control.GetValue(ErrorsProperty); |
|||
} |
|||
public static void SetErrors(Control control, IEnumerable<Exception> 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<Exception> UnpackException(Exception exception) |
|||
{ |
|||
if (exception != null) |
|||
{ |
|||
var aggregate = exception as AggregateException; |
|||
var exceptions = aggregate == null ? |
|||
(IEnumerable<Exception>)new[] { exception } : |
|||
aggregate.InnerExceptions; |
|||
var filtered = exceptions.Where(x => !(x is BindingChainException)).ToList(); |
|||
|
|||
if (filtered.Count > 0) |
|||
{ |
|||
return filtered; |
|||
} |
|||
} |
|||
|
|||
return null; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,38 @@ |
|||
<Style xmlns="https://github.com/avaloniaui" |
|||
Selector="DataValidationErrors"> |
|||
<Setter Property="Template"> |
|||
<ControlTemplate> |
|||
<DockPanel LastChildFill="True"> |
|||
<ContentControl DockPanel.Dock="Right" |
|||
ContentTemplate="{TemplateBinding ErrorTemplate}" |
|||
DataContext="{TemplateBinding Owner}" |
|||
Content="{Binding (DataValidationErrors.Errors)}" |
|||
IsVisible="{Binding (DataValidationErrors.HasErrors)}"/> |
|||
<ContentPresenter Name="PART_ContentPresenter" |
|||
Background="{TemplateBinding Background}" |
|||
BorderBrush="{TemplateBinding BorderBrush}" |
|||
BorderThickness="{TemplateBinding BorderThickness}" |
|||
ContentTemplate="{TemplateBinding ContentTemplate}" |
|||
Content="{TemplateBinding Content}" |
|||
Padding="{TemplateBinding Padding}"/> |
|||
</DockPanel> |
|||
</ControlTemplate> |
|||
</Setter> |
|||
<Setter Property="ErrorTemplate"> |
|||
<DataTemplate> |
|||
<Canvas Width="14" Height="14" Margin="4 0 1 0" |
|||
Background="#00FFFFFF"> |
|||
<Canvas.Styles> |
|||
<Style Selector="ToolTip"> |
|||
<Setter Property="Background" Value="{DynamicResource ErrorBrushLight}"/> |
|||
<Setter Property="BorderBrush" Value="{DynamicResource ErrorBrush}"/> |
|||
</Style> |
|||
</Canvas.Styles> |
|||
<ToolTip.Tip> |
|||
<ItemsControl Items="{Binding}" MemberSelector="Message"/> |
|||
</ToolTip.Tip> |
|||
<Path Data="M14,7 A7,7 0 0,0 0,7 M0,7 A7,7 0 1,0 14,7 M7,3l0,5 M7,9l0,2" Stroke="{DynamicResource ErrorBrush}" StrokeThickness="2"/> |
|||
</Canvas> |
|||
</DataTemplate> |
|||
</Setter> |
|||
</Style> |
|||
Loading…
Reference in new issue