Browse Source

Merge branch 'master' into tokenizer

pull/1125/head
danwalmsley 9 years ago
committed by GitHub
parent
commit
47f5019d10
  1. 6
      .gitignore
  2. 4
      .gitmodules
  3. 1
      .ncrunch/Avalonia.Direct2D1.RenderTests.v3.ncrunchproject
  4. 9
      .ncrunch/Avalonia.Markup.UnitTests.netcoreapp1.1.v3.ncrunchproject
  5. 1
      .ncrunch/Avalonia.Skia.RenderTests.v3.ncrunchproject
  6. 3
      .travis.yml
  7. 53
      Avalonia.sln
  8. 4
      appveyor.yml
  9. 2
      build.cake
  10. 5
      build/MonoMac.props
  11. 2
      build/Moq.props
  12. 5
      build/ReactiveUI.props
  13. 13
      build/Rx.props
  14. 19
      build/UnitTests.NetCore.targets
  15. 2
      build/XUnit.props
  16. 13
      docs/guidelines/build.md
  17. 99
      packages.cake
  18. 1
      samples/BindingTest/App.xaml.cs
  19. 2
      samples/BindingTest/BindingTest.csproj
  20. 10
      samples/BindingTest/ViewModels/MainWindowViewModel.cs
  21. 2
      samples/BindingTest/ViewModels/NestedCommandViewModel.cs
  22. 2
      samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj
  23. 35
      samples/ControlCatalog.NetCore/Program.cs
  24. 14
      samples/ControlCatalog/ControlCatalog.csproj
  25. 33
      samples/ControlCatalog/DecoratedWindow.xaml
  26. 53
      samples/ControlCatalog/DecoratedWindow.xaml.cs
  27. 2
      samples/ControlCatalog/MainView.xaml
  28. 9
      samples/ControlCatalog/MainView.xaml.cs
  29. 5
      samples/ControlCatalog/MainWindow.xaml.cs
  30. 12
      samples/ControlCatalog/Pages/DialogsPage.xaml
  31. 46
      samples/ControlCatalog/Pages/DialogsPage.xaml.cs
  32. 53
      samples/ControlCatalog/Pages/ToolTipPage.xaml
  33. 1
      samples/RenderTest/Program.cs
  34. 1
      samples/RenderTest/RenderTest.csproj
  35. 10
      samples/RenderTest/ViewModels/MainWindowViewModel.cs
  36. 1
      samples/VirtualizationTest/Program.cs
  37. 25
      samples/VirtualizationTest/ViewModels/MainWindowViewModel.cs
  38. 1
      samples/VirtualizationTest/VirtualizationTest.csproj
  39. 2
      src/Avalonia.Animation/Avalonia.Animation.csproj
  40. 2
      src/Avalonia.Base/Avalonia.Base.csproj
  41. 3
      src/Avalonia.Base/AvaloniaPropertyRegistry.cs
  42. 54
      src/Avalonia.Base/Collections/AvaloniaDictionary.cs
  43. 51
      src/Avalonia.Controls/Application.cs
  44. 2
      src/Avalonia.Controls/Avalonia.Controls.csproj
  45. 70
      src/Avalonia.Controls/Button.cs
  46. 134
      src/Avalonia.Controls/Control.cs
  47. 2
      src/Avalonia.Controls/DropDown.cs
  48. 1
      src/Avalonia.Controls/IControl.cs
  49. 7
      src/Avalonia.Controls/Platform/IWindowImpl.cs
  50. 2
      src/Avalonia.Controls/Presenters/TextPresenter.cs
  51. 26
      src/Avalonia.Controls/Primitives/Popup.cs
  52. 24
      src/Avalonia.Controls/Primitives/RangeBase.cs
  53. 103
      src/Avalonia.Controls/Primitives/ScrollBar.cs
  54. 4
      src/Avalonia.Controls/Primitives/ToggleButton.cs
  55. 102
      src/Avalonia.Controls/Primitives/Track.cs
  56. 82
      src/Avalonia.Controls/RepeatButton.cs
  57. 4
      src/Avalonia.Controls/ScrollViewer.cs
  58. 55
      src/Avalonia.Controls/Slider.cs
  59. 2
      src/Avalonia.Controls/SystemDialog.cs
  60. 236
      src/Avalonia.Controls/ToolTip.cs
  61. 98
      src/Avalonia.Controls/ToolTipService.cs
  62. 24
      src/Avalonia.Controls/TopLevel.cs
  63. 24
      src/Avalonia.Controls/Window.cs
  64. 2
      src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.csproj
  65. 2
      src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj
  66. 6
      src/Avalonia.DotNetCoreRuntime/AppBuilder.cs
  67. 3
      src/Avalonia.DotNetCoreRuntime/Avalonia.DotNetCoreRuntime.csproj
  68. 13
      src/Avalonia.DotNetFrameworkRuntime/AppBuilder.cs
  69. 94
      src/Avalonia.DotNetFrameworkRuntime/Avalonia.DotNetFrameworkRuntime.csproj
  70. 15
      src/Avalonia.DotNetFrameworkRuntime/Properties/AssemblyInfo.cs
  71. 3
      src/Avalonia.HtmlRenderer/Avalonia.HtmlRenderer.csproj
  72. 12
      src/Avalonia.HtmlRenderer/Compat/Api.cs
  73. 2
      src/Avalonia.Input/Avalonia.Input.csproj
  74. 2
      src/Avalonia.Interactivity/Avalonia.Interactivity.csproj
  75. 2
      src/Avalonia.Layout/Avalonia.Layout.csproj
  76. 2
      src/Avalonia.Logging.Serilog/Avalonia.Logging.Serilog.csproj
  77. 24
      src/Avalonia.ReactiveUI/AppBuilderExtensions.cs
  78. 76
      src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj
  79. 3
      src/Avalonia.ReactiveUI/Properties/AssemblyInfo.cs
  80. 23
      src/Avalonia.ReactiveUI/Registrations.cs
  81. 34
      src/Avalonia.ReactiveUI/Shims.cs
  82. 1
      src/Avalonia.ReactiveUI/src
  83. 3
      src/Avalonia.Styling/Avalonia.Styling.csproj
  84. 19
      src/Avalonia.Styling/Controls/IResourceDictionary.cs
  85. 15
      src/Avalonia.Styling/Controls/IResourceNode.cs
  86. 33
      src/Avalonia.Styling/Controls/IResourceProvider.cs
  87. 101
      src/Avalonia.Styling/Controls/ResourceDictionary.cs
  88. 65
      src/Avalonia.Styling/Controls/ResourceProviderExtensions.cs
  89. 11
      src/Avalonia.Styling/Controls/ResourcesChangedEventArgs.cs
  90. 11
      src/Avalonia.Styling/LogicalTree/ILogical.cs
  91. 2
      src/Avalonia.Styling/Properties/AssemblyInfo.cs
  92. 32
      src/Avalonia.Styling/Styling/ISetStyleParent.cs
  93. 13
      src/Avalonia.Styling/Styling/IStyle.cs
  94. 92
      src/Avalonia.Styling/Styling/Style.cs
  95. 42
      src/Avalonia.Styling/Styling/StyleExtensions.cs
  96. 90
      src/Avalonia.Styling/Styling/StyleResources.cs
  97. 149
      src/Avalonia.Styling/Styling/Styles.cs
  98. 2
      src/Avalonia.Themes.Default/Avalonia.Themes.Default.csproj
  99. 1
      src/Avalonia.Themes.Default/DefaultTheme.xaml
  100. 44
      src/Avalonia.Themes.Default/RepeatButton.xaml

6
.gitignore

@ -159,6 +159,12 @@ $RECYCLE.BIN/
*.userprefs
*.nugetreferenceswitcher
#################
## Rider
#################
.idea
#################
## Cake
#################

4
.gitmodules

@ -1,7 +1,3 @@
[submodule "src/Avalonia.ReactiveUI/src"]
path = src/Avalonia.ReactiveUI/src
url = https://github.com/AvaloniaUI/ReactiveUI.git
branch = avalonia-snapshot
[submodule "src/Avalonia.HtmlRenderer/external"]
path = src/Avalonia.HtmlRenderer/external
url = https://github.com/AvaloniaUI/HTML-Renderer.git

1
.ncrunch/Avalonia.Direct2D1.RenderTests.v3.ncrunchproject

@ -1,6 +1,7 @@
<ProjectConfiguration>
<Settings>
<DefaultTestTimeout>1000</DefaultTestTimeout>
<IgnoreThisComponentCompletely>True</IgnoreThisComponentCompletely>
<PreviouslyBuiltSuccessfully>True</PreviouslyBuiltSuccessfully>
</Settings>
</ProjectConfiguration>

9
.ncrunch/Avalonia.Markup.UnitTests.netcoreapp1.1.v3.ncrunchproject

@ -0,0 +1,9 @@
<ProjectConfiguration>
<Settings>
<IgnoredTests>
<NamedTestSelector>
<TestName>Avalonia.Markup.UnitTests.Data.Plugins.DataAnnotationsValidationPluginTests.Produces_Aggregate_BindingNotificationsx</TestName>
</NamedTestSelector>
</IgnoredTests>
</Settings>
</ProjectConfiguration>

1
.ncrunch/Avalonia.Skia.RenderTests.v3.ncrunchproject

@ -1,6 +1,7 @@
<ProjectConfiguration>
<Settings>
<DefaultTestTimeout>1000</DefaultTestTimeout>
<IgnoreThisComponentCompletely>True</IgnoreThisComponentCompletely>
<PreviouslyBuiltSuccessfully>True</PreviouslyBuiltSuccessfully>
</Settings>
</ProjectConfiguration>

3
.travis.yml

@ -3,13 +3,14 @@ os:
- linux
- osx
dist: trusty
osx_image: xcode8.3
env:
global:
- DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1
- DOTNET_CLI_TELEMETRY_OPTOUT=1
mono:
- latest
dotnet: 1.0.1
dotnet: 2.0.0
script:
- ./build.sh --target "Travis" --platform "Mono" --configuration "Release"
notifications:

53
Avalonia.sln

