Browse Source

Merge branch 'master' into DisableButtonOnNullCommandBinding

pull/894/head
Jeremy Koritzinsky 9 years ago
committed by GitHub
parent
commit
6d32e6a36c
  1. 5
      .ncrunch/Avalonia.Base.UnitTests.net461.v3.ncrunchproject
  2. 5
      .ncrunch/Avalonia.Controls.UnitTests.net461.v3.ncrunchproject
  3. 5
      .ncrunch/Avalonia.Input.UnitTests.net461.v3.ncrunchproject
  4. 5
      .ncrunch/Avalonia.Interactivity.UnitTests.net461.v3.ncrunchproject
  5. 5
      .ncrunch/Avalonia.Interactivity.UnitTests.netcoreapp1.1.v3.ncrunchproject
  6. 5
      .ncrunch/Avalonia.Layout.UnitTests.net461.v3.ncrunchproject
  7. 5
      .ncrunch/Avalonia.Markup.UnitTests.net461.v3.ncrunchproject
  8. 5
      .ncrunch/Avalonia.Markup.Xaml.UnitTests.net461.v3.ncrunchproject
  9. 5
      .ncrunch/Avalonia.Styling.UnitTests.net461.v3.ncrunchproject
  10. 5
      .ncrunch/Avalonia.UnitTests.net461.v3.ncrunchproject
  11. 5
      .ncrunch/Direct3DInteropSample.v3.ncrunchproject
  12. 1
      Avalonia.sln.DotSettings
  13. 1
      appveyor.yml
  14. 43
      build.cake
  15. 5
      build/SkiaSharp.Desktop.props
  16. 3
      build/SkiaSharp.props
  17. 2
      docs/guidelines/build.md
  18. 2
      docs/index.md
  19. 2
      docs/tutorial/gettingstarted.md
  20. 24
      packages.cake
  21. 14
      parameters.cake
  22. 2
      readme.md
  23. 3
      samples/ControlCatalog.Desktop/ControlCatalog.Desktop.csproj
  24. 2
      samples/interop/Direct3DInteropSample/MainWindow.cs
  25. 1
      samples/interop/GtkInteropDemo/GtkInteropDemo.csproj
  26. 1
      samples/interop/WindowsInteropTest/WindowsInteropTest.csproj
  27. 1
      src/Android/Avalonia.Android/AndroidPlatform.cs
  28. 17
      src/Avalonia.Base/AvaloniaObject.cs
  29. 2
      src/Avalonia.Base/AvaloniaProperty.cs
  30. 25
      src/Avalonia.Base/Collections/AvaloniaDictionary.cs
  31. 4
      src/Avalonia.Base/PriorityValue.cs
  32. 237
      src/Avalonia.Base/Utilities/TypeUtilities.cs
  33. 24
      src/Avalonia.Controls/AppBuilderBase.cs
  34. 2
      src/Avalonia.Controls/Button.cs
  35. 2
      src/Avalonia.Controls/Control.cs
  36. 12
      src/Avalonia.Controls/Embedding/EmbeddableControlRoot.cs
  37. 2
      src/Avalonia.Controls/ItemsControl.cs
  38. 3
      src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs
  39. 7
      src/Avalonia.Controls/Primitives/PopupRoot.cs
  40. 7
      src/Avalonia.Controls/TextBox.cs
  41. 35
      src/Avalonia.Controls/TopLevel.cs
  42. 28
      src/Avalonia.Controls/Window.cs
  43. 34
      src/Avalonia.Controls/WindowBase.cs
  44. 4
      src/Avalonia.DesignerSupport/DesignerAssist.cs
  45. 8
      src/Avalonia.DotNetFrameworkRuntime/AppBuilder.cs
  46. 7
      src/Avalonia.Layout/Layoutable.cs
  47. 2
      src/Avalonia.Styling/Controls/NameScope.cs
  48. 7
      src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs
  49. 2
      src/Avalonia.Visuals/Rendering/ZIndexComparer.cs
  50. 2
      src/Avalonia.Visuals/VisualTree/VisualExtensions.cs
  51. 1
      src/Gtk/Avalonia.Cairo/Media/DrawingContext.cs
  52. 6
      src/Gtk/Avalonia.Cairo/Properties/AssemblyInfo.cs
  53. 6
      src/Gtk/Avalonia.Gtk/Properties/AssemblyInfo.cs
  54. 1
      src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs
  55. 5
      src/Gtk/Avalonia.Gtk3/Properties/AssemblyInfo.cs
  56. 1
      src/Gtk/Avalonia.Gtk3/SystemDialogs.cs
  57. 1
      src/Linux/Avalonia.LinuxFramebuffer/LinuxFramebufferPlatform.cs
  58. 2
      src/Markup/Avalonia.Markup.Xaml/Parsers/SelectorParser.cs
  59. 4
      src/Markup/Avalonia.Markup/Data/Plugins/TaskStreamPlugin.cs
  60. 64
      src/Markup/Avalonia.Markup/DefaultValueConverter.cs
  61. 6
      src/Shared/SharedAssemblyInfo.cs
  62. 1
      src/Skia/Avalonia.Skia.Desktop.NetStandard/Avalonia.Skia.Desktop.NetStandard.csproj
  63. 2
      src/Skia/Avalonia.Skia.Desktop/Avalonia.Skia.Desktop.csproj
  64. 4
      src/Skia/Avalonia.Skia.Desktop/Properties/AssemblyInfo.cs
  65. 4
      src/Skia/Avalonia.Skia/DrawingContextImpl.cs
  66. 2
      src/Skia/Avalonia.Skia/FormattedTextImpl.cs
  67. 4
      src/Windows/Avalonia.Win32/Embedding/WinFormsAvaloniaControlHost.cs
  68. 1
      src/iOS/Avalonia.iOS/iOSPlatform.cs
  69. 25
      tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization_Simple.cs
  70. 53
      tests/Avalonia.Layout.UnitTests/MeasureTests.cs
  71. 17
      tests/Avalonia.Markup.Xaml.UnitTests/Parsers/SelectorParserTests.cs

5
.ncrunch/Avalonia.Base.UnitTests.net461.v3.ncrunchproject

@ -0,0 +1,5 @@
<ProjectConfiguration>
<Settings>
<PreviouslyBuiltSuccessfully>True</PreviouslyBuiltSuccessfully>
</Settings>
</ProjectConfiguration>

5
.ncrunch/Avalonia.Controls.UnitTests.net461.v3.ncrunchproject

@ -0,0 +1,5 @@
<ProjectConfiguration>
<Settings>
<PreviouslyBuiltSuccessfully>True</PreviouslyBuiltSuccessfully>
</Settings>
</ProjectConfiguration>

5
.ncrunch/Avalonia.Input.UnitTests.net461.v3.ncrunchproject

@ -0,0 +1,5 @@
<ProjectConfiguration>
<Settings>
<PreviouslyBuiltSuccessfully>True</PreviouslyBuiltSuccessfully>
</Settings>
</ProjectConfiguration>

5
.ncrunch/Avalonia.Interactivity.UnitTests.net461.v3.ncrunchproject

@ -0,0 +1,5 @@
<ProjectConfiguration>
<Settings>
<PreviouslyBuiltSuccessfully>True</PreviouslyBuiltSuccessfully>
</Settings>
</ProjectConfiguration>

5
.ncrunch/Avalonia.Interactivity.UnitTests.netcoreapp1.1.v3.ncrunchproject

@ -0,0 +1,5 @@
<ProjectConfiguration>
<Settings>
<PreviouslyBuiltSuccessfully>True</PreviouslyBuiltSuccessfully>
</Settings>
</ProjectConfiguration>

5
.ncrunch/Avalonia.Layout.UnitTests.net461.v3.ncrunchproject

@ -0,0 +1,5 @@
<ProjectConfiguration>
<Settings>
<PreviouslyBuiltSuccessfully>True</PreviouslyBuiltSuccessfully>
</Settings>
</ProjectConfiguration>

5
.ncrunch/Avalonia.Markup.UnitTests.net461.v3.ncrunchproject

@ -0,0 +1,5 @@
<ProjectConfiguration>
<Settings>
<PreviouslyBuiltSuccessfully>True</PreviouslyBuiltSuccessfully>
</Settings>
</ProjectConfiguration>

5
.ncrunch/Avalonia.Markup.Xaml.UnitTests.net461.v3.ncrunchproject

@ -0,0 +1,5 @@
<ProjectConfiguration>
<Settings>
<PreviouslyBuiltSuccessfully>True</PreviouslyBuiltSuccessfully>
</Settings>
</ProjectConfiguration>

5
.ncrunch/Avalonia.Styling.UnitTests.net461.v3.ncrunchproject

@ -0,0 +1,5 @@
<ProjectConfiguration>
<Settings>
<PreviouslyBuiltSuccessfully>True</PreviouslyBuiltSuccessfully>
</Settings>
</ProjectConfiguration>

5
.ncrunch/Avalonia.UnitTests.net461.v3.ncrunchproject

@ -0,0 +1,5 @@
<ProjectConfiguration>
<Settings>
<PreviouslyBuiltSuccessfully>True</PreviouslyBuiltSuccessfully>
</Settings>
</ProjectConfiguration>

5
.ncrunch/Direct3DInteropSample.v3.ncrunchproject

@ -0,0 +1,5 @@
<ProjectConfiguration>
<Settings>
<PreviouslyBuiltSuccessfully>True</PreviouslyBuiltSuccessfully>
</Settings>
</ProjectConfiguration>

1
Avalonia.sln.DotSettings

@ -1,4 +1,5 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation"> <wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=3E53A01A_002DB331_002D47F3_002DB828_002D4A5717E77A24_002Fd_003Aglass/@EntryIndexedValue">ExplicitlyExcluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=6417B24E_002D49C2_002D4985_002D8DB2_002D3AB9D898EC91/@EntryIndexedValue">ExplicitlyExcluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=6417B24E_002D49C2_002D4985_002D8DB2_002D3AB9D898EC91/@EntryIndexedValue">ExplicitlyExcluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=E3A1060B_002D50D0_002D44E8_002D88B6_002DF44EF2E5BD72_002Ff_003Ahtml_002Ehtm/@EntryIndexedValue">ExplicitlyExcluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=E3A1060B_002D50D0_002D44E8_002D88B6_002DF44EF2E5BD72_002Ff_003Ahtml_002Ehtm/@EntryIndexedValue">ExplicitlyExcluded</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=RedundantUsingDirective/@EntryIndexedValue">HINT</s:String> <s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=RedundantUsingDirective/@EntryIndexedValue">HINT</s:String>

1
appveyor.yml

@ -35,6 +35,7 @@ test: off
artifacts: artifacts:
- path: artifacts\nuget\*.nupkg - path: artifacts\nuget\*.nupkg
- path: artifacts\zip\*.zip - path: artifacts\zip\*.zip
- path: artifacts\inspectcode.xml
cache: cache:
- gtk-sharp-2.12.26.msi - gtk-sharp-2.12.26.msi
- dotnet-1.0.1.exe - dotnet-1.0.1.exe

43
build.cake

