diff --git a/src/Avalonia.Base/AvaloniaProperty.cs b/src/Avalonia.Base/AvaloniaProperty.cs
index a39b8e6e1d..31a7f21a1b 100644
--- a/src/Avalonia.Base/AvaloniaProperty.cs
+++ b/src/Avalonia.Base/AvaloniaProperty.cs
@@ -257,6 +257,7 @@ namespace Avalonia
/// The default value of the property.
/// Whether the property inherits its value.
/// The default binding mode for the property.
+ /// A value validation callback.
///
/// A method that gets called before and after the property starts being notified on an
/// object; the bool argument will be true before and false afterwards. This callback is
@@ -268,6 +269,7 @@ namespace Avalonia
TValue defaultValue = default(TValue),
bool inherits = false,
BindingMode defaultBindingMode = BindingMode.OneWay,
+ Func validate = null,
Action notifying = null)
where TOwner : IAvaloniaObject
{
@@ -282,6 +284,7 @@ namespace Avalonia
typeof(TOwner),
metadata,
inherits,
+ validate,
notifying);
AvaloniaPropertyRegistry.Instance.Register(typeof(TOwner), result);
return result;
@@ -297,12 +300,14 @@ namespace Avalonia
/// The default value of the property.
/// Whether the property inherits its value.
/// The default binding mode for the property.
+ /// A value validation callback.
/// A
public static AttachedProperty RegisterAttached(
string name,
TValue defaultValue = default(TValue),
bool inherits = false,
- BindingMode defaultBindingMode = BindingMode.OneWay)
+ BindingMode defaultBindingMode = BindingMode.OneWay,
+ Func validate = null)
where THost : IAvaloniaObject
{
Contract.Requires(name != null);
diff --git a/src/Avalonia.Base/BoxedValue.cs b/src/Avalonia.Base/BoxedValue.cs
deleted file mode 100644
index 5fc515f299..0000000000
--- a/src/Avalonia.Base/BoxedValue.cs
+++ /dev/null
@@ -1,28 +0,0 @@
-// 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.
-
-namespace Avalonia
-{
- ///
- /// Represents boxed value of type .
- ///
- /// Type of stored value.
- internal readonly struct BoxedValue
- {
- public BoxedValue(T value)
- {
- Boxed = value;
- Typed = value;
- }
-
- ///
- /// Boxed value.
- ///
- public object Boxed { get; }
-
- ///
- /// Typed value.
- ///
- public T Typed { get; }
- }
-}
diff --git a/src/Avalonia.Base/PropertyStore/BindingEntry.cs b/src/Avalonia.Base/PropertyStore/BindingEntry.cs
index 2a7cf098a5..a53dda2aef 100644
--- a/src/Avalonia.Base/PropertyStore/BindingEntry.cs
+++ b/src/Avalonia.Base/PropertyStore/BindingEntry.cs
@@ -77,6 +77,11 @@ namespace Avalonia.PropertyStore
private void UpdateValue(BindingValue value)
{
+ if (value.HasValue && Property.ValidateValue?.Invoke(value.Value) == false)
+ {
+ value = Property.GetDefaultValue(_owner.GetType());
+ }
+
if (value.Type == BindingValueType.DoNothing)
{
return;
diff --git a/src/Avalonia.Base/StyledProperty.cs b/src/Avalonia.Base/StyledProperty.cs
index 4eb85a046e..62443b424c 100644
--- a/src/Avalonia.Base/StyledProperty.cs
+++ b/src/Avalonia.Base/StyledProperty.cs
@@ -17,14 +17,16 @@ namespace Avalonia
/// The type of the class that registers the property.
/// The property metadata.
/// Whether the property inherits its value.
+ /// A value validation callback.
/// A callback.
public StyledProperty(
string name,
Type ownerType,
StyledPropertyMetadata metadata,
bool inherits = false,
+ Func validate = null,
Action notifying = null)
- : base(name, ownerType, metadata, inherits, notifying)
+ : base(name, ownerType, metadata, inherits, validate, notifying)
{
}
diff --git a/src/Avalonia.Base/StyledPropertyBase.cs b/src/Avalonia.Base/StyledPropertyBase.cs
index 129b1f3c12..8e7bfd6467 100644
--- a/src/Avalonia.Base/StyledPropertyBase.cs
+++ b/src/Avalonia.Base/StyledPropertyBase.cs
@@ -2,7 +2,6 @@
// 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.Diagnostics;
using Avalonia.Data;
using Avalonia.Reactive;
@@ -23,12 +22,14 @@ namespace Avalonia
/// The type of the class that registers the property.
/// The property metadata.
/// Whether the property inherits its value.
+ /// A value validation callback.
/// A callback.
protected StyledPropertyBase(
string name,
Type ownerType,
StyledPropertyMetadata metadata,
bool inherits = false,
+ Func validate = null,
Action notifying = null)
: base(name, ownerType, metadata, notifying)
{
@@ -41,6 +42,13 @@ namespace Avalonia
}
_inherits = inherits;
+ ValidateValue = validate;
+
+ if (validate?.Invoke(metadata.DefaultValue) == false)
+ {
+ throw new ArgumentException(
+ $"'{metadata.DefaultValue}' is not a valid default value for '{name}'.");
+ }
}
///
@@ -62,6 +70,11 @@ namespace Avalonia
///
public override bool Inherits => _inherits;
+ ///
+ /// Gets the value validation callback for the property.
+ ///
+ public Func ValidateValue { get; }
+
///
/// Gets the default value for the property on the specified type.
///
@@ -71,7 +84,7 @@ namespace Avalonia
{
Contract.Requires(type != null);
- return GetMetadata(type).DefaultValue.Typed;
+ return GetMetadata(type).DefaultValue;
}
///
@@ -123,6 +136,15 @@ namespace Avalonia
/// The metadata.
public void OverrideMetadata(Type type, StyledPropertyMetadata metadata)
{
+ if (ValidateValue != null)
+ {
+ if (!ValidateValue(metadata.DefaultValue))
+ {
+ throw new ArgumentException(
+ $"'{metadata.DefaultValue}' is not a valid default value for '{Name}'.");
+ }
+ }
+
base.OverrideMetadata(type, metadata);
}
@@ -209,7 +231,7 @@ namespace Avalonia
{
Contract.Requires(type != null);
- return GetMetadata(type).DefaultValue.Boxed;
+ return GetMetadata(type).DefaultValue;
}
[DebuggerHidden]
diff --git a/src/Avalonia.Base/StyledPropertyMetadata`1.cs b/src/Avalonia.Base/StyledPropertyMetadata`1.cs
index d4ce137e0a..50236e2c7c 100644
--- a/src/Avalonia.Base/StyledPropertyMetadata`1.cs
+++ b/src/Avalonia.Base/StyledPropertyMetadata`1.cs
@@ -12,25 +12,27 @@ namespace Avalonia
///
public class StyledPropertyMetadata : PropertyMetadata, IStyledPropertyMetadata
{
+ private Optional _defaultValue;
+
///
/// Initializes a new instance of the class.
///
/// The default value of the property.
/// The default binding mode.
public StyledPropertyMetadata(
- TValue defaultValue = default,
+ Optional defaultValue = default,
BindingMode defaultBindingMode = BindingMode.Default)
: base(defaultBindingMode)
{
- DefaultValue = new BoxedValue(defaultValue);
+ _defaultValue = defaultValue;
}
///
/// Gets the default value for the property.
///
- internal BoxedValue DefaultValue { get; private set; }
+ internal TValue DefaultValue => _defaultValue.ValueOrDefault();
- object IStyledPropertyMetadata.DefaultValue => DefaultValue.Boxed;
+ object IStyledPropertyMetadata.DefaultValue => DefaultValue;
///
public override void Merge(PropertyMetadata baseMetadata, AvaloniaProperty property)
@@ -39,9 +41,9 @@ namespace Avalonia
if (baseMetadata is StyledPropertyMetadata src)
{
- if (DefaultValue.Boxed == null)
+ if (!_defaultValue.HasValue)
{
- DefaultValue = src.DefaultValue;
+ _defaultValue = src.DefaultValue;
}
}
}
diff --git a/src/Avalonia.Base/ValueStore.cs b/src/Avalonia.Base/ValueStore.cs
index 44ee1c4c8f..09961b399b 100644
--- a/src/Avalonia.Base/ValueStore.cs
+++ b/src/Avalonia.Base/ValueStore.cs
@@ -66,6 +66,11 @@ namespace Avalonia
public void SetValue(StyledPropertyBase property, T value, BindingPriority priority)
{
+ if (property.ValidateValue?.Invoke(value) == false)
+ {
+ throw new ArgumentException($"{value} is not a valid value for '{property.Name}.");
+ }
+
if (_values.TryGetValue(property, out var slot))
{
SetExisting(slot, property, value, priority);
diff --git a/src/Avalonia.Controls.DataGrid/DataGrid.cs b/src/Avalonia.Controls.DataGrid/DataGrid.cs
index a217d02ecd..86133d5fdb 100644
--- a/src/Avalonia.Controls.DataGrid/DataGrid.cs
+++ b/src/Avalonia.Controls.DataGrid/DataGrid.cs
@@ -201,21 +201,13 @@ namespace Avalonia.Controls
public static readonly StyledProperty ColumnHeaderHeightProperty =
AvaloniaProperty.Register(
nameof(ColumnHeaderHeight),
- defaultValue: double.NaN/*,
- validate: ValidateColumnHeaderHeight*/);
+ defaultValue: double.NaN,
+ validate: IsValidColumnHeaderHeight);
- private static double ValidateColumnHeaderHeight(DataGrid grid, double value)
+ private static bool IsValidColumnHeaderHeight(double value)
{
- if (value < DATAGRID_minimumColumnHeaderHeight)
- {
- throw DataGridError.DataGrid.ValueMustBeGreaterThanOrEqualTo(nameof(value), nameof(ColumnHeaderHeight), DATAGRID_minimumColumnHeaderHeight);
- }
- if (value > DATAGRID_maxHeadersThickness)
- {
- throw DataGridError.DataGrid.ValueMustBeGreaterThanOrEqualTo(nameof(value), nameof(ColumnHeaderHeight), DATAGRID_maxHeadersThickness);
- }
-
- return value;
+ return double.IsNaN(value) ||
+ (value >= DATAGRID_minimumColumnHeaderHeight && value <= DATAGRID_maxHeadersThickness);
}
///
@@ -261,8 +253,8 @@ namespace Avalonia.Controls
public static readonly StyledProperty FrozenColumnCountProperty =
AvaloniaProperty.Register(
- nameof(FrozenColumnCount)/*,
- validate: ValidateFrozenColumnCount*/);
+ nameof(FrozenColumnCount),
+ validate: ValidateFrozenColumnCount);
///
/// Gets or sets the number of columns that the user cannot scroll horizontally.
@@ -273,15 +265,7 @@ namespace Avalonia.Controls
set { SetValue(FrozenColumnCountProperty, value); }
}
- private static int ValidateFrozenColumnCount(DataGrid grid, int value)
- {
- if (value < 0)
- {
- throw DataGridError.DataGrid.ValueMustBeGreaterThanOrEqualTo(nameof(value), nameof(FrozenColumnCount), 0);
- }
-
- return value;
- }
+ private static bool ValidateFrozenColumnCount(int value) => value >= 0;
public static readonly StyledProperty GridLinesVisibilityProperty =
AvaloniaProperty.Register(nameof(GridLinesVisibility));
@@ -395,30 +379,12 @@ namespace Avalonia.Controls
public static readonly StyledProperty MaxColumnWidthProperty =
AvaloniaProperty.Register(
nameof(MaxColumnWidth),
- defaultValue: DATAGRID_defaultMaxColumnWidth/*,
- validate: ValidateMaxColumnWidth*/);
+ defaultValue: DATAGRID_defaultMaxColumnWidth,
+ validate: IsValidColumnWidth);
- private static double ValidateMaxColumnWidth(DataGrid grid, double value)
+ private static bool IsValidColumnWidth(double value)
{
- if (double.IsNaN(value))
- {
- throw DataGridError.DataGrid.ValueCannotBeSetToNAN(nameof(MaxColumnWidth));
- }
- if (value < 0)
- {
- throw DataGridError.DataGrid.ValueMustBeGreaterThanOrEqualTo(nameof(value), nameof(MaxColumnWidth), 0);
- }
- if (grid.MinColumnWidth > value)
- {
- throw DataGridError.DataGrid.ValueMustBeGreaterThanOrEqualTo(nameof(value), nameof(MaxColumnWidth), nameof(MinColumnWidth));
- }
-
- if (value < 0)
- {
- throw DataGridError.DataGrid.ValueMustBeGreaterThanOrEqualTo(nameof(value), nameof(FrozenColumnCount), 0);
- }
-
- return value;
+ return !double.IsNaN(value) && value > 0;
}
///
@@ -433,29 +399,12 @@ namespace Avalonia.Controls
public static readonly StyledProperty MinColumnWidthProperty =
AvaloniaProperty.Register(
nameof(MinColumnWidth),
- defaultValue: DATAGRID_defaultMinColumnWidth/*,
- validate: ValidateMinColumnWidth*/);
+ defaultValue: DATAGRID_defaultMinColumnWidth,
+ validate: IsValidMinColumnWidth);
- private static double ValidateMinColumnWidth(DataGrid grid, double value)
+ private static bool IsValidMinColumnWidth(double value)
{
- if (double.IsNaN(value))
- {
- throw DataGridError.DataGrid.ValueCannotBeSetToNAN(nameof(MinColumnWidth));
- }
- if (value < 0)
- {
- throw DataGridError.DataGrid.ValueMustBeGreaterThanOrEqualTo(nameof(value), nameof(MinColumnWidth), 0);
- }
- if (double.IsPositiveInfinity(value))
- {
- throw DataGridError.DataGrid.ValueCannotBeSetToInfinity(nameof(MinColumnWidth));
- }
- if (grid.MaxColumnWidth < value)
- {
- throw DataGridError.DataGrid.ValueMustBeLessThanOrEqualTo(nameof(value), nameof(MinColumnWidth), nameof(MaxColumnWidth));
- }
-
- return value;
+ return !double.IsNaN(value) && !double.IsPositiveInfinity(value) && value >= 0;
}
///
@@ -482,20 +431,13 @@ namespace Avalonia.Controls
public static readonly StyledProperty RowHeightProperty =
AvaloniaProperty.Register(
nameof(RowHeight),
- defaultValue: double.NaN/*,
- validate: ValidateRowHeight*/);
- private static double ValidateRowHeight(DataGrid grid, double value)
+ defaultValue: double.NaN,
+ validate: IsValidRowHeight);
+ private static bool IsValidRowHeight(double value)
{
- if (value < DataGridRow.DATAGRIDROW_minimumHeight)
- {
- throw DataGridError.DataGrid.ValueMustBeGreaterThanOrEqualTo(nameof(value), nameof(RowHeight), 0);
- }
- if (value > DataGridRow.DATAGRIDROW_maximumHeight)
- {
- throw DataGridError.DataGrid.ValueMustBeLessThanOrEqualTo(nameof(value), nameof(RowHeight), DataGridRow.DATAGRIDROW_maximumHeight);
- }
-
- return value;
+ return double.IsNaN(value) ||
+ (value >= DataGridRow.DATAGRIDROW_minimumHeight &&
+ value <= DataGridRow.DATAGRIDROW_maximumHeight);
}
///
@@ -510,20 +452,13 @@ namespace Avalonia.Controls
public static readonly StyledProperty RowHeaderWidthProperty =
AvaloniaProperty.Register(
nameof(RowHeaderWidth),
- defaultValue: double.NaN/*,
- validate: ValidateRowHeaderWidth*/);
- private static double ValidateRowHeaderWidth(DataGrid grid, double value)
+ defaultValue: double.NaN,
+ validate: IsValidRowHeaderWidth);
+ private static bool IsValidRowHeaderWidth(double value)
{
- if (value < DATAGRID_minimumRowHeaderWidth)
- {
- throw DataGridError.DataGrid.ValueMustBeGreaterThanOrEqualTo(nameof(value), nameof(RowHeaderWidth), DATAGRID_minimumRowHeaderWidth);
- }
- if (value > DATAGRID_maxHeadersThickness)
- {
- throw DataGridError.DataGrid.ValueMustBeLessThanOrEqualTo(nameof(value), nameof(RowHeaderWidth), DATAGRID_maxHeadersThickness);
- }
-
- return value;
+ return double.IsNaN(value) ||
+ (value >= DATAGRID_minimumRowHeaderWidth &&
+ value <= DATAGRID_maxHeadersThickness);
}
///
diff --git a/src/Avalonia.Controls.DataGrid/DataGridRowGroupHeader.cs b/src/Avalonia.Controls.DataGrid/DataGridRowGroupHeader.cs
index bede7f481e..69dfed761f 100644
--- a/src/Avalonia.Controls.DataGrid/DataGridRowGroupHeader.cs
+++ b/src/Avalonia.Controls.DataGrid/DataGridRowGroupHeader.cs
@@ -67,26 +67,12 @@ namespace Avalonia.Controls
public static readonly StyledProperty SublevelIndentProperty =
AvaloniaProperty.Register(
nameof(SublevelIndent),
- defaultValue: DataGrid.DATAGRID_defaultRowGroupSublevelIndent/*,
- validate: ValidateSublevelIndent*/);
+ defaultValue: DataGrid.DATAGRID_defaultRowGroupSublevelIndent,
+ validate: IsValidSublevelIndent);
- private static double ValidateSublevelIndent(DataGridRowGroupHeader header, double value)
+ private static bool IsValidSublevelIndent(double value)
{
- // We don't need to revert to the old value if our input is bad because we never read this property value
- if (double.IsNaN(value))
- {
- throw DataGridError.DataGrid.ValueCannotBeSetToNAN(nameof(SublevelIndent));
- }
- else if (double.IsInfinity(value))
- {
- throw DataGridError.DataGrid.ValueCannotBeSetToInfinity(nameof(SublevelIndent));
- }
- else if (value < 0)
- {
- throw DataGridError.DataGrid.ValueMustBeGreaterThanOrEqualTo(nameof(value), nameof(SublevelIndent), 0);
- }
-
- return value;
+ return !double.IsNaN(value) && !double.IsInfinity(value) && value >= 0;
}
///
diff --git a/src/Avalonia.Controls/AutoCompleteBox.cs b/src/Avalonia.Controls/AutoCompleteBox.cs
index 1e1a62f4a4..6deddef0d0 100644
--- a/src/Avalonia.Controls/AutoCompleteBox.cs
+++ b/src/Avalonia.Controls/AutoCompleteBox.cs
@@ -377,8 +377,8 @@ namespace Avalonia.Controls
/// dependency property.
public static readonly StyledProperty MinimumPrefixLengthProperty =
AvaloniaProperty.Register(
- nameof(MinimumPrefixLength), 1/*,
- validate: ValidateMinimumPrefixLength*/);
+ nameof(MinimumPrefixLength), 1,
+ validate: IsValidMinimumPrefixLength);
///
/// Identifies the
@@ -391,8 +391,8 @@ namespace Avalonia.Controls
public static readonly StyledProperty MinimumPopulateDelayProperty =
AvaloniaProperty.Register(
nameof(MinimumPopulateDelay),
- TimeSpan.Zero/*,
- validate: ValidateMinimumPopulateDelay*/);
+ TimeSpan.Zero,
+ validate: IsValidMinimumPopulateDelay);
///
/// Identifies the
@@ -405,8 +405,8 @@ namespace Avalonia.Controls
public static readonly StyledProperty MaxDropDownHeightProperty =
AvaloniaProperty.Register(
nameof(MaxDropDownHeight),
- double.PositiveInfinity/*,
- validate: ValidateMaxDropDownHeight*/);
+ double.PositiveInfinity,
+ validate: IsValidMaxDropDownHeight);
///
/// Identifies the
@@ -494,8 +494,8 @@ namespace Avalonia.Controls
public static readonly StyledProperty FilterModeProperty =
AvaloniaProperty.Register(
nameof(FilterMode),
- defaultValue: AutoCompleteFilterMode.StartsWith/*,
- validate: ValidateFilterMode*/);
+ defaultValue: AutoCompleteFilterMode.StartsWith,
+ validate: IsValidFilterMode);
///
/// Identifies the
@@ -546,26 +546,11 @@ namespace Avalonia.Controls
o => o.AsyncPopulator,
(o, v) => o.AsyncPopulator = v);
- private static int ValidateMinimumPrefixLength(AutoCompleteBox control, int value)
- {
- Contract.Requires(value >= -1);
-
- return value;
- }
-
- private static TimeSpan ValidateMinimumPopulateDelay(AutoCompleteBox control, TimeSpan value)
- {
- Contract.Requires(value.TotalMilliseconds >= 0.0);
-
- return value;
- }
+ private static bool IsValidMinimumPrefixLength(int value) => value >= -1;
- private static double ValidateMaxDropDownHeight(AutoCompleteBox control, double value)
- {
- Contract.Requires(value >= 0.0);
+ private static bool IsValidMinimumPopulateDelay(TimeSpan value) => value.TotalMilliseconds >= 0.0;
- return value;
- }
+ private static bool IsValidMaxDropDownHeight(double value) => value >= 0.0;
private static bool IsValidFilterMode(AutoCompleteFilterMode mode)
{
@@ -590,12 +575,6 @@ namespace Avalonia.Controls
return false;
}
}
- private static AutoCompleteFilterMode ValidateFilterMode(AutoCompleteBox control, AutoCompleteFilterMode value)
- {
- Contract.Requires(IsValidFilterMode(value));
-
- return value;
- }
///
/// Handle the change of the IsEnabled property.
diff --git a/src/Avalonia.Controls/Calendar/Calendar.cs b/src/Avalonia.Controls/Calendar/Calendar.cs
index 58b7d7cb47..94f8ad41a0 100644
--- a/src/Avalonia.Controls/Calendar/Calendar.cs
+++ b/src/Avalonia.Controls/Calendar/Calendar.cs
@@ -351,8 +351,9 @@ namespace Avalonia.Controls
public static readonly StyledProperty DisplayModeProperty =
AvaloniaProperty.Register(
- nameof(DisplayMode)/*,
- validate: ValidateDisplayMode*/);
+ nameof(DisplayMode),
+ validate: IsValidDisplayMode);
+
///
/// Gets or sets a value indicating whether the calendar is displayed in
/// months, years, or decades.
@@ -417,17 +418,6 @@ namespace Avalonia.Controls
}
OnDisplayModeChanged(new CalendarModeChangedEventArgs((CalendarMode)e.OldValue, mode));
}
- private static CalendarMode ValidateDisplayMode(Calendar o, CalendarMode mode)
- {
- if(IsValidDisplayMode(mode))
- {
- return mode;
- }
- else
- {
- throw new ArgumentOutOfRangeException(nameof(mode), "Invalid DisplayMode");
- }
- }
private static bool IsValidDisplayMode(CalendarMode mode)
{
return mode == CalendarMode.Month
diff --git a/src/Avalonia.Controls/Calendar/DatePicker.cs b/src/Avalonia.Controls/Calendar/DatePicker.cs
index aa3a8fae3d..b4d4fed9fc 100644
--- a/src/Avalonia.Controls/Calendar/DatePicker.cs
+++ b/src/Avalonia.Controls/Calendar/DatePicker.cs
@@ -189,14 +189,14 @@ namespace Avalonia.Controls
public static readonly StyledProperty SelectedDateFormatProperty =
AvaloniaProperty.Register(
nameof(SelectedDateFormat),
- defaultValue: DatePickerFormat.Short/*,
- validate: ValidateSelectedDateFormat*/);
+ defaultValue: DatePickerFormat.Short,
+ validate: IsValidSelectedDateFormat);
public static readonly StyledProperty CustomDateFormatStringProperty =
AvaloniaProperty.Register(
nameof(CustomDateFormatString),
- defaultValue: "d"/*,
- validate: ValidateDateFormatString*/);
+ defaultValue: "d",
+ validate: IsValidDateFormatString);
public static readonly DirectProperty TextProperty =
AvaloniaProperty.RegisterDirect(
@@ -1146,27 +1146,9 @@ namespace Avalonia.Controls
|| value == DatePickerFormat.Short
|| value == DatePickerFormat.Custom;
}
- private static DatePickerFormat ValidateSelectedDateFormat(DatePicker dp, DatePickerFormat format)
+ private static bool IsValidDateFormatString(string formatString)
{
- if(IsValidSelectedDateFormat(format))
- {
- return format;
- }
- else
- {
- throw new ArgumentOutOfRangeException(nameof(format), "DatePickerFormat value is not valid.");
- }
- }
- private static string ValidateDateFormatString(DatePicker dp, string formatString)
- {
- if(string.IsNullOrWhiteSpace(formatString))
- {
- throw new ArgumentException("DateFormatString value is not valid.", nameof(formatString));
- }
- else
- {
- return formatString;
- }
+ return !string.IsNullOrWhiteSpace(formatString);
}
private static DateTime DiscardDayTime(DateTime d)
{
diff --git a/src/Avalonia.Controls/DefinitionBase.cs b/src/Avalonia.Controls/DefinitionBase.cs
index eae6cf5e30..6f121afbef 100644
--- a/src/Avalonia.Controls/DefinitionBase.cs
+++ b/src/Avalonia.Controls/DefinitionBase.cs
@@ -356,7 +356,7 @@ namespace Avalonia.Controls
/// b) contains only letters, digits and underscore ('_').
/// c) does not start with a digit.
///
- private static string SharedSizeGroupPropertyValueValid(Control _, string value)
+ private static bool SharedSizeGroupPropertyValueValid(string value)
{
Contract.Requires(value != null);
@@ -380,11 +380,11 @@ namespace Avalonia.Controls
if (i == id.Length)
{
- return value;
+ return true;
}
}
- throw new ArgumentException("Invalid SharedSizeGroup string.");
+ return false;
}
///
@@ -750,8 +750,8 @@ namespace Avalonia.Controls
///
public static readonly AttachedProperty SharedSizeGroupProperty =
AvaloniaProperty.RegisterAttached(
- "SharedSizeGroup"/*,
- validate: SharedSizeGroupPropertyValueValid*/);
+ "SharedSizeGroup",
+ validate: SharedSizeGroupPropertyValueValid);
///
/// Static ctor. Used for static registration of properties.
diff --git a/src/Avalonia.Controls/Grid.cs b/src/Avalonia.Controls/Grid.cs
index 7b57288e76..1781067abb 100644
--- a/src/Avalonia.Controls/Grid.cs
+++ b/src/Avalonia.Controls/Grid.cs
@@ -2740,12 +2740,8 @@ namespace Avalonia.Controls
public static readonly AttachedProperty ColumnProperty =
AvaloniaProperty.RegisterAttached(
"Column",
- defaultValue: 0/*,
- validate: (_, v) =>
- {
- if (v >= 0) return v;
- else throw new ArgumentException("Invalid Grid.Column value.");
- }*/);
+ defaultValue: 0,
+ validate: v => v >= 0);
///
/// Row property. This is an attached property.
@@ -2761,12 +2757,8 @@ namespace Avalonia.Controls
public static readonly AttachedProperty RowProperty =
AvaloniaProperty.RegisterAttached(
"Row",
- defaultValue: 0/*,
- validate: (_, v) =>
- {
- if (v >= 0) return v;
- else throw new ArgumentException("Invalid Grid.Row value.");
- }*/);
+ defaultValue: 0,
+ validate: v => v >= 0);
///
/// ColumnSpan property. This is an attached property.
@@ -2781,12 +2773,8 @@ namespace Avalonia.Controls
public static readonly AttachedProperty ColumnSpanProperty =
AvaloniaProperty.RegisterAttached(
"ColumnSpan",
- defaultValue: 1/*,
- validate: (_, v) =>
- {
- if (v >= 1) return v;
- else throw new ArgumentException("Invalid Grid.ColumnSpan value.");
- }*/);
+ defaultValue: 1,
+ validate: v => v >= 0);
///
/// RowSpan property. This is an attached property.
@@ -2801,12 +2789,8 @@ namespace Avalonia.Controls
public static readonly AttachedProperty RowSpanProperty =
AvaloniaProperty.RegisterAttached(
"RowSpan",
- defaultValue: 1/*,
- validate: (_, v) =>
- {
- if (v >= 1) return v;
- else throw new ArgumentException("Invalid Grid.RowSpan value.");
- }*/);
+ defaultValue: 1,
+ validate: v => v >= 0);
///
/// IsSharedSizeScope property marks scoping element for shared size.
diff --git a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Validation.cs b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Validation.cs
new file mode 100644
index 0000000000..70de7b449f
--- /dev/null
+++ b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Validation.cs
@@ -0,0 +1,82 @@
+// 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.Reactive.Subjects;
+using Xunit;
+
+namespace Avalonia.Base.UnitTests
+{
+ public class AvaloniaObjectTests_Validation
+ {
+ [Fact]
+ public void Registration_Throws_If_DefaultValue_Fails_Validation()
+ {
+ Assert.Throws(() =>
+ new StyledProperty(
+ "BadDefault",
+ typeof(Class1),
+ new StyledPropertyMetadata(101),
+ validate: Class1.ValidateFoo));
+ }
+
+ [Fact]
+ public void Metadata_Override_Throws_If_DefaultValue_Fails_Validation()
+ {
+ Assert.Throws(() => Class1.FooProperty.OverrideDefaultValue(101));
+ }
+
+ [Fact]
+ public void SetValue_Throws_If_Fails_Validation()
+ {
+ var target = new Class1();
+
+ Assert.Throws(() => target.SetValue(Class1.FooProperty, 101));
+ }
+
+ [Fact]
+ public void Reverts_To_DefaultValue_If_Binding_Fails_Validation()
+ {
+ var target = new Class1();
+ var source = new Subject();
+
+ target.Bind(Class1.FooProperty, source);
+ source.OnNext(150);
+
+ Assert.Equal(11, target.GetValue(Class1.FooProperty));
+ }
+
+ [Fact]
+ public void Reverts_To_DefaultValue_Even_In_Presence_Of_Other_Bindings()
+ {
+ var target = new Class1();
+ var source1 = new Subject();
+ var source2 = new Subject();
+
+ target.Bind(Class1.FooProperty, source1);
+ target.Bind(Class1.FooProperty, source2);
+ source1.OnNext(42);
+ source2.OnNext(150);
+
+ Assert.Equal(11, target.GetValue(Class1.FooProperty));
+ }
+
+ private class Class1 : AvaloniaObject
+ {
+ public static readonly StyledProperty FooProperty =
+ AvaloniaProperty.Register(
+ "Qux",
+ defaultValue: 11,
+ validate: ValidateFoo);
+
+ public static bool ValidateFoo(int value)
+ {
+ return value < 100;
+ }
+ }
+
+ private class Class2 : AvaloniaObject
+ {
+ }
+ }
+}