@ -1,6 +1,6 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26228.4
VisualStudioVersion = 15.0.26730.3
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Base", "src\Avalonia.Base\Avalonia.Base.csproj", "{B09B78D8-9B26-48B0-9149-D64A2F120F3F}"
EndProject
@ -129,7 +129,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WindowsInteropTest", "sampl
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GtkInteropDemo", "samples\interop\GtkInteropDemo\GtkInteropDemo.csproj", "{BD7F352C-6DC1-4740-BAF2-2D34A038728C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.DotNetFrameworkRuntime", "src\Avalonia.DotNetFrameworkRuntime\Avalonia.DotNetFrameworkRuntime.csproj", "{4A1ABB09-9047-4BD5-A4AD-A055E52C5EE0}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.DotNetFrameworkRuntime", "src\Avalonia.DotNetFrameworkRuntime\Avalonia.DotNetFrameworkRuntime.csproj", "{4A1ABB09-9047-4BD5-A4AD-A055E52C5EE0}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RenderTest", "samples\RenderTest\RenderTest.csproj", "{F1FDC5B0-4654-416F-AE69-E3E9BBD87801}"
EndProject
@ -159,6 +159,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Props", "Props", "{F3AC8BC1
build\Microsoft.Reactive.Testing.props = build\Microsoft.Reactive.Testing.props
build\Moq.props = build\Moq.props
build\NetCore.props = build\NetCore.props
build\ReactiveUI.props = build\ReactiveUI.props
build\Rx.props = build\Rx.props
build\Serilog.props = build\Serilog.props
build\Serilog.Sinks.Trace.props = build\Serilog.Sinks.Trace.props
@ -185,6 +186,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Win32.Interop", "s
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Skia.RenderTests", "tests\Avalonia.RenderTests\Avalonia.Skia.RenderTests.csproj", "{E1582370-37B3-403C-917F-8209551B1634}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "OSX", "OSX", "{A59C4C0A-64DF-4621-B450-2BA00D6F61E2}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.MonoMac", "src\OSX\Avalonia.MonoMac\Avalonia.MonoMac.csproj", "{CBFD5788-567D-401B-9DFA-74E4224025A0}"
EndProject
Global
GlobalSection(SharedMSBuildProjectFiles) = preSolution
src\Shared\RenderHelpers\RenderHelpers.projitems*{3c4c0cb4-0c0f-4450-a37b-148c84ff905f}*SharedItemsImports = 13
@ -2514,6 +2519,46 @@ Global
{E1582370-37B3-403C-917F-8209551B1634}.Release|Mono.Build.0 = Release|Any CPU
{E1582370-37B3-403C-917F-8209551B1634}.Release|x86.ActiveCfg = Release|Any CPU
{E1582370-37B3-403C-917F-8209551B1634}.Release|x86.Build.0 = Release|Any CPU
{CBFD5788-567D-401B-9DFA-74E4224025A0}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{CBFD5788-567D-401B-9DFA-74E4224025A0}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{CBFD5788-567D-401B-9DFA-74E4224025A0}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
{CBFD5788-567D-401B-9DFA-74E4224025A0}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
{CBFD5788-567D-401B-9DFA-74E4224025A0}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{CBFD5788-567D-401B-9DFA-74E4224025A0}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
{CBFD5788-567D-401B-9DFA-74E4224025A0}.Ad-Hoc|Mono.ActiveCfg = Debug|Any CPU
{CBFD5788-567D-401B-9DFA-74E4224025A0}.Ad-Hoc|Mono.Build.0 = Debug|Any CPU
{CBFD5788-567D-401B-9DFA-74E4224025A0}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU
{CBFD5788-567D-401B-9DFA-74E4224025A0}.Ad-Hoc|x86.Build.0 = Debug|Any CPU
{CBFD5788-567D-401B-9DFA-74E4224025A0}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
{CBFD5788-567D-401B-9DFA-74E4224025A0}.AppStore|Any CPU.Build.0 = Debug|Any CPU
{CBFD5788-567D-401B-9DFA-74E4224025A0}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
{CBFD5788-567D-401B-9DFA-74E4224025A0}.AppStore|iPhone.Build.0 = Debug|Any CPU
{CBFD5788-567D-401B-9DFA-74E4224025A0}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{CBFD5788-567D-401B-9DFA-74E4224025A0}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
{CBFD5788-567D-401B-9DFA-74E4224025A0}.AppStore|Mono.ActiveCfg = Debug|Any CPU
{CBFD5788-567D-401B-9DFA-74E4224025A0}.AppStore|Mono.Build.0 = Debug|Any CPU
{CBFD5788-567D-401B-9DFA-74E4224025A0}.AppStore|x86.ActiveCfg = Debug|Any CPU
{CBFD5788-567D-401B-9DFA-74E4224025A0}.AppStore|x86.Build.0 = Debug|Any CPU
{CBFD5788-567D-401B-9DFA-74E4224025A0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CBFD5788-567D-401B-9DFA-74E4224025A0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CBFD5788-567D-401B-9DFA-74E4224025A0}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{CBFD5788-567D-401B-9DFA-74E4224025A0}.Debug|iPhone.Build.0 = Debug|Any CPU
{CBFD5788-567D-401B-9DFA-74E4224025A0}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{CBFD5788-567D-401B-9DFA-74E4224025A0}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{CBFD5788-567D-401B-9DFA-74E4224025A0}.Debug|Mono.ActiveCfg = Debug|Any CPU
{CBFD5788-567D-401B-9DFA-74E4224025A0}.Debug|Mono.Build.0 = Debug|Any CPU
{CBFD5788-567D-401B-9DFA-74E4224025A0}.Debug|x86.ActiveCfg = Debug|Any CPU
{CBFD5788-567D-401B-9DFA-74E4224025A0}.Debug|x86.Build.0 = Debug|Any CPU
{CBFD5788-567D-401B-9DFA-74E4224025A0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CBFD5788-567D-401B-9DFA-74E4224025A0}.Release|Any CPU.Build.0 = Release|Any CPU
{CBFD5788-567D-401B-9DFA-74E4224025A0}.Release|iPhone.ActiveCfg = Release|Any CPU
{CBFD5788-567D-401B-9DFA-74E4224025A0}.Release|iPhone.Build.0 = Release|Any CPU
{CBFD5788-567D-401B-9DFA-74E4224025A0}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{CBFD5788-567D-401B-9DFA-74E4224025A0}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{CBFD5788-567D-401B-9DFA-74E4224025A0}.Release|Mono.ActiveCfg = Release|Any CPU
{CBFD5788-567D-401B-9DFA-74E4224025A0}.Release|Mono.Build.0 = Release|Any CPU
{CBFD5788-567D-401B-9DFA-74E4224025A0}.Release|x86.ActiveCfg = Release|Any CPU
{CBFD5788-567D-401B-9DFA-74E4224025A0}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -2571,5 +2616,9 @@ Global
{638580B0-7910-40EF-B674-DCB34DA308CD} = {A0CC0258-D18C-4AB3-854F-7101680FC3F9}
{CBC4FF2F-92D4-420B-BE21-9FE0B930B04E} = {B39A8919-9F95-48FE-AD7B-76E08B509888}
{E1582370-37B3-403C-917F-8209551B1634} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{CBFD5788-567D-401B-9DFA-74E4224025A0} = {A59C4C0A-64DF-4621-B450-2BA00D6F61E2}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {87366D66-1391-4D90-8999-95A620AD786A}
EndGlobalSection
EndGlobal

4
appveyor.yml

@ -17,9 +17,9 @@ init:
- ps: if (Test-Path env:nuget_address) {[System.IO.File]::AppendAllText("C:\Windows\System32\drivers\etc\hosts", "`n$($env:nuget_address)`tapi.nuget.org")}
install:
- if not exist gtk-sharp-2.12.26.msi appveyor DownloadFile http://download.xamarin.com/GTKforWindows/Windows/gtk-sharp-2.12.26.msi
- if not exist dotnet-1.0.1.exe appveyor DownloadFile https://go.microsoft.com/fwlink/?linkid=843448 -FileName "dotnet-1.0.1.exe"
- if not exist dotnet-2.0.0.exe appveyor DownloadFile https://download.microsoft.com/download/0/F/D/0FD852A4-7EA1-4E2A-983A-0484AC19B92C/dotnet-sdk-2.0.0-win-x64.exe -FileName "dotnet-2.0.0.exe"
- ps: Start-Process -FilePath "msiexec" -ArgumentList "/i gtk-sharp-2.12.26.msi /quiet /qn /norestart" -Wait
- ps: Start-Process -FilePath "dotnet-1.0.1.exe" -ArgumentList "/quiet" -Wait
- ps: Start-Process -FilePath "dotnet-2.0.0.exe" -ArgumentList "/quiet" -Wait
- cmd: set PATH=%programfiles(x86)%\GtkSharp\2.12\bin\;%PATH%
before_build:
- git submodule update --init

2
build.cake

@ -162,7 +162,7 @@ void RunCoreTest(string project, Parameters parameters, bool coreOnly = false)
project = System.IO.Path.Combine(project, System.IO.Path.GetFileName(project)+".csproj");
Information("Running tests from " + project);
DotNetCoreRestore(project);
var frameworks = new List<string>(){"netcoreapp1.1"};
var frameworks = new List<string>(){"netcoreapp2.0"};
if(parameters.IsRunningOnWindows)
frameworks.Add("net461");
foreach(var fw in frameworks)

5
build/MonoMac.props

@ -0,0 +1,5 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<PackageReference Include="MonoMac.NetStandard" Version="0.0.3" />
</ItemGroup>
</Project>

2
build/Moq.props

@ -1,5 +1,5 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<PackageReference Include="Moq" Version="4.7.25" />
<PackageReference Include="Moq" Version="4.7.99" />
</ItemGroup>
</Project>

5
build/ReactiveUI.props

@ -0,0 +1,5 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<PackageReference Include="reactiveui" Version="8.0.0-alpha0073" />
</ItemGroup>
</Project>

13
build/Rx.props

@ -1,11 +1,10 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<PackageReference Include="System.Reactive" Version="3.0.0" />
<PackageReference Include="System.Reactive.Core" Version="3.0.0" />
<PackageReference Include="System.Reactive.Interfaces" Version="3.0.0" />
<PackageReference Include="System.Reactive.Linq" Version="3.0.0" />
<PackageReference Include="System.Reactive.PlatformServices" Version="3.0.0" />
<PackageReference Condition="'$(TargetFramework)' == 'net45'" Include="System.Reactive.Windows.Threading" Version="3.0.0" />
<PackageReference Condition="'$(TargetFramework)' == 'net461'" Include="System.Reactive.Windows.Threading" Version="3.0.0" />
<PackageReference Include="System.Reactive" Version="3.1.1" />
<PackageReference Include="System.Reactive.Core" Version="3.1.1" />
<PackageReference Include="System.Reactive.Interfaces" Version="3.1.1" />
<PackageReference Include="System.Reactive.Linq" Version="3.1.1" />
<PackageReference Include="System.Reactive.PlatformServices" Version="3.1.1" />
<PackageReference Condition="$(TargetFramework.StartsWith('net4'))" Include="System.Reactive.Windows.Threading" Version="3.1.1" />
</ItemGroup>
</Project>

19
build/UnitTests.NetCore.targets

@ -3,25 +3,6 @@
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="System.Threading.Thread" Version="4.3.0" />
</ItemGroup>

2
build/XUnit.props

@ -9,7 +9,7 @@
<PackageReference Include="xunit.runner.console" Version="2.2.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.2.0" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netcoreapp1.1'">
<ItemGroup Condition="'$(TargetFramework)' == 'netcoreapp2.0'">
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.0.0" />
</ItemGroup>
<PropertyGroup>

13
docs/guidelines/build.md

@ -80,10 +80,11 @@ mono ./samples/ControlCatalog.Desktop/bin/Debug/ControlCatalog.Desktop.exe
### Building Avalonia in MonoDevelop
Unless you have a very current version of monodevelop (6.1.x or newer), it is necessary to manually
restore the Nuget depdendencies as [mentioned above](#restore-nuget-packages). You must then
disable MonoDevelop's inbuilt NuGet package manager add-in by going to `Tools -> Add-in Manager` or
it will complain that a newer version of NuGet is needed.
Flatpak version will *NOT* work. Version from https://github.com/cra0zy/monodevelop-run-installer/ might work if you are very lucky. Make sure that you have the latest version of Mono (from alpha update channel) and .NET Core SDK. Make sure to follow `FrameworkPathOverride` workaround from https://github.com/dotnet/sdk/issues/335
### Building and running Avalonia in Rider
For Linux/OSX you'll probably need to apply workaround from https://github.com/dotnet/sdk/issues/335
Just add `export FrameworkPathOverride=/usr/lib/mono/4.6.1-api` (or `export FrameworkPathOverride=/Library/Frameworks/Mono.framework/Versions/Current/lib/mono/4.6.1-api` for OSX)
Finally, select the `Debug | Mono` or `Release | Mono` build configuration and you should be good to
go!

99
packages.cake

@ -43,7 +43,6 @@ public class Packages
}
}
//new NuSpecDependency() { Id = "System.Threading.ThreadPool", TargetFramework = "netcoreapp1.0", Version = "4.3.0" },
public Packages(ICakeContext context, Parameters parameters)
{
// NUGET NUSPECS
@ -111,6 +110,7 @@ public class Packages
var SplatVersion = packageVersions["Splat"].FirstOrDefault().Item1;
var SpracheVersion = packageVersions["Sprache"].FirstOrDefault().Item1;
var SystemReactiveVersion = packageVersions["System.Reactive"].FirstOrDefault().Item1;
var ReactiveUIVersion = packageVersions["reactiveui"].FirstOrDefault().Item1;
var SystemValueTupleVersion = packageVersions["System.ValueTuple"].FirstOrDefault().Item1;
SkiaSharpVersion = packageVersions["SkiaSharp"].FirstOrDefault().Item1;
SkiaSharpLinuxVersion = packageVersions["Avalonia.Skia.Linux.Natives"].FirstOrDefault().Item1;
@ -124,6 +124,7 @@ public class Packages
context.Information("Package: Splat, version: {0}", SplatVersion);
context.Information("Package: Sprache, version: {0}", SpracheVersion);
context.Information("Package: System.Reactive, version: {0}", SystemReactiveVersion);
context.Information("Package: reactiveui, version: {0}", ReactiveUIVersion);
context.Information("Package: System.ValueTuple, version: {0}", SystemValueTupleVersion);
context.Information("Package: SkiaSharp, version: {0}", SkiaSharpVersion);
context.Information("Package: Avalonia.Skia.Linux.Natives, version: {0}", SkiaSharpLinuxVersion);
@ -176,7 +177,6 @@ public class Packages
new [] { "./src/", "Avalonia.Visuals", ".xml" },
new [] { "./src/", "Avalonia.Styling", ".dll" },
new [] { "./src/", "Avalonia.Styling", ".xml" },
new [] { "./src/", "Avalonia.ReactiveUI", ".dll" },
new [] { "./src/", "Avalonia.Themes.Default", ".dll" },
new [] { "./src/", "Avalonia.Themes.Default", ".xml" },
new [] { "./src/Markup/", "Avalonia.Markup", ".dll" },
@ -186,12 +186,12 @@ public class Packages
};
var coreLibrariesFiles = coreLibraries.Select((lib) => {
return (FilePath)context.File(lib[0] + lib[1] + "/bin/" + parameters.DirSuffix + "/netstandard1.3/" + lib[1] + lib[2]);
return (FilePath)context.File(lib[0] + lib[1] + "/bin/" + parameters.DirSuffix + "/netstandard2.0/" + lib[1] + lib[2]);
}).ToList();
var coreLibrariesNuSpecContent = coreLibrariesFiles.Select((file) => {
return new NuSpecContent {
Source = file.FullPath, Target = "lib/netstandard1.3"
Source = file.FullPath, Target = "lib/netstandard2.0"
};
});
@ -203,14 +203,14 @@ public class Packages
var netcoreappCoreLibrariesNuSpecContent = coreLibrariesFiles.Select((file) => {
return new NuSpecContent {
Source = file.FullPath, Target = "lib/netcoreapp1.0"
Source = file.FullPath, Target = "lib/netcoreapp2.0"
};
});
var net45RuntimePlatformExtensions = new [] {".xml", ".dll"};
var net45RuntimePlatform = net45RuntimePlatformExtensions.Select(libSuffix => {
return new NuSpecContent {
Source = ((FilePath)context.File("./src/Avalonia.DotNetFrameworkRuntime/bin/" + parameters.DirSuffix + "/Avalonia.DotNetFrameworkRuntime" + libSuffix)).FullPath,
Source = ((FilePath)context.File("./src/Avalonia.DotNetFrameworkRuntime/bin/" + parameters.DirSuffix + "/net461/Avalonia.DotNetFrameworkRuntime" + libSuffix)).FullPath,
Target = "lib/net45"
};
});
@ -218,8 +218,8 @@ public class Packages
var netCoreRuntimePlatformExtensions = new [] {".xml", ".dll"};
var netCoreRuntimePlatform = netCoreRuntimePlatformExtensions.Select(libSuffix => {
return new NuSpecContent {
Source = ((FilePath)context.File("./src/Avalonia.DotNetCoreRuntime/bin/" + parameters.DirSuffix + "/netcoreapp1.0/Avalonia.DotNetCoreRuntime" + libSuffix)).FullPath,
Target = "lib/netcoreapp1.0"
Source = ((FilePath)context.File("./src/Avalonia.DotNetCoreRuntime/bin/" + parameters.DirSuffix + "/netcoreapp2.0/Avalonia.DotNetCoreRuntime" + libSuffix)).FullPath,
Target = "lib/netcoreapp2.0"
};
});
@ -238,15 +238,15 @@ public class Packages
new NuSpecDependency() { Id = "Sprache", Version = SpracheVersion },
new NuSpecDependency() { Id = "System.Reactive", Version = SystemReactiveVersion },
//.NET Core
new NuSpecDependency() { Id = "System.Threading.ThreadPool", TargetFramework = "netcoreapp1.0", Version = "4.3.0" },
new NuSpecDependency() { Id = "Microsoft.Extensions.DependencyModel", TargetFramework = "netcoreapp1.0", Version = "1.1.0" },
new NuSpecDependency() { Id = "NETStandard.Library", TargetFramework = "netcoreapp1.0", Version = "1.6.0" },
new NuSpecDependency() { Id = "Splat", TargetFramework = "netcoreapp1.0", Version = SplatVersion },
new NuSpecDependency() { Id = "Serilog", TargetFramework = "netcoreapp1.0", Version = SerilogVersion },
new NuSpecDependency() { Id = "Sprache", TargetFramework = "netcoreapp1.0", Version = SpracheVersion },
new NuSpecDependency() { Id = "System.Reactive", TargetFramework = "netcoreapp1.0", Version = SystemReactiveVersion },
new NuSpecDependency() { Id = "System.Threading.ThreadPool", TargetFramework = "netcoreapp2.0", Version = "4.3.0" },
new NuSpecDependency() { Id = "Microsoft.Extensions.DependencyModel", TargetFramework = "netcoreapp2.0", Version = "1.1.0" },
new NuSpecDependency() { Id = "NETStandard.Library", TargetFramework = "netcoreapp2.0", Version = "1.6.0" },
new NuSpecDependency() { Id = "Splat", TargetFramework = "netcoreapp2.0", Version = SplatVersion },
new NuSpecDependency() { Id = "Serilog", TargetFramework = "netcoreapp2.0", Version = SerilogVersion },
new NuSpecDependency() { Id = "Sprache", TargetFramework = "netcoreapp2.0", Version = SpracheVersion },
new NuSpecDependency() { Id = "System.Reactive", TargetFramework = "netcoreapp2.0", Version = SystemReactiveVersion },
}
.Deps(new string[]{null, "netcoreapp1.0"},
.Deps(new string[]{null, "netcoreapp2.0"},
"System.ValueTuple", "System.ComponentModel.TypeConverter", "System.ComponentModel.Primitives",
"System.Runtime.Serialization.Primitives", "System.Xml.XmlDocument", "System.Xml.ReaderWriter")
.ToArray(),
@ -269,11 +269,28 @@ public class Packages
},
Files = new []
{
new NuSpecContent { Source = "Avalonia.HtmlRenderer.dll", Target = "lib/netstandard1.3" }
new NuSpecContent { Source = "Avalonia.HtmlRenderer.dll", Target = "lib/netstandard2.0" }
},
BasePath = context.Directory("./src/Avalonia.HtmlRenderer/bin/" + parameters.DirSuffix + "/netstandard1.3"),
BasePath = context.Directory("./src/Avalonia.HtmlRenderer/bin/" + parameters.DirSuffix + "/netstandard2.0"),
OutputDirectory = parameters.NugetRoot
}
},
///////////////////////////////////////////////////////////////////////////////
// Avalonia.ReactiveUI
///////////////////////////////////////////////////////////////////////////////
new NuGetPackSettings()
{
Id = "Avalonia.ReactiveUI",
Dependencies = new DependencyBuilder(this)
{
new NuSpecDependency() { Id = "Avalonia", Version = parameters.Version },
}.Deps(new string[] {null}, "reactiveui"),
Files = new []
{
new NuSpecContent { Source = "Avalonia.ReactiveUI.dll", Target = "lib/netstandard2.0" }
},
BasePath = context.Directory("./src/Avalonia.ReactiveUI/bin/" + parameters.DirSuffix + "/netstandard2.0"),
OutputDirectory = parameters.NugetRoot
},
};
var nuspecNuGetSettingsMobile = new []
@ -331,7 +348,7 @@ public class Packages
Files = new []
{
new NuSpecContent { Source = "Avalonia.Win32/bin/" + parameters.DirSuffix + "/Avalonia.Win32.dll", Target = "lib/net45" },
new NuSpecContent { Source = "Avalonia.Win32.NetStandard/bin/" + parameters.DirSuffix + "/netstandard1.3/Avalonia.Win32.dll", Target = "lib/netstandard1.3" }
new NuSpecContent { Source = "Avalonia.Win32.NetStandard/bin/" + parameters.DirSuffix + "/netstandard2.0/Avalonia.Win32.dll", Target = "lib/netstandard2.0" }
},
BasePath = context.Directory("./src/Windows"),
OutputDirectory = parameters.NugetRoot
@ -352,9 +369,9 @@ public class Packages
},
Files = new []
{
new NuSpecContent { Source = "Avalonia.Direct2D1.dll", Target = "lib/netstandard1.3" }
new NuSpecContent { Source = "Avalonia.Direct2D1.dll", Target = "lib/netstandard2.0" }
},
BasePath = context.Directory("./src/Windows/Avalonia.Direct2D1/bin/" + parameters.DirSuffix + "/netstandard1.3"),
BasePath = context.Directory("./src/Windows/Avalonia.Direct2D1/bin/" + parameters.DirSuffix + "/netstandard2.0"),
OutputDirectory = parameters.NugetRoot
},
///////////////////////////////////////////////////////////////////////////////
@ -386,9 +403,9 @@ public class Packages
},
Files = new []
{
new NuSpecContent { Source = "Avalonia.Gtk3.dll", Target = "lib/netstandard1.3" }
new NuSpecContent { Source = "Avalonia.Gtk3.dll", Target = "lib/netstandard2.0" }
},
BasePath = context.Directory("./src/Gtk/Avalonia.Gtk3/bin/" + parameters.DirSuffix + "/netstandard1.3"),
BasePath = context.Directory("./src/Gtk/Avalonia.Gtk3/bin/" + parameters.DirSuffix + "/netstandard2.0"),
OutputDirectory = parameters.NugetRoot
},
///////////////////////////////////////////////////////////////////////////////
@ -418,18 +435,32 @@ public class Packages
{
new NuSpecDependency() { Id = "Avalonia", Version = parameters.Version },
new NuSpecDependency() { Id = "SkiaSharp", Version = SkiaSharpVersion },
new NuSpecDependency() { Id = "Avalonia", Version = parameters.Version, TargetFramework="netcoreapp1.1" },
new NuSpecDependency() { Id = "SkiaSharp", Version = SkiaSharpVersion, TargetFramework="netcoreapp1.1" },
new NuSpecDependency() { Id = "Avalonia.Skia.Linux.Natives", Version = SkiaSharpLinuxVersion, TargetFramework="netcoreapp1.1" },
new NuSpecDependency() { Id = "Avalonia", Version = parameters.Version, TargetFramework="netcoreapp2.0" },
new NuSpecDependency() { Id = "SkiaSharp", Version = SkiaSharpVersion, TargetFramework="netcoreapp2.0" },
new NuSpecDependency() { Id = "Avalonia.Skia.Linux.Natives", Version = SkiaSharpLinuxVersion, TargetFramework="netcoreapp2.0" },
new NuSpecDependency() { Id = "Avalonia", Version = parameters.Version, TargetFramework="net461" },
new NuSpecDependency() { Id = "SkiaSharp", Version = SkiaSharpVersion, TargetFramework="net461" },
new NuSpecDependency() { Id = "Avalonia.Skia.Linux.Natives", Version = SkiaSharpLinuxVersion, TargetFramework="net461" }
},
Files = new []
{
new NuSpecContent { Source = "Avalonia.Skia.dll", Target = "lib/netstandard1.3" }
new NuSpecContent { Source = "Avalonia.Skia.dll", Target = "lib/netstandard2.0" }
},
BasePath = context.Directory("./src/Skia/Avalonia.Skia/bin/" + parameters.DirSuffix + "/netstandard2.0"),
OutputDirectory = parameters.NugetRoot
},
new NuGetPackSettings()
{
Id = "Avalonia.MonoMac",
Dependencies = new DependencyBuilder(this)
{
new NuSpecDependency() { Id = "Avalonia", Version = parameters.Version }
}.Dep("MonoMac.NetStandard").ToArray(),
Files = new []
{
new NuSpecContent { Source = "netstandard2.0/Avalonia.MonoMac.dll", Target = "lib/netstandard2.0" },
},
BasePath = context.Directory("./src/Skia/Avalonia.Skia/bin/" + parameters.DirSuffix + "/netstandard1.3"),
BasePath = context.Directory("./src/OSX/Avalonia.MonoMac/bin/" + parameters.DirSuffix),
OutputDirectory = parameters.NugetRoot
},
///////////////////////////////////////////////////////////////////////////////
@ -447,10 +478,12 @@ public class Packages
new NuSpecDependency() { Id = "Avalonia.Win32", TargetFramework="net45", Version = parameters.Version },
new NuSpecDependency() { Id = "Avalonia.Skia", TargetFramework="net45", Version = parameters.Version },
new NuSpecDependency() { Id = "Avalonia.Gtk3", TargetFramework="net45", Version = parameters.Version },
new NuSpecDependency() { Id = "Avalonia.MonoMac", TargetFramework="net45", Version = parameters.Version },
//.NET Core
new NuSpecDependency() { Id = "Avalonia.Win32", TargetFramework="netcoreapp1.0", Version = parameters.Version },
new NuSpecDependency() { Id = "Avalonia.Skia", TargetFramework="netcoreapp1.0", Version = parameters.Version },
new NuSpecDependency() { Id = "Avalonia.Gtk3", TargetFramework="netcoreapp1.0", Version = parameters.Version }
new NuSpecDependency() { Id = "Avalonia.Win32", TargetFramework="netcoreapp2.0", Version = parameters.Version },
new NuSpecDependency() { Id = "Avalonia.Skia", TargetFramework="netcoreapp2.0", Version = parameters.Version },
new NuSpecDependency() { Id = "Avalonia.Gtk3", TargetFramework="netcoreapp2.0", Version = parameters.Version },
new NuSpecDependency() { Id = "Avalonia.MonoMac", TargetFramework="netcoreapp2.0", Version = parameters.Version }
},
Files = new NuSpecContent[]
{
@ -488,7 +521,7 @@ public class Packages
},
Files = new []
{
new NuSpecContent { Source = "Avalonia.LinuxFramebuffer/bin/" + parameters.DirSuffix + "/netstandard1.3/Avalonia.LinuxFramebuffer.dll", Target = "lib/netstandard1.3" }
new NuSpecContent { Source = "Avalonia.LinuxFramebuffer/bin/" + parameters.DirSuffix + "/netstandard2.0/Avalonia.LinuxFramebuffer.dll", Target = "lib/netstandard2.0" }
},
BasePath = context.Directory("./src/Linux/"),
OutputDirectory = parameters.NugetRoot