@ -5,7 +5,8 @@
#addin "nuget:?package=Polly&version=4.2.0" #addin "nuget:?package=Polly&version=4.2.0"
#addin "nuget:?package=NuGet.Core&version=2.12.0" #addin "nuget:?package=NuGet.Core&version=2.12.0"
#tool "nuget:https://dotnet.myget.org/F/nuget-build/?package=NuGet.CommandLine&version=4.3.0-preview1-3980&prerelease" #tool "nuget:https://dotnet.myget.org/F/nuget-build/?package=NuGet.CommandLine&version=4.3.0-preview1-3980&prerelease"
#tool "nuget:?package=JetBrains.dotMemoryUnit&version=2.1.20150828.125449" #tool "nuget:?package=JetBrains.dotMemoryUnit&version=2.3.20160517.113140"
#tool "JetBrains.ReSharper.CommandLineTools"
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// TOOLS // TOOLS
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
@ -104,7 +105,7 @@ Task("Restore-NuGet-Packages")
.Does(() => .Does(() =>
{ {
var maxRetryCount = 5; var maxRetryCount = 5;
var toolTimeout = 1d; var toolTimeout = 2d;
Policy Policy
.Handle<Exception>() .Handle<Exception>()
.Retry(maxRetryCount, (exception, retryCount, context) => { .Retry(maxRetryCount, (exception, retryCount, context) => {
@ -279,11 +280,15 @@ Task("Zip-Files")
Zip(parameters.ZipSourceControlCatalogDesktopDirs, Zip(parameters.ZipSourceControlCatalogDesktopDirs,
parameters.ZipTargetControlCatalogDesktopDirs, parameters.ZipTargetControlCatalogDesktopDirs,
GetFiles(parameters.ZipSourceControlCatalogDesktopDirs.FullPath + "/*.dll") + GetFiles(parameters.ZipSourceControlCatalogDesktopDirs.FullPath + "/*.dll") +
GetFiles(parameters.ZipSourceControlCatalogDesktopDirs.FullPath + "/*.config") +
GetFiles(parameters.ZipSourceControlCatalogDesktopDirs.FullPath + "/*.so") +
GetFiles(parameters.ZipSourceControlCatalogDesktopDirs.FullPath + "/*.dylib") +
GetFiles(parameters.ZipSourceControlCatalogDesktopDirs.FullPath + "/*.exe")); GetFiles(parameters.ZipSourceControlCatalogDesktopDirs.FullPath + "/*.exe"));
}); });
Task("Create-NuGet-Packages") Task("Create-NuGet-Packages")
.IsDependentOn("Run-Unit-Tests") .IsDependentOn("Run-Unit-Tests")
.IsDependentOn("Inspect")
.Does(() => .Does(() =>
{ {
foreach(var nuspec in packages.NuspecNuGetSettings) foreach(var nuspec in packages.NuspecNuGetSettings)
@ -331,7 +336,6 @@ Task("Publish-NuGet")
.WithCriteria(() => !parameters.IsLocalBuild) .WithCriteria(() => !parameters.IsLocalBuild)
.WithCriteria(() => !parameters.IsPullRequest) .WithCriteria(() => !parameters.IsPullRequest)
.WithCriteria(() => parameters.IsMainRepo) .WithCriteria(() => parameters.IsMainRepo)
.WithCriteria(() => parameters.IsMasterBranch)
.WithCriteria(() => parameters.IsNuGetRelease) .WithCriteria(() => parameters.IsNuGetRelease)
.Does(() => .Does(() =>
{ {
@ -360,6 +364,39 @@ Task("Publish-NuGet")
Information("Publish-NuGet Task failed, but continuing with next Task..."); Information("Publish-NuGet Task failed, but continuing with next Task...");
}); });
Task("Inspect")
.WithCriteria(parameters.IsRunningOnWindows)
.IsDependentOn("Restore-NuGet-Packages")
.Does(() =>
{
var badIssues = new []{"PossibleNullReferenceException"};
var whitelist = new []{"tests", "src\\android", "src\\ios",
"src\\windows\\avalonia.designer", "src\\avalonia.htmlrenderer\\external"};
Information("Running code inspections");
StartProcess("tools\\JetBrains.ReSharper.CommandLineTools\\tools\\inspectcode.exe",
new ProcessSettings{ Arguments = "--output=artifacts\\inspectcode.xml --profile=Avalonia.sln.DotSettings Avalonia.sln" });
Information("Analyzing report");
var doc = XDocument.Parse(System.IO.File.ReadAllText("artifacts\\inspectcode.xml"));
var failBuild = false;
foreach(var xml in doc.Descendants("Issue"))
{
var typeId = xml.Attribute("TypeId").Value.ToString();
if(badIssues.Contains(typeId))
{
var file = xml.Attribute("File").Value.ToString().ToLower();
if(whitelist.Any(wh => file.StartsWith(wh)))
continue;
var line = xml.Attribute("Line").Value.ToString();
Error(typeId + " - " + file + " on line " + line);
failBuild = true;
}
}
if(failBuild)
throw new Exception("Issues found");
});
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// TARGETS // TARGETS
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////

5
build/SkiaSharp.Desktop.props

@ -1,5 +0,0 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<PackageReference Include="Avalonia.Skia.Linux.Natives" Version="1.56.1.3" />
</ItemGroup>
</Project>

3
build/SkiaSharp.props

@ -1,5 +1,6 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup> <ItemGroup>
<PackageReference Include="SkiaSharp" Version="1.56.1-beta" /> <PackageReference Include="SkiaSharp" Version="1.57.1" />
<PackageReference Condition="$(TargetFramework.Trim('.').ToLower().StartsWith('netframework'))" Include="Avalonia.Skia.Linux.Natives" Version="1.57.1.3" />
</ItemGroup> </ItemGroup>
</Project> </Project>

2
docs/guidelines/build.md

@ -2,7 +2,7 @@
## Windows ## Windows
Avalonia requires at least Visual Studio 2015 to build on Windows. Avalonia requires at least Visual Studio 2017 to build on Windows.
### Install GTK Sharp ### Install GTK Sharp

2
docs/index.md

@ -10,7 +10,7 @@ What does alpha mean? Well, it means that it's now at a stage where you can have
## How do I try it out ## How do I try it out
The easiest way to try out Avalonia is to install the [Visual Studio Extension](https://visualstudiogallery.msdn.microsoft.com/a4542e8a-b56c-4295-8df1-7e220178b873). The easiest way to try out Avalonia is to install the [Visual Studio Extension](https://marketplace.visualstudio.com/items?itemName=AvaloniaTeam.AvaloniaforVisualStudio).
This will add a Avalonia project template and a Window template to the standard Visual Studo "Add" dialog (yes, icons still to come :) ): This will add a Avalonia project template and a Window template to the standard Visual Studo "Add" dialog (yes, icons still to come :) ):

2
docs/tutorial/gettingstarted.md

@ -4,7 +4,7 @@
![](images/add-dialogs.png) ![](images/add-dialogs.png)
The easiest way to try out Avalonia is to install the [Visual Studio Extension](https://visualstudiogallery.msdn.microsoft.com/e1c6ae1f-6fd9-467d-8f62-1e28b4225213). The easiest way to try out Avalonia is to install the [Visual Studio Extension](https://marketplace.visualstudio.com/items?itemName=AvaloniaTeam.AvaloniaforVisualStudio).
This will add a Avalonia project template and a Window template to the standard Visual Studo “Add” This will add a Avalonia project template and a Window template to the standard Visual Studo “Add”
dialog (yes, icons still to come :) ): dialog (yes, icons still to come :) ):

24
packages.cake

@ -7,6 +7,7 @@ public class Packages
public FilePath[] BinFiles { get; private set; } public FilePath[] BinFiles { get; private set; }
public string NugetPackagesDir {get; private set;} public string NugetPackagesDir {get; private set;}
public string SkiaSharpVersion {get; private set; } public string SkiaSharpVersion {get; private set; }
public string SkiaSharpLinuxVersion {get; private set; }
public Packages(ICakeContext context, Parameters parameters) public Packages(ICakeContext context, Parameters parameters)
{ {
// NUGET NUSPECS // NUGET NUSPECS
@ -75,6 +76,7 @@ public class Packages
var SpracheVersion = packageVersions["Sprache"].FirstOrDefault().Item1; var SpracheVersion = packageVersions["Sprache"].FirstOrDefault().Item1;
var SystemReactiveVersion = packageVersions["System.Reactive"].FirstOrDefault().Item1; var SystemReactiveVersion = packageVersions["System.Reactive"].FirstOrDefault().Item1;
SkiaSharpVersion = packageVersions["SkiaSharp"].FirstOrDefault().Item1; SkiaSharpVersion = packageVersions["SkiaSharp"].FirstOrDefault().Item1;
SkiaSharpLinuxVersion = packageVersions["Avalonia.Skia.Linux.Natives"].FirstOrDefault().Item1;
var SharpDXVersion = packageVersions["SharpDX"].FirstOrDefault().Item1; var SharpDXVersion = packageVersions["SharpDX"].FirstOrDefault().Item1;
var SharpDXDirect2D1Version = packageVersions["SharpDX.Direct2D1"].FirstOrDefault().Item1; var SharpDXDirect2D1Version = packageVersions["SharpDX.Direct2D1"].FirstOrDefault().Item1;
var SharpDXDirect3D11Version = packageVersions["SharpDX.Direct3D11"].FirstOrDefault().Item1; var SharpDXDirect3D11Version = packageVersions["SharpDX.Direct3D11"].FirstOrDefault().Item1;
@ -85,6 +87,7 @@ public class Packages
context.Information("Package: Sprache, version: {0}", SpracheVersion); context.Information("Package: Sprache, version: {0}", SpracheVersion);
context.Information("Package: System.Reactive, version: {0}", SystemReactiveVersion); context.Information("Package: System.Reactive, version: {0}", SystemReactiveVersion);
context.Information("Package: SkiaSharp, version: {0}", SkiaSharpVersion); context.Information("Package: SkiaSharp, version: {0}", SkiaSharpVersion);
context.Information("Package: Avalonia.Skia.Linux.Natives, version: {0}", SkiaSharpLinuxVersion);
context.Information("Package: SharpDX, version: {0}", SharpDXVersion); context.Information("Package: SharpDX, version: {0}", SharpDXVersion);
context.Information("Package: SharpDX.Direct2D1, version: {0}", SharpDXDirect2D1Version); context.Information("Package: SharpDX.Direct2D1, version: {0}", SharpDXDirect2D1Version);
context.Information("Package: SharpDX.Direct3D11, version: {0}", SharpDXDirect3D11Version); context.Information("Package: SharpDX.Direct3D11, version: {0}", SharpDXDirect3D11Version);
@ -425,10 +428,7 @@ public class Packages
{ {
new NuSpecDependency() { Id = "Avalonia", Version = parameters.Version }, new NuSpecDependency() { Id = "Avalonia", Version = parameters.Version },
new NuSpecDependency() { Id = "SkiaSharp", Version = SkiaSharpVersion }, new NuSpecDependency() { Id = "SkiaSharp", Version = SkiaSharpVersion },
//netstandard1.3 new NuSpecDependency() { Id = "Avalonia.Skia.Linux.Natives", Version = SkiaSharpLinuxVersion }
new NuSpecDependency() { Id = "Avalonia", TargetFramework = "netstandard1.3", Version = parameters.Version },
new NuSpecDependency() { Id = "SkiaSharp", TargetFramework = "netstandard1.3", Version = SkiaSharpVersion },
new NuSpecDependency() { Id = "NETStandard.Library", TargetFramework = "netstandard1.3", Version = "1.6.0" }
}, },
Files = new [] Files = new []
{ {
@ -446,11 +446,17 @@ public class Packages
Id = "Avalonia.Desktop", Id = "Avalonia.Desktop",
Dependencies = new [] Dependencies = new []
{ {
new NuSpecDependency() { Id = "Avalonia.Win32", Version = parameters.Version }, //Full .NET
new NuSpecDependency() { Id = "Avalonia.Direct2D1", Version = parameters.Version }, new NuSpecDependency() { Id = "Avalonia.Direct2D1", TargetFramework="net45", Version = parameters.Version },
new NuSpecDependency() { Id = "Avalonia.Gtk", Version = parameters.Version }, new NuSpecDependency() { Id = "Avalonia.Gtk", TargetFramework="net45", Version = parameters.Version },
new NuSpecDependency() { Id = "Avalonia.Cairo", Version = parameters.Version }, new NuSpecDependency() { Id = "Avalonia.Cairo", TargetFramework="net45", Version = parameters.Version },
new NuSpecDependency() { Id = "Avalonia.Skia.Desktop", Version = parameters.Version } new NuSpecDependency() { Id = "Avalonia.Win32", TargetFramework="net45", Version = parameters.Version },
new NuSpecDependency() { Id = "Avalonia.Skia.Desktop", TargetFramework="net45", Version = parameters.Version },
new NuSpecDependency() { Id = "Avalonia.Gtk3", TargetFramework="net45", Version = parameters.Version },
//.NET Core
new NuSpecDependency() { Id = "Avalonia.Win32", TargetFramework="netcoreapp1.1", Version = parameters.Version },
new NuSpecDependency() { Id = "Avalonia.Skia.Desktop", TargetFramework="netcoreapp1.1", Version = parameters.Version },
new NuSpecDependency() { Id = "Avalonia.Gtk3", TargetFramework="netcoreapp1.1", Version = parameters.Version }
}, },
Files = new NuSpecContent[] Files = new NuSpecContent[]
{ {

14
parameters.cake

@ -75,17 +75,25 @@ public class Parameters
IsReleasable = StringComparer.OrdinalIgnoreCase.Equals(ReleasePlatform, Platform) IsReleasable = StringComparer.OrdinalIgnoreCase.Equals(ReleasePlatform, Platform)
&& StringComparer.OrdinalIgnoreCase.Equals(ReleaseConfiguration, Configuration); && StringComparer.OrdinalIgnoreCase.Equals(ReleaseConfiguration, Configuration);
IsMyGetRelease = !IsTagged && IsReleasable; IsMyGetRelease = !IsTagged && IsReleasable;
IsNuGetRelease = IsTagged && IsReleasable;
// VERSION // VERSION
Version = context.Argument("force-nuget-version", context.ParseAssemblyInfo(AssemblyInfoPath).AssemblyVersion); Version = context.Argument("force-nuget-version", context.ParseAssemblyInfo(AssemblyInfoPath).AssemblyVersion);
if (IsRunningOnAppVeyor) if (IsRunningOnAppVeyor)
{ {
string tagVersion = null;
if (IsTagged) if (IsTagged)
{ {
// Use Tag Name as version var tag = buildSystem.AppVeyor.Environment.Repository.Tag.Name;
Version = buildSystem.AppVeyor.Environment.Repository.Tag.Name; var nugetReleasePrefix = "nuget-release-";
IsNuGetRelease = IsTagged && IsReleasable && tag.StartsWith(nugetReleasePrefix);
if(IsNuGetRelease)
tagVersion = tag.Substring(nugetReleasePrefix.Length);
}
if(tagVersion != null)
{
Version = tagVersion;
} }
else else
{ {

2
readme.md

@ -42,7 +42,7 @@ using Direct2D and other operating systems using Gtk & Cairo.
Avalonia is now in alpha. What does "alpha" mean? Well, it means that it's now at a stage where you Avalonia is now in alpha. What does "alpha" mean? Well, it means that it's now at a stage where you
can have a play and hopefully create simple applications. There's now a [Visual can have a play and hopefully create simple applications. There's now a [Visual
Studio Extension](https://visualstudiogallery.msdn.microsoft.com/e1c6ae1f-6fd9-467d-8f62-1e28b4225213) Studio Extension](https://marketplace.visualstudio.com/items?itemName=AvaloniaTeam.AvaloniaforVisualStudio)
containing project and item templates that will help you get started, and containing project and item templates that will help you get started, and
there's an initial complement of controls. There's still a lot missing, and you there's an initial complement of controls. There's still a lot missing, and you
*will* find bugs, and the API *will* change, but this represents the first time *will* find bugs, and the API *will* change, but this represents the first time

3
samples/ControlCatalog.Desktop/ControlCatalog.Desktop.csproj

@ -26,7 +26,7 @@
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget> <PlatformTarget>x86</PlatformTarget>
<DebugType>pdbonly</DebugType> <DebugType>pdbonly</DebugType>
<Optimize>true</Optimize> <Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath> <OutputPath>bin\Release\</OutputPath>
@ -142,6 +142,7 @@
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="..\..\build\Serilog.props" /> <Import Project="..\..\build\Serilog.props" />
<Import Project="..\..\build\SkiaSharp.props" />
<Import Project="..\..\build\Serilog.Sinks.Trace.props" /> <Import Project="..\..\build\Serilog.Sinks.Trace.props" />
<Import Project="$(MSBuildThisFileDirectory)..\..\src\Shared\nuget.workaround.targets" /> <Import Project="$(MSBuildThisFileDirectory)..\..\src\Shared\nuget.workaround.targets" />
</Project> </Project>

2
samples/interop/Direct3DInteropSample/MainWindow.cs

@ -58,7 +58,7 @@ namespace Direct3DInteropSample
new ModeDescription((int)ClientSize.Width, (int)ClientSize.Height, new ModeDescription((int)ClientSize.Width, (int)ClientSize.Height,
new Rational(60, 1), Format.R8G8B8A8_UNorm), new Rational(60, 1), Format.R8G8B8A8_UNorm),
IsWindowed = true, IsWindowed = true,
OutputHandle = PlatformImpl.Handle.Handle, OutputHandle = PlatformImpl?.Handle.Handle ?? IntPtr.Zero,
SampleDescription = new SampleDescription(1, 0), SampleDescription = new SampleDescription(1, 0),
SwapEffect = SwapEffect.Discard, SwapEffect = SwapEffect.Discard,
Usage = Usage.RenderTargetOutput Usage = Usage.RenderTargetOutput

1
samples/interop/GtkInteropDemo/GtkInteropDemo.csproj

@ -149,6 +149,7 @@
<Name>ControlCatalog</Name> <Name>ControlCatalog</Name>
</ProjectReference> </ProjectReference>
</ItemGroup> </ItemGroup>
<Import Project="..\..\..\build\Rx.props" />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="$(MSBuildThisFileDirectory)..\..\..\src\Shared\nuget.workaround.targets" /> <Import Project="$(MSBuildThisFileDirectory)..\..\..\src\Shared\nuget.workaround.targets" />
</Project> </Project>

1
samples/interop/WindowsInteropTest/WindowsInteropTest.csproj

@ -179,6 +179,7 @@
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
</Page> </Page>
</ItemGroup> </ItemGroup>
<Import Project="..\..\..\build\Rx.props" />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="$(MSBuildThisFileDirectory)..\..\..\src\Shared\nuget.workaround.targets" /> <Import Project="$(MSBuildThisFileDirectory)..\..\..\src\Shared\nuget.workaround.targets" />
</Project> </Project>

1
src/Android/Avalonia.Android/AndroidPlatform.cs

@ -53,6 +53,7 @@ namespace Avalonia.Android
.Bind<IKeyboardDevice>().ToSingleton<AndroidKeyboardDevice>() .Bind<IKeyboardDevice>().ToSingleton<AndroidKeyboardDevice>()
.Bind<IMouseDevice>().ToSingleton<AndroidMouseDevice>() .Bind<IMouseDevice>().ToSingleton<AndroidMouseDevice>()
.Bind<IPlatformSettings>().ToConstant(Instance) .Bind<IPlatformSettings>().ToConstant(Instance)
.Bind<IRendererFactory>().ToConstant(ImmediateRenderer.Factory)
.Bind<IPlatformThreadingInterface>().ToConstant(new AndroidThreadingInterface()) .Bind<IPlatformThreadingInterface>().ToConstant(new AndroidThreadingInterface())
.Bind<ISystemDialogImpl>().ToTransient<SystemDialogImpl>() .Bind<ISystemDialogImpl>().ToTransient<SystemDialogImpl>()
.Bind<IWindowingPlatform>().ToConstant(Instance) .Bind<IWindowingPlatform>().ToConstant(Instance)

17
src/Avalonia.Base/AvaloniaObject.cs

@ -578,13 +578,13 @@ namespace Avalonia
if (notification == null) if (notification == null)
{ {
return TypeUtilities.CastOrDefault(value, type); return TypeUtilities.ConvertImplicitOrDefault(value, type);
} }
else else
{ {
if (notification.HasValue) if (notification.HasValue)
{ {
notification.SetValue(TypeUtilities.CastOrDefault(notification.Value, type)); notification.SetValue(TypeUtilities.ConvertImplicitOrDefault(notification.Value, type));
} }
return notification; return notification;
@ -622,14 +622,9 @@ namespace Avalonia
/// <returns>The default value.</returns> /// <returns>The default value.</returns>
private object GetDefaultValue(AvaloniaProperty property) private object GetDefaultValue(AvaloniaProperty property)
{ {
if (property.Inherits && _inheritanceParent != null) if (property.Inherits && _inheritanceParent is AvaloniaObject aobj)
{ return aobj.GetValueInternal(property);
return (_inheritanceParent as AvaloniaObject).GetValueInternal(property); return ((IStyledPropertyAccessor) property).GetDefaultValue(GetType());
}
else
{
return ((IStyledPropertyAccessor)property).GetDefaultValue(GetType());
}
} }
/// <summary> /// <summary>
@ -735,7 +730,7 @@ namespace Avalonia
ThrowNotRegistered(property); ThrowNotRegistered(property);
} }
if (!TypeUtilities.TryCast(property.PropertyType, value, out value)) if (!TypeUtilities.TryConvertImplicit(property.PropertyType, value, out value))
{ {
throw new ArgumentException(string.Format( throw new ArgumentException(string.Format(
"Invalid value for Property '{0}': '{1}' ({2})", "Invalid value for Property '{0}': '{1}' ({2})",

2
src/Avalonia.Base/AvaloniaProperty.cs

@ -476,7 +476,7 @@ namespace Avalonia
/// <returns>True if the value is valid, otherwise false.</returns> /// <returns>True if the value is valid, otherwise false.</returns>
public bool IsValidValue(object value) public bool IsValidValue(object value)
{ {
return TypeUtilities.TryCast(PropertyType, value, out value); return TypeUtilities.TryConvertImplicit(PropertyType, value, out value);
} }
/// <summary> /// <summary>

25
src/Avalonia.Base/Collections/AvaloniaDictionary.cs

@ -103,11 +103,9 @@ namespace Avalonia.Collections
_inner = new Dictionary<TKey, TValue>(); _inner = new Dictionary<TKey, TValue>();
if (PropertyChanged != null) PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Count"));
{ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs($"Item[]"));
PropertyChanged(this, new PropertyChangedEventArgs("Count"));
PropertyChanged(this, new PropertyChangedEventArgs($"Item[]"));
}
if (CollectionChanged != null) if (CollectionChanged != null)
{ {
@ -144,12 +142,9 @@ namespace Avalonia.Collections
if (_inner.TryGetValue(key, out value)) if (_inner.TryGetValue(key, out value))
{ {
if (PropertyChanged != null) PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Count"));
{ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs($"Item[{key}]"));
PropertyChanged(this, new PropertyChangedEventArgs("Count"));
PropertyChanged(this, new PropertyChangedEventArgs($"Item[{key}]"));
}
if (CollectionChanged != null) if (CollectionChanged != null)
{ {
var e = new NotifyCollectionChangedEventArgs( var e = new NotifyCollectionChangedEventArgs(
@ -199,11 +194,9 @@ namespace Avalonia.Collections
private void NotifyAdd(TKey key, TValue value) private void NotifyAdd(TKey key, TValue value)
{ {
if (PropertyChanged != null) PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Count"));
{ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs($"Item[{key}]"));
PropertyChanged(this, new PropertyChangedEventArgs("Count"));
PropertyChanged(this, new PropertyChangedEventArgs($"Item[{key}]"));
}
if (CollectionChanged != null) if (CollectionChanged != null)
{ {

4
src/Avalonia.Base/PriorityValue.cs

@ -249,7 +249,7 @@ namespace Avalonia
value = (notification.HasValue) ? notification.Value : null; value = (notification.HasValue) ? notification.Value : null;
} }
if (TypeUtilities.TryCast(_valueType, value, out castValue)) if (TypeUtilities.TryConvertImplicit(_valueType, value, out castValue))
{ {
var old = _value; var old = _value;
@ -285,7 +285,7 @@ namespace Avalonia
Property.Name, Property.Name,
_valueType, _valueType,
value, value,
value.GetType()); value?.GetType());
} }
} }
} }

237
src/Avalonia.Base/Utilities/TypeUtilities.cs

@ -14,17 +14,61 @@ namespace Avalonia.Utilities
/// </summary> /// </summary>
public static class TypeUtilities public static class TypeUtilities
{ {
private static readonly Dictionary<Type, List<Type>> Conversions = new Dictionary<Type, List<Type>>() private static int[] Conversions =
{ {
{ typeof(decimal), new List<Type> { typeof(sbyte), typeof(byte), typeof(short), typeof(ushort), typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(char) } }, 0b101111111111101, // Boolean
{ typeof(double), new List<Type> { typeof(sbyte), typeof(byte), typeof(short), typeof(ushort), typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(char), typeof(float) } }, 0b100001111111110, // Char
{ typeof(float), new List<Type> { typeof(sbyte), typeof(byte), typeof(short), typeof(ushort), typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(char), typeof(float) } }, 0b101111111111111, // SByte
{ typeof(ulong), new List<Type> { typeof(byte), typeof(ushort), typeof(uint), typeof(char) } }, 0b101111111111111, // Byte
{ typeof(long), new List<Type> { typeof(sbyte), typeof(byte), typeof(short), typeof(ushort), typeof(int), typeof(uint), typeof(char) } }, 0b101111111111111, // Int16
{ typeof(uint), new List<Type> { typeof(byte), typeof(ushort), typeof(char) } }, 0b101111111111111, // UInt16
{ typeof(int), new List<Type> { typeof(sbyte), typeof(byte), typeof(short), typeof(ushort), typeof(char) } }, 0b101111111111111, // Int32
{ typeof(ushort), new List<Type> { typeof(byte), typeof(char) } }, 0b101111111111111, // UInt32
{ typeof(short), new List<Type> { typeof(byte) } } 0b101111111111111, // Int64
0b101111111111111, // UInt64
0b101111111111101, // Single
0b101111111111101, // Double
0b101111111111101, // Decimal
0b110000000000000, // DateTime
0b111111111111111, // String
};
private static int[] ImplicitConversions =
{
0b000000000000001, // Boolean
0b001110111100010, // Char
0b001110101010100, // SByte
0b001111111111000, // Byte
0b001110101010000, // Int16
0b001111111100000, // UInt16
0b001110101000000, // Int32
0b001111110000000, // UInt32
0b001110100000000, // Int64
0b001111000000000, // UInt64
0b000110000000000, // Single
0b000100000000000, // Double
0b001000000000000, // Decimal
0b010000000000000, // DateTime
0b100000000000000, // String
};
private static Type[] InbuiltTypes =
{
typeof(Boolean),
typeof(Char),
typeof(SByte),
typeof(Byte),
typeof(Int16),
typeof(UInt16),
typeof(Int32),
typeof(UInt32),
typeof(Int64),
typeof(UInt64),
typeof(Single),
typeof(Double),
typeof(Decimal),
typeof(DateTime),
typeof(String),
}; };
private static readonly Type[] NumericTypes = new[] private static readonly Type[] NumericTypes = new[]
@ -54,49 +98,104 @@ namespace Avalonia.Utilities
} }
/// <summary> /// <summary>
/// Try to cast a value to a type, using implicit conversions if possible. /// Try to convert a value to a type by any means possible.
/// </summary> /// </summary>
/// <param name="to">The type to cast to.</param> /// <param name="to">The type to cast to.</param>
/// <param name="value">The value to cast.</param> /// <param name="value">The value to cast.</param>
/// <param name="culture">The culture to use.</param>
/// <param name="result">If sucessful, contains the cast value.</param> /// <param name="result">If sucessful, contains the cast value.</param>
/// <returns>True if the cast was sucessful, otherwise false.</returns> /// <returns>True if the cast was sucessful, otherwise false.</returns>
public static bool TryCast(Type to, object value, out object result) public static bool TryConvert(Type to, object value, CultureInfo culture, out object result)
{ {
Contract.Requires<ArgumentNullException>(to != null);
if (value == null) if (value == null)
{ {
result = null; result = null;
return AcceptsNull(to); return AcceptsNull(to);
} }
var from = value.GetType();
if (value == AvaloniaProperty.UnsetValue) if (value == AvaloniaProperty.UnsetValue)
{ {
result = value; result = value;
return true; return true;
} }
else if (to.GetTypeInfo().IsAssignableFrom(from.GetTypeInfo()))
var from = value.GetType();
var fromTypeInfo = from.GetTypeInfo();
var toTypeInfo = to.GetTypeInfo();
if (toTypeInfo.IsAssignableFrom(fromTypeInfo))
{ {
result = value; result = value;
return true; return true;
} }
else if (Conversions.ContainsKey(to) && Conversions[to].Contains(from))
if (to == typeof(string))
{ {
result = Convert.ChangeType(value, to); result = Convert.ToString(value);
return true; return true;
} }
else
if (toTypeInfo.IsEnum && from == typeof(string))
{
if (Enum.IsDefined(to, (string)value))
{
result = Enum.Parse(to, (string)value);
return true;
}
}
if (!fromTypeInfo.IsEnum && toTypeInfo.IsEnum)
{ {
var cast = from.GetRuntimeMethods() result = null;
.FirstOrDefault(m => m.Name == "op_Implicit" && m.ReturnType == to);
if (TryConvert(Enum.GetUnderlyingType(to), value, culture, out object enumValue))
{
result = Enum.ToObject(to, enumValue);
return true;
}
}
if (cast != null) if (fromTypeInfo.IsEnum && IsNumeric(to))
{
try
{ {
result = cast.Invoke(null, new[] { value }); result = Convert.ChangeType((int)value, to, culture);
return true; return true;
} }
catch
{
result = null;
return false;
}
}
var convertableFrom = Array.IndexOf(InbuiltTypes, from);
var convertableTo = Array.IndexOf(InbuiltTypes, to);
if (convertableFrom != -1 && convertableTo != -1)
{
if ((Conversions[convertableFrom] & 1 << convertableTo) != 0)
{
try
{
result = Convert.ChangeType(value, to, culture);
return true;
}
catch
{
result = null;
return false;
}
}
}
var cast = from.GetRuntimeMethods()
.FirstOrDefault(m => (m.Name == "op_Implicit" || m.Name == "op_Explicit") && m.ReturnType == to);
if (cast != null)
{
result = cast.Invoke(null, new[] { value });
return true;
} }
result = null; result = null;
@ -104,15 +203,14 @@ namespace Avalonia.Utilities
} }
/// <summary> /// <summary>
/// Try to convert a value to a type, using <see cref="System.Convert"/> if possible, /// Try to convert a value to a type using the implicit conversions allowed by the C#
/// otherwise using <see cref="TryCast(Type, object, out object)"/>. /// language.
/// </summary> /// </summary>
/// <param name="to">The type to cast to.</param> /// <param name="to">The type to cast to.</param>
/// <param name="value">The value to cast.</param> /// <param name="value">The value to cast.</param>
/// <param name="culture">The culture to use.</param>
/// <param name="result">If sucessful, contains the cast value.</param> /// <param name="result">If sucessful, contains the cast value.</param>
/// <returns>True if the cast was sucessful, otherwise false.</returns> /// <returns>True if the cast was sucessful, otherwise false.</returns>
public static bool TryConvert(Type to, object value, CultureInfo culture, out object result) public static bool TryConvertImplicit(Type to, object value, out object result)
{ {
if (value == null) if (value == null)
{ {
@ -120,54 +218,44 @@ namespace Avalonia.Utilities
return AcceptsNull(to); return AcceptsNull(to);
} }
var from = value.GetType();
if (value == AvaloniaProperty.UnsetValue) if (value == AvaloniaProperty.UnsetValue)
{ {
result = value; result = value;
return true; return true;
} }
if (to.GetTypeInfo().IsAssignableFrom(from.GetTypeInfo())) var from = value.GetType();
{ var fromTypeInfo = from.GetTypeInfo();
result = value; var toTypeInfo = to.GetTypeInfo();
return true;
}
if (to == typeof(string)) if (toTypeInfo.IsAssignableFrom(fromTypeInfo))
{ {
result = Convert.ToString(value); result = value;
return true; return true;
} }
if (to.GetTypeInfo().IsEnum && from == typeof(string)) var convertableFrom = Array.IndexOf(InbuiltTypes, from);
{ var convertableTo = Array.IndexOf(InbuiltTypes, to);
if (Enum.IsDefined(to, (string)value))
{
result = Enum.Parse(to, (string)value);
return true;
}
}
bool containsFrom = Conversions.ContainsKey(from);
bool containsTo = Conversions.ContainsKey(to);
if ((containsFrom && containsTo) || (from == typeof(string) && containsTo)) if (convertableFrom != -1 && convertableTo != -1)
{ {
try if ((ImplicitConversions[convertableFrom] & 1 << convertableTo) != 0)
{
result = Convert.ChangeType(value, to, culture);
return true;
}
catch
{ {
result = null; try
return false; {
result = Convert.ChangeType(value, to, CultureInfo.InvariantCulture);
return true;
}
catch
{
result = null;
return false;
}
} }
} }
var cast = from.GetRuntimeMethods() var cast = from.GetRuntimeMethods()
.FirstOrDefault(m => (m.Name == "op_Implicit" || m.Name == "op_Explicit") && m.ReturnType == to); .FirstOrDefault(m => m.Name == "op_Implicit" && m.ReturnType == to);
if (cast != null) if (cast != null)
{ {
@ -180,29 +268,28 @@ namespace Avalonia.Utilities
} }
/// <summary> /// <summary>
/// Casts a value to a type, returning the default for that type if the value could not be /// Convert a value to a type by any means possible, returning the default for that type
/// cast. /// if the value could not be converted.
/// </summary> /// </summary>
/// <param name="value">The value to cast.</param> /// <param name="value">The value to cast.</param>
/// <param name="type">The type to cast to..</param> /// <param name="type">The type to cast to..</param>
/// <param name="culture">The culture to use.</param>
/// <returns>A value of <paramref name="type"/>.</returns> /// <returns>A value of <paramref name="type"/>.</returns>
public static object CastOrDefault(object value, Type type) public static object ConvertOrDefault(object value, Type type, CultureInfo culture)
{ {
var typeInfo = type.GetTypeInfo(); return TryConvert(type, value, culture, out object result) ? result : Default(type);
object result; }
if (TypeUtilities.TryCast(type, value, out result)) /// <summary>
{ /// Convert a value to a type using the implicit conversions allowed by the C# language or
return result; /// return the default for the type if the value could not be converted.
} /// </summary>
else if (typeInfo.IsValueType) /// <param name="value">The value to cast.</param>
{ /// <param name="type">The type to cast to..</param>
return Activator.CreateInstance(type); /// <returns>A value of <paramref name="type"/>.</returns>
} public static object ConvertImplicitOrDefault(object value, Type type)
else {
{ return TryConvertImplicit(type, value, out object result) ? result : Default(type);
return null;
}
} }
/// <summary> /// <summary>

24
src/Avalonia.Controls/AppBuilderBase.cs

@ -55,8 +55,7 @@ namespace Avalonia.Controls
public Action<TAppBuilder> AfterSetupCallback { get; private set; } = builder => { }; public Action<TAppBuilder> AfterSetupCallback { get; private set; } = builder => { };
/// <summary> /// <summary>
/// Gets or sets a method to call before <see cref="Start{TMainWindow}"/> is called on the /// Gets or sets a method to call before Startis called on the <see cref="Application"/>.
/// <see cref="Application"/>.
/// </summary> /// </summary>
public Action<TAppBuilder> BeforeStartCallback { get; private set; } = builder => { }; public Action<TAppBuilder> BeforeStartCallback { get; private set; } = builder => { };
@ -94,8 +93,7 @@ namespace Avalonia.Controls
protected TAppBuilder Self => (TAppBuilder) this; protected TAppBuilder Self => (TAppBuilder) this;
/// <summary> /// <summary>
/// Registers a callback to call before <see cref="Start{TMainWindow}"/> is called on the /// Registers a callback to call before Start is called on the <see cref="Application"/>.
/// <see cref="Application"/>.
/// </summary> /// </summary>
/// <param name="callback">The callback.</param> /// <param name="callback">The callback.</param>
/// <returns>An <typeparamref name="TAppBuilder"/> instance.</returns> /// <returns>An <typeparamref name="TAppBuilder"/> instance.</returns>
@ -129,6 +127,24 @@ namespace Avalonia.Controls
Instance.Run(window); Instance.Run(window);
} }
/// <summary>
/// Starts the application with the provided instance of <typeparamref name="TMainWindow"/>.
/// </summary>
/// <typeparam name="TMainWindow">The window type.</typeparam>
/// <param name="mainWindow">Instance of type TMainWindow to use when starting the app</param>
/// <param name="dataContextProvider">A delegate that will be called to create a data context for the window (optional).</param>
public void Start<TMainWindow>(TMainWindow mainWindow, Func<object> dataContextProvider = null)
where TMainWindow : Window
{
Setup();
BeforeStartCallback(Self);
if (dataContextProvider != null)
mainWindow.DataContext = dataContextProvider();
mainWindow.Show();
Instance.Run(mainWindow);
}
/// <summary> /// <summary>
/// Sets up the platform-specific services for the application, but does not run it. /// Sets up the platform-specific services for the application, but does not run it.
/// </summary> /// </summary>

2
src/Avalonia.Controls/Button.cs

@ -291,7 +291,7 @@ namespace Avalonia.Controls
{ {
var button = e.Sender as Button; var button = e.Sender as Button;
var isDefault = (bool)e.NewValue; var isDefault = (bool)e.NewValue;
var inputRoot = button.VisualRoot as IInputElement; var inputRoot = button?.VisualRoot as IInputElement;
if (inputRoot != null) if (inputRoot != null)
{ {

2
src/Avalonia.Controls/Control.cs

@ -645,7 +645,7 @@ namespace Avalonia.Controls
if (_focusAdorner != null) if (_focusAdorner != null)
{ {
var adornerLayer = _focusAdorner.Parent as Panel; var adornerLayer = (IPanel)_focusAdorner.Parent;
adornerLayer.Children.Remove(_focusAdorner); adornerLayer.Children.Remove(_focusAdorner);
_focusAdorner = null; _focusAdorner = null;
} }

12
src/Avalonia.Controls/Embedding/EmbeddableControlRoot.cs

@ -4,6 +4,7 @@ using Avalonia.Input;
using Avalonia.Layout; using Avalonia.Layout;
using Avalonia.Platform; using Avalonia.Platform;
using Avalonia.Styling; using Avalonia.Styling;
using JetBrains.Annotations;
namespace Avalonia.Controls.Embedding namespace Avalonia.Controls.Embedding
{ {
@ -18,6 +19,7 @@ namespace Avalonia.Controls.Embedding
{ {
} }
[CanBeNull]
public new IEmbeddableWindowImpl PlatformImpl => (IEmbeddableWindowImpl) base.PlatformImpl; public new IEmbeddableWindowImpl PlatformImpl => (IEmbeddableWindowImpl) base.PlatformImpl;
public void Prepare() public void Prepare()
@ -39,8 +41,9 @@ namespace Avalonia.Controls.Embedding
protected override Size MeasureOverride(Size availableSize) protected override Size MeasureOverride(Size availableSize)
{ {
base.MeasureOverride(PlatformImpl.ClientSize); var cs = PlatformImpl?.ClientSize ?? default(Size);
return PlatformImpl.ClientSize; base.MeasureOverride(cs);
return cs;
} }
private readonly NameScope _nameScope = new NameScope(); private readonly NameScope _nameScope = new NameScope();
@ -63,9 +66,6 @@ namespace Avalonia.Controls.Embedding
public void Unregister(string name) => _nameScope.Unregister(name); public void Unregister(string name) => _nameScope.Unregister(name);
Type IStyleable.StyleKey => typeof(EmbeddableControlRoot); Type IStyleable.StyleKey => typeof(EmbeddableControlRoot);
public void Dispose() public void Dispose() => PlatformImpl?.Dispose();
{
PlatformImpl.Dispose();
}
} }
} }

2
src/Avalonia.Controls/ItemsControl.cs

@ -354,7 +354,7 @@ namespace Avalonia.Controls
} }
var collection = sender as ICollection; var collection = sender as ICollection;
PseudoClasses.Set(":empty", collection.Count == 0); PseudoClasses.Set(":empty", collection == null || collection.Count == 0);
} }
/// <summary> /// <summary>

3
src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs

@ -155,8 +155,7 @@ namespace Avalonia.Controls.Presenters
case NotifyCollectionChangedAction.Add: case NotifyCollectionChangedAction.Add:
CreateAndRemoveContainers(); CreateAndRemoveContainers();
if (e.NewStartingIndex >= FirstIndex && if (e.NewStartingIndex < NextIndex)
e.NewStartingIndex < NextIndex)
{ {
RecycleContainers(); RecycleContainers();
} }

7
src/Avalonia.Controls/Primitives/PopupRoot.cs

@ -9,6 +9,7 @@ using Avalonia.Layout;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Platform; using Avalonia.Platform;
using Avalonia.VisualTree; using Avalonia.VisualTree;
using JetBrains.Annotations;
namespace Avalonia.Controls.Primitives namespace Avalonia.Controls.Primitives
{ {
@ -49,6 +50,7 @@ namespace Avalonia.Controls.Primitives
/// <summary> /// <summary>
/// Gets the platform-specific window implementation. /// Gets the platform-specific window implementation.
/// </summary> /// </summary>
[CanBeNull]
public new IPopupImpl PlatformImpl => (IPopupImpl)base.PlatformImpl; public new IPopupImpl PlatformImpl => (IPopupImpl)base.PlatformImpl;
/// <summary> /// <summary>
@ -65,10 +67,7 @@ namespace Avalonia.Controls.Primitives
IVisual IHostedVisualTreeRoot.Host => Parent; IVisual IHostedVisualTreeRoot.Host => Parent;
/// <inheritdoc/> /// <inheritdoc/>
public void Dispose() public void Dispose() => PlatformImpl?.Dispose();
{
this.PlatformImpl.Dispose();
}
/// <inheritdoc/> /// <inheritdoc/>
protected override void OnTemplateApplied(TemplateAppliedEventArgs e) protected override void OnTemplateApplied(TemplateAppliedEventArgs e)

7
src/Avalonia.Controls/TextBox.cs

@ -720,7 +720,7 @@ namespace Avalonia.Controls
if (pos < text.Length) if (pos < text.Length)
{ {
--pos; --pos;
if (pos > 0 && Text[pos - 1] == '\r' && Text[pos] == '\n') if (pos > 0 && text[pos - 1] == '\r' && text[pos] == '\n')
{ {
--pos; --pos;
} }
@ -771,6 +771,9 @@ namespace Avalonia.Controls
private string GetSelection() private string GetSelection()
{ {
var text = Text;
if (string.IsNullOrEmpty(text))
return "";
var selectionStart = SelectionStart; var selectionStart = SelectionStart;
var selectionEnd = SelectionEnd; var selectionEnd = SelectionEnd;
var start = Math.Min(selectionStart, selectionEnd); var start = Math.Min(selectionStart, selectionEnd);
@ -779,7 +782,7 @@ namespace Avalonia.Controls
{ {
return ""; return "";
} }
return Text.Substring(start, end - start); return text.Substring(start, end - start);
} }
private int GetLine(int caretIndex, IList<FormattedTextLine> lines) private int GetLine(int caretIndex, IList<FormattedTextLine> lines)

35
src/Avalonia.Controls/TopLevel.cs

@ -14,6 +14,7 @@ using Avalonia.Platform;
using Avalonia.Rendering; using Avalonia.Rendering;
using Avalonia.Styling; using Avalonia.Styling;
using Avalonia.VisualTree; using Avalonia.VisualTree;
using JetBrains.Annotations;
namespace Avalonia.Controls namespace Avalonia.Controls
{ {
@ -92,25 +93,25 @@ namespace Avalonia.Controls
var rendererFactory = TryGetService<IRendererFactory>(dependencyResolver); var rendererFactory = TryGetService<IRendererFactory>(dependencyResolver);
Renderer = rendererFactory?.CreateRenderer(this, renderLoop); Renderer = rendererFactory?.CreateRenderer(this, renderLoop);
PlatformImpl.SetInputRoot(this); impl.SetInputRoot(this);
PlatformImpl.Closed = HandleClosed; impl.Closed = HandleClosed;
PlatformImpl.Input = HandleInput; impl.Input = HandleInput;
PlatformImpl.Paint = HandlePaint; impl.Paint = HandlePaint;
PlatformImpl.Resized = HandleResized; impl.Resized = HandleResized;
PlatformImpl.ScalingChanged = HandleScalingChanged; impl.ScalingChanged = HandleScalingChanged;
_keyboardNavigationHandler?.SetOwner(this); _keyboardNavigationHandler?.SetOwner(this);
_accessKeyHandler?.SetOwner(this); _accessKeyHandler?.SetOwner(this);
styler?.ApplyStyles(this); styler?.ApplyStyles(this);
ClientSize = PlatformImpl.ClientSize; ClientSize = impl.ClientSize;
this.GetObservable(PointerOverElementProperty) this.GetObservable(PointerOverElementProperty)
.Select( .Select(
x => (x as InputElement)?.GetObservable(CursorProperty) ?? Observable.Empty<Cursor>()) x => (x as InputElement)?.GetObservable(CursorProperty) ?? Observable.Empty<Cursor>())
.Switch().Subscribe(cursor => PlatformImpl.SetCursor(cursor?.PlatformCursor)); .Switch().Subscribe(cursor => PlatformImpl?.SetCursor(cursor?.PlatformCursor));
if (_applicationLifecycle != null) if (_applicationLifecycle != null)
{ {
@ -135,10 +136,8 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Gets the platform-specific window implementation. /// Gets the platform-specific window implementation.
/// </summary> /// </summary>
public ITopLevelImpl PlatformImpl [CanBeNull]
{ public ITopLevelImpl PlatformImpl { get; private set; }
get;
}
/// <summary> /// <summary>
/// Gets the renderer for the window. /// Gets the renderer for the window.
@ -177,7 +176,7 @@ namespace Avalonia.Controls
Size ILayoutRoot.MaxClientSize => Size.Infinity; Size ILayoutRoot.MaxClientSize => Size.Infinity;
/// <inheritdoc/> /// <inheritdoc/>
double ILayoutRoot.LayoutScaling => PlatformImpl.Scaling; double ILayoutRoot.LayoutScaling => PlatformImpl?.Scaling ?? 1;
IStyleHost IStyleHost.StylingParent IStyleHost IStyleHost.StylingParent
{ {
@ -189,25 +188,27 @@ namespace Avalonia.Controls
/// <inheritdoc/> /// <inheritdoc/>
protected virtual IRenderTarget CreateRenderTarget() protected virtual IRenderTarget CreateRenderTarget()
{ {
if(PlatformImpl == null)
throw new InvalidOperationException("Cann't create render target, PlatformImpl is null (might be already disposed)");
return _renderInterface.CreateRenderTarget(PlatformImpl.Surfaces); return _renderInterface.CreateRenderTarget(PlatformImpl.Surfaces);
} }
/// <inheritdoc/> /// <inheritdoc/>
void IRenderRoot.Invalidate(Rect rect) void IRenderRoot.Invalidate(Rect rect)
{ {
PlatformImpl.Invalidate(rect); PlatformImpl?.Invalidate(rect);
} }
/// <inheritdoc/> /// <inheritdoc/>
Point IRenderRoot.PointToClient(Point p) Point IRenderRoot.PointToClient(Point p)
{ {
return PlatformImpl.PointToClient(p); return PlatformImpl?.PointToClient(p) ?? default(Point);
} }
/// <inheritdoc/> /// <inheritdoc/>
Point IRenderRoot.PointToScreen(Point p) Point IRenderRoot.PointToScreen(Point p)
{ {
return PlatformImpl.PointToScreen(p); return PlatformImpl?.PointToScreen(p) ?? default(Point);
} }
/// <summary> /// <summary>
@ -224,6 +225,8 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
protected virtual void HandleClosed() protected virtual void HandleClosed()
{ {
PlatformImpl = null;
Closed?.Invoke(this, EventArgs.Empty); Closed?.Invoke(this, EventArgs.Empty);
Renderer?.Dispose(); Renderer?.Dispose();
Renderer = null; Renderer = null;

28
src/Avalonia.Controls/Window.cs

@ -12,6 +12,7 @@ using Avalonia.Platform;
using Avalonia.Styling; using Avalonia.Styling;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using JetBrains.Annotations;
namespace Avalonia.Controls namespace Avalonia.Controls
{ {
@ -87,11 +88,11 @@ namespace Avalonia.Controls
static Window() static Window()
{ {
BackgroundProperty.OverrideDefaultValue(typeof(Window), Brushes.White); BackgroundProperty.OverrideDefaultValue(typeof(Window), Brushes.White);
TitleProperty.Changed.AddClassHandler<Window>((s, e) => s.PlatformImpl.SetTitle((string)e.NewValue)); TitleProperty.Changed.AddClassHandler<Window>((s, e) => s.PlatformImpl?.SetTitle((string)e.NewValue));
HasSystemDecorationsProperty.Changed.AddClassHandler<Window>( HasSystemDecorationsProperty.Changed.AddClassHandler<Window>(
(s, e) => s.PlatformImpl.SetSystemDecorations((bool) e.NewValue)); (s, e) => s.PlatformImpl?.SetSystemDecorations((bool) e.NewValue));
IconProperty.Changed.AddClassHandler<Window>((s, e) => s.PlatformImpl.SetIcon(((WindowIcon)e.NewValue).PlatformImpl)); IconProperty.Changed.AddClassHandler<Window>((s, e) => s.PlatformImpl?.SetIcon(((WindowIcon)e.NewValue).PlatformImpl));
} }
/// <summary> /// <summary>
@ -109,7 +110,7 @@ namespace Avalonia.Controls
public Window(IWindowImpl impl) public Window(IWindowImpl impl)
: base(impl) : base(impl)
{ {
_maxPlatformClientSize = this.PlatformImpl.MaxClientSize; _maxPlatformClientSize = PlatformImpl?.MaxClientSize ?? default(Size);
} }
/// <inheritdoc/> /// <inheritdoc/>
@ -129,6 +130,7 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Gets the platform-specific window implementation. /// Gets the platform-specific window implementation.
/// </summary> /// </summary>
[CanBeNull]
public new IWindowImpl PlatformImpl => (IWindowImpl)base.PlatformImpl; public new IWindowImpl PlatformImpl => (IWindowImpl)base.PlatformImpl;
/// <summary> /// <summary>
@ -164,8 +166,12 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
public WindowState WindowState public WindowState WindowState
{ {
get { return this.PlatformImpl.WindowState; } get { return PlatformImpl?.WindowState ?? WindowState.Normal; }
set { this.PlatformImpl.WindowState = value; } set
{
if (PlatformImpl != null)
PlatformImpl.WindowState = value;
}
} }
/// <summary> /// <summary>
@ -189,7 +195,7 @@ namespace Avalonia.Controls
public void Close() public void Close()
{ {
s_windows.Remove(this); s_windows.Remove(this);
PlatformImpl.Dispose(); PlatformImpl?.Dispose();
IsVisible = false; IsVisible = false;
} }
@ -221,7 +227,7 @@ namespace Avalonia.Controls
{ {
using (BeginAutoSizing()) using (BeginAutoSizing())
{ {
PlatformImpl.Hide(); PlatformImpl?.Hide();
} }
IsVisible = false; IsVisible = false;
@ -240,7 +246,7 @@ namespace Avalonia.Controls
using (BeginAutoSizing()) using (BeginAutoSizing())
{ {
PlatformImpl.Show(); PlatformImpl?.Show();
} }
} }
@ -278,7 +284,7 @@ namespace Avalonia.Controls
var activated = affectedWindows.Where(w => w.IsActive).FirstOrDefault(); var activated = affectedWindows.Where(w => w.IsActive).FirstOrDefault();
SetIsEnabled(affectedWindows, false); SetIsEnabled(affectedWindows, false);
var modal = PlatformImpl.ShowDialog(); var modal = PlatformImpl?.ShowDialog();
var result = new TaskCompletionSource<TResult>(); var result = new TaskCompletionSource<TResult>();
Observable.FromEventPattern<EventHandler, EventArgs>( Observable.FromEventPattern<EventHandler, EventArgs>(
@ -287,7 +293,7 @@ namespace Avalonia.Controls
.Take(1) .Take(1)
.Subscribe(_ => .Subscribe(_ =>
{ {
modal.Dispose(); modal?.Dispose();
SetIsEnabled(affectedWindows, true); SetIsEnabled(affectedWindows, true);
activated?.Activate(); activated?.Activate();
result.SetResult((TResult)_dialogResult); result.SetResult((TResult)_dialogResult);

34
src/Avalonia.Controls/WindowBase.cs

@ -9,6 +9,7 @@ using Avalonia.Controls.Primitives;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Layout; using Avalonia.Layout;
using Avalonia.Platform; using Avalonia.Platform;
using JetBrains.Annotations;
namespace Avalonia.Controls namespace Avalonia.Controls
{ {
@ -43,10 +44,10 @@ namespace Avalonia.Controls
public WindowBase(IWindowBaseImpl impl, IAvaloniaDependencyResolver dependencyResolver) : base(impl, dependencyResolver) public WindowBase(IWindowBaseImpl impl, IAvaloniaDependencyResolver dependencyResolver) : base(impl, dependencyResolver)
{ {
PlatformImpl.Activated = HandleActivated; impl.Activated = HandleActivated;
PlatformImpl.Deactivated = HandleDeactivated; impl.Deactivated = HandleDeactivated;
PlatformImpl.PositionChanged = HandlePositionChanged; impl.PositionChanged = HandlePositionChanged;
this.GetObservable(ClientSizeProperty).Skip(1).Subscribe(x => PlatformImpl.Resize(x)); this.GetObservable(ClientSizeProperty).Skip(1).Subscribe(x => PlatformImpl?.Resize(x));
} }
/// <summary> /// <summary>
@ -64,6 +65,7 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
public event EventHandler<PointEventArgs> PositionChanged; public event EventHandler<PointEventArgs> PositionChanged;
[CanBeNull]
public new IWindowBaseImpl PlatformImpl => (IWindowBaseImpl) base.PlatformImpl; public new IWindowBaseImpl PlatformImpl => (IWindowBaseImpl) base.PlatformImpl;
/// <summary> /// <summary>
@ -80,8 +82,12 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
public Point Position public Point Position
{ {
get { return PlatformImpl.Position; } get { return PlatformImpl?.Position ?? default(Point); }
set { PlatformImpl.Position = value; } set
{
if (PlatformImpl is IWindowBaseImpl impl)
impl.Position = value;
}
} }
/// <summary> /// <summary>
@ -98,7 +104,7 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
public void Activate() public void Activate()
{ {
PlatformImpl.Activate(); PlatformImpl?.Activate();
} }
/// <summary> /// <summary>
@ -110,7 +116,7 @@ namespace Avalonia.Controls
try try
{ {
PlatformImpl.Hide(); PlatformImpl?.Hide();
IsVisible = false; IsVisible = false;
} }
finally finally
@ -131,7 +137,7 @@ namespace Avalonia.Controls
EnsureInitialized(); EnsureInitialized();
IsVisible = true; IsVisible = true;
LayoutManager.Instance.ExecuteInitialLayoutPass(this); LayoutManager.Instance.ExecuteInitialLayoutPass(this);
PlatformImpl.Show(); PlatformImpl?.Show();
} }
finally finally
{ {
@ -163,10 +169,10 @@ namespace Avalonia.Controls
{ {
using (BeginAutoSizing()) using (BeginAutoSizing())
{ {
PlatformImpl.Resize(finalSize); PlatformImpl?.Resize(finalSize);
} }
return base.ArrangeOverride(PlatformImpl.ClientSize); return base.ArrangeOverride(PlatformImpl?.ClientSize ?? default(Size));
} }
/// <summary> /// <summary>
@ -174,7 +180,7 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
protected void EnsureInitialized() protected void EnsureInitialized()
{ {
if (!this.IsInitialized) if (!IsInitialized)
{ {
var init = (ISupportInitialize)this; var init = (ISupportInitialize)this;
init.BeginInit(); init.BeginInit();
@ -268,12 +274,12 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Starts moving a window with left button being held. Should be called from left mouse button press event handler /// Starts moving a window with left button being held. Should be called from left mouse button press event handler
/// </summary> /// </summary>
public void BeginMoveDrag() => PlatformImpl.BeginMoveDrag(); public void BeginMoveDrag() => PlatformImpl?.BeginMoveDrag();
/// <summary> /// <summary>
/// Starts resizing a window. This function is used if an application has window resizing controls. /// Starts resizing a window. This function is used if an application has window resizing controls.
/// Should be called from left mouse button press event handler /// Should be called from left mouse button press event handler
/// </summary> /// </summary>
public void BeginResizeDrag(WindowEdge edge) => PlatformImpl.BeginResizeDrag(edge); public void BeginResizeDrag(WindowEdge edge) => PlatformImpl?.BeginResizeDrag(edge);
} }
} }

4
src/Avalonia.DesignerSupport/DesignerAssist.cs

@ -75,7 +75,7 @@ namespace Avalonia.DesignerSupport
private static void SetScalingFactor(double factor) private static void SetScalingFactor(double factor)
{ {
PlatformManager.SetDesignerScalingFactor(factor); PlatformManager.SetDesignerScalingFactor(factor);
s_currentWindow?.PlatformImpl.Resize(s_currentWindow.ClientSize); s_currentWindow?.PlatformImpl?.Resize(s_currentWindow.ClientSize);
} }
static Window s_currentWindow; static Window s_currentWindow;
@ -149,6 +149,8 @@ namespace Avalonia.DesignerSupport
s_currentWindow = window; s_currentWindow = window;
window.Show(); window.Show();
Design.ApplyDesignerProperties(window, control); Design.ApplyDesignerProperties(window, control);
// ReSharper disable once PossibleNullReferenceException
// Always not null at this point
Api.OnWindowCreated?.Invoke(window.PlatformImpl.Handle.Handle); Api.OnWindowCreated?.Invoke(window.PlatformImpl.Handle.Handle);
Api.OnResize?.Invoke(); Api.OnResize?.Invoke();
} }

8
src/Avalonia.DotNetFrameworkRuntime/AppBuilder.cs

@ -82,7 +82,13 @@ namespace Avalonia
private void LoadAssembliesInDirectory() private void LoadAssembliesInDirectory()
{ {
foreach (var file in new FileInfo(Assembly.GetEntryAssembly().Location).Directory.EnumerateFiles("*.dll")) var location = Assembly.GetEntryAssembly().Location;
if (string.IsNullOrWhiteSpace(location))
return;
var dir = new FileInfo(location).Directory;
if (dir == null)
return;
foreach (var file in dir.EnumerateFiles("*.dll"))
{ {
try try
{ {

7
src/Avalonia.Layout/Layoutable.cs

@ -456,10 +456,9 @@ namespace Avalonia.Layout
ApplyTemplate(); ApplyTemplate();
var constrained = LayoutHelper var constrained = LayoutHelper.ApplyLayoutConstraints(
.ApplyLayoutConstraints(this, availableSize) this,
.Deflate(margin); availableSize.Deflate(margin));
var measured = MeasureOverride(constrained); var measured = MeasureOverride(constrained);
var width = measured.Width; var width = measured.Width;

2
src/Avalonia.Styling/Controls/NameScope.cs

@ -50,7 +50,7 @@ namespace Avalonia.Controls
return result; return result;
} }
visual = (visual as ILogical).LogicalParent as Visual; visual = (visual as ILogical)?.LogicalParent as Visual;
} }
return null; return null;

7
src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs

@ -20,6 +20,11 @@ namespace Avalonia.Rendering
/// </remarks> /// </remarks>
public class ImmediateRenderer : RendererBase, IRenderer, IVisualBrushRenderer public class ImmediateRenderer : RendererBase, IRenderer, IVisualBrushRenderer
{ {
class ImmediateRendererFactory : IRendererFactory
{
public IRenderer CreateRenderer(IRenderRoot root, IRenderLoop renderLoop) => new ImmediateRenderer(root);
}
private readonly IVisual _root; private readonly IVisual _root;
private readonly IRenderRoot _renderRoot; private readonly IRenderRoot _renderRoot;
private IRenderTarget _renderTarget; private IRenderTarget _renderTarget;
@ -36,6 +41,8 @@ namespace Avalonia.Rendering
_renderRoot = root as IRenderRoot; _renderRoot = root as IRenderRoot;
} }
public static IRendererFactory Factory { get; } = new ImmediateRendererFactory();
/// <inheritdoc/> /// <inheritdoc/>
public bool DrawFps { get; set; } public bool DrawFps { get; set; }

2
src/Avalonia.Visuals/Rendering/ZIndexComparer.cs

@ -8,6 +8,6 @@ namespace Avalonia.Rendering
{ {
public static readonly ZIndexComparer Instance = new ZIndexComparer(); public static readonly ZIndexComparer Instance = new ZIndexComparer();
public int Compare(IVisual x, IVisual y) => x.ZIndex.CompareTo(y.ZIndex); public int Compare(IVisual x, IVisual y) => (x?.ZIndex ?? 0).CompareTo(y?.ZIndex ?? 0);
} }
} }

2
src/Avalonia.Visuals/VisualTree/VisualExtensions.cs

@ -189,7 +189,7 @@ namespace Avalonia.VisualTree
{ {
Contract.Requires<ArgumentNullException>(visual != null); Contract.Requires<ArgumentNullException>(visual != null);
return visual.VisualRoot as IRenderRoot ?? visual.VisualRoot; return visual as IRenderRoot ?? visual.VisualRoot;
} }
/// <summary> /// <summary>

1
src/Gtk/Avalonia.Cairo/Media/DrawingContext.cs

@ -9,6 +9,7 @@ using Avalonia.Cairo.Media.Imaging;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Platform; using Avalonia.Platform;
using Avalonia.Rendering; using Avalonia.Rendering;
// ReSharper disable PossibleNullReferenceException
namespace Avalonia.Cairo.Media namespace Avalonia.Cairo.Media
{ {

6
src/Gtk/Avalonia.Cairo/Properties/AssemblyInfo.cs

@ -40,6 +40,6 @@ using System.Runtime.InteropServices;
[assembly: AssemblyVersion("1.0.0.0")] [assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: ExportRenderingSubsystem(OperatingSystemType.WinNT, 2, "Cairo", typeof(CairoPlatform), nameof(CairoPlatform.Initialize), RequiresWindowingSubsystem = "GTK")] [assembly: ExportRenderingSubsystem(OperatingSystemType.WinNT, 3, "Cairo", typeof(CairoPlatform), nameof(CairoPlatform.Initialize), RequiresWindowingSubsystem = "GTK")]
[assembly: ExportRenderingSubsystem(OperatingSystemType.Linux, 1, "Cairo", typeof(CairoPlatform), nameof(CairoPlatform.Initialize), RequiresWindowingSubsystem = "GTK")] [assembly: ExportRenderingSubsystem(OperatingSystemType.Linux, 2, "Cairo", typeof(CairoPlatform), nameof(CairoPlatform.Initialize), RequiresWindowingSubsystem = "GTK")]
[assembly: ExportRenderingSubsystem(OperatingSystemType.OSX, 2, "Cairo", typeof(CairoPlatform), nameof(CairoPlatform.Initialize), RequiresWindowingSubsystem = "GTK")] [assembly: ExportRenderingSubsystem(OperatingSystemType.OSX, 3, "Cairo", typeof(CairoPlatform), nameof(CairoPlatform.Initialize), RequiresWindowingSubsystem = "GTK")]

6
src/Gtk/Avalonia.Gtk/Properties/AssemblyInfo.cs

@ -22,7 +22,7 @@ using System.Runtime.CompilerServices;
// and "{Major}.{Minor}.{Build}.*" will update just the revision. // and "{Major}.{Minor}.{Build}.*" will update just the revision.
[assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("1.0.*")]
[assembly: ExportWindowingSubsystem(OperatingSystemType.WinNT, 2, "GTK", typeof(GtkPlatform), nameof(GtkPlatform.Initialize))] [assembly: ExportWindowingSubsystem(OperatingSystemType.WinNT, 3, "GTK", typeof(GtkPlatform), nameof(GtkPlatform.Initialize))]
[assembly: ExportWindowingSubsystem(OperatingSystemType.Linux, 1, "GTK", typeof(GtkPlatform), nameof(GtkPlatform.Initialize))] [assembly: ExportWindowingSubsystem(OperatingSystemType.Linux, 2, "GTK", typeof(GtkPlatform), nameof(GtkPlatform.Initialize))]
[assembly: ExportWindowingSubsystem(OperatingSystemType.OSX, 2, "GTK", typeof(GtkPlatform), nameof(GtkPlatform.Initialize))] [assembly: ExportWindowingSubsystem(OperatingSystemType.OSX, 3, "GTK", typeof(GtkPlatform), nameof(GtkPlatform.Initialize))]

1
src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs

@ -39,6 +39,7 @@ namespace Avalonia.Gtk3
.Bind<IPlatformThreadingInterface>().ToConstant(Instance) .Bind<IPlatformThreadingInterface>().ToConstant(Instance)
.Bind<ISystemDialogImpl>().ToSingleton<SystemDialog>() .Bind<ISystemDialogImpl>().ToSingleton<SystemDialog>()
.Bind<IRenderLoop>().ToConstant(new DefaultRenderLoop(60)) .Bind<IRenderLoop>().ToConstant(new DefaultRenderLoop(60))
.Bind<IRendererFactory>().ToConstant(ImmediateRenderer.Factory)
.Bind<IPlatformIconLoader>().ToConstant(new PlatformIconLoader()); .Bind<IPlatformIconLoader>().ToConstant(new PlatformIconLoader());
} }

5
src/Gtk/Avalonia.Gtk3/Properties/AssemblyInfo.cs

@ -2,6 +2,8 @@
using System.Reflection; using System.Reflection;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using Avalonia.Gtk3;
using Avalonia.Platform;
// General Information about an assembly is controlled through the following // General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information // set of attributes. Change these attribute values to modify the information
@ -28,3 +30,6 @@ using System.Runtime.InteropServices;
// [assembly: AssemblyVersion("1.0.*")] // [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")] [assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: ExportWindowingSubsystem(OperatingSystemType.WinNT, 2, "GTK3", typeof(Gtk3Platform), nameof(Gtk3Platform.Initialize))]
[assembly: ExportWindowingSubsystem(OperatingSystemType.Linux, 1, "GTK3", typeof(Gtk3Platform), nameof(Gtk3Platform.Initialize))]
[assembly: ExportWindowingSubsystem(OperatingSystemType.OSX, 2, "GTK3", typeof(Gtk3Platform), nameof(Gtk3Platform.Initialize))]

1
src/Gtk/Avalonia.Gtk3/SystemDialogs.cs

@ -28,6 +28,7 @@ namespace Avalonia.Gtk3
List<IDisposable> disposables = null; List<IDisposable> disposables = null;
Action dispose = () => Action dispose = () =>
{ {
// ReSharper disable once PossibleNullReferenceException
foreach (var d in disposables) foreach (var d in disposables)
d.Dispose(); d.Dispose();
disposables.Clear(); disposables.Clear();

1
src/Linux/Avalonia.LinuxFramebuffer/LinuxFramebufferPlatform.cs

@ -35,6 +35,7 @@ namespace Avalonia.LinuxFramebuffer
.Bind<IKeyboardDevice>().ToConstant(KeyboardDevice) .Bind<IKeyboardDevice>().ToConstant(KeyboardDevice)
.Bind<IMouseDevice>().ToConstant(MouseDevice) .Bind<IMouseDevice>().ToConstant(MouseDevice)
.Bind<IPlatformSettings>().ToSingleton<PlatformSettings>() .Bind<IPlatformSettings>().ToSingleton<PlatformSettings>()
.Bind<IRendererFactory>().ToConstant(ImmediateRenderer.Factory)
.Bind<IPlatformThreadingInterface>().ToConstant(PlatformThreadingInterface.Instance) .Bind<IPlatformThreadingInterface>().ToConstant(PlatformThreadingInterface.Instance)
.Bind<IRenderLoop>().ToConstant(PlatformThreadingInterface.Instance); .Bind<IRenderLoop>().ToConstant(PlatformThreadingInterface.Instance);
} }

2
src/Markup/Avalonia.Markup.Xaml/Parsers/SelectorParser.cs

@ -68,7 +68,7 @@ namespace Avalonia.Markup.Xaml.Parsers
} }
else if (property != null) else if (property != null)
{ {
var type = result.TargetType; var type = result?.TargetType;
if (type == null) if (type == null)
{ {

4
src/Markup/Avalonia.Markup/Data/Plugins/TaskStreamPlugin.cs

@ -36,7 +36,7 @@ namespace Avalonia.Markup.Data.Plugins
if (task != null) if (task != null)
{ {
var resultProperty = task.GetType().GetTypeInfo().GetDeclaredProperty("Result"); var resultProperty = task.GetType().GetRuntimeProperty("Result");
if (resultProperty != null) if (resultProperty != null)
{ {
@ -61,7 +61,7 @@ namespace Avalonia.Markup.Data.Plugins
protected IObservable<object> HandleCompleted(Task task) protected IObservable<object> HandleCompleted(Task task)
{ {
var resultProperty = task.GetType().GetTypeInfo().GetDeclaredProperty("Result"); var resultProperty = task.GetType().GetRuntimeProperty("Result");
if (resultProperty != null) if (resultProperty != null)
{ {

64
src/Markup/Avalonia.Markup/DefaultValueConverter.cs

@ -3,10 +3,7 @@
using System; using System;
using System.Globalization; using System.Globalization;
using System.Linq;
using System.Reflection;
using Avalonia.Data; using Avalonia.Data;
using Avalonia.Logging;
using Avalonia.Utilities; using Avalonia.Utilities;
namespace Avalonia.Markup namespace Avalonia.Markup
@ -32,32 +29,28 @@ namespace Avalonia.Markup
/// <returns>The converted value.</returns> /// <returns>The converted value.</returns>
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{ {
object result; if (value == null)
if (value != null &&
(TypeUtilities.TryConvert(targetType, value, culture, out result) ||
TryConvertEnum(value, targetType, culture, out result)))
{ {
return result; return AvaloniaProperty.UnsetValue;
} }
if (value != null) if (TypeUtilities.TryConvert(targetType, value, culture, out object result))
{ {
string message; return result;
}
if (TypeUtilities.IsNumeric(targetType)) string message;
{
message = $"'{value}' is not a valid number.";
}
else
{
message = $"Could not convert '{value}' to '{targetType.Name}'.";
}
return new BindingNotification(new InvalidCastException(message), BindingErrorType.Error); if (TypeUtilities.IsNumeric(targetType))
{
message = $"'{value}' is not a valid number.";
}
else
{
message = $"Could not convert '{value}' to '{targetType.Name}'.";
} }
return AvaloniaProperty.UnsetValue; return new BindingNotification(new InvalidCastException(message), BindingErrorType.Error);
} }
/// <summary> /// <summary>
@ -72,34 +65,5 @@ namespace Avalonia.Markup
{ {
return Convert(value, targetType, parameter, culture); return Convert(value, targetType, parameter, culture);
} }
private bool TryConvertEnum(object value, Type targetType, CultureInfo cultur, out object result)
{
var valueTypeInfo = value.GetType().GetTypeInfo();
var targetTypeInfo = targetType.GetTypeInfo();
if (valueTypeInfo.IsEnum && !targetTypeInfo.IsEnum)
{
var enumValue = (int)value;
if (TypeUtilities.TryCast(targetType, enumValue, out result))
{
return true;
}
}
else if (!valueTypeInfo.IsEnum && targetTypeInfo.IsEnum)
{
object intValue;
if (TypeUtilities.TryCast(typeof(int), value, out intValue))
{
result = Enum.ToObject(targetType, intValue);
return true;
}
}
result = null;
return false;
}
} }
} }

6
src/Shared/SharedAssemblyInfo.cs

@ -13,6 +13,6 @@ using System.Resources;
[assembly: AssemblyTrademark("")] [assembly: AssemblyTrademark("")]
[assembly: NeutralResourcesLanguage("en")] [assembly: NeutralResourcesLanguage("en")]
[assembly: AssemblyVersion("0.4.1")] [assembly: AssemblyVersion("0.5.1")]
[assembly: AssemblyFileVersion("0.4.1")] [assembly: AssemblyFileVersion("0.5.1")]
[assembly: AssemblyInformationalVersion("0.4.1")] [assembly: AssemblyInformationalVersion("0.5.1")]

1
src/Skia/Avalonia.Skia.Desktop.NetStandard/Avalonia.Skia.Desktop.NetStandard.csproj

@ -40,7 +40,6 @@
<ProjectReference Include="..\..\Avalonia.Visuals\Avalonia.Visuals.csproj" /> <ProjectReference Include="..\..\Avalonia.Visuals\Avalonia.Visuals.csproj" />
</ItemGroup> </ItemGroup>
<Import Project="..\..\..\build\SkiaSharp.props" /> <Import Project="..\..\..\build\SkiaSharp.props" />
<Import Project="..\..\..\build\SkiaSharp.Desktop.props" />
<Import Project="..\Avalonia.Skia\Avalonia.Skia.projitems" Label="Shared" /> <Import Project="..\Avalonia.Skia\Avalonia.Skia.projitems" Label="Shared" />
<Import Project="..\..\Shared\RenderHelpers\RenderHelpers.projitems" Label="Shared" /> <Import Project="..\..\Shared\RenderHelpers\RenderHelpers.projitems" Label="Shared" />
</Project> </Project>

2
src/Skia/Avalonia.Skia.Desktop/Avalonia.Skia.Desktop.csproj

@ -50,7 +50,7 @@
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Optimize>true</Optimize> <Optimize>true</Optimize>
<DebugType>pdbonly</DebugType> <DebugType>pdbonly</DebugType>
<PlatformTarget>x86</PlatformTarget> <PlatformTarget>AnyCPU</PlatformTarget>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup> </PropertyGroup>

4
src/Skia/Avalonia.Skia.Desktop/Properties/AssemblyInfo.cs

@ -37,4 +37,6 @@ using System.Runtime.InteropServices;
[assembly: AssemblyVersion("1.0.0.0")] [assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: ExportRenderingSubsystem(OperatingSystemType.WinNT, 3, "Skia", typeof(SkiaPlatform), nameof(SkiaPlatform.Initialize))] [assembly: ExportRenderingSubsystem(OperatingSystemType.WinNT, 2, "Skia", typeof(SkiaPlatform), nameof(SkiaPlatform.Initialize))]
[assembly: ExportRenderingSubsystem(OperatingSystemType.OSX, 1, "Skia", typeof(SkiaPlatform), nameof(SkiaPlatform.Initialize))]
[assembly: ExportRenderingSubsystem(OperatingSystemType.Linux, 1, "Skia", typeof(SkiaPlatform), nameof(SkiaPlatform.Initialize))]

4
src/Skia/Avalonia.Skia/DrawingContextImpl.cs

@ -285,7 +285,7 @@ namespace Avalonia.Skia
paint.StrokeCap = SKStrokeCap.Butt; paint.StrokeCap = SKStrokeCap.Butt;
if (pen.LineJoin == PenLineJoin.Miter) if (pen.LineJoin == PenLineJoin.Miter)
paint.StrokeJoin = SKStrokeJoin.Mitter; paint.StrokeJoin = SKStrokeJoin.Miter;
else if (pen.LineJoin == PenLineJoin.Round) else if (pen.LineJoin == PenLineJoin.Round)
paint.StrokeJoin = SKStrokeJoin.Round; paint.StrokeJoin = SKStrokeJoin.Round;
else else
@ -397,7 +397,7 @@ namespace Avalonia.Skia
public void PopOpacityMask() public void PopOpacityMask()
{ {
Canvas.SaveLayer(new SKPaint { XferMode = SKXferMode.DstIn }); Canvas.SaveLayer(new SKPaint { BlendMode = SKBlendMode.DstIn });
using (var paintWrapper = maskStack.Pop()) using (var paintWrapper = maskStack.Pop())
{ {
Canvas.DrawPaint(paintWrapper.Paint); Canvas.DrawPaint(paintWrapper.Paint);

2
src/Skia/Avalonia.Skia/FormattedTextImpl.cs

@ -42,7 +42,7 @@ namespace Avalonia.Skia
_paint.Typeface = skiaTypeface; _paint.Typeface = skiaTypeface;
_paint.TextSize = (float)(typeface?.FontSize ?? 12); _paint.TextSize = (float)(typeface?.FontSize ?? 12);
_paint.TextAlign = textAlignment.ToSKTextAlign(); _paint.TextAlign = textAlignment.ToSKTextAlign();
_paint.XferMode = SKXferMode.Src; _paint.BlendMode = SKBlendMode.Src;
_wrapping = wrapping; _wrapping = wrapping;
_constraint = constraint; _constraint = constraint;

4
src/Windows/Avalonia.Win32/Embedding/WinFormsAvaloniaControlHost.cs

@ -15,7 +15,7 @@ namespace Avalonia.Win32.Embedding
{ {
private readonly EmbeddableControlRoot _root = new EmbeddableControlRoot(); private readonly EmbeddableControlRoot _root = new EmbeddableControlRoot();
private IntPtr WindowHandle => ((WindowImpl) _root.PlatformImpl).Handle.Handle; private IntPtr WindowHandle => ((WindowImpl) _root?.PlatformImpl)?.Handle?.Handle ?? IntPtr.Zero;
public WinFormsAvaloniaControlHost() public WinFormsAvaloniaControlHost()
{ {
@ -25,6 +25,8 @@ namespace Avalonia.Win32.Embedding
if (_root.IsFocused) if (_root.IsFocused)
FocusManager.Instance.Focus(null); FocusManager.Instance.Focus(null);
_root.GotFocus += RootGotFocus; _root.GotFocus += RootGotFocus;
// ReSharper disable once PossibleNullReferenceException
// Always non-null at this point
_root.PlatformImpl.LostFocus += PlatformImpl_LostFocus; _root.PlatformImpl.LostFocus += PlatformImpl_LostFocus;
FixPosition(); FixPosition();
} }

1
src/iOS/Avalonia.iOS/iOSPlatform.cs

@ -58,6 +58,7 @@ namespace Avalonia.iOS
.Bind<IStandardCursorFactory>().ToTransient<CursorFactory>() .Bind<IStandardCursorFactory>().ToTransient<CursorFactory>()
.Bind<IKeyboardDevice>().ToConstant(KeyboardDevice) .Bind<IKeyboardDevice>().ToConstant(KeyboardDevice)
.Bind<IMouseDevice>().ToConstant(MouseDevice) .Bind<IMouseDevice>().ToConstant(MouseDevice)
.Bind<IRendererFactory>().ToConstant(ImmediateRenderer.Factory)
.Bind<IPlatformSettings>().ToSingleton<PlatformSettings>() .Bind<IPlatformSettings>().ToSingleton<PlatformSettings>()
.Bind<IPlatformThreadingInterface>().ToConstant(PlatformThreadingInterface.Instance) .Bind<IPlatformThreadingInterface>().ToConstant(PlatformThreadingInterface.Instance)
.Bind<IPlatformIconLoader>().ToSingleton<PlatformIconLoader>() .Bind<IPlatformIconLoader>().ToSingleton<PlatformIconLoader>()

25
tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization_Simple.cs

@ -206,6 +206,30 @@ namespace Avalonia.Controls.UnitTests.Presenters
Assert.Equal(expected, actual); Assert.Equal(expected, actual);
} }
[Fact]
public void Inserting_Items_Before_Visibile_Containers_Should_Update_Containers()
{
var target = CreateTarget();
target.ApplyTemplate();
target.Measure(new Size(100, 100));
target.Arrange(new Rect(0, 0, 100, 100));
((ILogicalScrollable)target).Offset = new Vector(0, 5);
var expected = Enumerable.Range(5, 10).Select(x => $"Item {x}").ToList();
var items = (ObservableCollection<string>)target.Items;
var actual = target.Panel.Children.Select(x => x.DataContext).ToList();
Assert.Equal(expected, actual);
items.Insert(0, "Inserted");
expected = Enumerable.Range(4, 10).Select(x => $"Item {x}").ToList();
actual = target.Panel.Children.Select(x => x.DataContext).ToList();
Assert.Equal(expected, actual);
}
[Fact] [Fact]
public void Removing_First_Materialized_Item_Should_Update_Containers() public void Removing_First_Materialized_Item_Should_Update_Containers()
{ {
@ -477,7 +501,6 @@ namespace Avalonia.Controls.UnitTests.Presenters
target.Arrange(new Rect(0, 0, 100, 100)); target.Arrange(new Rect(0, 0, 100, 100));
var expected = Enumerable.Range(0, 6).Select(x => $"Item {x}").ToList(); var expected = Enumerable.Range(0, 6).Select(x => $"Item {x}").ToList();
var items = (ObservableCollection<string>)target.Items;
var actual = target.Panel.Children.Select(x => x.DataContext).ToList(); var actual = target.Panel.Children.Select(x => x.DataContext).ToList();
Assert.Equal(expected, actual); Assert.Equal(expected, actual);

53
tests/Avalonia.Layout.UnitTests/MeasureTests.cs

@ -100,5 +100,58 @@ namespace Avalonia.Layout.UnitTests
Assert.Equal(0, target.DesiredSize.Height); Assert.Equal(0, target.DesiredSize.Height);
} }
[Fact]
public void Margin_Should_Affect_AvailableSize()
{
MeasureTest target;
var outer = new Decorator
{
Width = 100,
Height = 100,
Child = target = new MeasureTest
{
Margin = new Thickness(10),
}
};
outer.Measure(Size.Infinity);
Assert.Equal(new Size(80, 80), target.AvailableSize);
}
[Fact]
public void Margin_Should_Be_Applied_Before_Width_Height()
{
MeasureTest target;
var outer = new Decorator
{
Width = 100,
Height = 100,
Child = target = new MeasureTest
{
Width = 80,
Height = 80,
Margin = new Thickness(10),
}
};
outer.Measure(Size.Infinity);
Assert.Equal(new Size(80, 80), target.AvailableSize);
}
class MeasureTest : Control
{
public Size? AvailableSize { get; private set; }
protected override Size MeasureOverride(Size availableSize)
{
AvailableSize = availableSize;
return availableSize;
}
}
} }
} }

17
tests/Avalonia.Markup.Xaml.UnitTests/Parsers/SelectorParserTests.cs

@ -0,0 +1,17 @@
using System;
using Avalonia.Controls;
using Avalonia.Markup.Xaml.Parsers;
using Xunit;
namespace Avalonia.Markup.Xaml.UnitTests.Parsers
{
public class SelectorParserTests
{
[Fact]
public void Parses_Boolean_Property_Selector()
{
var target = new SelectorParser((type, ns) => typeof(TextBlock));
var result = target.Parse("TextBlock[IsPointerOver=True]");
}
}
}
Loading…
Cancel
Save