Skip to content

Commit ffdf393

Browse files
committed
Extended the library to support ValidationRules in similar manner as the DataTemplateSelector extension.
And some corrections of the previous commit. Signed-off-by: Dima Enns <[email protected]>
1 parent 54aff51 commit ffdf393

13 files changed

+410
-7
lines changed

Sources/LambdaConverters.Wpf/EventSource.cs

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ public static class Keywords
1414
{
1515
public const EventKeywords Converters = (EventKeywords)0x4;
1616
public const EventKeywords Selectors = (EventKeywords)0x8;
17+
public const EventKeywords Rules = (EventKeywords)0x10;
1718
}
1819

1920
[NotNull]
@@ -510,5 +511,63 @@ public void UnableToCastToItemType(
510511
memberName,
511512
sourceFilePath,
512513
sourceLineNumber);
514+
515+
/// <summary>
516+
/// The {0} is null, conversion result is a value according to the specified error strategy ({1}).
517+
/// </summary>
518+
[Conditional("TRACE")]
519+
[Event(
520+
24,
521+
Version = 0,
522+
Message = "The {0} is null, conversion result is a value according to the specified error strategy ({1}).",
523+
Level = EventLevel.Warning,
524+
Keywords = Keywords.Rules,
525+
Opcode = EventOpcode.Info,
526+
Channel = EventChannel.Operational
527+
)]
528+
public void MissingRuleFunction(
529+
string callback,
530+
string errorStrategy,
531+
[CallerMemberName] string memberName = null,
532+
[CallerFilePath] string sourceFilePath = null,
533+
[CallerLineNumber] int sourceLineNumber = 0)
534+
=>
535+
WriteEvent(
536+
24,
537+
callback,
538+
errorStrategy,
539+
memberName,
540+
sourceFilePath,
541+
sourceLineNumber);
542+
543+
/// <summary>
544+
/// The value ({0}) cannot be cast to the specified input type ({1}), conversion result is a value according to the specified error strategy ({2}).
545+
/// </summary>
546+
[Conditional("TRACE")]
547+
[Event(
548+
25,
549+
Version = 0,
550+
Message = "The value ({0}) cannot be cast to the specified input type ({1}), conversion result is a value according to the specified error strategy ({2}).",
551+
Level = EventLevel.Warning,
552+
Keywords = Keywords.Rules,
553+
Opcode = EventOpcode.Info,
554+
Channel = EventChannel.Operational
555+
)]
556+
public void UnableToCastToRuleInputType(
557+
string itemType,
558+
string inputType,
559+
string errorStrategy,
560+
[CallerMemberName] string memberName = null,
561+
[CallerFilePath] string sourceFilePath = null,
562+
[CallerLineNumber] int sourceLineNumber = 0)
563+
=>
564+
WriteEvent(
565+
25,
566+
itemType,
567+
inputType,
568+
errorStrategy,
569+
memberName,
570+
sourceFilePath,
571+
sourceLineNumber);
513572
}
514573
}

Sources/LambdaConverters.Wpf/LambdaConverters.Wpf.csproj

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,10 @@
5757
<Reference Include="WindowsBase" />
5858
</ItemGroup>
5959
<ItemGroup>
60+
<Compile Include="Rule.cs" />
6061
<Compile Include="Selector.cs" />
6162
<Compile Include="Converter.cs" />
63+
<Compile Include="RuleErrorStrategy.cs" />
6264
<Compile Include="SelectorErrorStrategy.cs" />
6365
<Compile Include="ConverterErrorStrategy.cs" />
6466
<Compile Include="EventSource.cs" />
@@ -68,17 +70,19 @@
6870
<Compile Include="MultiValueConverterArgs[T].cs" />
6971
<Compile Include="MultiValueConverterArgs[T].IEquatable[MultiValueConverterArgs[T]].cs" />
7072
<Compile Include="Properties\AssemblyInfo.cs" />
73+
<Compile Include="Validator.cs" />
7174
<Compile Include="TemplateSelector.cs" />
7275
<Compile Include="ValueConverter.cs" />
76+
<Compile Include="ValidationRuleArgs[T].cs" />
7377
<Compile Include="ValueConverterArgs[T,P].cs" />
7478
<Compile Include="ValueConverterArgs[T,P].IEquatable[ValueConverterArgs[T,P]].cs" />
7579
<Compile Include="TemplateSelectorArgs[T].cs" />
7680
<Compile Include="ValueConverterArgs[T].cs" />
77-
<Compile Include="TemplateSelectorArgs[T].IEquatable[DataTemplateSelectorArgs[T]].cs" />
81+
<Compile Include="TemplateSelectorArgs[T].IEquatable[TemplateSelectorArgs[T]].cs" />
82+
<Compile Include="ValidationRuleArgs[T].IEquatable[ValidationRuleArgs[T]].cs" />
7883
<Compile Include="ValueConverterArgs[T].IEquatable[ValueConverterArgs[T]].cs" />
7984
</ItemGroup>
8085
<ItemGroup>
81-
<None Include="ClassDiagram1.cd" />
8286
<None Include="packages.config" />
8387
<None Include="Properties\LambdaConverters.public.snk" />
8488
</ItemGroup>