1
samples/BindingTest/App.xaml.cs

@ -20,6 +20,7 @@ namespace BindingTest
AppBuilder.Configure<App>()
.UsePlatformDetect()
.UseReactiveUI()
.Start<MainWindow>();
}

2
samples/BindingTest/BindingTest.csproj

@ -45,7 +45,6 @@
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
@ -163,4 +162,5 @@
<Import Project="..\..\build\Serilog.Sinks.Trace.props" />
<Import Project="..\..\build\Splat.props" />
<Import Project="..\..\build\Rx.props" />
<Import Project="..\..\build\ReactiveUI.props" />
</Project>

10
samples/BindingTest/ViewModels/MainWindowViewModel.cs

@ -28,15 +28,13 @@ namespace BindingTest.ViewModels
SelectedItems = new ObservableCollection<TestItem>();
ShuffleItems = ReactiveCommand.Create();
ShuffleItems.Subscribe(_ =>
ShuffleItems = ReactiveCommand.Create(() =>
{
var r = new Random();
Items.Move(r.Next(Items.Count), 1);
});
StringValueCommand = ReactiveCommand.Create();
StringValueCommand.Subscribe(param =>
StringValueCommand = ReactiveCommand.Create<object>(param =>
{
BooleanFlag = !BooleanFlag;
StringValue = param.ToString();
@ -58,7 +56,7 @@ namespace BindingTest.ViewModels
public ObservableCollection<TestItem> Items { get; }
public ObservableCollection<TestItem> SelectedItems { get; }
public ReactiveCommand<object> ShuffleItems { get; }
public ReactiveCommand ShuffleItems { get; }
public string BooleanString
{
@ -91,7 +89,7 @@ namespace BindingTest.ViewModels
}
public IObservable<string> CurrentTimeObservable { get; }
public ReactiveCommand<object> StringValueCommand { get; }
public ReactiveCommand StringValueCommand { get; }
public DataAnnotationsErrorViewModel DataAnnotationsValidation { get; } = new DataAnnotationsErrorViewModel();
public ExceptionErrorViewModel ExceptionDataValidation { get; } = new ExceptionErrorViewModel();

2
samples/BindingTest/ViewModels/NestedCommandViewModel.cs

@ -12,7 +12,7 @@ namespace BindingTest.ViewModels
{
public NestedCommandViewModel()
{
Command = ReactiveCommand.Create();
Command = ReactiveCommand.Create(() => { });
}
public ICommand Command { get; }

2
samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj

@ -2,7 +2,7 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp1.1</TargetFramework>
<TargetFramework>netcoreapp2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>

35
samples/ControlCatalog.NetCore/Program.cs

@ -1,24 +1,47 @@
using System;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
using Avalonia;
namespace ControlCatalog.NetCore
{
class Program
static class Program
{
static void Main(string[] args)
{
if (args.Contains("--fbdev")) AppBuilder.Configure<App>().InitializeWithLinuxFramebuffer(tl =>
if (args.Contains("--wait-for-attach"))
{
tl.Content = new MainView();
System.Threading.ThreadPool.QueueUserWorkItem(_ => ConsoleSilencer());
});
Console.WriteLine("Attach debugger and use 'Set next statement'");
while (true)
{
Thread.Sleep(100);
if (Debugger.IsAttached)
break;
}
}
if (args.Contains("--fbdev"))
AppBuilder.Configure<App>().InitializeWithLinuxFramebuffer(tl =>
{
tl.Content = new MainView();
System.Threading.ThreadPool.QueueUserWorkItem(_ => ConsoleSilencer());
});
else
AppBuilder.Configure<App>()
.UsePlatformDetect()
.CustomPlatformDetect()
.UseReactiveUI()
.Start<MainWindow>();
}
static AppBuilder CustomPlatformDetect(this AppBuilder builder)
{
//This is needed because we still aren't ready to have MonoMac backend as default one
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
return builder.UseSkia().UseMonoMac();
return builder.UsePlatformDetect();
}
static void ConsoleSilencer()
{
Console.CursorVisible = false;

14
samples/ControlCatalog/ControlCatalog.csproj

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard1.3</TargetFramework>
<TargetFramework>netstandard2.0</TargetFramework>
<EnableDefaultCompileItems>False</EnableDefaultCompileItems>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
</PropertyGroup>
@ -32,6 +32,12 @@
<EmbeddedResource Include="MainView.xaml">
<SubType>Designer</SubType>
</EmbeddedResource>
<EmbeddedResource Include="DecoratedWindow.xaml">
<SubType>Designer</SubType>
</EmbeddedResource>
<EmbeddedResource Include="Pages\DialogsPage.xaml">
<SubType>Designer</SubType>
</EmbeddedResource>
<EmbeddedResource Include="Pages\BorderPage.xaml">
<SubType>Designer</SubType>
</EmbeddedResource>
@ -83,9 +89,15 @@
<Compile Include="MainView.xaml.cs">
<DependentUpon>MainView.xaml</DependentUpon>
</Compile>
<Compile Include="DecoratedWindow.xaml.cs">
<DependentUpon>DecoratedWindow.xaml</DependentUpon>
</Compile>
<Compile Include="MainWindow.xaml.cs">
<DependentUpon>MainWindow.xaml</DependentUpon>
</Compile>
<Compile Include="Pages\DialogsPage.xaml.cs">
<DependentUpon>DialogsPage.xaml</DependentUpon>
</Compile>
<Compile Include="Pages\BorderPage.xaml.cs">
<DependentUpon>BorderPage.xaml</DependentUpon>
</Compile>

33
samples/ControlCatalog/DecoratedWindow.xaml

@ -0,0 +1,33 @@
<Window xmlns="https://github.com/avaloniaui" MinWidth="500" MinHeight="300"
Title="Avalonia Control Gallery"
Icon="resm:ControlCatalog.Assets.test_icon.ico?assembly=ControlCatalog"
xmlns:local="clr-namespace:ControlCatalog;assembly=ControlCatalog" HasSystemDecorations="False">
<Grid RowDefinitions="5,*,5" ColumnDefinitions="5,*,5">
<DockPanel Grid.Column="1" Grid.Row="1" >
<Grid Name="TitleBar" Background="LightBlue" DockPanel.Dock="Top" ColumnDefinitions="Auto,*,Auto">
<TextBlock VerticalAlignment="Center" Margin="5,0,0,0">Title</TextBlock>
<StackPanel Grid.Column="2" Orientation="Horizontal">
<StackPanel.Styles>
<Style Selector="Button">
<Setter Property="Margin" Value="2"/>
</Style>
</StackPanel.Styles>
<Button Name="MinimizeButton">_</Button>
<Button Name="MaximizeButton">[ ]</Button>
<Button Name="CloseButton">X</Button>
</StackPanel>
</Grid>
<Border Background="White" Margin="5">
<TextBlock>Hello world!</TextBlock>
</Border>
</DockPanel>
<Border Name="TopLeft" Background="Red"/>
<Border Name="TopRight" Background="Red" Grid.Column="2" />
<Border Name="BottomLeft" Background="Red" Grid.Row="2" />
<Border Name="BottomRight" Background="Red" Grid.Row="2" Grid.Column="2"/>
<Border Name="Top" Background="Blue" Grid.Column="1" />
<Border Name="Right" Background="Blue" Grid.Row="1" Grid.Column="2" />
<Border Name="Bottom" Background="Blue" Grid.Row="2" Grid.Column="1" />
<Border Name="Left" Background="Blue" Grid.Row="1" />
</Grid>
</Window>

53
samples/ControlCatalog/DecoratedWindow.xaml.cs

@ -0,0 +1,53 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using System;
using Avalonia.Input;
namespace ControlCatalog
{
public class DecoratedWindow : Window
{
public DecoratedWindow()
{
this.InitializeComponent();
this.AttachDevTools();
}
void SetupSide(string name, StandardCursorType cursor, WindowEdge edge)
{
var ctl = this.FindControl<Control>(name);
ctl.Cursor = new Cursor(cursor);
ctl.PointerPressed += delegate
{
PlatformImpl.BeginResizeDrag(edge);
};
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
this.FindControl<Control>("TitleBar").PointerPressed += delegate
{
PlatformImpl.BeginMoveDrag();
};
SetupSide("Left", StandardCursorType.LeftSide, WindowEdge.West);
SetupSide("Right", StandardCursorType.RightSide, WindowEdge.East);
SetupSide("Top", StandardCursorType.TopSide, WindowEdge.North);
SetupSide("Bottom", StandardCursorType.BottomSize, WindowEdge.South);
SetupSide("TopLeft", StandardCursorType.TopLeftCorner, WindowEdge.NorthWest);
SetupSide("TopRight", StandardCursorType.TopRightCorner, WindowEdge.NorthEast);
SetupSide("BottomLeft", StandardCursorType.BottomLeftCorner, WindowEdge.SouthWest);
SetupSide("BottomRight", StandardCursorType.BottomRightCorner, WindowEdge.SouthEast);
this.FindControl<Button>("MinimizeButton").Click += delegate { this.WindowState = WindowState.Minimized; };
this.FindControl<Button>("MaximizeButton").Click += delegate
{
WindowState = WindowState == WindowState.Maximized ? WindowState.Normal : WindowState.Maximized;
};
this.FindControl<Button>("CloseButton").Click += delegate
{
Close();
};
}
}
}

2
samples/ControlCatalog/MainView.xaml

@ -1,7 +1,7 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:pages="clr-namespace:ControlCatalog.Pages;assembly=ControlCatalog"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<TabControl Classes="sidebar">
<TabControl Classes="sidebar" Name="Sidebar">
<TabControl.Transition>
<CrossFade Duration="0.25"/>
</TabControl.Transition>

9
samples/ControlCatalog/MainView.xaml.cs

@ -1,6 +1,9 @@
using System.Collections;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using Avalonia.Platform;
using ControlCatalog.Pages;
namespace ControlCatalog
{
@ -9,6 +12,12 @@ namespace ControlCatalog
public MainView()
{
this.InitializeComponent();
if (AvaloniaLocator.Current.GetService<IRuntimePlatform>().GetRuntimeInfo().IsDesktop)
((IList) this.FindControl<TabControl>("Sidebar").Items).Add(new TabItem()
{
Header = "Dialogs",
Content = new DialogsPage()
});
}
private void InitializeComponent()

5
samples/ControlCatalog/MainWindow.xaml.cs

@ -1,6 +1,7 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using System;
namespace ControlCatalog
{
@ -10,7 +11,7 @@ namespace ControlCatalog
{
this.InitializeComponent();
this.AttachDevTools();
Renderer.DrawDirtyRects = Renderer.DrawFps = true;
//Renderer.DrawDirtyRects = Renderer.DrawFps = true;
}
private void InitializeComponent()
@ -19,7 +20,7 @@ namespace ControlCatalog
// so we must refer to this resource DLL statically. For
// now I am doing that here. But we need a better solution!!
var theme = new Avalonia.Themes.Default.DefaultTheme();
theme.FindResource("Button");
theme.TryGetResource("Button", out _);
AvaloniaXamlLoader.Load(this);
}
}

12
samples/ControlCatalog/Pages/DialogsPage.xaml

@ -0,0 +1,12 @@
<UserControl xmlns="https://github.com/avaloniaui">
<StackPanel Orientation="Vertical" Gap="4" Margin="4">
<Button Name="OpenFile">Open File</Button>
<Button Name="SaveFile">Save File</Button>
<Button Name="SelectFolder">Select Folder</Button>
<StackPanel Orientation="Horizontal">
<CheckBox Name="IsModal" IsChecked="True"/>
<TextBlock>Modal to window</TextBlock>
</StackPanel>
<Button Name="DecoratedWindow">Decorated window</Button>
</StackPanel>
</UserControl>

46
samples/ControlCatalog/Pages/DialogsPage.xaml.cs

@ -0,0 +1,46 @@
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
#pragma warning disable 4014
namespace ControlCatalog.Pages
{
public class DialogsPage : UserControl
{
public DialogsPage()
{
this.InitializeComponent();
this.FindControl<Button>("OpenFile").Click += delegate
{
new OpenFileDialog()
{
Title = "Open file"
}.ShowAsync(GetWindow());
};
this.FindControl<Button>("SaveFile").Click += delegate
{
new SaveFileDialog()
{
Title = "Save file"
}.ShowAsync(GetWindow());
};
this.FindControl<Button>("SelectFolder").Click += delegate
{
new OpenFolderDialog()
{
Title = "Select folder"
}.ShowAsync(GetWindow());
};
this.FindControl<Button>("DecoratedWindow").Click += delegate
{
new DecoratedWindow().Show();
};
}
Window GetWindow() => this.FindControl<CheckBox>("IsModal").IsChecked ? (Window)this.VisualRoot : null;
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}
}

53
samples/ControlCatalog/Pages/ToolTipPage.xaml

@ -1,22 +1,41 @@
<UserControl xmlns="https://github.com/avaloniaui">
<StackPanel Orientation="Vertical" Gap="4">
<TextBlock Classes="h1">ToolTip</TextBlock>
<TextBlock Classes="h2">A control which pops up a hint when a control is hovered</TextBlock>
<StackPanel Orientation="Vertical"
Gap="4">
<TextBlock Classes="h1">ToolTip</TextBlock>
<TextBlock Classes="h2">A control which pops up a hint when a control is hovered</TextBlock>
<StackPanel Orientation="Horizontal"
<Grid RowDefinitions="Auto,Auto"
ColumnDefinitions="Auto,Auto"
Margin="0,16,0,0"
HorizontalAlignment="Center"
Gap="16">
<Border Background="{StyleResource ThemeAccentBrush}"
Padding="48,48,48,48">
<ToolTip.Tip>
<StackPanel>
<TextBlock Classes="h1">ToolTip</TextBlock>
<TextBlock Classes="h2">A control which pops up a hint when a control is hovered</TextBlock>
</StackPanel>
</ToolTip.Tip>
<TextBlock>Hover Here</TextBlock>
</Border>
HorizontalAlignment="Center">
<Border Grid.Column="0"
Grid.Row="1"
Background="{StyleResource ThemeAccentBrush}"
Margin="5"
Padding="50"
ToolTip.Tip="This is a ToolTip">
<TextBlock>Hover Here</TextBlock>
</Border>
<CheckBox Grid.Column="1"
Margin="5"
Grid.Row="0"
IsChecked="{Binding ElementName=Border, Path=(ToolTip.IsOpen)}"
Content="ToolTip Open" />
<Border Name="Border"
Grid.Column="1"
Grid.Row="1"
Background="{StyleResource ThemeAccentBrush}"
Margin="5"
Padding="50"
ToolTip.Placement="Bottom">
<ToolTip.Tip>
<StackPanel>
<TextBlock Classes="h1">ToolTip</TextBlock>
<TextBlock Classes="h2">A control which pops up a hint when a control is hovered</TextBlock>
</StackPanel>
</ToolTip.Tip>
<TextBlock>ToolTip bottom placement</TextBlock>
</Border>
</Grid>
</StackPanel>
</StackPanel>
</UserControl>

1
samples/RenderTest/Program.cs

@ -18,6 +18,7 @@ namespace RenderTest
// again.
AppBuilder.Configure<App>()
.UsePlatformDetect()
.UseReactiveUI()
.Start<MainWindow>();
}

1
samples/RenderTest/RenderTest.csproj

@ -191,4 +191,5 @@
<Import Project="..\..\build\Serilog.Sinks.Trace.props" />
<Import Project="..\..\build\Splat.props" />
<Import Project="..\..\build\Rx.props" />
<Import Project="..\..\build\ReactiveUI.props" />
</Project>

10
samples/RenderTest/ViewModels/MainWindowViewModel.cs

@ -10,10 +10,8 @@ namespace RenderTest.ViewModels
public MainWindowViewModel()
{
ToggleDrawDirtyRects = ReactiveCommand.Create();
ToggleDrawDirtyRects.Subscribe(_ => DrawDirtyRects = !DrawDirtyRects);
ToggleDrawFps = ReactiveCommand.Create();
ToggleDrawFps.Subscribe(_ => DrawFps = !DrawFps);
ToggleDrawDirtyRects = ReactiveCommand.Create(() => DrawDirtyRects = !DrawDirtyRects);
ToggleDrawFps = ReactiveCommand.Create(() => DrawFps = !DrawFps);
}
public bool DrawDirtyRects
@ -28,7 +26,7 @@ namespace RenderTest.ViewModels
set { this.RaiseAndSetIfChanged(ref drawFps, value); }
}
public ReactiveCommand<object> ToggleDrawDirtyRects { get; }
public ReactiveCommand<object> ToggleDrawFps { get; }
public ReactiveCommand ToggleDrawDirtyRects { get; }
public ReactiveCommand ToggleDrawFps { get; }
}
}

1
samples/VirtualizationTest/Program.cs

@ -17,6 +17,7 @@ namespace VirtualizationTest
AppBuilder.Configure<App>()
.UsePlatformDetect()
.UseReactiveUI()
.Start<MainWindow>();
}

25
samples/VirtualizationTest/ViewModels/MainWindowViewModel.cs

@ -23,20 +23,15 @@ namespace VirtualizationTest.ViewModels
public MainWindowViewModel()
{
this.WhenAnyValue(x => x.ItemCount).Subscribe(ResizeItems);
RecreateCommand = ReactiveCommand.Create();
RecreateCommand.Subscribe(_ => Recreate());
RecreateCommand = ReactiveCommand.Create(() => Recreate());
AddItemCommand = ReactiveCommand.Create();
AddItemCommand.Subscribe(_ => AddItem());
AddItemCommand = ReactiveCommand.Create(() => AddItem());
RemoveItemCommand = ReactiveCommand.Create();
RemoveItemCommand.Subscribe(_ => Remove());
RemoveItemCommand = ReactiveCommand.Create(() => Remove());
SelectFirstCommand = ReactiveCommand.Create();
SelectFirstCommand.Subscribe(_ => SelectItem(0));
SelectFirstCommand = ReactiveCommand.Create(() => SelectItem(0));
SelectLastCommand = ReactiveCommand.Create();
SelectLastCommand.Subscribe(_ => SelectItem(Items.Count - 1));
SelectLastCommand = ReactiveCommand.Create(() => SelectItem(Items.Count - 1));
}
public string NewItemString
@ -78,11 +73,11 @@ namespace VirtualizationTest.ViewModels
public IEnumerable<ItemVirtualizationMode> VirtualizationModes =>
Enum.GetValues(typeof(ItemVirtualizationMode)).Cast<ItemVirtualizationMode>();
public ReactiveCommand<object> AddItemCommand { get; private set; }
public ReactiveCommand<object> RecreateCommand { get; private set; }
public ReactiveCommand<object> RemoveItemCommand { get; private set; }
public ReactiveCommand<object> SelectFirstCommand { get; private set; }
public ReactiveCommand<object> SelectLastCommand { get; private set; }
public ReactiveCommand AddItemCommand { get; private set; }
public ReactiveCommand RecreateCommand { get; private set; }
public ReactiveCommand RemoveItemCommand { get; private set; }
public ReactiveCommand SelectFirstCommand { get; private set; }
public ReactiveCommand SelectLastCommand { get; private set; }
private void ResizeItems(int count)
{

1
samples/VirtualizationTest/VirtualizationTest.csproj

@ -158,4 +158,5 @@
<Import Project="..\..\build\Serilog.Sinks.Trace.props" />
<Import Project="..\..\build\Splat.props" />
<Import Project="..\..\build\Rx.props" />
<Import Project="..\..\build\ReactiveUI.props" />
</Project>

2
src/Avalonia.Animation/Avalonia.Animation.csproj

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard1.3</TargetFramework>
<TargetFramework>netstandard2.0</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">

2
src/Avalonia.Base/Avalonia.Base.csproj

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard1.3</TargetFramework>
<TargetFramework>netstandard2.0</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<RootNamespace>Avalonia</RootNamespace>
</PropertyGroup>

3
src/Avalonia.Base/AvaloniaPropertyRegistry.cs

@ -47,6 +47,9 @@ namespace Avalonia
{
Dictionary<int, AvaloniaProperty> inner;
// Ensure the type's static ctor has been run.
RuntimeHelpers.RunClassConstructor(ownerType.TypeHandle);
if (_attached.TryGetValue(ownerType, out inner))
{
return inner.Values;

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

@ -16,6 +16,7 @@ namespace Avalonia.Collections
/// <typeparam name="TKey">The type of the dictionary key.</typeparam>
/// <typeparam name="TValue">The type of the dictionary value.</typeparam>
public class AvaloniaDictionary<TKey, TValue> : IDictionary<TKey, TValue>,
IDictionary,
INotifyCollectionChanged,
INotifyPropertyChanged
{
@ -51,6 +52,16 @@ namespace Avalonia.Collections
/// <inheritdoc/>
public ICollection<TValue> Values => _inner.Values;
bool IDictionary.IsFixedSize => ((IDictionary)_inner).IsFixedSize;
ICollection IDictionary.Keys => ((IDictionary)_inner).Keys;
ICollection IDictionary.Values => ((IDictionary)_inner).Values;
bool ICollection.IsSynchronized => ((IDictionary)_inner).IsSynchronized;
object ICollection.SyncRoot => ((IDictionary)_inner).SyncRoot;
/// <summary>
/// Gets or sets the named resource.
/// </summary>
@ -89,6 +100,8 @@ namespace Avalonia.Collections
}
}
object IDictionary.this[object key] { get => ((IDictionary)_inner)[key]; set => ((IDictionary)_inner)[key] = value; }
/// <inheritdoc/>
public void Add(TKey key, TValue value)
{
@ -118,10 +131,7 @@ namespace Avalonia.Collections
}
/// <inheritdoc/>
public bool ContainsKey(TKey key)
{
return _inner.ContainsKey(key);
}
public bool ContainsKey(TKey key) => _inner.ContainsKey(key);
/// <inheritdoc/>
public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
@ -130,21 +140,16 @@ namespace Avalonia.Collections
}
/// <inheritdoc/>
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
{
return _inner.GetEnumerator();
}
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() => _inner.GetEnumerator();
/// <inheritdoc/>
public bool Remove(TKey key)
{
TValue value;
if (_inner.TryGetValue(key, out value))
if (_inner.TryGetValue(key, out TValue value))
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Count"));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs($"Item[{key}]"));
if (CollectionChanged != null)
{
var e = new NotifyCollectionChangedEventArgs(
@ -163,16 +168,13 @@ namespace Avalonia.Collections
}
/// <inheritdoc/>
public bool TryGetValue(TKey key, out TValue value)
{
return _inner.TryGetValue(key, out value);
}
public bool TryGetValue(TKey key, out TValue value) => _inner.TryGetValue(key, out value);
/// <inheritdoc/>
IEnumerator IEnumerable.GetEnumerator()
{
return _inner.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator() => _inner.GetEnumerator();
/// <inheritdoc/>
void ICollection.CopyTo(Array array, int index) => ((ICollection)_inner).CopyTo(array, index);
/// <inheritdoc/>
void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> item)
@ -192,6 +194,18 @@ namespace Avalonia.Collections
return Remove(item.Key);
}
/// <inheritdoc/>
void IDictionary.Add(object key, object value) => Add((TKey)key, (TValue)value);
/// <inheritdoc/>
bool IDictionary.Contains(object key) => ((IDictionary) _inner).Contains(key);
/// <inheritdoc/>
IDictionaryEnumerator IDictionary.GetEnumerator() => ((IDictionary)_inner).GetEnumerator();
/// <inheritdoc/>
void IDictionary.Remove(object key) => Remove((TKey)key);
private void NotifyAdd(TKey key, TValue value)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Count"));

51
src/Avalonia.Controls/Application.cs

@ -29,7 +29,7 @@ namespace Avalonia
/// method.
/// - Tracks the lifetime of the application.
/// </remarks>
public class Application : IGlobalDataTemplates, IGlobalStyles, IStyleRoot, IApplicationLifecycle
public class Application : IApplicationLifecycle, IGlobalDataTemplates, IGlobalStyles, IStyleRoot, IResourceNode
{
/// <summary>
/// The application-global data templates.
@ -40,6 +40,7 @@ namespace Avalonia
new Lazy<IClipboard>(() => (IClipboard)AvaloniaLocator.Current.GetService(typeof(IClipboard)));
private readonly Styler _styler = new Styler();
private Styles _styles;
private IResourceDictionary _resources;
/// <summary>
/// Initializes a new instance of the <see cref="Application"/> class.
@ -49,6 +50,9 @@ namespace Avalonia
OnExit += OnExiting;
}
/// <inheritdoc/>
public event EventHandler<ResourcesChangedEventArgs> ResourcesChanged;
/// <summary>
/// Gets the current instance of the <see cref="Application"/> class.
/// </summary>
@ -97,6 +101,34 @@ namespace Avalonia
/// </summary>
public IClipboard Clipboard => _clipboard.Value;
/// <summary>
/// Gets the application's global resource dictionary.
/// </summary>
public IResourceDictionary Resources
{
get => _resources ?? (Resources = new ResourceDictionary());
set
{
Contract.Requires<ArgumentNullException>(value != null);
var hadResources = false;
if (_resources != null)
{
hadResources = _resources.Count > 0;
_resources.ResourcesChanged -= ResourcesChanged;
}
_resources = value;
_resources.ResourcesChanged += ResourcesChanged;
if (hadResources || _resources.Count > 0)
{
ResourcesChanged?.Invoke(this, new ResourcesChangedEventArgs());
}
}
}
/// <summary>
/// Gets the application's global styles.
/// </summary>
@ -119,6 +151,12 @@ namespace Avalonia
/// <inheritdoc/>
bool IStyleHost.IsStylesInitialized => _styles != null;
/// <inheritdoc/>
bool IResourceProvider.HasResources => _resources?.Count > 0;
/// <inheritdoc/>
IResourceNode IResourceNode.ResourceParent => null;
/// <summary>
/// Initializes the application by loading XAML etc.
/// </summary>
@ -145,13 +183,20 @@ namespace Avalonia
{
OnExit?.Invoke(this, EventArgs.Empty);
}
/// <inheritdoc/>
bool IResourceProvider.TryGetResource(string key, out object value)
{
value = null;
return (_resources?.TryGetResource(key, out value) ?? false) ||
Styles.TryGetResource(key, out value);
}
/// <summary>
/// Sent when the application is exiting.
/// </summary>
public event EventHandler OnExit;
/// <summary>
/// Called when the application is exiting.
/// </summary>

2
src/Avalonia.Controls/Avalonia.Controls.csproj

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard1.3</TargetFramework>
<TargetFramework>netstandard2.0</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">

70
src/Avalonia.Controls/Button.cs

@ -2,13 +2,10 @@
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using System.Linq;
using System.Windows.Input;
using Avalonia.Data;
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.Rendering;
using Avalonia.VisualTree;
namespace Avalonia.Controls
{
@ -68,19 +65,22 @@ namespace Avalonia.Controls
/// Defines the <see cref="Click"/> event.
/// </summary>
public static readonly RoutedEvent<RoutedEventArgs> ClickEvent =
RoutedEvent.Register<Button, RoutedEventArgs>("Click", RoutingStrategies.Bubble);
RoutedEvent.Register<Button, RoutedEventArgs>(nameof(Click), RoutingStrategies.Bubble);
private ICommand _command;
public static readonly StyledProperty<bool> IsPressedProperty =
AvaloniaProperty.Register<Button, bool>(nameof(IsPressed));
/// <summary>
/// Initializes static members of the <see cref="Button"/> class.
/// </summary>
static Button()
{
FocusableProperty.OverrideDefaultValue(typeof(Button), true);
ClickEvent.AddClassHandler<Button>(x => x.OnClick);
CommandProperty.Changed.Subscribe(CommandChanged);
IsDefaultProperty.Changed.Subscribe(IsDefaultChanged);
PseudoClass(IsPressedProperty, ":pressed");
}
/// <summary>
@ -138,6 +138,12 @@ namespace Avalonia.Controls
set { SetValue(IsDefaultProperty, value); }
}
public bool IsPressed
{
get { return GetValue(IsPressedProperty); }
private set { SetValue(IsPressedProperty, value); }
}
/// <inheritdoc/>
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
@ -145,9 +151,7 @@ namespace Avalonia.Controls
if (IsDefault)
{
var inputElement = e.Root as IInputElement;
if (inputElement != null)
if (e.Root is IInputElement inputElement)
{
ListenForDefault(inputElement);
}
@ -159,16 +163,16 @@ namespace Avalonia.Controls
{
if (e.Key == Key.Enter)
{
RaiseClickEvent();
OnClick();
e.Handled = true;
}
else if (e.Key == Key.Space)
{
if (ClickMode == ClickMode.Press)
{
RaiseClickEvent();
OnClick();
}
IsPressed = true;
e.Handled = true;
}
@ -182,9 +186,9 @@ namespace Avalonia.Controls
{
if (ClickMode == ClickMode.Release)
{
RaiseClickEvent();
OnClick();
}
IsPressed = false;
e.Handled = true;
}
}
@ -196,9 +200,7 @@ namespace Avalonia.Controls
if (IsDefault)
{
var inputElement = e.Root as IInputElement;
if (inputElement != null)
if (e.Root is IInputElement inputElement)
{
StopListeningForDefault(inputElement);
}
@ -208,9 +210,11 @@ namespace Avalonia.Controls
/// <summary>
/// Invokes the <see cref="Click"/> event.
/// </summary>
/// <param name="e">The event args.</param>
protected virtual void OnClick(RoutedEventArgs e)
protected virtual void OnClick()
{
var e = new RoutedEventArgs(ClickEvent);
RaiseEvent(e);
if (Command != null)
{
Command.Execute(CommandParameter);
@ -225,13 +229,13 @@ namespace Avalonia.Controls
if (e.MouseButton == MouseButton.Left)
{
PseudoClasses.Add(":pressed");
e.Device.Capture(this);
IsPressed = true;
e.Handled = true;
if (ClickMode == ClickMode.Press)
{
RaiseClickEvent();
OnClick();
}
}
}
@ -244,12 +248,12 @@ namespace Avalonia.Controls
if (e.MouseButton == MouseButton.Left)
{
e.Device.Capture(null);
PseudoClasses.Remove(":pressed");
IsPressed = false;
e.Handled = true;
if (ClickMode == ClickMode.Release && new Rect(Bounds.Size).Contains(e.GetPosition(this)))
{
RaiseClickEvent();
OnClick();
}
}
}
@ -272,9 +276,7 @@ namespace Avalonia.Controls
/// <param name="e">The event args.</param>
private static void CommandChanged(AvaloniaPropertyChangedEventArgs e)
{
var button = e.Sender as Button;
if (button != null)
if (e.Sender is Button button)
{
var oldCommand = e.OldValue as ICommand;
var newCommand = e.NewValue as ICommand;
@ -301,9 +303,8 @@ namespace Avalonia.Controls
{
var button = e.Sender as Button;
var isDefault = (bool)e.NewValue;
var inputRoot = button?.VisualRoot as IInputElement;
if (inputRoot != null)
if (button?.VisualRoot is IInputElement inputRoot)
{
if (isDefault)
{
@ -346,19 +347,6 @@ namespace Avalonia.Controls
root.RemoveHandler(KeyDownEvent, RootKeyDown);
}
/// <summary>
/// Raises the <see cref="Click"/> event.
/// </summary>
private void RaiseClickEvent()
{
RoutedEventArgs click = new RoutedEventArgs
{
RoutedEvent = ClickEvent,
};
RaiseEvent(click);
}
/// <summary>
/// Called when a key is pressed on the input root and the button <see cref="IsDefault"/>.
/// </summary>
@ -368,7 +356,7 @@ namespace Avalonia.Controls
{
if (e.Key == Key.Enter && IsVisible && IsEnabled)
{
RaiseClickEvent();
OnClick();
}
}
}

134
src/Avalonia.Controls/Control.cs

@ -97,8 +97,9 @@ namespace Avalonia.Controls
private bool _isAttachedToLogicalTree;
private IAvaloniaList<ILogical> _logicalChildren;
private INameScope _nameScope;
private bool _styled;
private IResourceDictionary _resources;
private Styles _styles;
private bool _styled;
private Subject<IStyleable> _styleDetach = new Subject<IStyleable>();
/// <summary>
@ -153,6 +154,11 @@ namespace Avalonia.Controls
/// </remarks>
public event EventHandler Initialized;
/// <summary>
/// Occurs when a resource in this control or a parent control has changed.
/// </summary>
public event EventHandler<ResourcesChangedEventArgs> ResourcesChanged;
/// <summary>
/// Gets or sets the name of the control.
/// </summary>
@ -262,7 +268,32 @@ namespace Avalonia.Controls
/// each control may in addition define its own styles which are applied to the control
/// itself and its children.
/// </remarks>
public Styles Styles => _styles ?? (_styles = new Styles());
public Styles Styles
{
get { return _styles ?? (Styles = new Styles()); }
set
{
Contract.Requires<ArgumentNullException>(value != null);
if (_styles != value)
{
if (_styles != null)
{
(_styles as ISetStyleParent)?.SetParent(null);
_styles.ResourcesChanged -= ThisResourcesChanged;
}
_styles = value;
if (value is ISetStyleParent setParent && setParent.ResourceParent == null)
{
setParent.SetParent(this);
}
_styles.ResourcesChanged += ThisResourcesChanged;
}
}
}
/// <summary>
/// Gets the control's logical parent.
@ -278,6 +309,34 @@ namespace Avalonia.Controls
set { SetValue(ContextMenuProperty, value); }
}
/// <summary>
/// Gets or sets the control's resource dictionary.
/// </summary>
public IResourceDictionary Resources
{
get => _resources ?? (Resources = new ResourceDictionary());
set
{
Contract.Requires<ArgumentNullException>(value != null);
var hadResources = false;
if (_resources != null)
{
hadResources = _resources.Count > 0;
_resources.ResourcesChanged -= ThisResourcesChanged;
}
_resources = value;
_resources.ResourcesChanged += ThisResourcesChanged;
if (hadResources || _resources.Count > 0)
{
((ILogical)this).NotifyResourcesChanged(new ResourcesChangedEventArgs());
}
}
}
/// <summary>
/// Gets or sets a user-defined object attached to the control.
/// </summary>
@ -296,9 +355,35 @@ namespace Avalonia.Controls
internal set { SetValue(TemplatedParentProperty, value); }
}
/// <summary>
/// Gets the control's logical children.
/// </summary>
protected IAvaloniaList<ILogical> LogicalChildren
{
get
{
if (_logicalChildren == null)
{
var list = new AvaloniaList<ILogical>();
list.ResetBehavior = ResetBehavior.Remove;
list.Validate = ValidateLogicalChild;
list.CollectionChanged += LogicalChildrenCollectionChanged;
_logicalChildren = list;
}
return _logicalChildren;
}
}
/// <inheritdoc/>
bool IDataTemplateHost.IsDataTemplatesInitialized => _dataTemplates != null;
/// <summary>
/// Gets the <see cref="Classes"/> collection in a form that allows adding and removing
/// pseudoclasses.
/// </summary>
protected IPseudoClasses PseudoClasses => Classes;
/// <summary>
/// Gets a value indicating whether the element is attached to a rooted logical tree.
/// </summary>
@ -314,6 +399,12 @@ namespace Avalonia.Controls
/// </summary>
IAvaloniaReadOnlyList<ILogical> ILogical.LogicalChildren => LogicalChildren;
/// <inheritdoc/>
bool IResourceProvider.HasResources => _resources?.Count > 0 || Styles.HasResources;
/// <inheritdoc/>
IResourceNode IResourceNode.ResourceParent => ((IStyleHost)this).StylingParent as IResourceNode;
/// <inheritdoc/>
IAvaloniaReadOnlyList<string> IStyleable.Classes => Classes;
@ -390,31 +481,24 @@ namespace Avalonia.Controls
this.OnDetachedFromLogicalTreeCore(e);
}
/// <summary>
/// Gets the control's logical children.
/// </summary>
protected IAvaloniaList<ILogical> LogicalChildren
/// <inheritdoc/>
void ILogical.NotifyResourcesChanged(ResourcesChangedEventArgs e)
{
get
{
if (_logicalChildren == null)
{
var list = new AvaloniaList<ILogical>();
list.ResetBehavior = ResetBehavior.Remove;
list.Validate = ValidateLogicalChild;
list.CollectionChanged += LogicalChildrenCollectionChanged;
_logicalChildren = list;
}
ResourcesChanged?.Invoke(this, new ResourcesChangedEventArgs());
return _logicalChildren;
foreach (var child in LogicalChildren)
{
child.NotifyResourcesChanged(e);
}
}
/// <summary>
/// Gets the <see cref="Classes"/> collection in a form that allows adding and removing
/// pseudoclasses.
/// </summary>
protected IPseudoClasses PseudoClasses => Classes;
/// <inheritdoc/>
bool IResourceProvider.TryGetResource(string key, out object value)
{
value = null;
return (_resources?.TryGetResource(key, out value) ?? false) ||
(_styles?.TryGetResource(key, out value) ?? false);
}
/// <summary>
/// Sets the control's logical parent.
@ -450,6 +534,7 @@ namespace Avalonia.Controls
}
_parent = (IControl)parent;
((ILogical)this).NotifyResourcesChanged(new ResourcesChangedEventArgs());
if (_parent is IStyleRoot || _parent?.IsAttachedToLogicalTree == true || this is IStyleRoot)
{
@ -841,5 +926,10 @@ namespace Avalonia.Controls
}
}
}
private void ThisResourcesChanged(object sender, ResourcesChangedEventArgs e)
{
((ILogical)this).NotifyResourcesChanged(e);
}
}
}

2
src/Avalonia.Controls/DropDown.cs

@ -120,7 +120,7 @@ namespace Avalonia.Controls
/// <inheritdoc/>
protected override void OnPointerPressed(PointerPressedEventArgs e)
{
if (!IsDropDownOpen && ((IVisual)e.Source).GetVisualRoot() != typeof(PopupRoot))
if (!IsDropDownOpen && ((IVisual)e.Source).GetVisualRoot() is PopupRoot)
{
IsDropDownOpen = true;
e.Handled = true;

1
src/Avalonia.Controls/IControl.cs

@ -20,6 +20,7 @@ namespace Avalonia.Controls
ILayoutable,
IInputElement,
INamed,
IResourceNode,
IStyleable,
IStyleHost
{

7
src/Avalonia.Controls/Platform/IWindowImpl.cs

@ -31,7 +31,7 @@ namespace Avalonia.Platform
IDisposable ShowDialog();
/// <summary>
/// Enables of disables system window decorations (title bar, buttons, etc)
/// Enables or disables system window decorations (title bar, buttons, etc)
/// </summary>
void SetSystemDecorations(bool enabled);
@ -39,5 +39,10 @@ namespace Avalonia.Platform
/// Sets the icon of this window.
/// </summary>
void SetIcon(IWindowIconImpl icon);
/// <summary>
/// Enables or disables the taskbar icon
/// </summary>
void ShowTaskbarIcon(bool value);
}
}

2
src/Avalonia.Controls/Presenters/TextPresenter.cs

@ -115,7 +115,7 @@ namespace Avalonia.Controls.Presenters
if (_highlightBrush == null)
{
_highlightBrush = (IBrush)this.FindStyleResource("HighlightBrush");
_highlightBrush = (IBrush)this.FindResource("HighlightBrush");
}
foreach (var rect in rects)

26
src/Avalonia.Controls/Primitives/Popup.cs

@ -277,7 +277,7 @@ namespace Avalonia.Controls.Primitives
{
base.OnDetachedFromLogicalTree(e);
_topLevel = null;
if (_popupRoot != null)
{
((ISetLogicalParent)_popupRoot).SetParent(null);
@ -327,34 +327,40 @@ namespace Avalonia.Controls.Primitives
/// </summary>
/// <returns>The popup's position in screen coordinates.</returns>
protected virtual Point GetPosition()
{
return GetPosition(PlacementTarget ?? this.GetVisualParent<Control>(), PlacementMode, PopupRoot,
HorizontalOffset, VerticalOffset);
}
internal static Point GetPosition(Control target, PlacementMode placement, PopupRoot popupRoot, double horizontalOffset, double verticalOffset)
{
var zero = default(Point);
var mode = PlacementMode;
var target = PlacementTarget ?? this.GetVisualParent<Control>();
var mode = placement;
if (target?.GetVisualRoot() == null)
{
mode = PlacementMode.Pointer;
}
}
switch (mode)
{
case PlacementMode.Pointer:
if(PopupRoot != null)
if (popupRoot != null)
{
// Scales the Horizontal and Vertical offset to screen co-ordinates.
var screenOffset = new Point(HorizontalOffset * (PopupRoot as ILayoutRoot).LayoutScaling, VerticalOffset * (PopupRoot as ILayoutRoot).LayoutScaling);
return (((IInputRoot)PopupRoot)?.MouseDevice?.Position ?? default(Point)) + screenOffset;
var screenOffset = new Point(horizontalOffset * (popupRoot as ILayoutRoot).LayoutScaling,
verticalOffset * (popupRoot as ILayoutRoot).LayoutScaling);
return (((IInputRoot)popupRoot)?.MouseDevice?.Position ?? default(Point)) + screenOffset;
}
return default(Point);
case PlacementMode.Bottom:
return target?.PointToScreen(new Point(0 + HorizontalOffset, target.Bounds.Height + VerticalOffset)) ?? zero;
return target?.PointToScreen(new Point(0 + horizontalOffset, target.Bounds.Height + verticalOffset)) ??
zero;
case PlacementMode.Right:
return target?.PointToScreen(new Point(target.Bounds.Width + HorizontalOffset, 0 + VerticalOffset)) ?? zero;
return target?.PointToScreen(new Point(target.Bounds.Width + horizontalOffset, 0 + verticalOffset)) ?? zero;
default:
throw new InvalidOperationException("Invalid value for Popup.PlacementMode");

24
src/Avalonia.Controls/Primitives/RangeBase.cs

@ -38,6 +38,18 @@ namespace Avalonia.Controls.Primitives
o => o.Value,
(o, v) => o.Value = v);
/// <summary>
/// Defines the <see cref="SmallChange"/> property.
/// </summary>
public static readonly StyledProperty<double> SmallChangeProperty =
AvaloniaProperty.Register<RangeBase, double>(nameof(SmallChange), 0.1);
/// <summary>
/// Defines the <see cref="LargeChange"/> property.
/// </summary>
public static readonly StyledProperty<double> LargeChangeProperty =
AvaloniaProperty.Register<RangeBase, double>(nameof(LargeChange), 1);
private double _minimum;
private double _maximum = 100.0;
private double _value;
@ -103,6 +115,18 @@ namespace Avalonia.Controls.Primitives
}
}
public double SmallChange
{
get => GetValue(SmallChangeProperty);
set => SetValue(SmallChangeProperty, value);
}
public double LargeChange
{
get => GetValue(LargeChangeProperty);
set => SetValue(LargeChangeProperty, value);
}
/// <summary>
/// Throws an exception if the double valus is NaN or Inf.
/// </summary>

103
src/Avalonia.Controls/Primitives/ScrollBar.cs

@ -5,6 +5,7 @@ using System;
using System.Reactive;
using System.Reactive.Linq;
using Avalonia.Data;
using Avalonia.Interactivity;
namespace Avalonia.Controls.Primitives
{
@ -31,13 +32,18 @@ namespace Avalonia.Controls.Primitives
public static readonly StyledProperty<Orientation> OrientationProperty =
AvaloniaProperty.Register<ScrollBar, Orientation>(nameof(Orientation));
private Button _lineUpButton;
private Button _lineDownButton;
private Button _pageUpButton;
private Button _pageDownButton;
/// <summary>
/// Initializes static members of the <see cref="ScrollBar"/> class.
/// </summary>
static ScrollBar()
{
PseudoClass(OrientationProperty, o => o == Avalonia.Controls.Orientation.Vertical, ":vertical");
PseudoClass(OrientationProperty, o => o == Avalonia.Controls.Orientation.Horizontal, ":horizontal");
PseudoClass(OrientationProperty, o => o == Orientation.Vertical, ":vertical");
PseudoClass(OrientationProperty, o => o == Orientation.Horizontal, ":horizontal");
}
/// <summary>
@ -97,12 +103,101 @@ namespace Avalonia.Controls.Primitives
return false;
case ScrollBarVisibility.Auto:
var viewportSize = ViewportSize;
return double.IsNaN(viewportSize) || viewportSize < Maximum - Minimum;
return double.IsNaN(ViewportSize) || Maximum > 0;
default:
throw new InvalidOperationException("Invalid value for ScrollBar.Visibility.");
}
}
protected override void OnTemplateApplied(TemplateAppliedEventArgs e)
{
base.OnTemplateApplied(e);
if (_lineUpButton != null)
{
_lineUpButton.Click -= LineUpClick;
}
if (_lineDownButton != null)
{
_lineDownButton.Click -= LineDownClick;
}
if (_pageUpButton != null)
{
_pageUpButton.Click -= PageUpClick;
}
if (_pageDownButton != null)
{
_pageDownButton.Click -= PageDownClick;
}
_lineUpButton = e.NameScope.Find<Button>("PART_LineUpButton");
_lineDownButton = e.NameScope.Find<Button>("PART_LineDownButton");
_pageUpButton = e.NameScope.Find<Button>("PART_PageUpButton");
_pageDownButton = e.NameScope.Find<Button>("PART_PageDownButton");
if (_lineUpButton != null)
{
_lineUpButton.Click += LineUpClick;
}
if (_lineDownButton != null)
{
_lineDownButton.Click += LineDownClick;
}
if (_pageUpButton != null)
{
_pageUpButton.Click += PageUpClick;
}
if (_pageDownButton != null)
{
_pageDownButton.Click += PageDownClick;
}
}
private void LineUpClick(object sender, RoutedEventArgs e)
{
SmallDecrement();
}
private void LineDownClick(object sender, RoutedEventArgs e)
{
SmallIncrement();
}
private void PageUpClick(object sender, RoutedEventArgs e)
{
LargeDecrement();
}
private void PageDownClick(object sender, RoutedEventArgs e)
{
LargeIncrement();
}
private void SmallDecrement()
{
Value = Math.Max(Value - SmallChange * ViewportSize, Minimum);
}
private void SmallIncrement()
{
Value = Math.Min(Value + SmallChange * ViewportSize, Maximum);
}
private void LargeDecrement()
{
Value = Math.Max(Value - LargeChange * ViewportSize, Minimum);
}
private void LargeIncrement()
{
Value = Math.Min(Value + LargeChange * ViewportSize, Maximum);
}
}
}

