Browse Source

Created modular validation plugin system instead of being hard-coded to just INotifyDataErrorInfo. Current plugins are for Exception validation and INotifyDataErrorInfo. Currently not in use anywhere.

pull/514/head
Jeremy Koritzinsky 11 years ago
parent
commit
76f5249f59
  1. 52
      src/Markup/Perspex.Markup/Data/Plugins/ExceptionValidationCheckerPlugin.cs
  2. 14
      src/Markup/Perspex.Markup/Data/Plugins/IValidationCheckerPlugin.cs
  3. 73
      src/Markup/Perspex.Markup/Data/Plugins/IndeiValidationCheckerPlugin.cs
  4. 34
      src/Markup/Perspex.Markup/Data/Plugins/ValidationCheckerBase.cs
  5. 4
      src/Markup/Perspex.Markup/Perspex.Markup.csproj
  6. 20
      src/Perspex.Base/Data/ValidationStatus.cs
  7. 1
      src/Perspex.Base/Perspex.Base.csproj
  8. 8
      tests/Perspex.Markup.UnitTests/Data/InpcPluginTests.cs

52
src/Markup/Perspex.Markup/Data/Plugins/ExceptionValidationCheckerPlugin.cs

@ -0,0 +1,52 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Perspex.Data;
namespace Perspex.Markup.Data.Plugins
{
public class ExceptionValidationCheckerPlugin : IValidationCheckerPlugin
{
public ValidationCheckerBase Start(WeakReference reference, string name, IPropertyAccessor accessor, Action<ValidationStatus> callback)
{
return new ExceptionValidationChecker(reference, name, accessor, callback);
}
private class ExceptionValidationChecker : ValidationCheckerBase
{
public ExceptionValidationChecker(WeakReference reference, string name, IPropertyAccessor accessor, Action<ValidationStatus> callback)
: base(reference, name, accessor, callback)
{
}
public override bool SetValue(object value, BindingPriority priority)
{
try
{
var success = base.SetValue(value, priority);
SendValidationCallback(new ExceptionValidationStatus(null));
return success;
}
catch (Exception ex)
{
SendValidationCallback(new ExceptionValidationStatus(ex));
}
return false;
}
}
private class ExceptionValidationStatus : ValidationStatus
{
public ExceptionValidationStatus(Exception exception)
{
Exception = exception;
}
public Exception Exception { get; }
public override bool IsValid => Exception != null;
}
}
}

14
src/Markup/Perspex.Markup/Data/Plugins/IValidationCheckerPlugin.cs

@ -0,0 +1,14 @@
using Perspex.Data;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Perspex.Markup.Data.Plugins
{
public interface IValidationCheckerPlugin
{
ValidationCheckerBase Start(WeakReference reference, string name, IPropertyAccessor accessor, Action<ValidationStatus> callback);
}
}

73
src/Markup/Perspex.Markup/Data/Plugins/IndeiValidationCheckerPlugin.cs

@ -0,0 +1,73 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Perspex.Data;
using System.ComponentModel;
using System.Collections;
using Perspex.Utilities;
namespace Perspex.Markup.Data.Plugins
{
class IndeiValidationCheckerPlugin : IValidationCheckerPlugin
{
public ValidationCheckerBase Start(WeakReference reference, string name, IPropertyAccessor accessor, Action<ValidationStatus> callback)
{
return new IndeiValidationChecker(reference, name, accessor, callback);
}
private class IndeiValidationChecker : ValidationCheckerBase, IWeakSubscriber<DataErrorsChangedEventArgs>
{
public IndeiValidationChecker(WeakReference reference, string name, IPropertyAccessor accessor, Action<ValidationStatus> callback)
: base(reference, name, accessor, callback)
{
var target = reference.Target as INotifyDataErrorInfo;
if (target != null)
{
if (target.HasErrors)
{
SendValidationCallback(new IndeiValidationStatus(target.GetErrors(name)));
}
WeakSubscriptionManager.Subscribe(
target,
nameof(target.ErrorsChanged),
this);
}
}
public override void Dispose()
{
base.Dispose();
var target = _reference.Target as INotifyDataErrorInfo;
if (target != null)
{
WeakSubscriptionManager.Unsubscribe(
target,
nameof(target.ErrorsChanged),
this);
}
}
public void OnEvent(object sender, DataErrorsChangedEventArgs e)
{
if (e.PropertyName == _name || string.IsNullOrEmpty(e.PropertyName))
{
var indei = _reference.Target as INotifyDataErrorInfo;
SendValidationCallback(new IndeiValidationStatus(indei.GetErrors(e.PropertyName)));
}
}
}
private class IndeiValidationStatus : ValidationStatus
{
public IndeiValidationStatus(IEnumerable errors)
{
Errors = errors;
}
public override bool IsValid => !Errors.OfType<object>().Any();
public IEnumerable Errors { get; }
}
}
}