Sources/LambdaConverters.Wpf/Rule.cs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
using System;
2+
using System.Windows.Controls;
3+
using JetBrains.Annotations;
4+
5+
namespace LambdaConverters
6+
{
7+
internal abstract class Rule : System.Windows.Controls.ValidationRule
8+
{
9+
internal Rule(
10+
RuleErrorStrategy errorStrategy,
11+
object defaultInputTypeValue,
12+
[NotNull] Type inputType,
13+
bool isRuleFunctionAvailable)
14+
{
15+
ErrorStrategy = errorStrategy;
16+
DefaultInputTypeValue = defaultInputTypeValue;
17+
InputType = inputType;
18+
IsRuleFunctionAvailable = isRuleFunctionAvailable;
19+
}
20+
21+
internal RuleErrorStrategy ErrorStrategy { get; }
22+
23+
internal object DefaultInputTypeValue { get; }
24+
25+
[NotNull]
26+
internal Type InputType { get; }
27+
28+
internal bool IsRuleFunctionAvailable { get; }
29+
30+
[Pure]
31+
internal ValidationResult GetErrorValue(ValidationResult defaultValue)
32+
{
33+
switch (ErrorStrategy)
34+
{
35+
case RuleErrorStrategy.ReturnDefaultValue:
36+
return defaultValue;
37+
38+
case RuleErrorStrategy.ReturnInvalid:
39+
return new ValidationResult(false, null);
40+
41+
case RuleErrorStrategy.ReturnValid:
42+
return new ValidationResult(true, null);
43+
44+
default:
45+
throw new NotSupportedException();
46+
}
47+
}
48+
}
49+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
using System.Windows.Controls;
2+
3+
namespace LambdaConverters
4+
{
5+
/// <summary>
6+
/// Defines the validation rule error strategy.
7+
/// </summary>
8+
public enum RuleErrorStrategy
9+
{
10+
/// <summary>
11+
/// The default of <see cref="ValidationRule" /> is returned.
12+
/// </summary>
13+
ReturnDefaultValue,
14+
15+
/// <summary>
16+
/// An invalid validation result is returned.
17+
/// </summary>
18+
ReturnInvalid,
19+
20+
/// <summary>
21+
/// A valid validation result is returned.
22+
/// </summary>
23+
ReturnValid
24+
}
25+
}

Sources/LambdaConverters.Wpf/Selector.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,12 @@ internal Selector(
1010
SelectorErrorStrategy errorStrategy,
1111
object defaultInputTypeValue,
1212
[NotNull] Type inputType,
13-
bool isConvertFunctionAvailable)
13+
bool isSelectFunctionAvailable)
1414
{
1515
ErrorStrategy = errorStrategy;
1616
DefaultInputTypeValue = defaultInputTypeValue;
1717
InputType = inputType;
18-
IsSelectFunctionAvailable = isConvertFunctionAvailable;
18+
IsSelectFunctionAvailable = isSelectFunctionAvailable;
1919
}
2020

2121
internal SelectorErrorStrategy ErrorStrategy { get; }

Sources/LambdaConverters.Wpf/TemplateSelector.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ protected override DataTemplate SelectTemplateInternal(object item, DependencyOb
7272
}
7373