4
src/Avalonia.Controls/Primitives/ToggleButton.cs

@ -29,10 +29,10 @@ namespace Avalonia.Controls.Primitives
set { SetAndRaise(IsCheckedProperty, ref _isChecked, value); }
}
protected override void OnClick(RoutedEventArgs e)
protected override void OnClick()
{
Toggle();
base.OnClick(e);
base.OnClick();
}
protected virtual void Toggle()

102
src/Avalonia.Controls/Primitives/Track.cs

@ -10,7 +10,7 @@ namespace Avalonia.Controls.Primitives
public class Track : Control
{
public static readonly DirectProperty<Track, double> MinimumProperty =
RangeBase.MinimumProperty.AddOwner<Track>(o => o.Minimum, (o,v) => o.Minimum = v);
RangeBase.MinimumProperty.AddOwner<Track>(o => o.Minimum, (o, v) => o.Minimum = v);
public static readonly DirectProperty<Track, double> MaximumProperty =
RangeBase.MaximumProperty.AddOwner<Track>(o => o.Maximum, (o, v) => o.Maximum = v);
@ -25,7 +25,13 @@ namespace Avalonia.Controls.Primitives
ScrollBar.OrientationProperty.AddOwner<Track>();
public static readonly StyledProperty<Thumb> ThumbProperty =
AvaloniaProperty.Register<Track, Thumb>("Thumb");
AvaloniaProperty.Register<Track, Thumb>(nameof(Thumb));
public static readonly StyledProperty<Button> IncreaseButtonProperty =
AvaloniaProperty.Register<Track, Button>(nameof(IncreaseButton));
public static readonly StyledProperty<Button> DecreaseButtonProperty =
AvaloniaProperty.Register<Track, Button>(nameof(DecreaseButton));
private double _minimum;
private double _maximum = 100.0;
@ -34,6 +40,8 @@ namespace Avalonia.Controls.Primitives
static Track()
{
ThumbProperty.Changed.AddClassHandler<Track>(x => x.ThumbChanged);
IncreaseButtonProperty.Changed.AddClassHandler<Track>(x => x.ButtonChanged);
DecreaseButtonProperty.Changed.AddClassHandler<Track>(x => x.ButtonChanged);
AffectsArrange(MinimumProperty, MaximumProperty, ValueProperty, OrientationProperty);
}
@ -74,6 +82,18 @@ namespace Avalonia.Controls.Primitives
set { SetValue(ThumbProperty, value); }
}
public Button IncreaseButton
{
get { return GetValue(IncreaseButtonProperty); }
set { SetValue(IncreaseButtonProperty, value); }
}
public Button DecreaseButton
{
get { return GetValue(DecreaseButtonProperty); }
set { SetValue(DecreaseButtonProperty, value); }
}
protected override Size MeasureOverride(Size availableSize)
{
var thumb = Thumb;
@ -98,34 +118,54 @@ namespace Avalonia.Controls.Primitives
protected override Size ArrangeOverride(Size finalSize)
{
var thumb = Thumb;
var increaseButton = IncreaseButton;
var decreaseButton = DecreaseButton;
if (thumb != null)
var range = Maximum - Minimum;
var offset = Math.Min(Value - Minimum, range);
var viewportSize = ViewportSize;
var extent = range + viewportSize;
if (Orientation == Orientation.Horizontal)
{
var range = Maximum - Minimum;
var thumbFraction = ViewportSize / range;
var valueFraction = (Value - Minimum) / range;
var thumbWidth = double.IsNaN(viewportSize) ? thumb?.DesiredSize.Width ?? 0 : finalSize.Width * viewportSize / extent;
var remaining = finalSize.Width - thumbWidth;
var firstWidth = range <= 0 ? 0 : remaining * offset / range;
if (double.IsNaN(valueFraction) || double.IsInfinity(valueFraction))
if (decreaseButton != null)
{
valueFraction = 0;
thumbFraction = 1;
decreaseButton.Arrange(new Rect(0, 0, firstWidth, finalSize.Height));
}
else if (double.IsNaN(thumbFraction) || double.IsInfinity(thumbFraction))
if (thumb != null)
{
thumbFraction = 0;
thumb.Arrange(new Rect(firstWidth, 0, thumbWidth, finalSize.Height));
}
if (Orientation == Orientation.Horizontal)
if (increaseButton != null)
{
var width = Math.Max(finalSize.Width * thumbFraction, thumb.MinWidth);
var x = (finalSize.Width - width) * valueFraction;
thumb.Arrange(new Rect(x, 0, width, finalSize.Height));
increaseButton.Arrange(new Rect(firstWidth + thumbWidth, 0, remaining - firstWidth, finalSize.Height));
}
else
}
else
{
var thumbHeight = double.IsNaN(viewportSize) ? thumb?.DesiredSize.Height ?? 0 : finalSize.Height * viewportSize / extent;
var remaining = finalSize.Height - thumbHeight;
var firstHeight = range <= 0 ? 0 : remaining * offset / range;
if (decreaseButton != null)
{
var height = Math.Max(finalSize.Height * thumbFraction, thumb.MinHeight);
var y = (finalSize.Height - height) * valueFraction;
thumb.Arrange(new Rect(0, y, finalSize.Width, height));
decreaseButton.Arrange(new Rect(0, 0, finalSize.Width, firstHeight));
}
if (thumb != null)
{
thumb.Arrange(new Rect(0, firstHeight, finalSize.Width, thumbHeight));
}
if (increaseButton != null)
{
increaseButton.Arrange(new Rect(0, firstHeight + thumbHeight, finalSize.Width, remaining - firstHeight));
}
}
@ -140,10 +180,10 @@ namespace Avalonia.Controls.Primitives
if (oldThumb != null)
{
oldThumb.DragDelta -= ThumbDragged;
}
LogicalChildren.Clear();
VisualChildren.Clear();
LogicalChildren.Remove(oldThumb);
VisualChildren.Remove(oldThumb);
}
if (newThumb != null)
{
@ -153,6 +193,24 @@ namespace Avalonia.Controls.Primitives
}
}
private void ButtonChanged(AvaloniaPropertyChangedEventArgs e)
{
var oldButton = (Button)e.OldValue;
var newButton = (Button)e.NewValue;
if (oldButton != null)
{
LogicalChildren.Remove(oldButton);
VisualChildren.Remove(oldButton);
}
if (newButton != null)
{
LogicalChildren.Add(newButton);
VisualChildren.Add(newButton);
}
}
private void ThumbDragged(object sender, VectorEventArgs e)
{
double range = Maximum - Minimum;

82
src/Avalonia.Controls/RepeatButton.cs

@ -0,0 +1,82 @@
using System;
using Avalonia.Input;
using Avalonia.Threading;
namespace Avalonia.Controls
{
public class RepeatButton : Button
{
/// <summary>
/// Defines the <see cref="Delay"/> property.
/// </summary>
public static readonly StyledProperty<int> DelayProperty =
AvaloniaProperty.Register<Button, int>(nameof(Delay), 100);
private DispatcherTimer _repeatTimer;
/// <summary>
/// Gets or sets the amount of time, in milliseconds, of repeating clicks.
/// </summary>
public int Delay
{
get { return GetValue(DelayProperty); }
set { SetValue(DelayProperty, value); }
}
private void StartTimer()
{
if (_repeatTimer == null)
{
_repeatTimer = new DispatcherTimer();
_repeatTimer.Tick += (o, e) => OnClick();
}
if (_repeatTimer.IsEnabled) return;
_repeatTimer.Interval = TimeSpan.FromMilliseconds(Delay);
_repeatTimer.Start();
}
private void StopTimer()
{
_repeatTimer?.Stop();
}
protected override void OnKeyDown(KeyEventArgs e)
{
base.OnKeyDown(e);
if (e.Key == Key.Space)
{
StartTimer();
}
}
protected override void OnKeyUp(KeyEventArgs e)
{
base.OnKeyUp(e);
StopTimer();
}
protected override void OnPointerPressed(PointerPressedEventArgs e)
{
base.OnPointerPressed(e);
if (e.MouseButton == MouseButton.Left)
{
StartTimer();
}
}
protected override void OnPointerReleased(PointerReleasedEventArgs e)
{
base.OnPointerReleased(e);
if (e.MouseButton == MouseButton.Left)
{
StopTimer();
}
}
}
}

4
src/Avalonia.Controls/ScrollViewer.cs

@ -275,7 +275,7 @@ namespace Avalonia.Controls
/// </summary>
protected double HorizontalScrollBarViewportSize
{
get { return Max((_viewport.Width / _extent.Width) * (_extent.Width - _viewport.Width), 0); }
get { return _viewport.Width; }
}
/// <summary>
@ -308,7 +308,7 @@ namespace Avalonia.Controls
/// </summary>
protected double VerticalScrollBarViewportSize
{
get { return Max((_viewport.Height / _extent.Height) * (_extent.Height - _viewport.Height), 0); }
get { return _viewport.Height; }
}
/// <summary>

55
src/Avalonia.Controls/Slider.cs

@ -33,6 +33,8 @@ namespace Avalonia.Controls
// Slider required parts
private Track _track;
private Button _decreaseButton;
private Button _increaseButton;
/// <summary>
/// Initializes static members of the <see cref="Slider"/> class.
@ -44,6 +46,8 @@ namespace Avalonia.Controls
Thumb.DragStartedEvent.AddClassHandler<Slider>(x => x.OnThumbDragStarted, RoutingStrategies.Bubble);
Thumb.DragDeltaEvent.AddClassHandler<Slider>(x => x.OnThumbDragDelta, RoutingStrategies.Bubble);
Thumb.DragCompletedEvent.AddClassHandler<Slider>(x => x.OnThumbDragCompleted, RoutingStrategies.Bubble);
SmallChangeProperty.OverrideDefaultValue<Slider>(1);
LargeChangeProperty.OverrideDefaultValue<Slider>(10);
}
/// <summary>
@ -83,7 +87,54 @@ namespace Avalonia.Controls
/// <inheritdoc/>
protected override void OnTemplateApplied(TemplateAppliedEventArgs e)
{
_track = e.NameScope.Get<Track>("PART_Track");
if (_decreaseButton != null)
{
_decreaseButton.Click -= DecreaseClick;
}
if (_increaseButton != null)
{
_increaseButton.Click -= IncreaseClick;
}
_decreaseButton = e.NameScope.Find<Button>("PART_DecreaseButton");
_track = e.NameScope.Find<Track>("PART_Track");
_increaseButton = e.NameScope.Find<Button>("PART_IncreaseButton");
if (_decreaseButton != null)
{
_decreaseButton.Click += DecreaseClick;
}
if (_increaseButton != null)
{
_increaseButton.Click += IncreaseClick;
}
}
private void DecreaseClick(object sender, RoutedEventArgs e)
{
ChangeValueBy(-LargeChange);
}
private void IncreaseClick(object sender, RoutedEventArgs e)
{
ChangeValueBy(LargeChange);
}
private void ChangeValueBy(double by)
{
if (IsSnapToTickEnabled)
{
by = by < 0 ? Math.Min(-TickFrequency, by) : Math.Max(TickFrequency, by);
}
var value = Value;
var next = SnapToTick(Math.Max(Math.Min(value + by, Maximum), Minimum));
if (next != value)
{
Value = next;
}
}
/// <summary>
@ -101,7 +152,7 @@ namespace Avalonia.Controls
protected virtual void OnThumbDragDelta(VectorEventArgs e)
{
Thumb thumb = e.Source as Thumb;
if (thumb != null && _track.Thumb == thumb)
if (thumb != null && _track?.Thumb == thumb)
{
MoveToNextTick(_track.Value);
}

2
src/Avalonia.Controls/SystemDialog.cs

@ -20,7 +20,7 @@ namespace Avalonia.Controls
{
public string DefaultExtension { get; set; }
public async Task<string> ShowAsync(Window window = null)
public async Task<string> ShowAsync(Window window)
=>
((await AvaloniaLocator.Current.GetService<ISystemDialogImpl>().ShowFileDialogAsync(this, window?.PlatformImpl)) ??
new string[0]).FirstOrDefault();

236
src/Avalonia.Controls/ToolTip.cs

@ -3,11 +3,7 @@
using System;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using Avalonia.Controls.Primitives;
using Avalonia.Input;
using Avalonia.Threading;
using Avalonia.VisualTree;
namespace Avalonia.Controls
{
@ -29,29 +25,50 @@ namespace Avalonia.Controls
AvaloniaProperty.RegisterAttached<ToolTip, Control, object>("Tip");
/// <summary>
/// The popup window used to display the active tooltip.
/// Defines the ToolTip.IsOpen attached property.
/// </summary>
private static PopupRoot s_popup;
public static readonly AttachedProperty<bool> IsOpenProperty =
AvaloniaProperty.RegisterAttached<ToolTip, Control, bool>("IsOpen");
/// <summary>
/// The control that the currently visible tooltip is attached to.
/// Defines the ToolTip.Placement property.
/// </summary>
private static Control s_current;
public static readonly AttachedProperty<PlacementMode> PlacementProperty =
AvaloniaProperty.RegisterAttached<Popup, Control, PlacementMode>("Placement", defaultValue: PlacementMode.Pointer);
/// <summary>
/// Observable fired when a tooltip should be displayed for a control. The output from this
/// observable is throttled and calls <see cref="ShowToolTip(Control)"/> when the time
/// period expires.
/// Defines the ToolTip.HorizontalOffset property.
/// </summary>
private static readonly Subject<Control> s_show = new Subject<Control>();
public static readonly AttachedProperty<double> HorizontalOffsetProperty =
AvaloniaProperty.RegisterAttached<Popup, Control, double>("HorizontalOffset");
/// <summary>
/// Defines the ToolTip.VerticalOffset property.
/// </summary>
public static readonly AttachedProperty<double> VerticalOffsetProperty =
AvaloniaProperty.RegisterAttached<Popup, Control, double>("VerticalOffset", 20);
/// <summary>
/// Defines the ToolTip.ShowDelay property.
/// </summary>
public static readonly AttachedProperty<int> ShowDelayProperty =
AvaloniaProperty.RegisterAttached<Popup, Control, int>("ShowDelay", 400);
/// <summary>
/// Stores the curernt <see cref="ToolTip"/> instance in the control.
/// </summary>
private static readonly AttachedProperty<ToolTip> ToolTipProperty =
AvaloniaProperty.RegisterAttached<ToolTip, Control, ToolTip>("ToolTip");
private PopupRoot _popup;
/// <summary>
/// Initializes static members of the <see cref="ToolTip"/> class.
/// </summary>
static ToolTip()
{
TipProperty.Changed.Subscribe(TipChanged);
s_show.Throttle(TimeSpan.FromSeconds(0.5), AvaloniaScheduler.Instance).Subscribe(ShowToolTip);
TipProperty.Changed.Subscribe(ToolTipService.Instance.TipChanged);
IsOpenProperty.Changed.Subscribe(IsOpenChanged);
}
/// <summary>
@ -77,101 +94,160 @@ namespace Avalonia.Controls
}
/// <summary>
/// called when the <see cref="TipProperty"/> property changes on a control.
/// Gets the value of the ToolTip.IsOpen attached property.
/// </summary>
/// <param name="e">The event args.</param>
private static void TipChanged(AvaloniaPropertyChangedEventArgs e)
/// <param name="element">The control to get the property from.</param>
/// <returns>
/// A value indicating whether the tool tip is visible.
/// </returns>
public static bool GetIsOpen(Control element)
{
var control = (Control)e.Sender;
return element.GetValue(IsOpenProperty);
}
if (e.OldValue != null)
{
control.PointerEnter -= ControlPointerEnter;
control.PointerLeave -= ControlPointerLeave;
}
/// <summary>
/// Sets the value of the ToolTip.IsOpen attached property.
/// </summary>
/// <param name="element">The control to get the property from.</param>
/// <param name="value">A value indicating whether the tool tip is visible.</param>
public static void SetIsOpen(Control element, bool value)
{
element.SetValue(IsOpenProperty, value);
}
if (e.NewValue != null)
{
control.PointerEnter += ControlPointerEnter;
control.PointerLeave += ControlPointerLeave;
}
/// <summary>
/// Gets the value of the ToolTip.Placement attached property.
/// </summary>
/// <param name="element">The control to get the property from.</param>
/// <returns>
/// A value indicating how the tool tip is positioned.
/// </returns>
public static PlacementMode GetPlacement(Control element)
{
return element.GetValue(PlacementProperty);
}
/// <summary>
/// Shows a tooltip for the specified control.
/// Sets the value of the ToolTip.Placement attached property.
/// </summary>
/// <param name="control">The control.</param>
private static void ShowToolTip(Control control)
/// <param name="element">The control to get the property from.</param>
/// <param name="value">A value indicating how the tool tip is positioned.</param>
public static void SetPlacement(Control element, PlacementMode value)
{
if (control != null && control.IsVisible && control.GetVisualRoot() != null)
{
var cp = (control.GetVisualRoot() as IInputRoot)?.MouseDevice?.GetPosition(control);
element.SetValue(PlacementProperty, value);
}
if (cp.HasValue && control.IsVisible && new Rect(control.Bounds.Size).Contains(cp.Value))
{
var position = control.PointToScreen(cp.Value) + new Vector(0, 22);
if (s_popup == null)
{
s_popup = new PopupRoot();
s_popup.Content = new ToolTip();
}
else
{
((ISetLogicalParent)s_popup).SetParent(null);
}
((ISetLogicalParent)s_popup).SetParent(control);
((ToolTip)s_popup.Content).Content = GetTip(control);
s_popup.Position = position;
s_popup.Show();
s_current = control;
}
}
/// <summary>
/// Gets the value of the ToolTip.HorizontalOffset attached property.
/// </summary>
/// <param name="element">The control to get the property from.</param>
/// <returns>
/// A value indicating how the tool tip is positioned.
/// </returns>
public static double GetHorizontalOffset(Control element)
{
return element.GetValue(HorizontalOffsetProperty);
}
/// <summary>
/// Sets the value of the ToolTip.HorizontalOffset attached property.
/// </summary>
/// <param name="element">The control to get the property from.</param>
/// <param name="value">A value indicating how the tool tip is positioned.</param>
public static void SetHorizontalOffset(Control element, double value)
{
element.SetValue(HorizontalOffsetProperty, value);
}
/// <summary>
/// Gets the value of the ToolTip.VerticalOffset attached property.
/// </summary>
/// <param name="element">The control to get the property from.</param>
/// <returns>
/// A value indicating how the tool tip is positioned.
/// </returns>
public static double GetVerticalOffset(Control element)
{
return element.GetValue(VerticalOffsetProperty);
}
/// <summary>
/// Sets the value of the ToolTip.VerticalOffset attached property.
/// </summary>
/// <param name="element">The control to get the property from.</param>
/// <param name="value">A value indicating how the tool tip is positioned.</param>
public static void SetVerticalOffset(Control element, double value)
{
element.SetValue(VerticalOffsetProperty, value);
}
/// <summary>
/// Called when the pointer enters a control with an attached tooltip.
/// Gets the value of the ToolTip.ShowDelay attached property.
/// </summary>
/// <param name="sender">The event sender.</param>
/// <param name="e">The event args.</param>
private static void ControlPointerEnter(object sender, PointerEventArgs e)
/// <param name="element">The control to get the property from.</param>
/// <returns>
/// A value indicating the time, in milliseconds, before a tool tip opens.
/// </returns>
public static int GetShowDelay(Control element)
{
s_current = (Control)sender;
s_show.OnNext(s_current);
return element.GetValue(ShowDelayProperty);
}
/// <summary>
/// Called when the pointer leaves a control with an attached tooltip.
/// Sets the value of the ToolTip.ShowDelay attached property.
/// </summary>
/// <param name="sender">The event sender.</param>
/// <param name="e">The event args.</param>
private static void ControlPointerLeave(object sender, PointerEventArgs e)
/// <param name="element">The control to get the property from.</param>
/// <param name="value">A value indicating the time, in milliseconds, before a tool tip opens.</param>
public static void SetShowDelay(Control element, int value)
{
var control = (Control)sender;
element.SetValue(ShowDelayProperty, value);
}
private static void IsOpenChanged(AvaloniaPropertyChangedEventArgs e)
{
var control = (Control)e.Sender;
if (control == s_current)
if ((bool)e.NewValue)
{
if (s_popup != null)
var tip = GetTip(control);
if (tip == null) return;
var toolTip = control.GetValue(ToolTipProperty);
if (toolTip == null || (tip != toolTip && tip != toolTip.Content))
{
DisposeTooltip();
s_show.OnNext(null);
toolTip?.Close();
toolTip = tip as ToolTip ?? new ToolTip { Content = tip };
control.SetValue(ToolTipProperty, toolTip);
}
toolTip.Open(control);
}
else
{
var toolTip = control.GetValue(ToolTipProperty);
toolTip?.Close();
}
}
private static void DisposeTooltip()
private void Open(Control control)
{
if (s_popup != null)
{
// Clear the ToolTip's Content in case it has control content: this will
// reset its visual parent allowing it to be used again.
((ToolTip)s_popup.Content).Content = null;
Close();
_popup = new PopupRoot { Content = this };
((ISetLogicalParent)_popup).SetParent(control);
_popup.Position = Popup.GetPosition(control, GetPlacement(control), _popup,
GetHorizontalOffset(control), GetVerticalOffset(control));
_popup.Show();
}
// Dispose of the popup.
s_popup.Dispose();
s_popup = null;
private void Close()
{
if (_popup != null)
{
_popup.Content = null;
_popup.Hide();
_popup = null;
}
}
}

98
src/Avalonia.Controls/ToolTipService.cs

@ -0,0 +1,98 @@
using System;
using Avalonia.Input;
using Avalonia.Threading;
namespace Avalonia.Controls
{
/// <summary>
/// Handeles <see cref="ToolTip"/> interaction with controls.
/// </summary>
internal sealed class ToolTipService
{
public static ToolTipService Instance { get; } = new ToolTipService();
private DispatcherTimer _timer;
private ToolTipService() { }
/// <summary>
/// called when the <see cref="ToolTip.TipProperty"/> property changes on a control.
/// </summary>
/// <param name="e">The event args.</param>
internal void TipChanged(AvaloniaPropertyChangedEventArgs e)
{
var control = (Control)e.Sender;
if (e.OldValue != null)
{
control.PointerEnter -= ControlPointerEnter;
control.PointerLeave -= ControlPointerLeave;
}
if (e.NewValue != null)
{
control.PointerEnter += ControlPointerEnter;
control.PointerLeave += ControlPointerLeave;
}
}
/// <summary>
/// Called when the pointer enters a control with an attached tooltip.
/// </summary>
/// <param name="sender">The event sender.</param>
/// <param name="e">The event args.</param>
private void ControlPointerEnter(object sender, PointerEventArgs e)
{
StopTimer();
var control = (Control)sender;
var showDelay = ToolTip.GetShowDelay(control);
if (showDelay == 0)
{
Open(control);
}
else
{
StartShowTimer(showDelay, control);
}
}
/// <summary>
/// Called when the pointer leaves a control with an attached tooltip.
/// </summary>
/// <param name="sender">The event sender.</param>
/// <param name="e">The event args.</param>
private void ControlPointerLeave(object sender, PointerEventArgs e)
{
var control = (Control)sender;
Close(control);
}
private void StartShowTimer(int showDelay, Control control)
{
_timer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(showDelay) };
_timer.Tick += (o, e) => Open(control);
_timer.Start();
}
private void Open(Control control)
{
StopTimer();
ToolTip.SetIsOpen(control, true);
}
private void Close(Control control)
{
StopTimer();
ToolTip.SetIsOpen(control, false);
}
private void StopTimer()
{
_timer?.Stop();
_timer = null;
}
}
}

24
src/Avalonia.Controls/TopLevel.cs

@ -10,9 +10,11 @@ using Avalonia.Input;
using Avalonia.Input.Raw;
using Avalonia.Layout;
using Avalonia.Logging;
using Avalonia.LogicalTree;
using Avalonia.Platform;
using Avalonia.Rendering;
using Avalonia.Styling;
using Avalonia.Utilities;
using Avalonia.VisualTree;
using JetBrains.Annotations;
@ -26,7 +28,13 @@ namespace Avalonia.Controls
/// It handles scheduling layout, styling and rendering as well as
/// tracking the widget's <see cref="ClientSize"/>.
/// </remarks>
public abstract class TopLevel : ContentControl, IInputRoot, ILayoutRoot, IRenderRoot, ICloseable, IStyleRoot
public abstract class TopLevel : ContentControl,
IInputRoot,
ILayoutRoot,
IRenderRoot,
ICloseable,
IStyleRoot,
IWeakSubscriber<ResourcesChangedEventArgs>
{
/// <summary>
/// Defines the <see cref="ClientSize"/> property.
@ -100,7 +108,6 @@ namespace Avalonia.Controls
impl.Resized = HandleResized;
impl.ScalingChanged = HandleScalingChanged;
_keyboardNavigationHandler?.SetOwner(this);
_accessKeyHandler?.SetOwner(this);
styler?.ApplyStyles(this);
@ -116,6 +123,14 @@ namespace Avalonia.Controls
{
_applicationLifecycle.OnExit += OnApplicationExiting;
}
if (((IStyleHost)this).StylingParent is IResourceProvider applicationResources)
{
WeakSubscriptionManager.Subscribe(
applicationResources,
nameof(IResourceProvider.ResourcesChanged),
this);
}
}
/// <summary>
@ -165,6 +180,11 @@ namespace Avalonia.Controls
/// <inheritdoc/>
IMouseDevice IInputRoot.MouseDevice => PlatformImpl?.MouseDevice;
void IWeakSubscriber<ResourcesChangedEventArgs>.OnEvent(object sender, ResourcesChangedEventArgs e)
{
((ILogical)this).NotifyResourcesChanged(e);
}
/// <summary>
/// Gets or sets a value indicating whether access keys are shown in the window.
/// </summary>

24
src/Avalonia.Controls/Window.cs

@ -61,11 +61,17 @@ namespace Avalonia.Controls
AvaloniaProperty.Register<Window, SizeToContent>(nameof(SizeToContent));
/// <summary>
/// Enables of disables system window decorations (title bar, buttons, etc)
/// Enables or disables system window decorations (title bar, buttons, etc)
/// </summary>
public static readonly StyledProperty<bool> HasSystemDecorationsProperty =
AvaloniaProperty.Register<Window, bool>(nameof(HasSystemDecorations), true);
/// <summary>
/// Enables or disables the taskbar icon
/// </summary>
public static readonly StyledProperty<bool> ShowInTaskbarProperty =
AvaloniaProperty.Register<Window, bool>(nameof(ShowInTaskbar), true);
/// <summary>
/// Defines the <see cref="Title"/> property.
/// </summary>
@ -92,6 +98,8 @@ namespace Avalonia.Controls
HasSystemDecorationsProperty.Changed.AddClassHandler<Window>(
(s, e) => s.PlatformImpl?.SetSystemDecorations((bool) e.NewValue));
ShowInTaskbarProperty.Changed.AddClassHandler<Window>((w, e) => w.PlatformImpl?.ShowTaskbarIcon((bool)e.NewValue));
IconProperty.Changed.AddClassHandler<Window>((s, e) => s.PlatformImpl?.SetIcon(((WindowIcon)e.NewValue).PlatformImpl));
}
@ -152,7 +160,7 @@ namespace Avalonia.Controls
}
/// <summary>
/// Enables of disables system window decorations (title bar, buttons, etc)
/// Enables or disables system window decorations (title bar, buttons, etc)
/// </summary>
///
public bool HasSystemDecorations
@ -160,6 +168,16 @@ namespace Avalonia.Controls
get { return GetValue(HasSystemDecorationsProperty); }
set { SetValue(HasSystemDecorationsProperty, value); }
}
/// <summary>
/// Enables or disables the taskbar icon
/// </summary>
///
public bool ShowInTaskbar
{
get { return GetValue(ShowInTaskbarProperty); }
set { SetValue(ShowInTaskbarProperty, value); }
}
/// <summary>
/// Gets or sets the minimized/maximized state of the window.