34
src/Markup/Perspex.Markup/Data/Plugins/ValidationCheckerBase.cs

@ -0,0 +1,34 @@
using System;
using Perspex.Data;
namespace Perspex.Markup.Data.Plugins
{
public abstract class ValidationCheckerBase : IPropertyAccessor
{
protected readonly WeakReference _reference;
protected readonly string _name;
private readonly IPropertyAccessor _accessor;
private readonly Action<ValidationStatus> _callback;
protected ValidationCheckerBase(WeakReference reference, string name, IPropertyAccessor accessor, Action<ValidationStatus> callback)
{
_reference = reference;
_name = name;
_accessor = accessor;
_callback = callback;
}
public Type PropertyType => _accessor.PropertyType;
public object Value => _accessor.Value;
public virtual void Dispose() => _accessor.Dispose();
public virtual bool SetValue(object value, BindingPriority priority) => _accessor.SetValue(value, priority);
protected void SendValidationCallback(ValidationStatus status)
{
_callback?.Invoke(status);
}
}
}

4
src/Markup/Perspex.Markup/Perspex.Markup.csproj

@ -46,6 +46,10 @@
<Compile Include="Data\ExpressionParseException.cs" />
<Compile Include="Data\ExpressionSubject.cs" />
<Compile Include="ControlLocator.cs" />
<Compile Include="Data\Plugins\ExceptionValidationCheckerPlugin.cs" />
<Compile Include="Data\Plugins\IndeiValidationCheckerPlugin.cs" />
<Compile Include="Data\Plugins\ValidationCheckerBase.cs" />
<Compile Include="Data\Plugins\IValidationCheckerPlugin.cs" />
<Compile Include="Data\Plugins\PerspexPropertyAccessorPlugin.cs" />
<Compile Include="Data\Plugins\InpcPropertyAccessorPlugin.cs" />
<Compile Include="Data\Plugins\IPropertyAccessor.cs" />

20
src/Perspex.Base/Data/ValidationStatus.cs

@ -0,0 +1,20 @@
// Copyright (c) The Perspex 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.Text;
using System.Threading.Tasks;
namespace Perspex.Data
{
/// <summary>
/// Contains information on if the current object passed validation.
/// Subclasses of this class contain additional information depending on the method of validation checking.
/// </summary>
public abstract class ValidationStatus
{
public abstract bool IsValid { get; }
}
}

1
src/Perspex.Base/Perspex.Base.csproj

@ -44,6 +44,7 @@
<Link>Properties\SharedAssemblyInfo.cs</Link>
</Compile>
<Compile Include="Data\BindingError.cs" />
<Compile Include="Data\ValidationStatus.cs" />
<Compile Include="Diagnostics\INotifyCollectionChangedDebug.cs" />
<Compile Include="Data\AssignBindingAttribute.cs" />
<Compile Include="Data\BindingOperations.cs" />

8
tests/Perspex.Markup.UnitTests/Data/InpcPluginTests.cs

@ -19,7 +19,11 @@ namespace Perspex.Markup.UnitTests.Data
public int NoValidationTest
{
get { return noValidationTest; }
set { noValidationTest = value; NotifyPropertyChanged(); }
set
{
noValidationTest = value;
NotifyPropertyChanged();
}
}
public bool HasErrors
@ -79,7 +83,7 @@ namespace Perspex.Markup.UnitTests.Data
var plugin = new InpcPropertyAccessorPlugin();
var source = new InpcTest { NoValidationTest = 0 };
var changeFired = false;
plugin.Start(new WeakReference(source), nameof(InpcTest.NoValidationTest), _ => changeFired = true, _ => { });
var accessor = plugin.Start(new WeakReference(source), nameof(InpcTest.NoValidationTest), _ => changeFired = true, _ => { });
source.NoValidationTest = 1;
Assert.True(changeFired);

Loading…
Cancel
Save