diff --git a/Avalonia.sln b/Avalonia.sln
index 3ed931933a..cf440972b2 100644
--- a/Avalonia.sln
+++ b/Avalonia.sln
@@ -1,4 +1,5 @@
-Microsoft Visual Studio Solution File, Format Version 12.00
+
+Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.27130.2027
MinimumVisualStudioVersion = 10.0.40219.1
@@ -130,6 +131,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Props", "Props", "{F3AC8BC1
ProjectSection(SolutionItems) = preProject
build\Base.props = build\Base.props
build\Binding.props = build\Binding.props
+ build\BuildTargets.targets = build\BuildTargets.targets
build\JetBrains.Annotations.props = build\JetBrains.Annotations.props
build\JetBrains.dotMemoryUnit.props = build\JetBrains.dotMemoryUnit.props
build\Magick.NET-Q16-AnyCPU.props = build\Magick.NET-Q16-AnyCPU.props
@@ -147,7 +149,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Props", "Props", "{F3AC8BC1
build\Splat.props = build\Splat.props
build\System.Memory.props = build\System.Memory.props
build\XUnit.props = build\XUnit.props
- build\BuildTargets.targets = build\BuildTargets.targets
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Targets", "Targets", "{4D6FAF79-58B4-482F-9122-0668C346364C}"
@@ -189,11 +190,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia", "packages\Avalon
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Desktop", "src\Avalonia.Desktop\Avalonia.Desktop.csproj", "{3C471044-3640-45E3-B1B2-16D2FF8399EE}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Build.Tasks", "src\Avalonia.Build.Tasks\Avalonia.Build.Tasks.csproj", "{BF28998D-072C-439A-AFBB-2FE5021241E0}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Build.Tasks", "src\Avalonia.Build.Tasks\Avalonia.Build.Tasks.csproj", "{BF28998D-072C-439A-AFBB-2FE5021241E0}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "_build", "nukebuild\_build.csproj", "{3F00BC43-5095-477F-93D8-E65B08179A00}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Animation.UnitTests", "tests\Avalonia.Animation.UnitTests\Avalonia.Animation.UnitTests.csproj", "{AF227847-E65C-4BE9-BCE9-B551357788E0}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Animation.UnitTests", "tests\Avalonia.Animation.UnitTests\Avalonia.Animation.UnitTests.csproj", "{AF227847-E65C-4BE9-BCE9-B551357788E0}"
EndProject
Global
GlobalSection(SharedMSBuildProjectFiles) = preSolution
@@ -220,10 +221,6 @@ Global
Release|iPhoneSimulator = Release|iPhoneSimulator
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {3F00BC43-5095-477F-93D8-E65B08179A00}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {3F00BC43-5095-477F-93D8-E65B08179A00}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {3F00BC43-5095-477F-93D8-E65B08179A00}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {3F00BC43-5095-477F-93D8-E65B08179A00}.Release|Any CPU.Build.0 = Release|Any CPU
{B09B78D8-9B26-48B0-9149-D64A2F120F3F}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU
{B09B78D8-9B26-48B0-9149-D64A2F120F3F}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU
{B09B78D8-9B26-48B0-9149-D64A2F120F3F}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU
@@ -1722,6 +1719,30 @@ Global
{BF28998D-072C-439A-AFBB-2FE5021241E0}.Release|iPhone.Build.0 = Release|Any CPU
{BF28998D-072C-439A-AFBB-2FE5021241E0}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{BF28998D-072C-439A-AFBB-2FE5021241E0}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
+ {3F00BC43-5095-477F-93D8-E65B08179A00}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU
+ {3F00BC43-5095-477F-93D8-E65B08179A00}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU
+ {3F00BC43-5095-477F-93D8-E65B08179A00}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU
+ {3F00BC43-5095-477F-93D8-E65B08179A00}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU
+ {3F00BC43-5095-477F-93D8-E65B08179A00}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU
+ {3F00BC43-5095-477F-93D8-E65B08179A00}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU
+ {3F00BC43-5095-477F-93D8-E65B08179A00}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
+ {3F00BC43-5095-477F-93D8-E65B08179A00}.AppStore|Any CPU.Build.0 = Release|Any CPU
+ {3F00BC43-5095-477F-93D8-E65B08179A00}.AppStore|iPhone.ActiveCfg = Release|Any CPU
+ {3F00BC43-5095-477F-93D8-E65B08179A00}.AppStore|iPhone.Build.0 = Release|Any CPU
+ {3F00BC43-5095-477F-93D8-E65B08179A00}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU
+ {3F00BC43-5095-477F-93D8-E65B08179A00}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU
+ {3F00BC43-5095-477F-93D8-E65B08179A00}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {3F00BC43-5095-477F-93D8-E65B08179A00}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {3F00BC43-5095-477F-93D8-E65B08179A00}.Debug|iPhone.ActiveCfg = Debug|Any CPU
+ {3F00BC43-5095-477F-93D8-E65B08179A00}.Debug|iPhone.Build.0 = Debug|Any CPU
+ {3F00BC43-5095-477F-93D8-E65B08179A00}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {3F00BC43-5095-477F-93D8-E65B08179A00}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {3F00BC43-5095-477F-93D8-E65B08179A00}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {3F00BC43-5095-477F-93D8-E65B08179A00}.Release|Any CPU.Build.0 = Release|Any CPU
+ {3F00BC43-5095-477F-93D8-E65B08179A00}.Release|iPhone.ActiveCfg = Release|Any CPU
+ {3F00BC43-5095-477F-93D8-E65B08179A00}.Release|iPhone.Build.0 = Release|Any CPU
+ {3F00BC43-5095-477F-93D8-E65B08179A00}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
+ {3F00BC43-5095-477F-93D8-E65B08179A00}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{AF227847-E65C-4BE9-BCE9-B551357788E0}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{AF227847-E65C-4BE9-BCE9-B551357788E0}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{AF227847-E65C-4BE9-BCE9-B551357788E0}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index 8c5380e65e..fd40118888 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -38,6 +38,13 @@ jobs:
inputs:
version: '2.1.403'
+ - task: CmdLine@2
+ displayName: 'Install Mono 5.18'
+ inputs:
+ script: |
+ curl -o ./mono.pkg https://download.mono-project.com/archive/5.18.0/macos-10-universal/MonoFramework-MDK-5.18.0.225.macos10.xamarin.universal.pkg
+ sudo installer -verbose -pkg ./mono.pkg -target /
+
- task: Xcode@5
inputs:
actions: 'build'
@@ -51,7 +58,9 @@ jobs:
- task: CmdLine@2
displayName: 'Install CastXML'
inputs:
- script: brew install castxml
+ script: |
+ brew update
+ brew install castxml
- task: CmdLine@2
displayName: 'Install Nuke'
diff --git a/build/NetFX.props b/build/NetFX.props
index d8b2daf13a..4d2841714b 100644
--- a/build/NetFX.props
+++ b/build/NetFX.props
@@ -3,4 +3,9 @@
/usr/lib/mono/4.6.1-api
/Library/Frameworks/Mono.framework/Versions/Current/lib/mono/4.6.1-api
+
+ /usr/lib/mono/4.7-api/
+ /Library/Frameworks/Mono.framework/Versions/Current/lib/mono/4.7-api
+
+
diff --git a/build/UnitTests.NetFX.props b/build/UnitTests.NetFX.props
new file mode 100644
index 0000000000..e9a29d80fe
--- /dev/null
+++ b/build/UnitTests.NetFX.props
@@ -0,0 +1,8 @@
+
+
+
+
+ PreserveNewest
+
+
+
diff --git a/build/xunit.runner.mono.json b/build/xunit.runner.mono.json
new file mode 100644
index 0000000000..4fb88b54c6
--- /dev/null
+++ b/build/xunit.runner.mono.json
@@ -0,0 +1 @@
+{ "appDomain": "denied" }
diff --git a/global.json b/global.json
index d11e78bd7f..6d12c28846 100644
--- a/global.json
+++ b/global.json
@@ -1,7 +1,7 @@
{
"msbuild-sdks": {
"Microsoft.Build.Traversal": "1.0.43",
- "MSBuild.Sdk.Extras": "1.6.46",
+ "MSBuild.Sdk.Extras": "1.6.65",
"AggregatePackage.NuGet.Sdk" : "0.1.12"
}
}
diff --git a/nukebuild/Build.cs b/nukebuild/Build.cs
index a14842cfd3..bb31034299 100644
--- a/nukebuild/Build.cs
+++ b/nukebuild/Build.cs
@@ -5,6 +5,7 @@ using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
+using System.Xml.Linq;
using Nuke.Common;
using Nuke.Common.Git;
using Nuke.Common.ProjectModel;
@@ -56,6 +57,17 @@ partial class Build : NukeBuild
Information("IsReleasable: " + Parameters.IsReleasable);
Information("IsMyGetRelease: " + Parameters.IsMyGetRelease);
Information("IsNuGetRelease: " + Parameters.IsNuGetRelease);
+
+ void ExecWait(string preamble, string command, string args)
+ {
+ Console.WriteLine(preamble);
+ Process.Start(new ProcessStartInfo(command, args) {UseShellExecute = false}).WaitForExit();
+ }
+ ExecWait("dotnet version:", "dotnet", "--version");
+ if (Parameters.IsRunningOnUnix)
+ ExecWait("Mono version:", "mono", "--version");
+
+
}
Target Clean => _ => _.Executes(() =>
@@ -92,16 +104,24 @@ partial class Build : NukeBuild
);
});
- void RunCoreTest(string project, bool coreOnly = false)
+ void RunCoreTest(string project)
{
if(!project.EndsWith(".csproj"))
project = System.IO.Path.Combine(project, System.IO.Path.GetFileName(project)+".csproj");
Information("Running tests from " + project);
- var frameworks = new List(){"netcoreapp2.0"};
+ XDocument xdoc;
+ using (var s = File.OpenRead(project))
+ xdoc = XDocument.Load(s);
+
+ List frameworks = null;
+ var targets = xdoc.Root.Descendants("TargetFrameworks").FirstOrDefault();
+ if (targets != null)
+ frameworks = targets.Value.Split(';').Where(f => !string.IsNullOrWhiteSpace(f)).ToList();
+ else
+ frameworks = new List {xdoc.Root.Descendants("TargetFramework").First().Value};
+
foreach(var fw in frameworks)
{
- if(!fw.StartsWith("netcoreapp") && coreOnly)
- continue;
Information("Running for " + fw);
DotNetTest(c =>
{
@@ -124,18 +144,18 @@ partial class Build : NukeBuild
.DependsOn(Compile)
.Executes(() =>
{
- RunCoreTest("./tests/Avalonia.Animation.UnitTests", false);
- RunCoreTest("./tests/Avalonia.Base.UnitTests", false);
- RunCoreTest("./tests/Avalonia.Controls.UnitTests", false);
- RunCoreTest("./tests/Avalonia.Input.UnitTests", false);
- RunCoreTest("./tests/Avalonia.Interactivity.UnitTests", false);
- RunCoreTest("./tests/Avalonia.Layout.UnitTests", false);
- RunCoreTest("./tests/Avalonia.Markup.UnitTests", false);
- RunCoreTest("./tests/Avalonia.Markup.Xaml.UnitTests", false);
- RunCoreTest("./tests/Avalonia.Styling.UnitTests", false);
- RunCoreTest("./tests/Avalonia.Visuals.UnitTests", false);
- RunCoreTest("./tests/Avalonia.Skia.UnitTests", false);
- RunCoreTest("./tests/Avalonia.ReactiveUI.UnitTests", false);
+ RunCoreTest("./tests/Avalonia.Animation.UnitTests");
+ RunCoreTest("./tests/Avalonia.Base.UnitTests");
+ RunCoreTest("./tests/Avalonia.Controls.UnitTests");
+ RunCoreTest("./tests/Avalonia.Input.UnitTests");
+ RunCoreTest("./tests/Avalonia.Interactivity.UnitTests");
+ RunCoreTest("./tests/Avalonia.Layout.UnitTests");
+ RunCoreTest("./tests/Avalonia.Markup.UnitTests");
+ RunCoreTest("./tests/Avalonia.Markup.Xaml.UnitTests");
+ RunCoreTest("./tests/Avalonia.Styling.UnitTests");
+ RunCoreTest("./tests/Avalonia.Visuals.UnitTests");
+ RunCoreTest("./tests/Avalonia.Skia.UnitTests");
+ RunCoreTest("./tests/Avalonia.ReactiveUI.UnitTests");
});
Target RunRenderTests => _ => _
@@ -143,9 +163,9 @@ partial class Build : NukeBuild
.DependsOn(Compile)
.Executes(() =>
{
- RunCoreTest("./tests/Avalonia.Skia.RenderTests/Avalonia.Skia.RenderTests.csproj", true);
+ RunCoreTest("./tests/Avalonia.Skia.RenderTests/Avalonia.Skia.RenderTests.csproj");
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
- RunCoreTest("./tests/Avalonia.Direct2D1.RenderTests/Avalonia.Direct2D1.RenderTests.csproj", true);
+ RunCoreTest("./tests/Avalonia.Direct2D1.RenderTests/Avalonia.Direct2D1.RenderTests.csproj");
});
Target RunDesignerTests => _ => _
@@ -153,7 +173,7 @@ partial class Build : NukeBuild
.DependsOn(Compile)
.Executes(() =>
{
- RunCoreTest("./tests/Avalonia.DesignerSupport.Tests", false);
+ RunCoreTest("./tests/Avalonia.DesignerSupport.Tests");
});
[PackageExecutable("JetBrains.dotMemoryUnit", "dotMemoryUnit.exe")] readonly Tool DotMemoryUnit;
diff --git a/nukebuild/BuildParameters.cs b/nukebuild/BuildParameters.cs
index 5f8ec7267e..afd1950859 100644
--- a/nukebuild/BuildParameters.cs
+++ b/nukebuild/BuildParameters.cs
@@ -14,13 +14,13 @@ using static Nuke.Common.Tools.MSBuild.MSBuildTasks;
public partial class Build
{
[Parameter("configuration")]
- public string NukeArgConfiguration { get; set; }
+ public string Configuration { get; set; }
[Parameter("skip-tests")]
- public bool NukeArgSkipTests { get; set; }
+ public bool SkipTests { get; set; }
[Parameter("force-nuget-version")]
- public string NukeArgForceNugetVersion { get; set; }
+ public string ForceNugetVersion { get; set; }
public class BuildParameters
{
@@ -64,8 +64,8 @@ public partial class Build
public BuildParameters(Build b)
{
// ARGUMENTS
- Configuration = b.NukeArgConfiguration ?? "Release";
- SkipTests = b.NukeArgSkipTests;
+ Configuration = b.Configuration ?? "Release";
+ SkipTests = b.SkipTests;
// CONFIGURATION
MainRepo = "https://github.com/AvaloniaUI/Avalonia";
@@ -102,7 +102,7 @@ public partial class Build
IsNuGetRelease = IsMainRepo && IsReleasable && IsReleaseBranch;
// VERSION
- Version = b.NukeArgForceNugetVersion ?? GetVersion();
+ Version = b.ForceNugetVersion ?? GetVersion();
if (IsRunningOnAzure)
{
diff --git a/src/Avalonia.Animation/AnimatorKeyFrame.cs b/src/Avalonia.Animation/AnimatorKeyFrame.cs
index 78a9a1e412..36d15e518e 100644
--- a/src/Avalonia.Animation/AnimatorKeyFrame.cs
+++ b/src/Avalonia.Animation/AnimatorKeyFrame.cs
@@ -1,5 +1,6 @@
using System;
using System.ComponentModel;
+using Avalonia.Animation.Animators;
using Avalonia.Data;
using Avalonia.Reactive;
diff --git a/src/Avalonia.Animation/IterationCount.cs b/src/Avalonia.Animation/IterationCount.cs
index eafc483868..e9cd0686d8 100644
--- a/src/Avalonia.Animation/IterationCount.cs
+++ b/src/Avalonia.Animation/IterationCount.cs
@@ -44,7 +44,7 @@ namespace Avalonia.Animation
{
if (type > IterationType.Infinite)
{
- throw new ArgumentException("Invalid value", "type");
+ throw new ArgumentException("Invalid value", nameof(type));
}
_type = type;
diff --git a/src/Avalonia.Base/Collections/AvaloniaDictionary.cs b/src/Avalonia.Base/Collections/AvaloniaDictionary.cs
index e8dc2a5ed7..4de4c540a0 100644
--- a/src/Avalonia.Base/Collections/AvaloniaDictionary.cs
+++ b/src/Avalonia.Base/Collections/AvaloniaDictionary.cs
@@ -148,7 +148,7 @@ namespace Avalonia.Collections
{
if (_inner.TryGetValue(key, out TValue value))
{
- PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Count"));
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Count)));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs($"Item[{key}]"));
if (CollectionChanged != null)
@@ -209,7 +209,7 @@ namespace Avalonia.Collections
private void NotifyAdd(TKey key, TValue value)
{
- PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Count"));
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Count)));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs($"Item[{key}]"));
diff --git a/src/Avalonia.Base/Collections/AvaloniaList.cs b/src/Avalonia.Base/Collections/AvaloniaList.cs
index d5a4e63003..4d4a561b08 100644
--- a/src/Avalonia.Base/Collections/AvaloniaList.cs
+++ b/src/Avalonia.Base/Collections/AvaloniaList.cs
@@ -511,7 +511,7 @@ namespace Avalonia.Collections
///
private void NotifyCountChanged()
{
- PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Count"));
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Count)));
}
///
diff --git a/src/Avalonia.Base/Data/Core/ExpressionNode.cs b/src/Avalonia.Base/Data/Core/ExpressionNode.cs
index f2f3ed9bfc..8a2dd46b86 100644
--- a/src/Avalonia.Base/Data/Core/ExpressionNode.cs
+++ b/src/Avalonia.Base/Data/Core/ExpressionNode.cs
@@ -147,6 +147,7 @@ namespace Avalonia.Data.Core
private void StopListening()
{
StopListeningCore();
+ _listening = false;
}
private BindingNotification TargetNullNotification()
diff --git a/src/Avalonia.Base/Platform/IAssetLoader.cs b/src/Avalonia.Base/Platform/IAssetLoader.cs
index 691fa2fc12..880ed62046 100644
--- a/src/Avalonia.Base/Platform/IAssetLoader.cs
+++ b/src/Avalonia.Base/Platform/IAssetLoader.cs
@@ -65,6 +65,7 @@ namespace Avalonia.Platform
/// Gets all assets of a folder and subfolders that match specified uri.
///
/// The URI.
+ /// The base URI.
/// All matching assets as a tuple of the absolute path to the asset and the assembly containing the asset
IEnumerable GetAssets(Uri uri, Uri baseUri);
}
diff --git a/src/Avalonia.Controls/Calendar/CalendarItem.cs b/src/Avalonia.Controls/Calendar/CalendarItem.cs
index 577555333f..fb6dacaf81 100644
--- a/src/Avalonia.Controls/Calendar/CalendarItem.cs
+++ b/src/Avalonia.Controls/Calendar/CalendarItem.cs
@@ -52,7 +52,7 @@ namespace Avalonia.Controls.Primitives
internal Calendar Owner { get; set; }
internal CalendarDayButton CurrentButton { get; set; }
- public static StyledProperty HeaderBackgroundProperty = Calendar.HeaderBackgroundProperty.AddOwner();
+ public static readonly StyledProperty HeaderBackgroundProperty = Calendar.HeaderBackgroundProperty.AddOwner();
public IBrush HeaderBackground
{
get { return GetValue(HeaderBackgroundProperty); }
diff --git a/src/Avalonia.Controls/Calendar/DatePicker.cs b/src/Avalonia.Controls/Calendar/DatePicker.cs
index 2270598623..70de8bec16 100644
--- a/src/Avalonia.Controls/Calendar/DatePicker.cs
+++ b/src/Avalonia.Controls/Calendar/DatePicker.cs
@@ -954,7 +954,7 @@ namespace Avalonia.Controls
}
else
{
- var dateValidationError = new DatePickerDateValidationErrorEventArgs(new ArgumentOutOfRangeException("text", "SelectedDate value is not valid."), text);
+ var dateValidationError = new DatePickerDateValidationErrorEventArgs(new ArgumentOutOfRangeException(nameof(text), "SelectedDate value is not valid."), text);
OnDateValidationError(dateValidationError);
if (dateValidationError.ThrowException)
diff --git a/src/Avalonia.Controls/GridLength.cs b/src/Avalonia.Controls/GridLength.cs
index f6a608cd71..02be95b647 100644
--- a/src/Avalonia.Controls/GridLength.cs
+++ b/src/Avalonia.Controls/GridLength.cs
@@ -56,12 +56,12 @@ namespace Avalonia.Controls
{
if (value < 0 || double.IsNaN(value) || double.IsInfinity(value))
{
- throw new ArgumentException("Invalid value", "value");
+ throw new ArgumentException("Invalid value", nameof(value));
}
if (type < GridUnitType.Auto || type > GridUnitType.Star)
{
- throw new ArgumentException("Invalid value", "type");
+ throw new ArgumentException("Invalid value", nameof(type));
}
_type = type;
diff --git a/src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs b/src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs
index de68eb0ab0..57db1e19bb 100644
--- a/src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs
+++ b/src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs
@@ -487,7 +487,7 @@ namespace Avalonia.Controls
{
if (e == null)
{
- throw new ArgumentNullException("e");
+ throw new ArgumentNullException(nameof(e));
}
var handler = Spinned;
diff --git a/src/Avalonia.Controls/Primitives/AdornerLayer.cs b/src/Avalonia.Controls/Primitives/AdornerLayer.cs
index 5308a062ec..ed590679cd 100644
--- a/src/Avalonia.Controls/Primitives/AdornerLayer.cs
+++ b/src/Avalonia.Controls/Primitives/AdornerLayer.cs
@@ -13,7 +13,7 @@ namespace Avalonia.Controls.Primitives
// TODO: Need to track position of adorned elements and move the adorner if they move.
public class AdornerLayer : Panel, ICustomSimpleHitTest
{
- public static AttachedProperty AdornedElementProperty =
+ public static readonly AttachedProperty AdornedElementProperty =
AvaloniaProperty.RegisterAttached("AdornedElement");
private static readonly AttachedProperty s_adornedElementInfoProperty =
diff --git a/src/Avalonia.Controls/Viewbox.cs b/src/Avalonia.Controls/Viewbox.cs
index db753f4ab4..3a070d07b2 100644
--- a/src/Avalonia.Controls/Viewbox.cs
+++ b/src/Avalonia.Controls/Viewbox.cs
@@ -12,7 +12,7 @@ namespace Avalonia.Controls
///
/// The stretch property
///
- public static AvaloniaProperty StretchProperty =
+ public static readonly AvaloniaProperty StretchProperty =
AvaloniaProperty.RegisterDirect(nameof(Stretch),
v => v.Stretch, (c, v) => c.Stretch = v, Stretch.Uniform);
diff --git a/src/Avalonia.Input/Cursors.cs b/src/Avalonia.Input/Cursors.cs
index 6f67f3b08c..d3618f30f3 100644
--- a/src/Avalonia.Input/Cursors.cs
+++ b/src/Avalonia.Input/Cursors.cs
@@ -47,7 +47,7 @@ namespace Avalonia.Input
public class Cursor
{
- public static Cursor Default = new Cursor(StandardCursorType.Arrow);
+ public static readonly Cursor Default = new Cursor(StandardCursorType.Arrow);
internal Cursor(IPlatformHandle platformCursor)
{
diff --git a/src/Avalonia.Input/DataFormats.cs b/src/Avalonia.Input/DataFormats.cs
index 559d2cb643..cf5a6592e1 100644
--- a/src/Avalonia.Input/DataFormats.cs
+++ b/src/Avalonia.Input/DataFormats.cs
@@ -5,11 +5,11 @@
///
/// Dataformat for plaintext
///
- public static string Text = nameof(Text);
+ public static readonly string Text = nameof(Text);
///
/// Dataformat for one or more filenames
///
- public static string FileNames = nameof(FileNames);
+ public static readonly string FileNames = nameof(FileNames);
}
-}
\ No newline at end of file
+}
diff --git a/src/Avalonia.Input/DragDrop.cs b/src/Avalonia.Input/DragDrop.cs
index b5aba55f6c..c58b764b1d 100644
--- a/src/Avalonia.Input/DragDrop.cs
+++ b/src/Avalonia.Input/DragDrop.cs
@@ -9,21 +9,21 @@ namespace Avalonia.Input
///
/// Event which is raised, when a drag-and-drop operation enters the element.
///
- public static RoutedEvent DragEnterEvent = RoutedEvent.Register("DragEnter", RoutingStrategies.Bubble, typeof(DragDrop));
+ public static readonly RoutedEvent DragEnterEvent = RoutedEvent.Register("DragEnter", RoutingStrategies.Bubble, typeof(DragDrop));
///
/// Event which is raised, when a drag-and-drop operation leaves the element.
///
- public static RoutedEvent DragLeaveEvent = RoutedEvent.Register("DragLeave", RoutingStrategies.Bubble, typeof(DragDrop));
+ public static readonly RoutedEvent DragLeaveEvent = RoutedEvent.Register("DragLeave", RoutingStrategies.Bubble, typeof(DragDrop));
///
/// Event which is raised, when a drag-and-drop operation is updated while over the element.
///
- public static RoutedEvent DragOverEvent = RoutedEvent.Register("DragOver", RoutingStrategies.Bubble, typeof(DragDrop));
+ public static readonly RoutedEvent DragOverEvent = RoutedEvent.Register("DragOver", RoutingStrategies.Bubble, typeof(DragDrop));
///
/// Event which is raised, when a drag-and-drop operation should complete over the element.
///
- public static RoutedEvent DropEvent = RoutedEvent.Register("Drop", RoutingStrategies.Bubble, typeof(DragDrop));
+ public static readonly RoutedEvent DropEvent = RoutedEvent.Register("Drop", RoutingStrategies.Bubble, typeof(DragDrop));
- public static AvaloniaProperty AllowDropProperty = AvaloniaProperty.RegisterAttached("AllowDrop", typeof(DragDrop), inherits: true);
+ public static readonly AvaloniaProperty AllowDropProperty = AvaloniaProperty.RegisterAttached("AllowDrop", typeof(DragDrop), inherits: true);
///
/// Gets a value indicating whether the given element can be used as the target of a drag-and-drop operation.
diff --git a/src/Avalonia.Styling/Styling/ChildSelector.cs b/src/Avalonia.Styling/Styling/ChildSelector.cs
index bcbf358721..8b1d2a8553 100644
--- a/src/Avalonia.Styling/Styling/ChildSelector.cs
+++ b/src/Avalonia.Styling/Styling/ChildSelector.cs
@@ -24,6 +24,9 @@ namespace Avalonia.Styling
///
public override bool InTemplate => _parent.InTemplate;
+ ///
+ public override bool IsCombinator => true;
+
///
public override Type TargetType => null;
@@ -43,11 +46,24 @@ namespace Avalonia.Styling
if (controlParent != null)
{
- return _parent.Match((IStyleable)controlParent, subscribe);
+ var parentMatch = _parent.Match((IStyleable)controlParent, subscribe);
+
+ if (parentMatch.Result == SelectorMatchResult.Sometimes)
+ {
+ return parentMatch;
+ }
+ else if (parentMatch.IsMatch)
+ {
+ return SelectorMatch.AlwaysThisInstance;
+ }
+ else
+ {
+ return SelectorMatch.NeverThisInstance;
+ }
}
else
{
- return SelectorMatch.False;
+ return SelectorMatch.NeverThisInstance;
}
}
diff --git a/src/Avalonia.Styling/Styling/DescendentSelector.cs b/src/Avalonia.Styling/Styling/DescendentSelector.cs
index 943b3f0161..a81908f23d 100644
--- a/src/Avalonia.Styling/Styling/DescendentSelector.cs
+++ b/src/Avalonia.Styling/Styling/DescendentSelector.cs
@@ -22,6 +22,9 @@ namespace Avalonia.Styling
_parent = parent;
}
+ ///
+ public override bool IsCombinator => true;
+
///
public override bool InTemplate => _parent.InTemplate;
@@ -51,16 +54,13 @@ namespace Avalonia.Styling
{
var match = _parent.Match((IStyleable)c, subscribe);
- if (match.ImmediateResult != null)
+ if (match.Result == SelectorMatchResult.Sometimes)
{
- if (match.ImmediateResult == true)
- {
- return SelectorMatch.True;
- }
+ descendantMatches.Add(match.Activator);
}
- else
+ else if (match.IsMatch)
{
- descendantMatches.Add(match.ObservableResult);
+ return SelectorMatch.AlwaysThisInstance;
}
}
}
@@ -71,7 +71,7 @@ namespace Avalonia.Styling
}
else
{
- return SelectorMatch.False;
+ return SelectorMatch.NeverThisInstance;
}
}
diff --git a/src/Avalonia.Styling/Styling/IStyle.cs b/src/Avalonia.Styling/Styling/IStyle.cs
index 3cf8491ae3..da2a08f04d 100644
--- a/src/Avalonia.Styling/Styling/IStyle.cs
+++ b/src/Avalonia.Styling/Styling/IStyle.cs
@@ -17,7 +17,12 @@ namespace Avalonia.Styling
///
/// The control that contains this style. May be null.
///
- void Attach(IStyleable control, IStyleHost container);
+ ///
+ /// True if the style can match a control of type
+ /// (even if it does not match this control specifically); false if the style
+ /// can never match.
+ ///
+ bool Attach(IStyleable control, IStyleHost container);
void Detach();
}
diff --git a/src/Avalonia.Styling/Styling/PropertyEqualsSelector.cs b/src/Avalonia.Styling/Styling/PropertyEqualsSelector.cs
index 541e8646b1..cfc0998fe0 100644
--- a/src/Avalonia.Styling/Styling/PropertyEqualsSelector.cs
+++ b/src/Avalonia.Styling/Styling/PropertyEqualsSelector.cs
@@ -30,6 +30,9 @@ namespace Avalonia.Styling
///
public override bool InTemplate => _previous?.InTemplate ?? false;
+ ///
+ public override bool IsCombinator => false;
+
///
/// Gets the name of the control to match.
///
@@ -78,7 +81,8 @@ namespace Avalonia.Styling
}
else
{
- return new SelectorMatch((control.GetValue(_property) ?? string.Empty).Equals(_value));
+ var result = (control.GetValue(_property) ?? string.Empty).Equals(_value);
+ return result ? SelectorMatch.AlwaysThisInstance : SelectorMatch.NeverThisInstance;
}
}
diff --git a/src/Avalonia.Styling/Styling/Selector.cs b/src/Avalonia.Styling/Styling/Selector.cs
index 24c6be9b7a..af209ea970 100644
--- a/src/Avalonia.Styling/Styling/Selector.cs
+++ b/src/Avalonia.Styling/Styling/Selector.cs
@@ -17,6 +17,14 @@ namespace Avalonia.Styling
///
public abstract bool InTemplate { get; }
+ ///
+ /// Gets a value indicating whether this selector is a combinator.
+ ///
+ ///
+ /// A combinator is a selector such as Child or Descendent which links simple selectors.
+ ///
+ public abstract bool IsCombinator { get; }
+
///
/// Gets the target type of the selector, if available.
///
@@ -33,25 +41,32 @@ namespace Avalonia.Styling
/// A .
public SelectorMatch Match(IStyleable control, bool subscribe = true)
{
- List> inputs = new List>();
- Selector selector = this;
+ var inputs = new List>();
+ var selector = this;
+ var alwaysThisType = true;
+ var hitCombinator = false;
while (selector != null)
{
- if (selector.InTemplate && control.TemplatedParent == null)
- {
- return SelectorMatch.False;
- }
+ hitCombinator |= selector.IsCombinator;
var match = selector.Evaluate(control, subscribe);
- if (match.ImmediateResult == false)
+ if (!match.IsMatch)
+ {
+ return hitCombinator ? SelectorMatch.NeverThisInstance : match;
+ }
+ else if (selector.InTemplate && control.TemplatedParent == null)
+ {
+ return SelectorMatch.NeverThisInstance;
+ }
+ else if (match.Result == SelectorMatchResult.AlwaysThisInstance)
{
- return match;
+ alwaysThisType = false;
}
- else if (match.ObservableResult != null)
+ else if (match.Result == SelectorMatchResult.Sometimes)
{
- inputs.Add(match.ObservableResult);
+ inputs.Add(match.Activator);
}
selector = selector.MovePrevious();
@@ -63,7 +78,9 @@ namespace Avalonia.Styling
}
else
{
- return SelectorMatch.True;
+ return alwaysThisType && !hitCombinator ?
+ SelectorMatch.AlwaysThisType :
+ SelectorMatch.AlwaysThisInstance;
}
}
diff --git a/src/Avalonia.Styling/Styling/SelectorMatch.cs b/src/Avalonia.Styling/Styling/SelectorMatch.cs
index f325578389..63b89e9e97 100644
--- a/src/Avalonia.Styling/Styling/SelectorMatch.cs
+++ b/src/Avalonia.Styling/Styling/SelectorMatch.cs
@@ -5,51 +5,95 @@ using System;
namespace Avalonia.Styling
{
+ ///
+ /// Describes how a matches a control and its type.
+ ///
+ public enum SelectorMatchResult
+ {
+ ///
+ /// The selector never matches this type.
+ ///
+ NeverThisType,
+
+ ///
+ /// The selector never matches this instance, but can match this type.
+ ///
+ NeverThisInstance,
+
+ ///
+ /// The selector always matches this type.
+ ///
+ AlwaysThisType,
+
+ ///
+ /// The selector always matches this instance, but doesn't always match this type.
+ ///
+ AlwaysThisInstance,
+
+ ///
+ /// The selector matches this instance based on the .
+ ///
+ Sometimes,
+ }
+
///
/// Holds the result of a match.
///
///
- /// There are two types of selectors - ones whose match can never change for a particular
- /// control (such as ) and ones whose result can
- /// change over time (such as . For the first
- /// category of selectors, the value of will be set but for the
- /// second, will be null and will
- /// hold an observable which tracks the match.
+ /// A selector match describes whether and how a matches a control, and
+ /// in addition whether the selector can ever match a control of the same type.
///
public class SelectorMatch
{
- public static readonly SelectorMatch False = new SelectorMatch(false);
+ ///
+ /// A selector match with the result of .
+ ///
+ public static readonly SelectorMatch NeverThisType = new SelectorMatch(SelectorMatchResult.NeverThisType);
- public static readonly SelectorMatch True = new SelectorMatch(true);
+ ///
+ /// A selector match with the result of .
+ ///
+ public static readonly SelectorMatch NeverThisInstance = new SelectorMatch(SelectorMatchResult.NeverThisInstance);
///
- /// Initializes a new instance of the class.
+ /// A selector match with the result of .
///
- /// The immediate match value.
- public SelectorMatch(bool match)
- {
- ImmediateResult = match;
- }
+ public static readonly SelectorMatch AlwaysThisType = new SelectorMatch(SelectorMatchResult.AlwaysThisType);
///
- /// Initializes a new instance of the class.
+ /// Gets a selector match with the result of .
///
- /// The observable match value.
+ public static readonly SelectorMatch AlwaysThisInstance = new SelectorMatch(SelectorMatchResult.AlwaysThisInstance);
+
+ ///
+ /// Initializes a new instance of the class with a
+ /// result.
+ ///
+ /// The match activator.
public SelectorMatch(IObservable match)
{
- ObservableResult = match;
+ Contract.Requires(match != null);
+
+ Result = SelectorMatchResult.Sometimes;
+ Activator = match;
}
+ private SelectorMatch(SelectorMatchResult result) => Result = result;
+
///
- /// Gets the immediate result of the selector match, in the case of selectors that cannot
- /// change over time.
+ /// Gets a value indicating whether the match was positive.
+ ///
+ public bool IsMatch => Result >= SelectorMatchResult.AlwaysThisType;
+
+ ///
+ /// Gets the result of the match.
///
- public bool? ImmediateResult { get; }
+ public SelectorMatchResult Result { get; }
///
/// Gets an observable which tracks the selector match, in the case of selectors that can
/// change over time.
///
- public IObservable ObservableResult { get; }
+ public IObservable Activator { get; }
}
}
diff --git a/src/Avalonia.Styling/Styling/Setter.cs b/src/Avalonia.Styling/Styling/Setter.cs
index 54d0a7d40f..cfe17a4291 100644
--- a/src/Avalonia.Styling/Styling/Setter.cs
+++ b/src/Avalonia.Styling/Styling/Setter.cs
@@ -69,7 +69,7 @@ namespace Avalonia.Styling
{
throw new ArgumentException(
"Cannot assign a control to Setter.Value. Wrap the control in a .",
- "value");
+ nameof(value));
}
_value = value;
diff --git a/src/Avalonia.Styling/Styling/Style.cs b/src/Avalonia.Styling/Styling/Style.cs
index 23c318a809..27fad58346 100644
--- a/src/Avalonia.Styling/Styling/Style.cs
+++ b/src/Avalonia.Styling/Styling/Style.cs
@@ -106,20 +106,14 @@ namespace Avalonia.Styling
///
bool IResourceProvider.HasResources => _resources?.Count > 0;
- ///
- /// Attaches the style to a control if the style's selector matches.
- ///
- /// The control to attach to.
- ///
- /// The control that contains this style. May be null.
- ///
- public void Attach(IStyleable control, IStyleHost container)
+ ///
+ public bool Attach(IStyleable control, IStyleHost container)
{
if (Selector != null)
{
var match = Selector.Match(control);
- if (match.ImmediateResult != false)
+ if (match.IsMatch)
{
var controlSubscriptions = GetSubscriptions(control);
@@ -129,9 +123,10 @@ namespace Avalonia.Styling
{
foreach (var animation in Animations)
{
- IObservable obsMatch = match.ObservableResult;
+ var obsMatch = match.Activator;
- if (match.ImmediateResult == true)
+ if (match.Result == SelectorMatchResult.AlwaysThisType ||
+ match.Result == SelectorMatchResult.AlwaysThisInstance)
{
obsMatch = Observable.Return(true);
}
@@ -143,13 +138,15 @@ namespace Avalonia.Styling
foreach (var setter in Setters)
{
- var sub = setter.Apply(this, control, match.ObservableResult);
+ var sub = setter.Apply(this, control, match.Activator);
subs.Add(sub);
}
controlSubscriptions.Add(subs);
Subscriptions.Add(subs);
}
+
+ return match.Result != SelectorMatchResult.NeverThisType;
}
else if (control == container)
{
@@ -165,7 +162,10 @@ namespace Avalonia.Styling
controlSubscriptions.Add(subs);
Subscriptions.Add(subs);
+ return true;
}
+
+ return false;
}
///
diff --git a/src/Avalonia.Styling/Styling/Styles.cs b/src/Avalonia.Styling/Styling/Styles.cs
index 22ed8bb241..51499b737a 100644
--- a/src/Avalonia.Styling/Styling/Styles.cs
+++ b/src/Avalonia.Styling/Styling/Styles.cs
@@ -2,6 +2,7 @@
// 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 Avalonia.Collections;
using Avalonia.Controls;
@@ -15,6 +16,7 @@ namespace Avalonia.Styling
{
private IResourceNode _parent;
private IResourceDictionary _resources;
+ private Dictionary> _cache;
public Styles()
{
@@ -34,6 +36,7 @@ namespace Avalonia.Styling
}
x.ResourcesChanged += SubResourceChanged;
+ _cache = null;
},
x =>
{
@@ -49,6 +52,7 @@ namespace Avalonia.Styling
}
x.ResourcesChanged -= SubResourceChanged;
+ _cache = null;
},
() => { });
}
@@ -97,11 +101,46 @@ namespace Avalonia.Styling
///
/// The control that contains this style. May be null.
///
- public void Attach(IStyleable control, IStyleHost container)
+ public bool Attach(IStyleable control, IStyleHost container)
{
- foreach (IStyle style in this)
+ if (_cache == null)
+ {
+ _cache = new Dictionary>();
+ }
+
+ if (_cache.TryGetValue(control.StyleKey, out var cached))
+ {
+ if (cached != null)
+ {
+ foreach (var style in cached)
+ {
+ style.Attach(control, container);
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+ else
{
- style.Attach(control, container);
+ List result = null;
+
+ foreach (var style in this)
+ {
+ if (style.Attach(control, container))
+ {
+ if (result == null)
+ {
+ result = new List();
+ }
+
+ result.Add(style);
+ }
+ }
+
+ _cache.Add(control.StyleKey, result);
+ return result != null;
}
}
diff --git a/src/Avalonia.Styling/Styling/TemplateSelector.cs b/src/Avalonia.Styling/Styling/TemplateSelector.cs
index 6919b4ad27..7530339883 100644
--- a/src/Avalonia.Styling/Styling/TemplateSelector.cs
+++ b/src/Avalonia.Styling/Styling/TemplateSelector.cs
@@ -23,6 +23,9 @@ namespace Avalonia.Styling
///
public override bool InTemplate => true;
+ ///
+ public override bool IsCombinator => true;
+
///
public override Type TargetType => null;
@@ -46,7 +49,7 @@ namespace Avalonia.Styling
"Cannot call Template selector on control with null TemplatedParent.");
}
- return _parent.Match(templatedParent, subscribe) ?? SelectorMatch.True;
+ return _parent.Match(templatedParent, subscribe);
}
protected override Selector MovePrevious() => null;
diff --git a/src/Avalonia.Styling/Styling/TypeNameAndClassSelector.cs b/src/Avalonia.Styling/Styling/TypeNameAndClassSelector.cs
index 94c0b75c6e..cd019e6535 100644
--- a/src/Avalonia.Styling/Styling/TypeNameAndClassSelector.cs
+++ b/src/Avalonia.Styling/Styling/TypeNameAndClassSelector.cs
@@ -68,6 +68,9 @@ namespace Avalonia.Styling
///
public override Type TargetType => _targetType ?? _previous?.TargetType;
+ ///
+ public override bool IsCombinator => false;
+
///
/// Whether the selector matches the concrete or any object which
/// implements .
@@ -101,21 +104,23 @@ namespace Avalonia.Styling
{
if (controlType != TargetType)
{
- return SelectorMatch.False;
+ return SelectorMatch.NeverThisType;
}
}
else
{
if (!TargetType.GetTypeInfo().IsAssignableFrom(controlType.GetTypeInfo()))
{
- return SelectorMatch.False;
+ return SelectorMatch.NeverThisType;
}
}
}
- if (Name != null && control.Name != Name)
+ if (Name != null)
{
- return SelectorMatch.False;
+ return control.Name == Name ?
+ SelectorMatch.AlwaysThisInstance :
+ SelectorMatch.NeverThisInstance;
}
if (_classes.IsValueCreated && _classes.Value.Count > 0)
@@ -127,12 +132,14 @@ namespace Avalonia.Styling
}
else
{
- return new SelectorMatch(Matches(control.Classes));
+ return Matches(control.Classes) ?
+ SelectorMatch.AlwaysThisInstance :
+ SelectorMatch.NeverThisInstance;
}
}
else
{
- return SelectorMatch.True;
+ return SelectorMatch.AlwaysThisType;
}
}
diff --git a/src/Avalonia.Visuals/Media/GradientStop.cs b/src/Avalonia.Visuals/Media/GradientStop.cs
index 00d96a0b3c..25a6eb10dc 100644
--- a/src/Avalonia.Visuals/Media/GradientStop.cs
+++ b/src/Avalonia.Visuals/Media/GradientStop.cs
@@ -11,13 +11,13 @@ namespace Avalonia.Media
///
/// Describes the property.
///
- public static StyledProperty OffsetProperty =
+ public static readonly StyledProperty OffsetProperty =
AvaloniaProperty.Register(nameof(Offset));
///
/// Describes the property.
///
- public static StyledProperty ColorProperty =
+ public static readonly StyledProperty ColorProperty =
AvaloniaProperty.Register(nameof(Color));
///
diff --git a/src/Avalonia.Visuals/Media/Typeface.cs b/src/Avalonia.Visuals/Media/Typeface.cs
index 6dde2bb591..37ac0953bf 100644
--- a/src/Avalonia.Visuals/Media/Typeface.cs
+++ b/src/Avalonia.Visuals/Media/Typeface.cs
@@ -7,7 +7,7 @@ namespace Avalonia.Media
///
public class Typeface
{
- public static Typeface Default = new Typeface(FontFamily.Default);
+ public static readonly Typeface Default = new Typeface(FontFamily.Default);
///
/// Initializes a new instance of the class.
diff --git a/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj b/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj
index 0b5e25fa9f..38d207a31d 100644
--- a/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj
+++ b/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj
@@ -11,6 +11,7 @@
+
diff --git a/src/Markup/Avalonia.Markup.Xaml/AvaloniaTypeConverters.cs b/src/Markup/Avalonia.Markup.Xaml/AvaloniaTypeConverters.cs
index fa9d364fc0..e09d9bfd17 100644
--- a/src/Markup/Avalonia.Markup.Xaml/AvaloniaTypeConverters.cs
+++ b/src/Markup/Avalonia.Markup.Xaml/AvaloniaTypeConverters.cs
@@ -51,6 +51,14 @@ namespace Avalonia.Markup.Xaml
/// The type converter.
public static Type GetTypeConverter(Type type)
{
+ if (type.IsConstructedGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
+ {
+ var inner = GetTypeConverter(type.GetGenericArguments()[0]);
+ if (inner == null)
+ return null;
+ return typeof(NullableTypeConverter<>).MakeGenericType(inner);
+ }
+
if (_converters.TryGetValue(type, out var result))
{
return result;
diff --git a/src/Markup/Avalonia.Markup.Xaml/Converters/NullableTypeConverter.cs b/src/Markup/Avalonia.Markup.Xaml/Converters/NullableTypeConverter.cs
new file mode 100644
index 0000000000..5e7a31da56
--- /dev/null
+++ b/src/Markup/Avalonia.Markup.Xaml/Converters/NullableTypeConverter.cs
@@ -0,0 +1,89 @@
+using System;
+using System.Collections;
+using System.ComponentModel;
+using System.Globalization;
+
+namespace Avalonia.Markup.Xaml.Converters
+{
+ public class NullableTypeConverter : TypeConverter where T : TypeConverter, new()
+ {
+ private TypeConverter _inner;
+
+ public NullableTypeConverter()
+ {
+ _inner = new T();
+ }
+
+ public NullableTypeConverter(TypeConverter inner)
+ {
+ _inner = inner;
+ }
+
+
+ public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
+ {
+ if (value == null)
+ return null;
+ return _inner.ConvertTo(context, culture, value, destinationType);
+ }
+
+ public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
+ {
+ if (value == null)
+ return null;
+ if (value as string == "")
+ return null;
+ return _inner.ConvertFrom(context, culture, value);
+ }
+
+ public override object CreateInstance(ITypeDescriptorContext context, IDictionary propertyValues)
+ {
+ return _inner.CreateInstance(context, propertyValues);
+ }
+
+ public override bool GetStandardValuesSupported(ITypeDescriptorContext context)
+ {
+ return _inner.GetStandardValuesSupported(context);
+ }
+
+ public override bool GetStandardValuesExclusive(ITypeDescriptorContext context)
+ {
+ return _inner.GetStandardValuesExclusive(context);
+ }
+
+ public override bool GetCreateInstanceSupported(ITypeDescriptorContext context)
+ {
+ return _inner.GetCreateInstanceSupported(context);
+ }
+
+ public override bool GetPropertiesSupported(ITypeDescriptorContext context)
+ {
+ return _inner.GetPropertiesSupported(context);
+ }
+
+ public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
+ {
+ return _inner.GetStandardValues(context);
+ }
+
+ public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes)
+ {
+ return _inner.GetProperties(context, value, attributes);
+ }
+
+ public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
+ {
+ return _inner.CanConvertTo(context, destinationType);
+ }
+
+ public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
+ {
+ return _inner.CanConvertFrom(context, sourceType);
+ }
+
+ public override bool IsValid(ITypeDescriptorContext context, object value)
+ {
+ return _inner.IsValid(context, value);
+ }
+ }
+}
diff --git a/src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs b/src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs
index fc2656f236..9273011cb9 100644
--- a/src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs
+++ b/src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs
@@ -62,12 +62,14 @@ namespace Avalonia.Markup.Xaml.Styling
IResourceNode IResourceNode.ResourceParent => _parent;
///
- public void Attach(IStyleable control, IStyleHost container)
+ public bool Attach(IStyleable control, IStyleHost container)
{
if (Source != null)
{
- Loaded.Attach(control, container);
+ return Loaded.Attach(control, container);
}
+
+ return false;
}
public void Detach()
diff --git a/src/Skia/Avalonia.Skia/SurfaceRenderTarget.cs b/src/Skia/Avalonia.Skia/SurfaceRenderTarget.cs
index dcd4b36d56..4b7eae1af4 100644
--- a/src/Skia/Avalonia.Skia/SurfaceRenderTarget.cs
+++ b/src/Skia/Avalonia.Skia/SurfaceRenderTarget.cs
@@ -45,6 +45,7 @@ namespace Avalonia.Skia
///
/// Create backing Skia surface.
///
+ /// GPU.
/// Width.
/// Height.
/// Format.
diff --git a/src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs b/src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs
index eb3a11b2a1..c9d6fa6c11 100644
--- a/src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs
+++ b/src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs
@@ -123,6 +123,7 @@ namespace Avalonia.Skia
///
/// Create framebuffer from given bitmap.
///
+ /// Parent bitmap impl.
/// Bitmap.
public BitmapFramebuffer(WriteableBitmapImpl parent, SKBitmap bitmap)
{
diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs
index 6174bc360f..f4eb0e2ea8 100644
--- a/src/Windows/Avalonia.Win32/WindowImpl.cs
+++ b/src/Windows/Avalonia.Win32/WindowImpl.cs
@@ -153,17 +153,24 @@ namespace Avalonia.Win32
public void Resize(Size value)
{
- if (value != ClientSize)
+ int requestedClientWidth = (int)(value.Width * Scaling);
+ int requestedClientHeight = (int)(value.Height * Scaling);
+ UnmanagedMethods.RECT clientRect;
+ UnmanagedMethods.GetClientRect(_hwnd, out clientRect);
+
+ // do comparison after scaling to avoid rounding issues
+ if (requestedClientWidth != clientRect.Width || requestedClientHeight != clientRect.Height)
{
- value *= Scaling;
-
+ UnmanagedMethods.RECT windowRect;
+ UnmanagedMethods.GetWindowRect(_hwnd, out windowRect);
+
UnmanagedMethods.SetWindowPos(
_hwnd,
IntPtr.Zero,
0,
0,
- (int)value.Width,
- (int)value.Height,
+ requestedClientWidth + (windowRect.Width - clientRect.Width),
+ requestedClientHeight + (windowRect.Height - clientRect.Height),
UnmanagedMethods.SetWindowPosFlags.SWP_RESIZE);
}
}
diff --git a/tests/Avalonia.Animation.UnitTests/Avalonia.Animation.UnitTests.csproj b/tests/Avalonia.Animation.UnitTests/Avalonia.Animation.UnitTests.csproj
index 1b2ba3c7de..ef515ce155 100644
--- a/tests/Avalonia.Animation.UnitTests/Avalonia.Animation.UnitTests.csproj
+++ b/tests/Avalonia.Animation.UnitTests/Avalonia.Animation.UnitTests.csproj
@@ -1,9 +1,11 @@
- netcoreapp2.0
+ netcoreapp2.0;net47
Library
+ true
+
@@ -22,4 +24,4 @@
-
\ No newline at end of file
+
diff --git a/tests/Avalonia.Base.UnitTests/Avalonia.Base.UnitTests.csproj b/tests/Avalonia.Base.UnitTests/Avalonia.Base.UnitTests.csproj
index 373b3f7196..6e885f33be 100644
--- a/tests/Avalonia.Base.UnitTests/Avalonia.Base.UnitTests.csproj
+++ b/tests/Avalonia.Base.UnitTests/Avalonia.Base.UnitTests.csproj
@@ -1,9 +1,11 @@
- netcoreapp2.0
+ netcoreapp2.0;net47
Library
+ true
+
diff --git a/tests/Avalonia.Base.UnitTests/Data/Core/ExpressionObserverTests_Property.cs b/tests/Avalonia.Base.UnitTests/Data/Core/ExpressionObserverTests_Property.cs
index c90683959e..b56afa33a4 100644
--- a/tests/Avalonia.Base.UnitTests/Data/Core/ExpressionObserverTests_Property.cs
+++ b/tests/Avalonia.Base.UnitTests/Data/Core/ExpressionObserverTests_Property.cs
@@ -558,11 +558,29 @@ namespace Avalonia.Base.UnitTests.Data.Core
var result = run();
result.Item1.Subscribe(x => { });
- GC.Collect();
+ // Mono trickery
+ GC.Collect(2);
+ GC.WaitForPendingFinalizers();
+ GC.WaitForPendingFinalizers();
+ GC.Collect(2);
+
Assert.Null(result.Item2.Target);
}
+ [Fact]
+ public void Should_Not_Throw_Exception_On_Unsubscribe_When_Already_Unsubscribed()
+ {
+ var source = new Class1 { Foo = "foo" };
+ var target = new PropertyAccessorNode("Foo", false);
+ Assert.NotNull(target);
+ target.Target = new WeakReference(source);
+ target.Subscribe(_ => { });
+ target.Unsubscribe();
+ target.Unsubscribe();
+ Assert.True(true);
+ }
+
private interface INext
{
int PropertyChangedSubscriptionCount { get; }
diff --git a/tests/Avalonia.Base.UnitTests/Data/Core/Plugins/DataAnnotationsValidationPluginTests.cs b/tests/Avalonia.Base.UnitTests/Data/Core/Plugins/DataAnnotationsValidationPluginTests.cs
index 2bffb7b84a..378c225e23 100644
--- a/tests/Avalonia.Base.UnitTests/Data/Core/Plugins/DataAnnotationsValidationPluginTests.cs
+++ b/tests/Avalonia.Base.UnitTests/Data/Core/Plugins/DataAnnotationsValidationPluginTests.cs
@@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
+using System.Linq;
using Avalonia.Data;
using Avalonia.Data.Core.Plugins;
using Avalonia.UnitTests;
@@ -86,17 +87,18 @@ namespace Avalonia.Markup.UnitTests.Data.Plugins
validator.SetValue("123456", BindingPriority.LocalValue);
validator.SetValue("abcdefghijklm", BindingPriority.LocalValue);
- Assert.Equal(new[]
- {
- new BindingNotification(null),
- new BindingNotification("123456"),
- new BindingNotification(
- new AggregateException(
- new ValidationException("The PhoneNumber field is not a valid phone number."),
- new ValidationException("The field PhoneNumber must be a string or array type with a maximum length of '10'.")),
- BindingErrorType.DataValidationError,
- "abcdefghijklm"),
- }, result);
+ Assert.Equal(3, result.Count);
+ Assert.Equal(new BindingNotification(null), result[0]);
+ Assert.Equal(new BindingNotification("123456"), result[1]);
+ var errorResult = (BindingNotification)result[2];
+ Assert.Equal(BindingErrorType.DataValidationError, errorResult.ErrorType);
+ Assert.Equal("abcdefghijklm", errorResult.Value);
+ var exceptions = ((AggregateException)(errorResult.Error)).InnerExceptions;
+ Assert.True(exceptions.Any(ex =>
+ ex.Message.Contains("The PhoneNumber field is not a valid phone number.")));
+ Assert.True(exceptions.Any(ex =>
+ ex.Message.Contains("The field PhoneNumber must be a string or array type with a maximum length of '10'.")));
+
}
private class Data
diff --git a/tests/Avalonia.Controls.UnitTests/Avalonia.Controls.UnitTests.csproj b/tests/Avalonia.Controls.UnitTests/Avalonia.Controls.UnitTests.csproj
index b418d56fcd..aa470bce9d 100644
--- a/tests/Avalonia.Controls.UnitTests/Avalonia.Controls.UnitTests.csproj
+++ b/tests/Avalonia.Controls.UnitTests/Avalonia.Controls.UnitTests.csproj
@@ -1,10 +1,12 @@
- netcoreapp2.0
+ netcoreapp2.0;net47
latest
Library
+ true
+
@@ -26,4 +28,4 @@
-
\ No newline at end of file
+
diff --git a/tests/Avalonia.Controls.UnitTests/GridLayoutTests.cs b/tests/Avalonia.Controls.UnitTests/GridLayoutTests.cs
index fbb90de505..93163f4a92 100644
--- a/tests/Avalonia.Controls.UnitTests/GridLayoutTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/GridLayoutTests.cs
@@ -1,4 +1,5 @@
-using System.Collections.Generic;
+using System.Collections;
+using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Avalonia.Controls.Utils;
@@ -16,7 +17,7 @@ namespace Avalonia.Controls.UnitTests
[InlineData("100, 200, 300", 600d, 600d, new[] { 100d, 200d, 300d })]
[InlineData("100, 200, 300", 400d, 400d, new[] { 100d, 200d, 100d })]
public void MeasureArrange_AllPixelLength_Correct(string length, double containerLength,
- double expectedDesiredLength, IList expectedLengthList)
+ double expectedDesiredLength, IList expectedLengthList)
{
TestRowDefinitionsOnly(length, containerLength, expectedDesiredLength, expectedLengthList);
}
@@ -25,7 +26,7 @@ namespace Avalonia.Controls.UnitTests
[InlineData("*,2*,3*", 0d, 0d, new[] { 0d, 0d, 0d })]
[InlineData("*,2*,3*", 600d, 0d, new[] { 100d, 200d, 300d })]
public void MeasureArrange_AllStarLength_Correct(string length, double containerLength,
- double expectedDesiredLength, IList expectedLengthList)
+ double expectedDesiredLength, IList expectedLengthList)
{
TestRowDefinitionsOnly(length, containerLength, expectedDesiredLength, expectedLengthList);
}
@@ -36,7 +37,7 @@ namespace Avalonia.Controls.UnitTests
[InlineData("100,2*,3*", 100d, 100d, new[] { 100d, 0d, 0d })]
[InlineData("100,2*,3*", 50d, 50d, new[] { 50d, 0d, 0d })]
public void MeasureArrange_MixStarPixelLength_Correct(string length, double containerLength,
- double expectedDesiredLength, IList expectedLengthList)
+ double expectedDesiredLength, IList expectedLengthList)
{
TestRowDefinitionsOnly(length, containerLength, expectedDesiredLength, expectedLengthList);
}
@@ -49,7 +50,7 @@ namespace Avalonia.Controls.UnitTests
[InlineData("100,200,Auto", 100d, 100d, new[] { 100d, 0d, 0d })]
[InlineData("100,200,Auto", 50d, 50d, new[] { 50d, 0d, 0d })]
public void MeasureArrange_MixAutoPixelLength_Correct(string length, double containerLength,
- double expectedDesiredLength, IList expectedLengthList)
+ double expectedDesiredLength, IList expectedLengthList)
{
TestRowDefinitionsOnly(length, containerLength, expectedDesiredLength, expectedLengthList);
}
@@ -58,7 +59,7 @@ namespace Avalonia.Controls.UnitTests
[InlineData("*,2*,Auto", 0d, 0d, new[] { 0d, 0d, 0d })]
[InlineData("*,2*,Auto", 600d, 0d, new[] { 200d, 400d, 0d })]
public void MeasureArrange_MixAutoStarLength_Correct(string length, double containerLength,
- double expectedDesiredLength, IList expectedLengthList)
+ double expectedDesiredLength, IList expectedLengthList)
{
TestRowDefinitionsOnly(length, containerLength, expectedDesiredLength, expectedLengthList);
}
@@ -69,14 +70,24 @@ namespace Avalonia.Controls.UnitTests
[InlineData("*,200,Auto", 200d, 200d, new[] { 0d, 200d, 0d })]
[InlineData("*,200,Auto", 100d, 100d, new[] { 0d, 100d, 0d })]
public void MeasureArrange_MixAutoStarPixelLength_Correct(string length, double containerLength,
- double expectedDesiredLength, IList expectedLengthList)
+ double expectedDesiredLength, IList expectedLengthList)
{
TestRowDefinitionsOnly(length, containerLength, expectedDesiredLength, expectedLengthList);
}
+
+ ///
+ /// This is needed because Mono somehow converts double array to object array in attribute metadata
+ ///
+ static void AssertEqual(IList expected, IReadOnlyList actual)
+ {
+ var conv = expected.Cast().ToArray();
+ Assert.Equal(conv, actual);
+ }
+
[SuppressMessage("ReSharper", "ParameterOnlyUsedForPreconditionCheck.Local")]
private static void TestRowDefinitionsOnly(string length, double containerLength,
- double expectedDesiredLength, IList expectedLengthList)
+ double expectedDesiredLength, IList expectedLengthList)
{
// Arrange
var layout = new GridLayout(new RowDefinitions(length));
@@ -84,11 +95,11 @@ namespace Avalonia.Controls.UnitTests
// Measure - Action & Assert
var measure = layout.Measure(containerLength);
Assert.Equal(expectedDesiredLength, measure.DesiredLength);
- Assert.Equal(expectedLengthList, measure.LengthList);
+ AssertEqual(expectedLengthList, measure.LengthList);
// Arrange - Action & Assert
var arrange = layout.Arrange(containerLength, measure);
- Assert.Equal(expectedLengthList, arrange.LengthList);
+ AssertEqual(expectedLengthList, arrange.LengthList);
}
[Theory]
@@ -99,7 +110,7 @@ namespace Avalonia.Controls.UnitTests
[InlineData("*,2*,Auto", 0d, new[] { Inf, Inf, 0d }, new[] { 0d, 0d, 0d })]
[InlineData("*,200,Auto", 200d, new[] { Inf, 200d, 0d }, new[] { 0d, 200d, 0d })]
public void MeasureArrange_InfiniteMeasure_Correct(string length, double expectedDesiredLength,
- IList expectedMeasureList, IList expectedArrangeList)
+ IList expectedMeasureList, IList expectedArrangeList)
{
// Arrange
var layout = new GridLayout(new RowDefinitions(length));
@@ -107,34 +118,34 @@ namespace Avalonia.Controls.UnitTests
// Measure - Action & Assert
var measure = layout.Measure(Inf);
Assert.Equal(expectedDesiredLength, measure.DesiredLength);
- Assert.Equal(expectedMeasureList, measure.LengthList);
+ AssertEqual(expectedMeasureList, measure.LengthList);
// Arrange - Action & Assert
var arrange = layout.Arrange(measure.DesiredLength, measure);
- Assert.Equal(expectedArrangeList, arrange.LengthList);
+ AssertEqual(expectedArrangeList, arrange.LengthList);
}
[Theory]
[InlineData("Auto,*,*", new[] { 100d, 100d, 100d }, 600d, 300d, new[] { 100d, 250d, 250d })]
public void MeasureArrange_ChildHasSize_Correct(string length,
- IList childLengthList, double containerLength,
- double expectedDesiredLength, IList expectedLengthList)
+ IList childLengthList, double containerLength,
+ double expectedDesiredLength, IList expectedLengthList)
{
// Arrange
var lengthList = new ColumnDefinitions(length);
var layout = new GridLayout(lengthList);
layout.AppendMeasureConventions(
Enumerable.Range(0, lengthList.Count).ToDictionary(x => x, x => (x, 1)),
- x => childLengthList[x]);
+ x => (double)childLengthList[x]);
// Measure - Action & Assert
var measure = layout.Measure(containerLength);
Assert.Equal(expectedDesiredLength, measure.DesiredLength);
- Assert.Equal(expectedLengthList, measure.LengthList);
+ AssertEqual(expectedLengthList, measure.LengthList);
// Arrange - Action & Assert
var arrange = layout.Arrange(containerLength, measure);
- Assert.Equal(expectedLengthList, arrange.LengthList);
+ AssertEqual(expectedLengthList, arrange.LengthList);
}
[Theory]
@@ -145,7 +156,7 @@ namespace Avalonia.Controls.UnitTests
[InlineData(160d, 160d, new[] { 100d, 20d, 40d }, new[] { 100d, 20d, 40d })]
public void MeasureArrange_ChildHasSizeAndHasMultiSpan_Correct(
double containerLength, double expectedDesiredLength,
- IList expectedMeasureLengthList, IList expectedArrangeLengthList)
+ IList expectedMeasureLengthList, IList expectedArrangeLengthList)
{
var length = "100,*,2*";
var childLengthList = new[] { 150d, 150d, 150d };
@@ -161,13 +172,13 @@ namespace Avalonia.Controls.UnitTests
// Measure - Action & Assert
var measure = layout.Measure(containerLength);
Assert.Equal(expectedDesiredLength, measure.DesiredLength);
- Assert.Equal(expectedMeasureLengthList, measure.LengthList);
+ AssertEqual(expectedMeasureLengthList, measure.LengthList);
// Arrange - Action & Assert
var arrange = layout.Arrange(
double.IsInfinity(containerLength) ? measure.DesiredLength : containerLength,
measure);
- Assert.Equal(expectedArrangeLengthList, arrange.LengthList);
+ AssertEqual(expectedArrangeLengthList, arrange.LengthList);
}
}
}
diff --git a/tests/Avalonia.Input.UnitTests/Avalonia.Input.UnitTests.csproj b/tests/Avalonia.Input.UnitTests/Avalonia.Input.UnitTests.csproj
index 1b2ba3c7de..ef515ce155 100644
--- a/tests/Avalonia.Input.UnitTests/Avalonia.Input.UnitTests.csproj
+++ b/tests/Avalonia.Input.UnitTests/Avalonia.Input.UnitTests.csproj
@@ -1,9 +1,11 @@
- netcoreapp2.0
+ netcoreapp2.0;net47
Library
+ true
+
@@ -22,4 +24,4 @@
-
\ No newline at end of file
+
diff --git a/tests/Avalonia.Interactivity.UnitTests/Avalonia.Interactivity.UnitTests.csproj b/tests/Avalonia.Interactivity.UnitTests/Avalonia.Interactivity.UnitTests.csproj
index bc883d0251..6b19d81034 100644
--- a/tests/Avalonia.Interactivity.UnitTests/Avalonia.Interactivity.UnitTests.csproj
+++ b/tests/Avalonia.Interactivity.UnitTests/Avalonia.Interactivity.UnitTests.csproj
@@ -1,9 +1,12 @@
netcoreapp2.0
+ netcoreapp2.0;net47
Library
+ true
+
@@ -20,4 +23,4 @@
-
\ No newline at end of file
+
diff --git a/tests/Avalonia.Markup.UnitTests/Avalonia.Markup.UnitTests.csproj b/tests/Avalonia.Markup.UnitTests/Avalonia.Markup.UnitTests.csproj
index 1680ecf798..aba5f2d469 100644
--- a/tests/Avalonia.Markup.UnitTests/Avalonia.Markup.UnitTests.csproj
+++ b/tests/Avalonia.Markup.UnitTests/Avalonia.Markup.UnitTests.csproj
@@ -1,9 +1,11 @@
- netcoreapp2.0
+ netcoreapp2.0;net47
Library
+ true
+
@@ -23,4 +25,4 @@
-
\ No newline at end of file
+
diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Avalonia.Markup.Xaml.UnitTests.csproj b/tests/Avalonia.Markup.Xaml.UnitTests/Avalonia.Markup.Xaml.UnitTests.csproj
index 735d5c421a..8b47bcec30 100644
--- a/tests/Avalonia.Markup.Xaml.UnitTests/Avalonia.Markup.Xaml.UnitTests.csproj
+++ b/tests/Avalonia.Markup.Xaml.UnitTests/Avalonia.Markup.Xaml.UnitTests.csproj
@@ -1,9 +1,11 @@
- netcoreapp2.0
+ netcoreapp2.0;net47
Library
+ true
+
@@ -36,4 +38,4 @@
-
\ No newline at end of file
+
diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Converters/NullableConverterTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Converters/NullableConverterTests.cs
new file mode 100644
index 0000000000..abe6fa84b0
--- /dev/null
+++ b/tests/Avalonia.Markup.Xaml.UnitTests/Converters/NullableConverterTests.cs
@@ -0,0 +1,31 @@
+using Avalonia.Controls;
+using Avalonia.UnitTests;
+using Xunit;
+
+namespace Avalonia.Markup.Xaml.UnitTests.Converters
+{
+ public class ClassWithNullableProperties
+ {
+ public Thickness? Thickness { get; set; }
+ public Orientation? Orientation { get; set; }
+ }
+
+ public class NullableConverterTests
+ {
+ [Fact]
+ public void Nullable_Types_Should_Still_Be_Converted_Properly()
+ {
+ using (UnitTestApplication.Start(TestServices.MockPlatformWrapper))
+ {
+ var xaml = @"";
+ var loader = new AvaloniaXamlLoader();
+ var data = (ClassWithNullableProperties)loader.Load(xaml, typeof(ClassWithNullableProperties).Assembly);
+ Assert.Equal(new Thickness(5), data.Thickness);
+ Assert.Equal(Orientation.Vertical, data.Orientation);
+ }
+ }
+ }
+}
diff --git a/tests/Avalonia.Styling.UnitTests/Avalonia.Styling.UnitTests.csproj b/tests/Avalonia.Styling.UnitTests/Avalonia.Styling.UnitTests.csproj
index e65dd848dd..3fd280f7ab 100644
--- a/tests/Avalonia.Styling.UnitTests/Avalonia.Styling.UnitTests.csproj
+++ b/tests/Avalonia.Styling.UnitTests/Avalonia.Styling.UnitTests.csproj
@@ -1,10 +1,12 @@
- netcoreapp2.0
+ netcoreapp2.0;net47
Library
CS0067
+ true
+
@@ -23,4 +25,4 @@
-
\ No newline at end of file
+
diff --git a/tests/Avalonia.Styling.UnitTests/SelectorTests_Child.cs b/tests/Avalonia.Styling.UnitTests/SelectorTests_Child.cs
index d837f2cb2f..0561837ad1 100644
--- a/tests/Avalonia.Styling.UnitTests/SelectorTests_Child.cs
+++ b/tests/Avalonia.Styling.UnitTests/SelectorTests_Child.cs
@@ -27,7 +27,7 @@ namespace Avalonia.Styling.UnitTests
var selector = default(Selector).OfType().Child().OfType();
- Assert.True(selector.Match(child).ImmediateResult);
+ Assert.Equal(SelectorMatchResult.AlwaysThisInstance, selector.Match(child).Result);
}
[Fact]
@@ -42,7 +42,7 @@ namespace Avalonia.Styling.UnitTests
var selector = default(Selector).OfType().Child().OfType();
- Assert.False(selector.Match(child).ImmediateResult);
+ Assert.Equal(SelectorMatchResult.NeverThisInstance, selector.Match(child).Result);
}
[Fact]
@@ -54,7 +54,7 @@ namespace Avalonia.Styling.UnitTests
child.LogicalParent = parent;
var selector = default(Selector).OfType().Class("foo").Child().OfType();
- var activator = selector.Match(child).ObservableResult;
+ var activator = selector.Match(child).Activator;
var result = new List();
Assert.False(await activator.Take(1));
@@ -70,7 +70,7 @@ namespace Avalonia.Styling.UnitTests
var control = new TestLogical3();
var selector = default(Selector).OfType().Child().OfType();
- Assert.False(selector.Match(control).ImmediateResult);
+ Assert.Equal(SelectorMatchResult.NeverThisInstance, selector.Match(control).Result);
}
[Fact]
diff --git a/tests/Avalonia.Styling.UnitTests/SelectorTests_Class.cs b/tests/Avalonia.Styling.UnitTests/SelectorTests_Class.cs
index 75599925b7..496998ecd9 100644
--- a/tests/Avalonia.Styling.UnitTests/SelectorTests_Class.cs
+++ b/tests/Avalonia.Styling.UnitTests/SelectorTests_Class.cs
@@ -40,9 +40,10 @@ namespace Avalonia.Styling.UnitTests
};
var target = default(Selector).Class("foo");
- var activator = target.Match(control).ObservableResult;
+ var match = target.Match(control);
- Assert.True(await activator.Take(1));
+ Assert.Equal(SelectorMatchResult.Sometimes, match.Result);
+ Assert.True(await match.Activator.Take(1));
}
[Fact]
@@ -54,9 +55,10 @@ namespace Avalonia.Styling.UnitTests
};
var target = default(Selector).Class("foo");
- var activator = target.Match(control).ObservableResult;
+ var match = target.Match(control);
- Assert.False(await activator.Take(1));
+ Assert.Equal(SelectorMatchResult.Sometimes, match.Result);
+ Assert.False(await match.Activator.Take(1));
}
[Fact]
@@ -69,9 +71,10 @@ namespace Avalonia.Styling.UnitTests
};
var target = default(Selector).Class("foo");
- var activator = target.Match(control).ObservableResult;
+ var match = target.Match(control);
- Assert.True(await activator.Take(1));
+ Assert.Equal(SelectorMatchResult.Sometimes, match.Result);
+ Assert.True(await match.Activator.Take(1));
}
[Fact]
@@ -80,7 +83,7 @@ namespace Avalonia.Styling.UnitTests
var control = new Control1();
var target = default(Selector).Class("foo");
- var activator = target.Match(control).ObservableResult;
+ var activator = target.Match(control).Activator;
Assert.False(await activator.Take(1));
control.Classes.Add("foo");
@@ -96,7 +99,7 @@ namespace Avalonia.Styling.UnitTests
};
var target = default(Selector).Class("foo");
- var activator = target.Match(control).ObservableResult;
+ var activator = target.Match(control).Activator;
Assert.True(await activator.Take(1));
control.Classes.Remove("foo");
@@ -108,7 +111,7 @@ namespace Avalonia.Styling.UnitTests
{
var control = new Control1();
var target = default(Selector).Class("foo").Class("bar");
- var activator = target.Match(control).ObservableResult;
+ var activator = target.Match(control).Activator;
Assert.False(await activator.Take(1));
control.Classes.Add("foo");
@@ -129,7 +132,7 @@ namespace Avalonia.Styling.UnitTests
};
var target = default(Selector).Class("foo");
- var activator = target.Match(control).ObservableResult;
+ var activator = target.Match(control).Activator;
var result = new List();
using (activator.Subscribe(x => result.Add(x)))
diff --git a/tests/Avalonia.Styling.UnitTests/SelectorTests_Descendent.cs b/tests/Avalonia.Styling.UnitTests/SelectorTests_Descendent.cs
index b75b59c212..56dad13186 100644
--- a/tests/Avalonia.Styling.UnitTests/SelectorTests_Descendent.cs
+++ b/tests/Avalonia.Styling.UnitTests/SelectorTests_Descendent.cs
@@ -26,7 +26,7 @@ namespace Avalonia.Styling.UnitTests
var selector = default(Selector).OfType().Descendant().OfType();
- Assert.True(selector.Match(child).ImmediateResult);
+ Assert.Equal(SelectorMatchResult.AlwaysThisInstance, selector.Match(child).Result);
}
[Fact]
@@ -41,7 +41,7 @@ namespace Avalonia.Styling.UnitTests
var selector = default(Selector).OfType().Descendant().OfType();
- Assert.True(selector.Match(child).ImmediateResult);
+ Assert.Equal(SelectorMatchResult.AlwaysThisInstance, selector.Match(child).Result);
}
[Fact]
@@ -56,7 +56,7 @@ namespace Avalonia.Styling.UnitTests
child.LogicalParent = parent;
var selector = default(Selector).OfType().Class("foo").Descendant().OfType();
- var activator = selector.Match(child).ObservableResult;
+ var activator = selector.Match(child).Activator;
Assert.True(await activator.Take(1));
}
@@ -74,7 +74,7 @@ namespace Avalonia.Styling.UnitTests
child.LogicalParent = parent;
var selector = default(Selector).OfType().Class("foo").Descendant().OfType();
- var activator = selector.Match(child).ObservableResult;
+ var activator = selector.Match(child).Activator;
Assert.False(await activator.Take(1));
}
@@ -90,7 +90,7 @@ namespace Avalonia.Styling.UnitTests
child.LogicalParent = parent;
var selector = default(Selector).OfType().Class("foo").Descendant().OfType();
- var activator = selector.Match(child).ObservableResult;
+ var activator = selector.Match(child).Activator;
Assert.False(await activator.Take(1));
parent.Classes.Add("foo");
diff --git a/tests/Avalonia.Styling.UnitTests/SelectorTests_Multiple.cs b/tests/Avalonia.Styling.UnitTests/SelectorTests_Multiple.cs
index 067b08b296..04b29376b0 100644
--- a/tests/Avalonia.Styling.UnitTests/SelectorTests_Multiple.cs
+++ b/tests/Avalonia.Styling.UnitTests/SelectorTests_Multiple.cs
@@ -14,7 +14,7 @@ namespace Avalonia.Styling.UnitTests
public class SelectorTests_Multiple
{
[Fact]
- public void Template_Child_Of_Control_With_Two_Classes()
+ public void Named_Template_Child_Of_Control_With_Two_Classes()
{
var template = new FuncControlTemplate(parent =>
{
@@ -40,9 +40,10 @@ namespace Avalonia.Styling.UnitTests
var border = (Border)((IVisual)control).VisualChildren.Single();
var values = new List();
- var activator = selector.Match(border).ObservableResult;
+ var match = selector.Match(border);
- activator.Subscribe(x => values.Add(x));
+ Assert.Equal(SelectorMatchResult.Sometimes, match.Result);
+ match.Activator.Subscribe(x => values.Add(x));
Assert.Equal(new[] { false }, values);
control.Classes.AddRange(new[] { "foo", "bar" });
@@ -51,6 +52,39 @@ namespace Avalonia.Styling.UnitTests
Assert.Equal(new[] { false, true, false }, values);
}
+ [Fact]
+ public void Named_OfType_Template_Child_Of_Control_With_Two_Classes_Wrong_Type()
+ {
+ var template = new FuncControlTemplate(parent =>
+ {
+ return new Border
+ {
+ Name = "border",
+ };
+ });
+
+ var control = new Button
+ {
+ Template = template,
+ };
+
+ control.ApplyTemplate();
+
+ var selector = default(Selector)
+ .OfType