2
src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.csproj

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard1.3</TargetFramework>
<TargetFramework>netstandard2.0</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">

2
src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard1.3</TargetFramework>
<TargetFramework>netstandard2.0</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">

6
src/Avalonia.DotNetCoreRuntime/AppBuilder.cs

@ -37,12 +37,15 @@ namespace Avalonia
/// <returns>An <see cref="AppBuilder"/> instance.</returns>
public AppBuilder UsePlatformDetect()
{
var os = RuntimePlatform.GetRuntimeInfo().OperatingSystem;
//We don't have the ability to load every assembly right now, so we are
//stuck with manual configuration here
//Helpers are extracted to separate methods to take the advantage of the fact
//that CLR doesn't try to load dependencies before referencing method is jitted
if (RuntimePlatform.GetRuntimeInfo().OperatingSystem == OperatingSystemType.WinNT)
if (os == OperatingSystemType.WinNT)
LoadWin32();
else if(os==OperatingSystemType.OSX)
LoadMonoMac();
else
LoadGtk3();
this.UseSkia();
@ -50,6 +53,7 @@ namespace Avalonia
return this;
}
void LoadMonoMac() => this.UseMonoMac();
void LoadWin32() => this.UseWin32();
void LoadGtk3() => this.UseGtk3();
}

