Skip to content

Commit 8884b6f

Browse files
authored
Restore ReactiveUI.Validation.AndroidX (#693)
1 parent 4503c3c commit 8884b6f

File tree

3 files changed

+206
-1
lines changed

3 files changed

+206
-1
lines changed
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
// Copyright (c) 2024 .NET Foundation and Contributors. All rights reserved.
2+
// Licensed to the .NET Foundation under one or more agreements.
3+
// The .NET Foundation licenses this file to you under the MIT license.
4+
// See the LICENSE file in the project root for full license information.
5+
6+
using System;
7+
using System.Diagnostics.CodeAnalysis;
8+
using System.Linq;
9+
using System.Linq.Expressions;
10+
using Google.Android.Material.TextField;
11+
using ReactiveUI.Validation.Abstractions;
12+
using ReactiveUI.Validation.Formatters;
13+
using ReactiveUI.Validation.Formatters.Abstractions;
14+
using ReactiveUI.Validation.Helpers;
15+
using ReactiveUI.Validation.ValidationBindings;
16+
using Splat;
17+
18+
// ReSharper disable once CheckNamespace
19+
namespace ReactiveUI.Validation.Extensions;
20+
21+
/// <summary>
22+
/// Android specific extensions methods associated to <see cref="IViewFor"/> instances.
23+
/// </summary>
24+
[SuppressMessage("Roslynator", "RCS1163", Justification = "Needed for Expression context.")]
25+
public static class ViewForExtensions
26+
{
27+
/// <summary>
28+
/// Platform binding to the TextInputLayout.
29+
/// </summary>
30+
/// <typeparam name="TView">IViewFor of TViewModel.</typeparam>
31+
/// <typeparam name="TViewModel">ViewModel type.</typeparam>
32+
/// <typeparam name="TViewModelProperty">ViewModel property type.</typeparam>
33+
/// <param name="view">IViewFor instance.</param>
34+
/// <param name="viewModel">ViewModel instance. Can be null, used for generic type resolution.</param>
35+
/// <param name="viewModelProperty">ViewModel property.</param>
36+
/// <param name="viewProperty">View property to bind the validation message.</param>
37+
/// <param name="formatter">
38+
/// Validation formatter. Defaults to <see cref="SingleLineFormatter"/>. In order to override the global
39+
/// default value, implement <see cref="IValidationTextFormatter{TOut}"/> and register an instance of
40+
/// IValidationTextFormatter&lt;string&gt; into Splat.Locator.
41+
/// </param>
42+
/// <returns>Returns a <see cref="IDisposable"/> object.</returns>
43+
[SuppressMessage("Design", "CA1801: Parameter unused", Justification = "Used for generic resolution.")]
44+
public static IDisposable BindValidation<TView, TViewModel, TViewModelProperty>(
45+
this TView view,
46+
TViewModel? viewModel,
47+
Expression<Func<TViewModel, TViewModelProperty?>> viewModelProperty,
48+
TextInputLayout viewProperty,
49+
IValidationTextFormatter<string>? formatter = null)
50+
where TView : IViewFor<TViewModel>
51+
where TViewModel : class, IReactiveObject, IValidatableViewModel
52+
{
53+
if (view is null)
54+
{
55+
throw new ArgumentNullException(nameof(view));
56+
}
57+
58+
if (viewModelProperty is null)
59+
{
60+
throw new ArgumentNullException(nameof(viewModelProperty));
61+
}
62+
63+
if (viewProperty is null)
64+
{
65+
throw new ArgumentNullException(nameof(viewProperty));
66+
}
67+
68+
formatter ??= Locator.Current.GetService<IValidationTextFormatter<string>>() ??
69+
SingleLineFormatter.Default;
70+
71+
return ValidationBinding.ForProperty(
72+
view,
73+
viewModelProperty,
74+
(_, errors) => viewProperty.Error = errors.FirstOrDefault(msg => !string.IsNullOrEmpty(msg)),
75+
formatter);
76+
}
77+
78+
/// <summary>
79+
/// Platform binding to the TextInputLayout.
80+
/// </summary>
81+
/// <remarks>Supports multiple validations for the same property.</remarks>
82+
/// <typeparam name="TView">IViewFor of TViewModel.</typeparam>
83+
/// <typeparam name="TViewModel">ViewModel type.</typeparam>
84+
/// <typeparam name="TViewModelProperty">ViewModel property type.</typeparam>
85+
/// <param name="view">IViewFor instance.</param>
86+
/// <param name="viewModel">ViewModel instance. Can be null, used for generic type resolution.</param>
87+
/// <param name="viewModelProperty">ViewModel property.</param>
88+
/// <param name="viewProperty">View property to bind the validation message.</param>
89+
/// <param name="formatter">
90+
/// Validation formatter. Defaults to <see cref="SingleLineFormatter"/>. In order to override the global
91+
/// default value, implement <see cref="IValidationTextFormatter{TOut}"/> and register an instance of
92+
/// IValidationTextFormatter&lt;string&gt; into Splat.Locator.
93+
/// </param>
94+
/// <returns>Returns a <see cref="IDisposable"/> object.</returns>
95+
[ExcludeFromCodeCoverage]
96+
[Obsolete("This method is no longer required, BindValidation now supports multiple validations.")]
97+
[SuppressMessage("Design", "CA1801: Parameter unused", Justification = "Used for generic resolution.")]
98+
public static IDisposable BindValidationEx<TView, TViewModel, TViewModelProperty>(
99+
this TView view,
100+
TViewModel? viewModel,
101+
Expression<Func<TViewModel, TViewModelProperty?>> viewModelProperty,
102+
TextInputLayout viewProperty,
103+
IValidationTextFormatter<string>? formatter = null)
104+
where TView : IViewFor<TViewModel>
105+
where TViewModel : class, IReactiveObject, IValidatableViewModel
106+
{
107+
if (view is null)
108+
{
109+
throw new ArgumentNullException(nameof(view));
110+
}
111+
112+
if (viewModelProperty is null)
113+
{
114+
throw new ArgumentNullException(nameof(viewModelProperty));
115+
}
116+
117+
if (viewProperty is null)
118+
{
119+
throw new ArgumentNullException(nameof(viewProperty));
120+
}
121+
122+
formatter ??= Locator.Current.GetService<IValidationTextFormatter<string>>() ??
123+
SingleLineFormatter.Default;
124+
125+
return ValidationBinding.ForProperty(
126+
view,
127+
viewModelProperty,
128+
(_, errors) => viewProperty.Error = errors.FirstOrDefault(msg => !string.IsNullOrEmpty(msg)),
129+
formatter);
130+
}
131+
132+
/// <summary>
133+
/// Platform binding to the TextInputLayout.
134+
/// </summary>
135+
/// <typeparam name="TView">IViewFor of TViewModel.</typeparam>
136+
/// <typeparam name="TViewModel">ViewModel type.</typeparam>
137+
/// <param name="view">IViewFor instance.</param>
138+
/// <param name="viewModel">ViewModel instance. Can be null, used for generic type resolution.</param>
139+
/// <param name="viewModelHelperProperty">ViewModel's ValidationHelper property.</param>
140+
/// <param name="viewProperty">View property to bind the validation message.</param>
141+
/// <param name="formatter">
142+
/// Validation formatter. Defaults to <see cref="SingleLineFormatter"/>. In order to override the global
143+
/// default value, implement <see cref="IValidationTextFormatter{TOut}"/> and register an instance of
144+
/// IValidationTextFormatter&lt;string&gt; into Splat.Locator.
145+
/// </param>
146+
/// <returns>Returns a <see cref="IDisposable"/> object.</returns>
147+
[SuppressMessage("Design", "CA1801: Parameter unused", Justification = "Used for generic resolution.")]
148+
public static IDisposable BindValidation<TView, TViewModel>(
149+
this TView view,
150+
TViewModel? viewModel,
151+
Expression<Func<TViewModel?, ValidationHelper?>> viewModelHelperProperty,
152+
TextInputLayout viewProperty,
153+
IValidationTextFormatter<string>? formatter = null)
154+
where TView : IViewFor<TViewModel>
155+
where TViewModel : class, IReactiveObject, IValidatableViewModel
156+
{
157+
if (view is null)
158+
{
159+
throw new ArgumentNullException(nameof(view));
160+
}
161+
162+
if (viewModelHelperProperty is null)
163+
{
164+
throw new ArgumentNullException(nameof(viewModelHelperProperty));
165+
}
166+
167+
if (viewProperty is null)
168+
{
169+
throw new ArgumentNullException(nameof(viewProperty));
170+
}
171+
172+
formatter ??= Locator.Current.GetService<IValidationTextFormatter<string>>() ??
173+
SingleLineFormatter.Default;
174+
175+
return ValidationBinding.ForValidationHelperProperty(
176+
view,
177+
viewModelHelperProperty,
178+
(_, errorText) => viewProperty.Error = errorText,
179+
formatter);
180+
}
181+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFrameworks>net8.0-android</TargetFrameworks>
5+
<PackageDescription>Provides ReactiveUI.Validation extensions for the AndroidX Library</PackageDescription>
6+
<PackageId>ReactiveUI.Validation.AndroidX</PackageId>
7+
<NoWarn>$(NoWarn);CS1591</NoWarn>
8+
<Nullable>enable</Nullable>
9+
</PropertyGroup>
10+
11+
<ItemGroup>
12+
<PackageReference Include="ReactiveUI.AndroidX" Version="20.*" />
13+
</ItemGroup>
14+
15+
<ItemGroup>
16+
<ProjectReference Include="..\ReactiveUI.Validation\ReactiveUI.Validation.csproj" />
17+
</ItemGroup>
18+
</Project>

src/ReactiveUI.Validation.sln

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11

22
Microsoft Visual Studio Solution File, Format Version 12.00
3-
# Visual Studio Version 17
3+
# 17
44
VisualStudioVersion = 17.9.34728.123
55
MinimumVisualStudioVersion = 10.0.40219.1
66
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ReactiveUI.Validation", "ReactiveUI.Validation\ReactiveUI.Validation.csproj", "{B62AABD0-22A4-470D-B6EB-F6B3EAE668DE}"
@@ -18,6 +18,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
1818
..\version.json = ..\version.json
1919
EndProjectSection
2020
EndProject
21+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ReactiveUI.Validation.AndroidX", "ReactiveUI.Validation.AndroidX\ReactiveUI.Validation.AndroidX.csproj", "{6E984244-95F9-4EA6-BEE2-F665C0C4BCFD}"
22+
EndProject
2123
Global
2224
GlobalSection(SolutionConfigurationPlatforms) = preSolution
2325
Debug|Any CPU = Debug|Any CPU
@@ -32,6 +34,10 @@ Global
3234
{892D51D9-7A3F-4E66-A871-082A63D9BE05}.Debug|Any CPU.Build.0 = Debug|Any CPU
3335
{892D51D9-7A3F-4E66-A871-082A63D9BE05}.Release|Any CPU.ActiveCfg = Release|Any CPU
3436
{892D51D9-7A3F-4E66-A871-082A63D9BE05}.Release|Any CPU.Build.0 = Release|Any CPU
37+
{6E984244-95F9-4EA6-BEE2-F665C0C4BCFD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
38+
{6E984244-95F9-4EA6-BEE2-F665C0C4BCFD}.Debug|Any CPU.Build.0 = Debug|Any CPU
39+
{6E984244-95F9-4EA6-BEE2-F665C0C4BCFD}.Release|Any CPU.ActiveCfg = Release|Any CPU
40+
{6E984244-95F9-4EA6-BEE2-F665C0C4BCFD}.Release|Any CPU.Build.0 = Release|Any CPU
3541
EndGlobalSection
3642
GlobalSection(SolutionProperties) = preSolution
3743
HideSolutionNode = FALSE

0 commit comments

Comments
 (0)