7474
/// <summary>
75-
/// Initializes a new instance of the <see cref="DataTemplateSelector" /> interface.
75+
/// Initializes a new instance of the <see cref="DataTemplateSelector" /> class.
7676
/// </summary>
7777
/// <typeparam name="I">The item type.</typeparam>
7878
/// <param name="selectFunction">The <see cref="DataTemplateSelector.SelectTemplate" /> method.</param>
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
using System;
2+
using System.Collections.Generic;
3+
4+
namespace LambdaConverters
5+
{
6+
partial struct ValidationRuleArgs<T> : IEquatable<ValidationRuleArgs<T>>
7+
{
8+
/// <summary>
9+
/// Implements the operator <c>==</c>.
10+
/// </summary>
11+
/// <param name="x">The left operand.</param>
12+
/// <param name="y">The right operand.</param>
13+
/// <returns>The result of the operator.</returns>
14+
public static bool operator ==(ValidationRuleArgs<T> x, ValidationRuleArgs<T> y) => Equals(x.Value, y.Value) && Equals(x.Culture, y.Culture);
15+
16+
/// <summary>
17+
/// Implements the operator <c>!=</c>.
18+
/// </summary>
19+
/// <param name="x">The left operand.</param>
20+
/// <param name="y">The right operand.</param>
21+
/// <returns>The result of the operator.</returns>
22+
public static bool operator !=(ValidationRuleArgs<T> x, ValidationRuleArgs<T> y) => !Equals(x.Value, y.Value) || !Equals(x.Culture, y.Culture);
23+
24+
/// <inheritdoc />
25+
public override int GetHashCode() => EqualityComparer<T>.Default.GetHashCode(Value) ^ (Culture?.GetHashCode() ?? 0);
26+
27+
/// <inheritdoc />
28+
public override bool Equals(object obj) => obj is ValidationRuleArgs<T> && Equals((ValidationRuleArgs<T>)obj);
29+
30+
/// <inheritdoc />
31+
public bool Equals(ValidationRuleArgs<T> other) => this == other;
32+
}
33+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
using System.Globalization;
2+
3+
namespace LambdaConverters
4+
{
5+
/// <summary>
6+
/// Provides data for parameterized validation rule functions.
7+
/// </summary>
8+
/// <typeparam name="T">The value type.</typeparam>
9+
public partial struct ValidationRuleArgs<T>
10+
{
11+
internal ValidationRuleArgs(T value, CultureInfo culture)
12+
{
13+
Value = value;
14+
Culture = culture;
15+
}
16+
17+
/// <summary>
18+
/// Gets the value.
19+
/// </summary>
20+
public T Value { get; }
21+
22+
/// <summary>
23+
/// Gets the culture.
24+
/// </summary>
25+
public CultureInfo Culture { get; }
26+
}
27+
}
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
using System;
2+
using System.Diagnostics;
3+
using System.Globalization;
4+
using System.Windows.Controls;
5+
using JetBrains.Annotations;
6+
7+
namespace LambdaConverters
8+
{
9+
/// <summary>
10+
/// A factory class used to create lambda-based instances of the <see cref="ValidationRule"/> class.
11+
/// </summary>
12+
public static class Validator
13+
{
14+
abstract class Rule : LambdaConverters.Rule
15+
{
16+
protected Rule(
17+
RuleErrorStrategy errorStrategy,
18+
object defaultInputTypeValue,
19+
[NotNull] Type inputType,
20+
bool isRuleFunctionAvailable)
21+
: base(
22+
errorStrategy,
23+
defaultInputTypeValue,
24+
inputType,
25+
isRuleFunctionAvailable) { }
26+
27+
protected abstract ValidationResult ValidateInternal(object value, CultureInfo cultureInfo);
28+
29+
public override ValidationResult Validate(object value, CultureInfo cultureInfo)
30+
{
31+
if (!IsRuleFunctionAvailable)
32+
{
33+
EventSource.Log.MissingRuleFunction("ruleFunction", ErrorStrategy.ToString());
34+
35+
return GetErrorValue(default(ValidationResult));
36+
}
37+
38+
return ValidateInternal(value, cultureInfo);
39+
}
40+
}
41+
42+
sealed class Rule<I> : Rule
43+
{
44+
readonly Func<ValidationRuleArgs<I>, ValidationResult> ruleFunction;
45+
46+
internal Rule(
47+
Func<ValidationRuleArgs<I>, ValidationResult> ruleFunction,
48+
RuleErrorStrategy errorStrategy)
49+
: base(errorStrategy, default(I), typeof(I), ruleFunction != null)
50+
{
51+
this.ruleFunction = ruleFunction;
52+
}
53+
54+
protected override ValidationResult ValidateInternal(object item, CultureInfo cultureInfo)
55+
{
56+
I inputValue;
57+
try
58+
{
59+
inputValue = (I)item;
60+
}
61+
catch (SystemException e) when (e is InvalidCastException || e is NullReferenceException)
62+
{
63+
EventSource.Log.UnableToCastToRuleInputType(item?.GetType().Name ?? "null", typeof(I).Name, ErrorStrategy.ToString());
64+
65+
return GetErrorValue(default(ValidationResult));
66+
}
67+
68+
Debug.Assert(ruleFunction != null);
69+
70+
return ruleFunction(new ValidationRuleArgs<I>(inputValue, cultureInfo));
71+
}
72+
}
73+
74+
/// <summary>
75+
/// Initializes a new instance of the <see cref="ValidationRule" /> class.
76+
/// </summary>
77+
/// <typeparam name="I">The value type.</typeparam>
78+
/// <param name="ruleFunction">The <see cref="ValidationRule.Validate" /> method.</param>
79+
/// <param name="errorStrategy">The error strategy.</param>
80+
/// <returns>An <see cref="ValidationRule" /> object.</returns>
81+
/// <exception cref="ArgumentOutOfRangeException">
82+
/// <paramref name="errorStrategy"/> is not a valid <see cref="RuleErrorStrategy"/> value.
83+
/// </exception>
84+
[Pure]
85+
[NotNull]
86+
public static ValidationRule Create<I>(
87+
Func<ValidationRuleArgs<I>, ValidationResult> ruleFunction = null,
88+
RuleErrorStrategy errorStrategy = RuleErrorStrategy.ReturnDefaultValue)
89+
{
90+
switch (errorStrategy)
91+
{
92+
case RuleErrorStrategy.ReturnDefaultValue:
93+
case RuleErrorStrategy.ReturnInvalid:
94+
case RuleErrorStrategy.ReturnValid:
95+
break;
96+
97+
default:
98+
throw new ArgumentOutOfRangeException(nameof(errorStrategy));
99+
}
100+
101+
return new Rule<I>(ruleFunction, errorStrategy);
102+
}
103+
}
104+
}

0 commit comments

Comments
 (0)