3
src/Avalonia.DotNetCoreRuntime/Avalonia.DotNetCoreRuntime.csproj

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp1.0</TargetFramework>
<TargetFramework>netcoreapp2.0</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
</PropertyGroup>
<PropertyGroup>
@ -16,6 +16,7 @@
<ProjectReference Include="..\Avalonia.Controls\Avalonia.Controls.csproj" />
<ProjectReference Include="..\Avalonia.Visuals\Avalonia.Visuals.csproj" />
<ProjectReference Include="..\Gtk\Avalonia.Gtk3\Avalonia.Gtk3.csproj" />
<ProjectReference Include="..\OSX\Avalonia.MonoMac\Avalonia.MonoMac.csproj" />
<ProjectReference Include="..\Skia\Avalonia.Skia\Avalonia.Skia.csproj" />
<ProjectReference Include="..\Windows\Avalonia.Win32.NetStandard\Avalonia.Win32.NetStandard.csproj" />
</ItemGroup>

13
src/Avalonia.DotNetFrameworkRuntime/AppBuilder.cs

@ -59,7 +59,11 @@ namespace Avalonia
from attribute in assembly.GetCustomAttributes<ExportWindowingSubsystemAttribute>()
where attribute.RequiredOS == os && CheckEnvironment(attribute.EnvironmentChecker)
orderby attribute.Priority ascending
select attribute).First();
select attribute).FirstOrDefault();
if (windowingSubsystemAttribute == null)
{
throw new InvalidOperationException("No windowing subsystem found. Are you missing assembly references?");
}
var renderingSubsystemAttribute = (from assembly in RuntimePlatform.GetLoadedAssemblies()
from attribute in assembly.GetCustomAttributes<ExportRenderingSubsystemAttribute>()
@ -67,7 +71,12 @@ namespace Avalonia
where attribute.RequiresWindowingSubsystem == null
|| attribute.RequiresWindowingSubsystem == windowingSubsystemAttribute.Name
orderby attribute.Priority ascending
select attribute).First();
select attribute).FirstOrDefault();
if (renderingSubsystemAttribute == null)
{
throw new InvalidOperationException("No rendering subsystem found. Are you missing assembly references?");
}
UseWindowingSubsystem(() => windowingSubsystemAttribute.InitializationType
.GetRuntimeMethod(windowingSubsystemAttribute.InitializationMethod, Type.EmptyTypes).Invoke(null, null),

94
src/Avalonia.DotNetFrameworkRuntime/Avalonia.DotNetFrameworkRuntime.csproj

@ -1,75 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{4A1ABB09-9047-4BD5-A4AD-A055E52C5EE0}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Avalonia.DotNetFrameworkRuntime</RootNamespace>
<AssemblyName>Avalonia.DotNetFrameworkRuntime</AssemblyName>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>TRACE;DEBUG;FULLDOTNET</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<DocumentationFile>bin\Debug\Avalonia.DotNetFrameworkRuntime.xml</DocumentationFile>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE;FULLDOTNET</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<DocumentationFile>bin\Release\Avalonia.DotNetFrameworkRuntime.xml</DocumentationFile>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\Shared\SharedAssemblyInfo.cs">
<Link>Properties\SharedAssemblyInfo.cs</Link>
</Compile>
<Compile Include="AppBuilder.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="RuntimeInfo.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Avalonia.Base\Avalonia.Base.csproj">
<Project>{B09B78D8-9B26-48B0-9149-D64A2F120F3F}</Project>
<Name>Avalonia.Base</Name>
</ProjectReference>
<ProjectReference Include="..\Avalonia.Controls\Avalonia.Controls.csproj">
<Project>{D2221C82-4A25-4583-9B43-D791E3F6820C}</Project>
<Name>Avalonia.Controls</Name>
</ProjectReference>
<ProjectReference Include="..\Avalonia.Visuals\Avalonia.Visuals.csproj">
<Project>{eb582467-6abb-43a1-b052-e981ba910e3a}</Project>
<Name>Avalonia.Visuals</Name>
</ProjectReference>
<ProjectReference Include="..\Avalonia.Styling\Avalonia.Styling.csproj">
<Project>{f1baa01a-f176-4c6a-b39d-5b40bb1b148f}</Project>
<Name>Avalonia.Styling</Name>
</ProjectReference>
</ItemGroup>
<Import Project="..\Shared\PlatformSupport\PlatformSupport.projitems" Label="Shared" />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="..\..\build\Rx.props" />
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net461</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<DocumentationFile>bin\$(Configuration)\Avalonia.DotNetFrameworkRuntime.xml</DocumentationFile>
<DefineConstants>$(DefineConstants);FULLDOTNET</DefineConstants>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<ItemGroup>
<Compile Include="..\Shared\SharedAssemblyInfo.cs">
<Link>Properties\SharedAssemblyInfo.cs</Link>
</Compile>
<ProjectReference Include="..\Avalonia.Base\Avalonia.Base.csproj" />
<ProjectReference Include="..\Avalonia.Controls\Avalonia.Controls.csproj" />
<ProjectReference Include="..\Avalonia.Visuals\Avalonia.Visuals.csproj" />
<ProjectReference Include="..\Avalonia.Styling\Avalonia.Styling.csproj" />
</ItemGroup>
<Import Project="..\Shared\PlatformSupport\PlatformSupport.projitems" Label="Shared" />
<Import Project="..\..\build\Rx.props" />
</Project>

15
src/Avalonia.DotNetFrameworkRuntime/Properties/AssemblyInfo.cs

@ -1,15 +0,0 @@
using System.Reflection;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Avalonia.DotNetFrameworkRuntime")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("4a1abb09-9047-4bd5-a4ad-a055e52c5ee0")]

3
src/Avalonia.HtmlRenderer/Avalonia.HtmlRenderer.csproj

@ -1,9 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard1.3</TargetFramework>
<TargetFramework>netstandard2.0</TargetFramework>
<EnableDefaultCompileItems>False</EnableDefaultCompileItems>
<EnableDefaultItems>False</EnableDefaultItems>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<NoWarn>CS0436</NoWarn>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<DebugSymbols>true</DebugSymbols>

12
src/Avalonia.HtmlRenderer/Compat/Api.cs

@ -4,18 +4,6 @@ using System.Text;
namespace System.Net
{
internal class AsyncCompletedEventArgs
{
public object UserState { get; set; }
public Exception Error { get; set; }
public bool Cancelled { get; set; }
public AsyncCompletedEventArgs(Exception error, bool cancelled, object userState)
{
}
}
class WebException : Exception
{
public object Response { get; set; }

2
src/Avalonia.Input/Avalonia.Input.csproj

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard1.3</TargetFramework>
<TargetFramework>netstandard2.0</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">

2
src/Avalonia.Interactivity/Avalonia.Interactivity.csproj

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard1.3</TargetFramework>
<TargetFramework>netstandard2.0</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">

2
src/Avalonia.Layout/Avalonia.Layout.csproj

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard1.3</TargetFramework>
<TargetFramework>netstandard2.0</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">

2
src/Avalonia.Logging.Serilog/Avalonia.Logging.Serilog.csproj

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard1.3</TargetFramework>
<TargetFramework>netstandard2.0</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">

24
src/Avalonia.ReactiveUI/AppBuilderExtensions.cs

@ -0,0 +1,24 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using Avalonia.Controls;
using Avalonia.Threading;
using ReactiveUI;
using System;
using System.Reactive.Concurrency;
using System.Threading;
namespace Avalonia
{
public static class AppBuilderExtensions
{
public static TAppBuilder UseReactiveUI<TAppBuilder>(this TAppBuilder builder)
where TAppBuilder : AppBuilderBase<TAppBuilder>, new()
{
return builder.AfterSetup(_ =>
{
RxApp.MainThreadScheduler = AvaloniaScheduler.Instance;
});
}
}
}

76
src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj

@ -1,83 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard1.3</TargetFramework>
<EnableDefaultCompileItems>False</EnableDefaultCompileItems>
<TargetFramework>netstandard2.0</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>TRACE;DEBUG;MONO PORTABLE;NETSTANDARD1_1</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE;MONO PORTABLE;RELEASE;NETSTANDARD1_1</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<None Remove="Shims.cs" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\Shared\SharedAssemblyInfo.cs" Link="Properties\SharedAssemblyInfo.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<Compile Include="Registrations.cs" />
<Compile Include="Shims.cs" />
<Compile Include="src\ReactiveUI\ExpressionMixins.cs" />
<Compile Include="src\ReactiveUI\ExpressionRewriter.cs" />
<Compile Include="src\ReactiveUI\Activation.cs" />
<Compile Include="src\ReactiveUI\ReactiveBinding.cs" />
<Compile Include="src\ReactiveUI\AutoPersistHelper.cs" />
<Compile Include="src\ReactiveUI\BindingTypeConverters.cs" />
<Compile Include="src\ReactiveUI\CollectionDebugView.cs" />
<Compile Include="src\ReactiveUI\CommandBinding.cs" />
<Compile Include="src\ReactiveUI\CompatMixins.cs" />
<Compile Include="src\ReactiveUI\ContractStubs.cs" />
<Compile Include="src\ReactiveUI\CreatesCommandBinding.cs" />
<Compile Include="src\ReactiveUI\DefaultPropertyBinding.cs" />
<Compile Include="src\ReactiveUI\Errors.cs" />
<Compile Include="src\ReactiveUI\IDependencyResolver.cs" />
<Compile Include="src\ReactiveUI\INPCObservableForProperty.cs" />
<Compile Include="src\ReactiveUI\Interfaces.cs" />
<Compile Include="src\ReactiveUI\IROObservableForProperty.cs" />
<Compile Include="src\ReactiveUI\MessageBus.cs" />
<Compile Include="src\ReactiveUI\MobileLifecycle.cs" />
<Compile Include="src\ReactiveUI\NullDefaultPropertyBindingProvider.cs" />
<Compile Include="src\ReactiveUI\ObservableAsPropertyHelper.cs" />
<Compile Include="src\ReactiveUI\ObservedChangedMixin.cs" />
<Compile Include="src\ReactiveUI\OrderedComparer.cs" />
<Compile Include="src\ReactiveUI\POCOObservableForProperty.cs" />
<Compile Include="src\ReactiveUI\PropertyBinding.cs" />
<Compile Include="src\ReactiveUI\ReactiveList.cs" />
<Compile Include="src\ReactiveUI\ReactiveCollectionMixins.cs" />
<Compile Include="src\ReactiveUI\ReactiveCommand.cs" />
<Compile Include="src\ReactiveUI\ReactiveNotifyPropertyChangedMixin.cs" />
<Compile Include="src\ReactiveUI\ReactiveObject.cs" />
<Compile Include="src\ReactiveUI\RefcountDisposeWrapper.cs" />
<Compile Include="src\ReactiveUI\Reflection.cs" />
<Compile Include="src\ReactiveUI\RegisterableInterfaces.cs" />
<Compile Include="src\ReactiveUI\Registrations.cs" />
<Compile Include="src\ReactiveUI\RoutableViewModelMixin.cs" />
<Compile Include="src\ReactiveUI\RoutingState.cs" />
<Compile Include="src\ReactiveUI\RxApp.cs" />
<Compile Include="src\ReactiveUI\ViewLocator.cs" />
<Compile Include="src\ReactiveUI\ScheduledSubject.cs" />
<Compile Include="src\ReactiveUI\VariadicTemplates.cs" />
<Compile Include="src\ReactiveUI\WaitForDispatcherScheduler.cs" />
<Compile Include="src\ReactiveUI\LoggingMixins.cs" />
<Compile Include="src\ReactiveUI\IReactiveObject.cs" />
<Compile Include="src\ReactiveUI\WeakEventManager.cs" />
<Compile Include="src\ReactiveUI\Legacy\ReactiveCommand.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Avalonia.Animation\Avalonia.Animation.csproj" />
<ProjectReference Include="..\Avalonia.Base\Avalonia.Base.csproj" />
<ProjectReference Include="..\Avalonia.Controls\Avalonia.Controls.csproj" />
@ -88,5 +15,6 @@
<ProjectReference Include="..\Avalonia.Visuals\Avalonia.Visuals.csproj" />
</ItemGroup>
<Import Project="..\..\build\Rx.props" />
<Import Project="..\..\build\ReactiveUI.props" />
<Import Project="..\..\build\Splat.props" />
</Project>

3
src/Avalonia.ReactiveUI/Properties/AssemblyInfo.cs

@ -3,7 +3,4 @@
using System.Reflection;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Avalonia.ReactiveUI")]

23
src/Avalonia.ReactiveUI/Registrations.cs

@ -1,23 +0,0 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using System.Reactive.Concurrency;
using System.Threading;
namespace ReactiveUI
{
/// <summary>
/// Ignore me. This class is a secret handshake between RxUI and RxUI.Xaml
/// in order to register certain classes on startup that would be difficult
/// to register otherwise.
/// </summary>
public class PlatformRegistrations : IWantsToRegisterStuff
{
public void Register(Action<Func<object>, Type> registerFunction)
{
RxApp.MainThreadScheduler = new SynchronizationContextScheduler(SynchronizationContext.Current);
}
}
}

34
src/Avalonia.ReactiveUI/Shims.cs

@ -1,34 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace System.Runtime.Serialization
{
class IgnoreDataMemberAttribute : Attribute
{
}
class DataMemberAttribute : Attribute
{
}
class OnDeserializedAttribute : Attribute
{
}
class DataContractAttribute : Attribute
{
}
class StreamingContext { }
}
namespace System.Diagnostics.Contracts
{
static class Contract
{
public static void Requires(bool condition)
{
}
}
}

1
src/Avalonia.ReactiveUI/src

@ -1 +0,0 @@
Subproject commit 3f725c808b1d4c8457f0d3204e0a071aa462cd75

3
src/Avalonia.Styling/Avalonia.Styling.csproj

@ -1,7 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard1.3</TargetFramework>
<TargetFramework>netstandard2.0</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<RootNamespace>Avalonia</RootNamespace>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<DebugSymbols>true</DebugSymbols>

19
src/Avalonia.Styling/Controls/IResourceDictionary.cs

@ -0,0 +1,19 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using System.Collections.Generic;
namespace Avalonia.Controls
{
/// <summary>
/// An indexed dictionary of resources.
/// </summary>
public interface IResourceDictionary : IResourceProvider, IDictionary<object, object>
{
/// <summary>
/// Gets a collection of child resource dictionaries.
/// </summary>
IList<IResourceProvider> MergedDictionaries { get; }
}
}

15
src/Avalonia.Styling/Controls/IResourceNode.cs

@ -0,0 +1,15 @@
using System;
namespace Avalonia.Controls
{
/// <summary>
/// Represents resource provider in a tree.
/// </summary>
public interface IResourceNode : IResourceProvider
{
/// <summary>
/// Gets the parent resource node, if any.
/// </summary>
IResourceNode ResourceParent { get; }
}
}

33
src/Avalonia.Styling/Controls/IResourceProvider.cs

@ -0,0 +1,33 @@
using System;
namespace Avalonia.Controls
{
/// <summary>
/// Represents an object that can be queried for resources.
/// </summary>
public interface IResourceProvider
{
/// <summary>
/// Raised when resources in the provider are changed.
/// </summary>
event EventHandler<ResourcesChangedEventArgs> ResourcesChanged;
/// <summary>
/// Gets a value indicating whether the element has resources.
/// </summary>
bool HasResources { get; }
/// <summary>
/// Tries to find a resource within the provider.
/// </summary>
/// <param name="key">The resource key.</param>
/// <param name="value">
/// When this method returns, contains the value associated with the specified key,
/// if the key is found; otherwise, null.
/// </param>
/// <returns>
/// True if the resource if found, otherwise false.
/// </returns>
bool TryGetResource(string key, out object value);
}
}

101
src/Avalonia.Styling/Controls/ResourceDictionary.cs

@ -0,0 +1,101 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using Avalonia.Collections;
namespace Avalonia.Controls
{
/// <summary>
/// An indexed dictionary of resources.
/// </summary>
public class ResourceDictionary : AvaloniaDictionary<object, object>, IResourceDictionary
{
private AvaloniaList<IResourceProvider> _mergedDictionaries;
/// <summary>
/// Initializes a new instance of the <see cref="ResourceDictionary"/> class.
/// </summary>
public ResourceDictionary()
{
CollectionChanged += OnCollectionChanged;
}
/// <inheritdoc/>
public event EventHandler<ResourcesChangedEventArgs> ResourcesChanged;
/// <inheritdoc/>
public IList<IResourceProvider> MergedDictionaries
{
get
{
if (_mergedDictionaries == null)
{
_mergedDictionaries = new AvaloniaList<IResourceProvider>();
_mergedDictionaries.ResetBehavior = ResetBehavior.Remove;
_mergedDictionaries.ForEachItem(
x =>
{
if (x.HasResources)
{
OnResourcesChanged();
}
x.ResourcesChanged += MergedDictionaryResourcesChanged;
},
x =>
{
if (x.HasResources)
{
OnResourcesChanged();
}
x.ResourcesChanged -= MergedDictionaryResourcesChanged;
},
() => { });
}
return _mergedDictionaries;
}
}
/// <inheritdoc/>
bool IResourceProvider.HasResources
{
get => Count > 0 || (_mergedDictionaries?.Any(x => x.HasResources) ?? false);
}
/// <inheritdoc/>
public bool TryGetResource(string key, out object value)
{
if (TryGetValue(key, out value))
{
return true;
}
if (_mergedDictionaries != null)
{
for (var i = _mergedDictionaries.Count - 1; i >= 0; --i)
{
if (_mergedDictionaries[i].TryGetResource(key, out value))
{
return true;
}
}
}
return false;
}
private void OnResourcesChanged()
{
ResourcesChanged?.Invoke(this, new ResourcesChangedEventArgs());
}
private void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) => OnResourcesChanged();
private void MergedDictionaryResourcesChanged(object sender, ResourcesChangedEventArgs e) => OnResourcesChanged();
}
}

65
src/Avalonia.Styling/Controls/ResourceProviderExtensions.cs

@ -0,0 +1,65 @@
using System;
using System.Reactive;
using System.Reactive.Linq;
namespace Avalonia.Controls
{
public static class ResourceProviderExtensions
{
/// <summary>
/// Finds the specified resource by searching up the logical tree and then global styles.
/// </summary>
/// <param name="control">The control.</param>
/// <param name="key">The resource key.</param>
/// <returns>The resource, or <see cref="AvaloniaProperty.UnsetValue"/> if not found.</returns>
public static object FindResource(this IResourceNode control, string key)
{
if (control.TryFindResource(key, out var value))
{
return value;
}
return AvaloniaProperty.UnsetValue;
}
/// <summary>
/// Tries to the specified resource by searching up the logical tree and then global styles.
/// </summary>
/// <param name="control">The control.</param>
/// <param name="key">The resource key.</param>
/// <param name="value">On return, contains the resource if found, otherwise null.</param>
/// <returns>True if the resource was found; otherwise false.</returns>
public static bool TryFindResource(this IResourceNode control, string key, out object value)
{
Contract.Requires<ArgumentNullException>(control != null);
Contract.Requires<ArgumentNullException>(key != null);
var current = control;
while (current != null)
{
if (current is IResourceNode host)
{
if (host.TryGetResource(key, out value))
{
return true;
}
}
current = current.ResourceParent;
}
value = null;
return false;
}
public static IObservable<object> GetResourceObservable(this IResourceNode target, string key)
{
return Observable.FromEventPattern<ResourcesChangedEventArgs>(
x => target.ResourcesChanged += x,
x => target.ResourcesChanged -= x)
.StartWith((EventPattern<ResourcesChangedEventArgs>)null)
.Select(x => target.FindResource(key));
}
}
}

11
src/Avalonia.Styling/Controls/ResourcesChangedEventArgs.cs

@ -0,0 +1,11 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
namespace Avalonia.Controls
{
public class ResourcesChangedEventArgs : EventArgs
{
}
}

11
src/Avalonia.Styling/LogicalTree/ILogical.cs

@ -3,6 +3,7 @@
using System;
using Avalonia.Collections;
using Avalonia.Controls;
namespace Avalonia.LogicalTree
{
@ -55,5 +56,15 @@ namespace Avalonia.LogicalTree
/// this method yourself.
/// </remarks>
void NotifyDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e);
/// <summary>
/// Notifies the control that a change has been made to resources that apply to it.
/// </summary>
/// <param name="e">The event args.</param>
/// <remarks>
/// This method will be called automatically by the framework, you should not need to call
/// this method yourself.
/// </remarks>
void NotifyResourcesChanged(ResourcesChangedEventArgs e);
}
}

2
src/Avalonia.Styling/Properties/AssemblyInfo.cs

@ -6,5 +6,7 @@ using System.Runtime.CompilerServices;
using Avalonia.Metadata;
[assembly: AssemblyTitle("Avalonia.Styling")]
[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Controls")]
[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.LogicalTree")]
[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Styling")]
[assembly: InternalsVisibleTo("Avalonia.Styling.UnitTests")]

32
src/Avalonia.Styling/Styling/ISetStyleParent.cs

@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.Text;
using Avalonia.Controls;
namespace Avalonia.Styling
{
/// <summary>
/// Defines an interface through which a <see cref="Style"/>'s parent can be set.
/// </summary>
/// <remarks>
/// You should not usually need to use this interface - it is for internal use only.
/// </remarks>
public interface ISetStyleParent : IStyle
{
/// <summary>
/// Sets the style parent.
/// </summary>
/// <param name="parent">The parent.</param>
void SetParent(IResourceNode parent);
/// <summary>
/// Notifies the style that a change has been made to resources that apply to it.
/// </summary>
/// <param name="e">The event args.</param>
/// <remarks>
/// This method will be called automatically by the framework, you should not need to call
/// this method yourself.
/// </remarks>
void NotifyResourcesChanged(ResourcesChangedEventArgs e);
}
}

13
src/Avalonia.Styling/Styling/IStyle.cs

@ -1,12 +1,14 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using Avalonia.Controls;
namespace Avalonia.Styling
{
/// <summary>
/// Defines the interface for styles.
/// </summary>
public interface IStyle
public interface IStyle : IResourceNode
{
/// <summary>
/// Attaches the style to a control if the style's selector matches.
@ -16,14 +18,5 @@ namespace Avalonia.Styling
/// The control that contains this style. May be null.
/// </param>
void Attach(IStyleable control, IStyleHost container);
/// <summary>
/// Tries to find a named resource within the style.
/// </summary>
/// <param name="name">The resource name.</param>
/// <returns>
/// The resource if found, otherwise <see cref="AvaloniaProperty.UnsetValue"/>.
/// </returns>
object FindResource(string name);
}
}

92
src/Avalonia.Styling/Styling/Style.cs

@ -3,7 +3,9 @@
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Reactive.Linq;
using Avalonia.Controls;
using Avalonia.Metadata;
namespace Avalonia.Styling
@ -11,12 +13,12 @@ namespace Avalonia.Styling
/// <summary>
/// Defines a style.
/// </summary>
public class Style : IStyle
public class Style : IStyle, ISetStyleParent
{
private static Dictionary<IStyleable, List<IDisposable>> _applied =
new Dictionary<IStyleable, List<IDisposable>>();
private StyleResources _resources;
private IResourceNode _parent;
private IResourceDictionary _resources;
/// <summary>
/// Initializes a new instance of the <see cref="Style"/> class.
@ -34,33 +36,33 @@ namespace Avalonia.Styling
Selector = selector(null);
}
/// <inheritdoc/>
public event EventHandler<ResourcesChangedEventArgs> ResourcesChanged;
/// <summary>
/// Gets or sets a dictionary of style resources.
/// </summary>
public StyleResources Resources
public IResourceDictionary Resources
{
get
get => _resources ?? (Resources = new ResourceDictionary());
set
{
if (_resources == null)
Contract.Requires<ArgumentNullException>(value != null);
var hadResources = false;
if (_resources != null)
{
_resources = new StyleResources();
hadResources = _resources.Count > 0;
_resources.ResourcesChanged -= ResourceDictionaryChanged;
}
return _resources;
}
_resources = value;
_resources.ResourcesChanged += ResourceDictionaryChanged;
set
{
var resources = Resources;
if (!Equals(resources, value))
if (hadResources || _resources.Count > 0)
{
foreach (var i in value)
{
resources[i.Key] = i.Value;
//resources.Add(i.Key, i.Value);
//(resources as IDictionary<string,object>).Add(i);
}
((ISetStyleParent)this).NotifyResourcesChanged(new ResourcesChangedEventArgs());
}
}
}
@ -76,6 +78,12 @@ namespace Avalonia.Styling
[Content]
public IList<ISetter> Setters { get; set; } = new List<ISetter>();
/// <inheritdoc/>
IResourceNode IResourceNode.ResourceParent => _parent;
/// <inheritdoc/>
bool IResourceProvider.HasResources => _resources?.Count > 0;
/// <summary>
/// Attaches the style to a control if the style's selector matches.
/// </summary>
@ -112,25 +120,11 @@ namespace Avalonia.Styling
}
}
/// <summary>
/// Tries to find a named resource within the style.
/// </summary>
/// <param name="name">The resource name.</param>
/// <returns>
/// The resource if found, otherwise <see cref="AvaloniaProperty.UnsetValue"/>.
/// </returns>
public object FindResource(string name)
/// <inheritdoc/>
public bool TryGetResource(string key, out object result)
{
object result = null;
if (_resources?.TryGetValue(name, out result) == true)
{
return result;
}
else
{
return AvaloniaProperty.UnsetValue;
}
result = null;
return _resources?.TryGetResource(key, out result) ?? false;
}
/// <summary>
@ -149,6 +143,23 @@ namespace Avalonia.Styling
}
}
/// <inheritdoc/>
void ISetStyleParent.NotifyResourcesChanged(ResourcesChangedEventArgs e)
{
ResourcesChanged?.Invoke(this, e);
}
/// <inheritdoc/>
void ISetStyleParent.SetParent(IResourceNode parent)
{
if (_parent != null && parent != null)
{
throw new InvalidOperationException("The Style already has a parent.");
}
_parent = parent;
}
private static List<IDisposable> GetSubscriptions(IStyleable control)
{
List<IDisposable> subscriptions;
@ -179,5 +190,10 @@ namespace Avalonia.Styling
_applied.Remove(control);
}
private void ResourceDictionaryChanged(object sender, ResourcesChangedEventArgs e)
{
ResourcesChanged?.Invoke(this, e);
}
}
}

42
src/Avalonia.Styling/Styling/StyleExtensions.cs

@ -1,42 +0,0 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
namespace Avalonia.Styling
{
public static class StyleExtensions
{
/// <summary>
/// Tries to find a named style resource.
/// </summary>
/// <param name="control">The control from which to find the resource.</param>
/// <param name="name">The resource name.</param>
/// <returns>
/// The resource if found, otherwise <see cref="AvaloniaProperty.UnsetValue"/>.
/// </returns>
public static object FindStyleResource(this IStyleHost control, string name)
{
Contract.Requires<ArgumentNullException>(control != null);
Contract.Requires<ArgumentNullException>(name != null);
Contract.Requires<ArgumentException>(!string.IsNullOrWhiteSpace(name));
while (control != null)
{
if (control.IsStylesInitialized)
{
var result = control.Styles.FindResource(name);
if (result != AvaloniaProperty.UnsetValue)
{
return result;
}
}
control = control.StylingParent;
}
return AvaloniaProperty.UnsetValue;
}
}
}

90
src/Avalonia.Styling/Styling/StyleResources.cs

@ -1,90 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
namespace Avalonia.Styling
{
/// <summary>
/// Holds resources for a <see cref="Style"/>.
/// </summary>
public class StyleResources : IDictionary<string, object>, IDictionary
{
private Dictionary<string, object> _inner = new Dictionary<string, object>();
public object this[string key]
{
get { return _inner[key]; }
set { _inner[key] = value; }
}
public int Count => _inner.Count;
ICollection<string> IDictionary<string, object>.Keys => _inner.Keys;
ICollection<object> IDictionary<string, object>.Values => _inner.Values;
bool ICollection<KeyValuePair<string, object>>.IsReadOnly => false;
object IDictionary.this[object key]
{
get { return ((IDictionary)_inner)[key]; }
set { ((IDictionary)_inner)[key] = value; }
}
ICollection IDictionary.Keys => _inner.Keys;
ICollection IDictionary.Values => _inner.Values;
bool ICollection.IsSynchronized => false;
object ICollection.SyncRoot => ((IDictionary)_inner).SyncRoot;
bool IDictionary.IsFixedSize => false;
bool IDictionary.IsReadOnly => false;
public void Add(string key, object value) => _inner.Add(key, value);
public void Clear() => _inner.Clear();
public bool ContainsKey(string key) => _inner.ContainsKey(key);
public bool Remove(string key) => _inner.Remove(key);
public IEnumerator<KeyValuePair<string, object>> GetEnumerator() => _inner.GetEnumerator();
public bool TryGetValue(string key, out object value) => _inner.TryGetValue(key, out value);
bool ICollection<KeyValuePair<string, object>>.Contains(KeyValuePair<string, object> item)
{
return ((IDictionary<string, object>)_inner).Contains(item);
}
void ICollection<KeyValuePair<string, object>>.Add(KeyValuePair<string, object> item)
{
((IDictionary<string, object>)_inner).Add(item);
}
void ICollection<KeyValuePair<string, object>>.CopyTo(KeyValuePair<string, object>[] array, int arrayIndex)
{
((IDictionary<string, object>)_inner).CopyTo(array, arrayIndex);
}
bool ICollection<KeyValuePair<string, object>>.Remove(KeyValuePair<string, object> item)
{
return ((IDictionary<string, object>)_inner).Remove(item);
}
void ICollection.CopyTo(Array array, int index) => ((IDictionary)_inner).CopyTo(array, index);
IEnumerator IEnumerable.GetEnumerator() => _inner.GetEnumerator();
IDictionaryEnumerator IDictionary.GetEnumerator() => ((IDictionary)_inner).GetEnumerator();
void IDictionary.Add(object key, object value) => ((IDictionary)_inner).Add(key, value);
bool IDictionary.Contains(object key) => ((IDictionary)_inner).Contains(key);
void IDictionary.Remove(object key) => ((IDictionary)_inner).Remove(key);
}
}

149
src/Avalonia.Styling/Styling/Styles.cs

@ -1,16 +1,95 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using System.Linq;
using Avalonia.Collections;
using Avalonia.Controls;
namespace Avalonia.Styling
{
/// <summary>
/// A style that consists of a number of child styles.
/// </summary>
public class Styles : AvaloniaList<IStyle>, IStyle
public class Styles : AvaloniaList<IStyle>, IStyle, ISetStyleParent
{
private IResourceNode _parent;
private IResourceDictionary _resources;
public Styles()
{
ResetBehavior = ResetBehavior.Remove;
this.ForEachItem(
x =>
{
if (x.ResourceParent == null && x is ISetStyleParent setParent)
{
setParent.SetParent(this);
setParent.NotifyResourcesChanged(new ResourcesChangedEventArgs());
}
if (x.HasResources)
{
ResourcesChanged?.Invoke(this, new ResourcesChangedEventArgs());
}
x.ResourcesChanged += SubResourceChanged;
},
x =>
{
if (x.ResourceParent == this && x is ISetStyleParent setParent)
{
setParent.SetParent(null);
setParent.NotifyResourcesChanged(new ResourcesChangedEventArgs());
}
if (x.HasResources)
{
ResourcesChanged?.Invoke(this, new ResourcesChangedEventArgs());
}
x.ResourcesChanged -= SubResourceChanged;
},
() => { });
}
/// <inheritdoc/>
public event EventHandler<ResourcesChangedEventArgs> ResourcesChanged;
/// <inheritdoc/>
public bool HasResources => _resources?.Count > 0 || this.Any(x => x.HasResources);
/// <summary>
/// Gets or sets a dictionary of style resources.
/// </summary>
public IResourceDictionary Resources
{
get => _resources ?? (Resources = new ResourceDictionary());
set
{
Contract.Requires<ArgumentNullException>(value != null);
var hadResources = false;
if (_resources != null)
{
hadResources = _resources.Count > 0;
_resources.ResourcesChanged -= ResourceDictionaryChanged;
}
_resources = value;
_resources.ResourcesChanged += ResourceDictionaryChanged;
if (hadResources || _resources.Count > 0)
{
((ISetStyleParent)this).NotifyResourcesChanged(new ResourcesChangedEventArgs());
}
}
}
/// <inheritdoc/>
IResourceNode IResourceNode.ResourceParent => _parent;
/// <summary>
/// Attaches the style to a control if the style's selector matches.
/// </summary>
@ -26,26 +105,68 @@ namespace Avalonia.Styling
}
}
/// <summary>
/// Tries to find a named resource within the style.
/// </summary>
/// <param name="name">The resource name.</param>
/// <returns>
/// The resource if found, otherwise <see cref="AvaloniaProperty.UnsetValue"/>.
/// </returns>
public object FindResource(string name)
/// <inheritdoc/>
public bool TryGetResource(string key, out object value)
{
if (_resources != null && _resources.TryGetValue(key, out value))
{
return true;
}
for (var i = Count - 1; i >= 0; --i)
{
if (this[i].TryGetResource(key, out value))
{
return true;
}
}
value = null;
return false;
}
/// <inheritdoc/>
void ISetStyleParent.SetParent(IResourceNode parent)
{
if (_parent != null && parent != null)
{
throw new InvalidOperationException("The Style already has a parent.");
}
_parent = parent;
}
/// <inheritdoc/>
void ISetStyleParent.NotifyResourcesChanged(ResourcesChangedEventArgs e)
{
ResourcesChanged?.Invoke(this, e);
}
private void ResourceDictionaryChanged(object sender, ResourcesChangedEventArgs e)
{
foreach (var style in this.Reverse())
foreach (var child in this)
{
var result = style.FindResource(name);
(child as ISetStyleParent)?.NotifyResourcesChanged(e);
}
ResourcesChanged?.Invoke(this, e);
}
if (result != AvaloniaProperty.UnsetValue)
private void SubResourceChanged(object sender, ResourcesChangedEventArgs e)
{
var foundSource = false;
foreach (var child in this)
{
if (foundSource)
{
return result;
(child as ISetStyleParent)?.NotifyResourcesChanged(e);
}
foundSource |= child == sender;
}
return AvaloniaProperty.UnsetValue;
ResourcesChanged?.Invoke(this, e);
}
}
}

2
src/Avalonia.Themes.Default/Avalonia.Themes.Default.csproj

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard1.3</TargetFramework>
<TargetFramework>netstandard2.0</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">

1
src/Avalonia.Themes.Default/DefaultTheme.xaml

@ -20,6 +20,7 @@
<StyleInclude Source="resm:Avalonia.Themes.Default.PopupRoot.xaml?assembly=Avalonia.Themes.Default"/>
<StyleInclude Source="resm:Avalonia.Themes.Default.ProgressBar.xaml?assembly=Avalonia.Themes.Default"/>
<StyleInclude Source="resm:Avalonia.Themes.Default.RadioButton.xaml?assembly=Avalonia.Themes.Default"/>
<StyleInclude Source="resm:Avalonia.Themes.Default.RepeatButton.xaml?assembly=Avalonia.Themes.Default" />
<StyleInclude Source="resm:Avalonia.Themes.Default.Separator.xaml?assembly=Avalonia.Themes.Default"/>
<StyleInclude Source="resm:Avalonia.Themes.Default.Slider.xaml?assembly=Avalonia.Themes.Default"/>
<StyleInclude Source="resm:Avalonia.Themes.Default.ScrollBar.xaml?assembly=Avalonia.Themes.Default"/>

44
src/Avalonia.Themes.Default/RepeatButton.xaml

@ -0,0 +1,44 @@
<Styles xmlns="https://github.com/avaloniaui">
<Style Selector="RepeatButton">
<Setter Property="Background"
Value="{StyleResource ThemeControlMidBrush}" />
<Setter Property="BorderBrush"
Value="{StyleResource ThemeBorderLightBrush}" />
<Setter Property="BorderThickness"
Value="{StyleResource ThemeBorderThickness}" />
<Setter Property="Foreground"
Value="{StyleResource ThemeForegroundBrush}" />
<Setter Property="HorizontalContentAlignment"
Value="Center" />
<Setter Property="VerticalContentAlignment"
Value="Center" />
<Setter Property="Padding"
Value="4" />
<Setter Property="Template">
<ControlTemplate>
<ContentPresenter Name="PART_ContentPresenter"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
Padding="{TemplateBinding Padding}"
TextBlock.Foreground="{TemplateBinding Foreground}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}" />
</ControlTemplate>
</Setter>
</Style>
<Style Selector="RepeatButton:pointerover /template/ ContentPresenter">
<Setter Property="BorderBrush"
Value="{StyleResource ThemeBorderMidBrush}" />
</Style>
<Style Selector="RepeatButton:pressed /template/ ContentPresenter">
<Setter Property="Background"
Value="{StyleResource ThemeControlDarkBrush}" />
</Style>
<Style Selector="RepeatButton:disabled">
<Setter Property="Opacity"
Value="{StyleResource ThemeDisabledOpacity}" />
</Style>
</Styles>

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save