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 *.userprefs
*.nugetreferenceswitcher *.nugetreferenceswitcher
#################
## Rider
#################
.idea
################# #################
## Cake ## 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"] [submodule "src/Avalonia.HtmlRenderer/external"]
path = src/Avalonia.HtmlRenderer/external path = src/Avalonia.HtmlRenderer/external
url = https://github.com/AvaloniaUI/HTML-Renderer.git url = https://github.com/AvaloniaUI/HTML-Renderer.git

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

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

3
.travis.yml

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

53
Avalonia.sln

@ -1,6 +1,6 @@
Microsoft Visual Studio Solution File, Format Version 12.00 Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15 # Visual Studio 15
VisualStudioVersion = 15.0.26228.4 VisualStudioVersion = 15.0.26730.3
MinimumVisualStudioVersion = 10.0.40219.1 MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Base", "src\Avalonia.Base\Avalonia.Base.csproj", "{B09B78D8-9B26-48B0-9149-D64A2F120F3F}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Base", "src\Avalonia.Base\Avalonia.Base.csproj", "{B09B78D8-9B26-48B0-9149-D64A2F120F3F}"
EndProject EndProject
@ -129,7 +129,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WindowsInteropTest", "sampl
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GtkInteropDemo", "samples\interop\GtkInteropDemo\GtkInteropDemo.csproj", "{BD7F352C-6DC1-4740-BAF2-2D34A038728C}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GtkInteropDemo", "samples\interop\GtkInteropDemo\GtkInteropDemo.csproj", "{BD7F352C-6DC1-4740-BAF2-2D34A038728C}"
EndProject 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 EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RenderTest", "samples\RenderTest\RenderTest.csproj", "{F1FDC5B0-4654-416F-AE69-E3E9BBD87801}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RenderTest", "samples\RenderTest\RenderTest.csproj", "{F1FDC5B0-4654-416F-AE69-E3E9BBD87801}"
EndProject 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\Microsoft.Reactive.Testing.props = build\Microsoft.Reactive.Testing.props
build\Moq.props = build\Moq.props build\Moq.props = build\Moq.props
build\NetCore.props = build\NetCore.props build\NetCore.props = build\NetCore.props
build\ReactiveUI.props = build\ReactiveUI.props
build\Rx.props = build\Rx.props build\Rx.props = build\Rx.props
build\Serilog.props = build\Serilog.props build\Serilog.props = build\Serilog.props
build\Serilog.Sinks.Trace.props = build\Serilog.Sinks.Trace.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 EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Skia.RenderTests", "tests\Avalonia.RenderTests\Avalonia.Skia.RenderTests.csproj", "{E1582370-37B3-403C-917F-8209551B1634}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Skia.RenderTests", "tests\Avalonia.RenderTests\Avalonia.Skia.RenderTests.csproj", "{E1582370-37B3-403C-917F-8209551B1634}"
EndProject 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 Global
GlobalSection(SharedMSBuildProjectFiles) = preSolution GlobalSection(SharedMSBuildProjectFiles) = preSolution
src\Shared\RenderHelpers\RenderHelpers.projitems*{3c4c0cb4-0c0f-4450-a37b-148c84ff905f}*SharedItemsImports = 13 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|Mono.Build.0 = Release|Any CPU
{E1582370-37B3-403C-917F-8209551B1634}.Release|x86.ActiveCfg = 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 {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 EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
@ -2571,5 +2616,9 @@ Global
{638580B0-7910-40EF-B674-DCB34DA308CD} = {A0CC0258-D18C-4AB3-854F-7101680FC3F9} {638580B0-7910-40EF-B674-DCB34DA308CD} = {A0CC0258-D18C-4AB3-854F-7101680FC3F9}
{CBC4FF2F-92D4-420B-BE21-9FE0B930B04E} = {B39A8919-9F95-48FE-AD7B-76E08B509888} {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E} = {B39A8919-9F95-48FE-AD7B-76E08B509888}
{E1582370-37B3-403C-917F-8209551B1634} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} {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 EndGlobalSection
EndGlobal 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")} - 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: 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 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 "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% - cmd: set PATH=%programfiles(x86)%\GtkSharp\2.12\bin\;%PATH%
before_build: before_build:
- git submodule update --init - 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"); project = System.IO.Path.Combine(project, System.IO.Path.GetFileName(project)+".csproj");
Information("Running tests from " + project); Information("Running tests from " + project);
DotNetCoreRestore(project); DotNetCoreRestore(project);
var frameworks = new List<string>(){"netcoreapp1.1"}; var frameworks = new List<string>(){"netcoreapp2.0"};
if(parameters.IsRunningOnWindows) if(parameters.IsRunningOnWindows)
frameworks.Add("net461"); frameworks.Add("net461");
foreach(var fw in frameworks) 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"> <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup> <ItemGroup>
<PackageReference Include="Moq" Version="4.7.25" /> <PackageReference Include="Moq" Version="4.7.99" />
</ItemGroup> </ItemGroup>
</Project> </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"> <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup> <ItemGroup>
<PackageReference Include="System.Reactive" Version="3.0.0" /> <PackageReference Include="System.Reactive" Version="3.1.1" />
<PackageReference Include="System.Reactive.Core" Version="3.0.0" /> <PackageReference Include="System.Reactive.Core" Version="3.1.1" />
<PackageReference Include="System.Reactive.Interfaces" Version="3.0.0" /> <PackageReference Include="System.Reactive.Interfaces" Version="3.1.1" />
<PackageReference Include="System.Reactive.Linq" Version="3.0.0" /> <PackageReference Include="System.Reactive.Linq" Version="3.1.1" />
<PackageReference Include="System.Reactive.PlatformServices" Version="3.0.0" /> <PackageReference Include="System.Reactive.PlatformServices" Version="3.1.1" />
<PackageReference Condition="'$(TargetFramework)' == 'net45'" Include="System.Reactive.Windows.Threading" Version="3.0.0" /> <PackageReference Condition="$(TargetFramework.StartsWith('net4'))" Include="System.Reactive.Windows.Threading" Version="3.1.1" />
<PackageReference Condition="'$(TargetFramework)' == 'net461'" Include="System.Reactive.Windows.Threading" Version="3.0.0" />
</ItemGroup> </ItemGroup>
</Project> </Project>

19
build/UnitTests.NetCore.targets

@ -3,25 +3,6 @@
<GenerateAssemblyInfo>false</GenerateAssemblyInfo> <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles> <GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>
</PropertyGroup> </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> <ItemGroup>
<PackageReference Include="System.Threading.Thread" Version="4.3.0" /> <PackageReference Include="System.Threading.Thread" Version="4.3.0" />
</ItemGroup> </ItemGroup>

2
build/XUnit.props

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

13
docs/guidelines/build.md

@ -80,10 +80,11 @@ mono ./samples/ControlCatalog.Desktop/bin/Debug/ControlCatalog.Desktop.exe
### Building Avalonia in MonoDevelop ### Building Avalonia in MonoDevelop
Unless you have a very current version of monodevelop (6.1.x or newer), it is necessary to manually 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
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 ### Building and running Avalonia in Rider
it will complain that a newer version of NuGet is needed.
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) public Packages(ICakeContext context, Parameters parameters)
{ {
// NUGET NUSPECS // NUGET NUSPECS
@ -111,6 +110,7 @@ public class Packages
var SplatVersion = packageVersions["Splat"].FirstOrDefault().Item1; var SplatVersion = packageVersions["Splat"].FirstOrDefault().Item1;
var SpracheVersion = packageVersions["Sprache"].FirstOrDefault().Item1; var SpracheVersion = packageVersions["Sprache"].FirstOrDefault().Item1;
var SystemReactiveVersion = packageVersions["System.Reactive"].FirstOrDefault().Item1; var SystemReactiveVersion = packageVersions["System.Reactive"].FirstOrDefault().Item1;
var ReactiveUIVersion = packageVersions["reactiveui"].FirstOrDefault().Item1;
var SystemValueTupleVersion = packageVersions["System.ValueTuple"].FirstOrDefault().Item1; var SystemValueTupleVersion = packageVersions["System.ValueTuple"].FirstOrDefault().Item1;
SkiaSharpVersion = packageVersions["SkiaSharp"].FirstOrDefault().Item1; SkiaSharpVersion = packageVersions["SkiaSharp"].FirstOrDefault().Item1;
SkiaSharpLinuxVersion = packageVersions["Avalonia.Skia.Linux.Natives"].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: Splat, version: {0}", SplatVersion);
context.Information("Package: Sprache, version: {0}", SpracheVersion); context.Information("Package: Sprache, version: {0}", SpracheVersion);
context.Information("Package: System.Reactive, version: {0}", SystemReactiveVersion); context.Information("Package: System.Reactive, version: {0}", SystemReactiveVersion);
context.Information("Package: reactiveui, version: {0}", ReactiveUIVersion);
context.Information("Package: System.ValueTuple, version: {0}", SystemValueTupleVersion); context.Information("Package: System.ValueTuple, version: {0}", SystemValueTupleVersion);
context.Information("Package: SkiaSharp, version: {0}", SkiaSharpVersion); context.Information("Package: SkiaSharp, version: {0}", SkiaSharpVersion);
context.Information("Package: Avalonia.Skia.Linux.Natives, version: {0}", SkiaSharpLinuxVersion); context.Information("Package: Avalonia.Skia.Linux.Natives, version: {0}", SkiaSharpLinuxVersion);
@ -176,7 +177,6 @@ public class Packages
new [] { "./src/", "Avalonia.Visuals", ".xml" }, new [] { "./src/", "Avalonia.Visuals", ".xml" },
new [] { "./src/", "Avalonia.Styling", ".dll" }, new [] { "./src/", "Avalonia.Styling", ".dll" },
new [] { "./src/", "Avalonia.Styling", ".xml" }, new [] { "./src/", "Avalonia.Styling", ".xml" },
new [] { "./src/", "Avalonia.ReactiveUI", ".dll" },
new [] { "./src/", "Avalonia.Themes.Default", ".dll" }, new [] { "./src/", "Avalonia.Themes.Default", ".dll" },
new [] { "./src/", "Avalonia.Themes.Default", ".xml" }, new [] { "./src/", "Avalonia.Themes.Default", ".xml" },
new [] { "./src/Markup/", "Avalonia.Markup", ".dll" }, new [] { "./src/Markup/", "Avalonia.Markup", ".dll" },
@ -186,12 +186,12 @@ public class Packages
}; };
var coreLibrariesFiles = coreLibraries.Select((lib) => { 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(); }).ToList();
var coreLibrariesNuSpecContent = coreLibrariesFiles.Select((file) => { var coreLibrariesNuSpecContent = coreLibrariesFiles.Select((file) => {
return new NuSpecContent { 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) => { var netcoreappCoreLibrariesNuSpecContent = coreLibrariesFiles.Select((file) => {
return new NuSpecContent { return new NuSpecContent {
Source = file.FullPath, Target = "lib/netcoreapp1.0" Source = file.FullPath, Target = "lib/netcoreapp2.0"
}; };
}); });
var net45RuntimePlatformExtensions = new [] {".xml", ".dll"}; var net45RuntimePlatformExtensions = new [] {".xml", ".dll"};
var net45RuntimePlatform = net45RuntimePlatformExtensions.Select(libSuffix => { var net45RuntimePlatform = net45RuntimePlatformExtensions.Select(libSuffix => {
return new NuSpecContent { 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" Target = "lib/net45"
}; };
}); });
@ -218,8 +218,8 @@ public class Packages
var netCoreRuntimePlatformExtensions = new [] {".xml", ".dll"}; var netCoreRuntimePlatformExtensions = new [] {".xml", ".dll"};
var netCoreRuntimePlatform = netCoreRuntimePlatformExtensions.Select(libSuffix => { var netCoreRuntimePlatform = netCoreRuntimePlatformExtensions.Select(libSuffix => {
return new NuSpecContent { return new NuSpecContent {
Source = ((FilePath)context.File("./src/Avalonia.DotNetCoreRuntime/bin/" + parameters.DirSuffix + "/netcoreapp1.0/Avalonia.DotNetCoreRuntime" + libSuffix)).FullPath, Source = ((FilePath)context.File("./src/Avalonia.DotNetCoreRuntime/bin/" + parameters.DirSuffix + "/netcoreapp2.0/Avalonia.DotNetCoreRuntime" + libSuffix)).FullPath,
Target = "lib/netcoreapp1.0" Target = "lib/netcoreapp2.0"
}; };
}); });
@ -238,15 +238,15 @@ public class Packages
new NuSpecDependency() { Id = "Sprache", Version = SpracheVersion }, new NuSpecDependency() { Id = "Sprache", Version = SpracheVersion },
new NuSpecDependency() { Id = "System.Reactive", Version = SystemReactiveVersion }, new NuSpecDependency() { Id = "System.Reactive", Version = SystemReactiveVersion },
//.NET Core //.NET Core
new NuSpecDependency() { Id = "System.Threading.ThreadPool", TargetFramework = "netcoreapp1.0", Version = "4.3.0" }, new NuSpecDependency() { Id = "System.Threading.ThreadPool", TargetFramework = "netcoreapp2.0", Version = "4.3.0" },
new NuSpecDependency() { Id = "Microsoft.Extensions.DependencyModel", TargetFramework = "netcoreapp1.0", Version = "1.1.0" }, new NuSpecDependency() { Id = "Microsoft.Extensions.DependencyModel", TargetFramework = "netcoreapp2.0", Version = "1.1.0" },
new NuSpecDependency() { Id = "NETStandard.Library", TargetFramework = "netcoreapp1.0", Version = "1.6.0" }, new NuSpecDependency() { Id = "NETStandard.Library", TargetFramework = "netcoreapp2.0", Version = "1.6.0" },
new NuSpecDependency() { Id = "Splat", TargetFramework = "netcoreapp1.0", Version = SplatVersion }, new NuSpecDependency() { Id = "Splat", TargetFramework = "netcoreapp2.0", Version = SplatVersion },
new NuSpecDependency() { Id = "Serilog", TargetFramework = "netcoreapp1.0", Version = SerilogVersion }, new NuSpecDependency() { Id = "Serilog", TargetFramework = "netcoreapp2.0", Version = SerilogVersion },
new NuSpecDependency() { Id = "Sprache", TargetFramework = "netcoreapp1.0", Version = SpracheVersion }, new NuSpecDependency() { Id = "Sprache", TargetFramework = "netcoreapp2.0", Version = SpracheVersion },
new NuSpecDependency() { Id = "System.Reactive", TargetFramework = "netcoreapp1.0", Version = SystemReactiveVersion }, 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.ValueTuple", "System.ComponentModel.TypeConverter", "System.ComponentModel.Primitives",
"System.Runtime.Serialization.Primitives", "System.Xml.XmlDocument", "System.Xml.ReaderWriter") "System.Runtime.Serialization.Primitives", "System.Xml.XmlDocument", "System.Xml.ReaderWriter")
.ToArray(), .ToArray(),
@ -269,11 +269,28 @@ public class Packages
}, },
Files = new [] 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 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 [] var nuspecNuGetSettingsMobile = new []
@ -331,7 +348,7 @@ public class Packages
Files = new [] Files = new []
{ {
new NuSpecContent { Source = "Avalonia.Win32/bin/" + parameters.DirSuffix + "/Avalonia.Win32.dll", Target = "lib/net45" }, 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"), BasePath = context.Directory("./src/Windows"),
OutputDirectory = parameters.NugetRoot OutputDirectory = parameters.NugetRoot
@ -352,9 +369,9 @@ public class Packages
}, },
Files = new [] 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 OutputDirectory = parameters.NugetRoot
}, },
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
@ -386,9 +403,9 @@ public class Packages
}, },
Files = new [] 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 OutputDirectory = parameters.NugetRoot
}, },
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
@ -418,18 +435,32 @@ public class Packages
{ {
new NuSpecDependency() { Id = "Avalonia", Version = parameters.Version }, new NuSpecDependency() { Id = "Avalonia", Version = parameters.Version },
new NuSpecDependency() { Id = "SkiaSharp", Version = SkiaSharpVersion }, new NuSpecDependency() { Id = "SkiaSharp", Version = SkiaSharpVersion },
new NuSpecDependency() { Id = "Avalonia", Version = parameters.Version, TargetFramework="netcoreapp1.1" }, new NuSpecDependency() { Id = "Avalonia", Version = parameters.Version, TargetFramework="netcoreapp2.0" },
new NuSpecDependency() { Id = "SkiaSharp", Version = SkiaSharpVersion, TargetFramework="netcoreapp1.1" }, new NuSpecDependency() { Id = "SkiaSharp", Version = SkiaSharpVersion, TargetFramework="netcoreapp2.0" },
new NuSpecDependency() { Id = "Avalonia.Skia.Linux.Natives", Version = SkiaSharpLinuxVersion, TargetFramework="netcoreapp1.1" }, new NuSpecDependency() { Id = "Avalonia.Skia.Linux.Natives", Version = SkiaSharpLinuxVersion, TargetFramework="netcoreapp2.0" },
new NuSpecDependency() { Id = "Avalonia", Version = parameters.Version, TargetFramework="net461" }, new NuSpecDependency() { Id = "Avalonia", Version = parameters.Version, TargetFramework="net461" },
new NuSpecDependency() { Id = "SkiaSharp", Version = SkiaSharpVersion, TargetFramework="net461" }, new NuSpecDependency() { Id = "SkiaSharp", Version = SkiaSharpVersion, TargetFramework="net461" },
new NuSpecDependency() { Id = "Avalonia.Skia.Linux.Natives", Version = SkiaSharpLinuxVersion, TargetFramework="net461" } new NuSpecDependency() { Id = "Avalonia.Skia.Linux.Natives", Version = SkiaSharpLinuxVersion, TargetFramework="net461" }
}, },
Files = new [] 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 OutputDirectory = parameters.NugetRoot
}, },
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
@ -447,10 +478,12 @@ public class Packages
new NuSpecDependency() { Id = "Avalonia.Win32", TargetFramework="net45", Version = parameters.Version }, new NuSpecDependency() { Id = "Avalonia.Win32", TargetFramework="net45", Version = parameters.Version },
new NuSpecDependency() { Id = "Avalonia.Skia", 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.Gtk3", TargetFramework="net45", Version = parameters.Version },
new NuSpecDependency() { Id = "Avalonia.MonoMac", TargetFramework="net45", Version = parameters.Version },
//.NET Core //.NET Core
new NuSpecDependency() { Id = "Avalonia.Win32", TargetFramework="netcoreapp1.0", Version = parameters.Version }, new NuSpecDependency() { Id = "Avalonia.Win32", TargetFramework="netcoreapp2.0", Version = parameters.Version },
new NuSpecDependency() { Id = "Avalonia.Skia", TargetFramework="netcoreapp1.0", Version = parameters.Version }, new NuSpecDependency() { Id = "Avalonia.Skia", TargetFramework="netcoreapp2.0", Version = parameters.Version },
new NuSpecDependency() { Id = "Avalonia.Gtk3", TargetFramework="netcoreapp1.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[] Files = new NuSpecContent[]
{ {
@ -488,7 +521,7 @@ public class Packages
}, },
Files = new [] 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/"), BasePath = context.Directory("./src/Linux/"),
OutputDirectory = parameters.NugetRoot OutputDirectory = parameters.NugetRoot

1
samples/BindingTest/App.xaml.cs

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

2
samples/BindingTest/BindingTest.csproj

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

10
samples/BindingTest/ViewModels/MainWindowViewModel.cs

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

2
samples/BindingTest/ViewModels/NestedCommandViewModel.cs

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

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

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

35
samples/ControlCatalog.NetCore/Program.cs

@ -1,24 +1,47 @@
using System; using System;
using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
using Avalonia; using Avalonia;
namespace ControlCatalog.NetCore namespace ControlCatalog.NetCore
{ {
class Program static class Program
{ {
static void Main(string[] args) static void Main(string[] args)
{ {
if (args.Contains("--fbdev")) AppBuilder.Configure<App>().InitializeWithLinuxFramebuffer(tl => if (args.Contains("--wait-for-attach"))
{ {
tl.Content = new MainView(); Console.WriteLine("Attach debugger and use 'Set next statement'");
System.Threading.ThreadPool.QueueUserWorkItem(_ => ConsoleSilencer()); 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 else
AppBuilder.Configure<App>() AppBuilder.Configure<App>()
.UsePlatformDetect() .CustomPlatformDetect()
.UseReactiveUI()
.Start<MainWindow>(); .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() static void ConsoleSilencer()
{ {
Console.CursorVisible = false; Console.CursorVisible = false;

14
samples/ControlCatalog/ControlCatalog.csproj

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netstandard1.3</TargetFramework> <TargetFramework>netstandard2.0</TargetFramework>
<EnableDefaultCompileItems>False</EnableDefaultCompileItems> <EnableDefaultCompileItems>False</EnableDefaultCompileItems>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo> <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
</PropertyGroup> </PropertyGroup>
@ -32,6 +32,12 @@
<EmbeddedResource Include="MainView.xaml"> <EmbeddedResource Include="MainView.xaml">
<SubType>Designer</SubType> <SubType>Designer</SubType>
</EmbeddedResource> </EmbeddedResource>
<EmbeddedResource Include="DecoratedWindow.xaml">
<SubType>Designer</SubType>
</EmbeddedResource>
<EmbeddedResource Include="Pages\DialogsPage.xaml">
<SubType>Designer</SubType>
</EmbeddedResource>
<EmbeddedResource Include="Pages\BorderPage.xaml"> <EmbeddedResource Include="Pages\BorderPage.xaml">
<SubType>Designer</SubType> <SubType>Designer</SubType>
</EmbeddedResource> </EmbeddedResource>
@ -83,9 +89,15 @@
<Compile Include="MainView.xaml.cs"> <Compile Include="MainView.xaml.cs">
<DependentUpon>MainView.xaml</DependentUpon> <DependentUpon>MainView.xaml</DependentUpon>
</Compile> </Compile>
<Compile Include="DecoratedWindow.xaml.cs">
<DependentUpon>DecoratedWindow.xaml</DependentUpon>
</Compile>
<Compile Include="MainWindow.xaml.cs"> <Compile Include="MainWindow.xaml.cs">
<DependentUpon>MainWindow.xaml</DependentUpon> <DependentUpon>MainWindow.xaml</DependentUpon>
</Compile> </Compile>
<Compile Include="Pages\DialogsPage.xaml.cs">
<DependentUpon>DialogsPage.xaml</DependentUpon>
</Compile>
<Compile Include="Pages\BorderPage.xaml.cs"> <Compile Include="Pages\BorderPage.xaml.cs">
<DependentUpon>BorderPage.xaml</DependentUpon> <DependentUpon>BorderPage.xaml</DependentUpon>
</Compile> </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" <UserControl xmlns="https://github.com/avaloniaui"
xmlns:pages="clr-namespace:ControlCatalog.Pages;assembly=ControlCatalog" xmlns:pages="clr-namespace:ControlCatalog.Pages;assembly=ControlCatalog"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<TabControl Classes="sidebar"> <TabControl Classes="sidebar" Name="Sidebar">
<TabControl.Transition> <TabControl.Transition>
<CrossFade Duration="0.25"/> <CrossFade Duration="0.25"/>
</TabControl.Transition> </TabControl.Transition>

9
samples/ControlCatalog/MainView.xaml.cs

@ -1,6 +1,9 @@
using System.Collections;
using Avalonia; using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using Avalonia.Platform;
using ControlCatalog.Pages;
namespace ControlCatalog namespace ControlCatalog
{ {
@ -9,6 +12,12 @@ namespace ControlCatalog
public MainView() public MainView()
{ {
this.InitializeComponent(); 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() private void InitializeComponent()

5
samples/ControlCatalog/MainWindow.xaml.cs

@ -1,6 +1,7 @@
using Avalonia; using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using System;
namespace ControlCatalog namespace ControlCatalog
{ {
@ -10,7 +11,7 @@ namespace ControlCatalog
{ {
this.InitializeComponent(); this.InitializeComponent();
this.AttachDevTools(); this.AttachDevTools();
Renderer.DrawDirtyRects = Renderer.DrawFps = true; //Renderer.DrawDirtyRects = Renderer.DrawFps = true;
} }
private void InitializeComponent() private void InitializeComponent()
@ -19,7 +20,7 @@ namespace ControlCatalog
// so we must refer to this resource DLL statically. For // so we must refer to this resource DLL statically. For
// now I am doing that here. But we need a better solution!! // now I am doing that here. But we need a better solution!!
var theme = new Avalonia.Themes.Default.DefaultTheme(); var theme = new Avalonia.Themes.Default.DefaultTheme();
theme.FindResource("Button"); theme.TryGetResource("Button", out _);
AvaloniaXamlLoader.Load(this); 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"> <UserControl xmlns="https://github.com/avaloniaui">
<StackPanel Orientation="Vertical" Gap="4"> <StackPanel Orientation="Vertical"
<TextBlock Classes="h1">ToolTip</TextBlock> Gap="4">
<TextBlock Classes="h2">A control which pops up a hint when a control is hovered</TextBlock> <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" Margin="0,16,0,0"
HorizontalAlignment="Center" HorizontalAlignment="Center">
Gap="16"> <Border Grid.Column="0"
<Border Background="{StyleResource ThemeAccentBrush}" Grid.Row="1"
Padding="48,48,48,48"> Background="{StyleResource ThemeAccentBrush}"
<ToolTip.Tip> Margin="5"
<StackPanel> Padding="50"
<TextBlock Classes="h1">ToolTip</TextBlock> ToolTip.Tip="This is a ToolTip">
<TextBlock Classes="h2">A control which pops up a hint when a control is hovered</TextBlock> <TextBlock>Hover Here</TextBlock>
</StackPanel> </Border>
</ToolTip.Tip> <CheckBox Grid.Column="1"
<TextBlock>Hover Here</TextBlock> Margin="5"
</Border> 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>
</StackPanel>
</UserControl> </UserControl>

1
samples/RenderTest/Program.cs

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

1
samples/RenderTest/RenderTest.csproj

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

10
samples/RenderTest/ViewModels/MainWindowViewModel.cs

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

1
samples/VirtualizationTest/Program.cs

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

25
samples/VirtualizationTest/ViewModels/MainWindowViewModel.cs

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

1
samples/VirtualizationTest/VirtualizationTest.csproj

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

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

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

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

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

3
src/Avalonia.Base/AvaloniaPropertyRegistry.cs

@ -47,6 +47,9 @@ namespace Avalonia
{ {
Dictionary<int, AvaloniaProperty> inner; Dictionary<int, AvaloniaProperty> inner;
// Ensure the type's static ctor has been run.
RuntimeHelpers.RunClassConstructor(ownerType.TypeHandle);
if (_attached.TryGetValue(ownerType, out inner)) if (_attached.TryGetValue(ownerType, out inner))
{ {
return inner.Values; 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="TKey">The type of the dictionary key.</typeparam>
/// <typeparam name="TValue">The type of the dictionary value.</typeparam> /// <typeparam name="TValue">The type of the dictionary value.</typeparam>
public class AvaloniaDictionary<TKey, TValue> : IDictionary<TKey, TValue>, public class AvaloniaDictionary<TKey, TValue> : IDictionary<TKey, TValue>,
IDictionary,
INotifyCollectionChanged, INotifyCollectionChanged,
INotifyPropertyChanged INotifyPropertyChanged
{ {
@ -51,6 +52,16 @@ namespace Avalonia.Collections
/// <inheritdoc/> /// <inheritdoc/>
public ICollection<TValue> Values => _inner.Values; 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> /// <summary>
/// Gets or sets the named resource. /// Gets or sets the named resource.
/// </summary> /// </summary>
@ -89,6 +100,8 @@ namespace Avalonia.Collections
} }
} }
object IDictionary.this[object key] { get => ((IDictionary)_inner)[key]; set => ((IDictionary)_inner)[key] = value; }
/// <inheritdoc/> /// <inheritdoc/>
public void Add(TKey key, TValue value) public void Add(TKey key, TValue value)
{ {
@ -118,10 +131,7 @@ namespace Avalonia.Collections
} }
/// <inheritdoc/> /// <inheritdoc/>
public bool ContainsKey(TKey key) public bool ContainsKey(TKey key) => _inner.ContainsKey(key);
{
return _inner.ContainsKey(key);
}
/// <inheritdoc/> /// <inheritdoc/>
public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex) public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
@ -130,21 +140,16 @@ namespace Avalonia.Collections
} }
/// <inheritdoc/> /// <inheritdoc/>
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() => _inner.GetEnumerator();
{
return _inner.GetEnumerator();
}
/// <inheritdoc/> /// <inheritdoc/>
public bool Remove(TKey key) public bool Remove(TKey key)
{ {
TValue value; if (_inner.TryGetValue(key, out TValue value))
if (_inner.TryGetValue(key, out value))
{ {
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Count")); PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Count"));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs($"Item[{key}]")); PropertyChanged?.Invoke(this, new PropertyChangedEventArgs($"Item[{key}]"));
if (CollectionChanged != null) if (CollectionChanged != null)
{ {
var e = new NotifyCollectionChangedEventArgs( var e = new NotifyCollectionChangedEventArgs(
@ -163,16 +168,13 @@ namespace Avalonia.Collections
} }
/// <inheritdoc/> /// <inheritdoc/>
public bool TryGetValue(TKey key, out TValue value) public bool TryGetValue(TKey key, out TValue value) => _inner.TryGetValue(key, out value);
{
return _inner.TryGetValue(key, out value);
}
/// <inheritdoc/> /// <inheritdoc/>
IEnumerator IEnumerable.GetEnumerator() IEnumerator IEnumerable.GetEnumerator() => _inner.GetEnumerator();
{
return _inner.GetEnumerator(); /// <inheritdoc/>
} void ICollection.CopyTo(Array array, int index) => ((ICollection)_inner).CopyTo(array, index);
/// <inheritdoc/> /// <inheritdoc/>
void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> item) void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> item)
@ -192,6 +194,18 @@ namespace Avalonia.Collections
return Remove(item.Key); 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) private void NotifyAdd(TKey key, TValue value)
{ {
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Count")); PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Count"));

51
src/Avalonia.Controls/Application.cs

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

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

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netstandard1.3</TargetFramework> <TargetFramework>netstandard2.0</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo> <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' "> <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. // Licensed under the MIT license. See licence.md file in the project root for full license information.
using System; using System;
using System.Linq;
using System.Windows.Input; using System.Windows.Input;
using Avalonia.Data; using Avalonia.Data;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Avalonia.Rendering;
using Avalonia.VisualTree;
namespace Avalonia.Controls namespace Avalonia.Controls
{ {
@ -68,19 +65,22 @@ namespace Avalonia.Controls
/// Defines the <see cref="Click"/> event. /// Defines the <see cref="Click"/> event.
/// </summary> /// </summary>
public static readonly RoutedEvent<RoutedEventArgs> ClickEvent = public static readonly RoutedEvent<RoutedEventArgs> ClickEvent =
RoutedEvent.Register<Button, RoutedEventArgs>("Click", RoutingStrategies.Bubble); RoutedEvent.Register<Button, RoutedEventArgs>(nameof(Click), RoutingStrategies.Bubble);
private ICommand _command; private ICommand _command;
public static readonly StyledProperty<bool> IsPressedProperty =
AvaloniaProperty.Register<Button, bool>(nameof(IsPressed));
/// <summary> /// <summary>
/// Initializes static members of the <see cref="Button"/> class. /// Initializes static members of the <see cref="Button"/> class.
/// </summary> /// </summary>
static Button() static Button()
{ {
FocusableProperty.OverrideDefaultValue(typeof(Button), true); FocusableProperty.OverrideDefaultValue(typeof(Button), true);
ClickEvent.AddClassHandler<Button>(x => x.OnClick);
CommandProperty.Changed.Subscribe(CommandChanged); CommandProperty.Changed.Subscribe(CommandChanged);
IsDefaultProperty.Changed.Subscribe(IsDefaultChanged); IsDefaultProperty.Changed.Subscribe(IsDefaultChanged);
PseudoClass(IsPressedProperty, ":pressed");
} }
/// <summary> /// <summary>
@ -138,6 +138,12 @@ namespace Avalonia.Controls
set { SetValue(IsDefaultProperty, value); } set { SetValue(IsDefaultProperty, value); }
} }
public bool IsPressed
{
get { return GetValue(IsPressedProperty); }
private set { SetValue(IsPressedProperty, value); }
}
/// <inheritdoc/> /// <inheritdoc/>
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{ {
@ -145,9 +151,7 @@ namespace Avalonia.Controls
if (IsDefault) if (IsDefault)
{ {
var inputElement = e.Root as IInputElement; if (e.Root is IInputElement inputElement)
if (inputElement != null)
{ {
ListenForDefault(inputElement); ListenForDefault(inputElement);
} }
@ -159,16 +163,16 @@ namespace Avalonia.Controls
{ {
if (e.Key == Key.Enter) if (e.Key == Key.Enter)
{ {
RaiseClickEvent(); OnClick();
e.Handled = true; e.Handled = true;
} }
else if (e.Key == Key.Space) else if (e.Key == Key.Space)
{ {
if (ClickMode == ClickMode.Press) if (ClickMode == ClickMode.Press)
{ {
RaiseClickEvent(); OnClick();
} }
IsPressed = true;
e.Handled = true; e.Handled = true;
} }
@ -182,9 +186,9 @@ namespace Avalonia.Controls
{ {
if (ClickMode == ClickMode.Release) if (ClickMode == ClickMode.Release)
{ {
RaiseClickEvent(); OnClick();
} }
IsPressed = false;
e.Handled = true; e.Handled = true;
} }
} }
@ -196,9 +200,7 @@ namespace Avalonia.Controls
if (IsDefault) if (IsDefault)
{ {
var inputElement = e.Root as IInputElement; if (e.Root is IInputElement inputElement)
if (inputElement != null)
{ {
StopListeningForDefault(inputElement); StopListeningForDefault(inputElement);
} }
@ -208,9 +210,11 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Invokes the <see cref="Click"/> event. /// Invokes the <see cref="Click"/> event.
/// </summary> /// </summary>
/// <param name="e">The event args.</param> protected virtual void OnClick()
protected virtual void OnClick(RoutedEventArgs e)
{ {
var e = new RoutedEventArgs(ClickEvent);
RaiseEvent(e);
if (Command != null) if (Command != null)
{ {
Command.Execute(CommandParameter); Command.Execute(CommandParameter);
@ -225,13 +229,13 @@ namespace Avalonia.Controls
if (e.MouseButton == MouseButton.Left) if (e.MouseButton == MouseButton.Left)
{ {
PseudoClasses.Add(":pressed");
e.Device.Capture(this); e.Device.Capture(this);
IsPressed = true;
e.Handled = true; e.Handled = true;
if (ClickMode == ClickMode.Press) if (ClickMode == ClickMode.Press)
{ {
RaiseClickEvent(); OnClick();
} }
} }
} }
@ -244,12 +248,12 @@ namespace Avalonia.Controls
if (e.MouseButton == MouseButton.Left) if (e.MouseButton == MouseButton.Left)
{ {
e.Device.Capture(null); e.Device.Capture(null);
PseudoClasses.Remove(":pressed"); IsPressed = false;
e.Handled = true; e.Handled = true;
if (ClickMode == ClickMode.Release && new Rect(Bounds.Size).Contains(e.GetPosition(this))) 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> /// <param name="e">The event args.</param>
private static void CommandChanged(AvaloniaPropertyChangedEventArgs e) private static void CommandChanged(AvaloniaPropertyChangedEventArgs e)
{ {
var button = e.Sender as Button; if (e.Sender is Button button)
if (button != null)
{ {
var oldCommand = e.OldValue as ICommand; var oldCommand = e.OldValue as ICommand;
var newCommand = e.NewValue as ICommand; var newCommand = e.NewValue as ICommand;
@ -301,9 +303,8 @@ namespace Avalonia.Controls
{ {
var button = e.Sender as Button; var button = e.Sender as Button;
var isDefault = (bool)e.NewValue; var isDefault = (bool)e.NewValue;
var inputRoot = button?.VisualRoot as IInputElement;
if (inputRoot != null) if (button?.VisualRoot is IInputElement inputRoot)
{ {
if (isDefault) if (isDefault)
{ {
@ -346,19 +347,6 @@ namespace Avalonia.Controls
root.RemoveHandler(KeyDownEvent, RootKeyDown); root.RemoveHandler(KeyDownEvent, RootKeyDown);
} }
/// <summary>
/// Raises the <see cref="Click"/> event.
/// </summary>
private void RaiseClickEvent()
{
RoutedEventArgs click = new RoutedEventArgs
{
RoutedEvent = ClickEvent,
};
RaiseEvent(click);
}
/// <summary> /// <summary>
/// Called when a key is pressed on the input root and the button <see cref="IsDefault"/>. /// Called when a key is pressed on the input root and the button <see cref="IsDefault"/>.
/// </summary> /// </summary>
@ -368,7 +356,7 @@ namespace Avalonia.Controls
{ {
if (e.Key == Key.Enter && IsVisible && IsEnabled) 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 bool _isAttachedToLogicalTree;
private IAvaloniaList<ILogical> _logicalChildren; private IAvaloniaList<ILogical> _logicalChildren;
private INameScope _nameScope; private INameScope _nameScope;
private bool _styled; private IResourceDictionary _resources;
private Styles _styles; private Styles _styles;
private bool _styled;
private Subject<IStyleable> _styleDetach = new Subject<IStyleable>(); private Subject<IStyleable> _styleDetach = new Subject<IStyleable>();
/// <summary> /// <summary>
@ -153,6 +154,11 @@ namespace Avalonia.Controls
/// </remarks> /// </remarks>
public event EventHandler Initialized; 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> /// <summary>
/// Gets or sets the name of the control. /// Gets or sets the name of the control.
/// </summary> /// </summary>
@ -262,7 +268,32 @@ namespace Avalonia.Controls
/// each control may in addition define its own styles which are applied to the control /// each control may in addition define its own styles which are applied to the control
/// itself and its children. /// itself and its children.
/// </remarks> /// </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> /// <summary>
/// Gets the control's logical parent. /// Gets the control's logical parent.
@ -278,6 +309,34 @@ namespace Avalonia.Controls
set { SetValue(ContextMenuProperty, value); } 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> /// <summary>
/// Gets or sets a user-defined object attached to the control. /// Gets or sets a user-defined object attached to the control.
/// </summary> /// </summary>
@ -296,9 +355,35 @@ namespace Avalonia.Controls
internal set { SetValue(TemplatedParentProperty, value); } 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/> /// <inheritdoc/>
bool IDataTemplateHost.IsDataTemplatesInitialized => _dataTemplates != null; 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> /// <summary>
/// Gets a value indicating whether the element is attached to a rooted logical tree. /// Gets a value indicating whether the element is attached to a rooted logical tree.
/// </summary> /// </summary>
@ -314,6 +399,12 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
IAvaloniaReadOnlyList<ILogical> ILogical.LogicalChildren => LogicalChildren; IAvaloniaReadOnlyList<ILogical> ILogical.LogicalChildren => LogicalChildren;
/// <inheritdoc/>
bool IResourceProvider.HasResources => _resources?.Count > 0 || Styles.HasResources;
/// <inheritdoc/>
IResourceNode IResourceNode.ResourceParent => ((IStyleHost)this).StylingParent as IResourceNode;
/// <inheritdoc/> /// <inheritdoc/>
IAvaloniaReadOnlyList<string> IStyleable.Classes => Classes; IAvaloniaReadOnlyList<string> IStyleable.Classes => Classes;
@ -390,31 +481,24 @@ namespace Avalonia.Controls
this.OnDetachedFromLogicalTreeCore(e); this.OnDetachedFromLogicalTreeCore(e);
} }
/// <summary> /// <inheritdoc/>
/// Gets the control's logical children. void ILogical.NotifyResourcesChanged(ResourcesChangedEventArgs e)
/// </summary>
protected IAvaloniaList<ILogical> LogicalChildren
{ {
get ResourcesChanged?.Invoke(this, new ResourcesChangedEventArgs());
{
if (_logicalChildren == null)
{
var list = new AvaloniaList<ILogical>();
list.ResetBehavior = ResetBehavior.Remove;
list.Validate = ValidateLogicalChild;
list.CollectionChanged += LogicalChildrenCollectionChanged;
_logicalChildren = list;
}
return _logicalChildren; foreach (var child in LogicalChildren)
{
child.NotifyResourcesChanged(e);
} }
} }
/// <summary> /// <inheritdoc/>
/// Gets the <see cref="Classes"/> collection in a form that allows adding and removing bool IResourceProvider.TryGetResource(string key, out object value)
/// pseudoclasses. {
/// </summary> value = null;
protected IPseudoClasses PseudoClasses => Classes; return (_resources?.TryGetResource(key, out value) ?? false) ||
(_styles?.TryGetResource(key, out value) ?? false);
}
/// <summary> /// <summary>
/// Sets the control's logical parent. /// Sets the control's logical parent.
@ -450,6 +534,7 @@ namespace Avalonia.Controls
} }
_parent = (IControl)parent; _parent = (IControl)parent;
((ILogical)this).NotifyResourcesChanged(new ResourcesChangedEventArgs());
if (_parent is IStyleRoot || _parent?.IsAttachedToLogicalTree == true || this is IStyleRoot) 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/> /// <inheritdoc/>
protected override void OnPointerPressed(PointerPressedEventArgs e) protected override void OnPointerPressed(PointerPressedEventArgs e)
{ {
if (!IsDropDownOpen && ((IVisual)e.Source).GetVisualRoot() != typeof(PopupRoot)) if (!IsDropDownOpen && ((IVisual)e.Source).GetVisualRoot() is PopupRoot)
{ {
IsDropDownOpen = true; IsDropDownOpen = true;
e.Handled = true; e.Handled = true;

1
src/Avalonia.Controls/IControl.cs

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

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

@ -31,7 +31,7 @@ namespace Avalonia.Platform
IDisposable ShowDialog(); IDisposable ShowDialog();
/// <summary> /// <summary>
/// Enables of disables system window decorations (title bar, buttons, etc) /// Enables or disables system window decorations (title bar, buttons, etc)
/// </summary> /// </summary>
void SetSystemDecorations(bool enabled); void SetSystemDecorations(bool enabled);
@ -39,5 +39,10 @@ namespace Avalonia.Platform
/// Sets the icon of this window. /// Sets the icon of this window.
/// </summary> /// </summary>
void SetIcon(IWindowIconImpl icon); 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) if (_highlightBrush == null)
{ {
_highlightBrush = (IBrush)this.FindStyleResource("HighlightBrush"); _highlightBrush = (IBrush)this.FindResource("HighlightBrush");
} }
foreach (var rect in rects) foreach (var rect in rects)

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

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

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

@ -5,6 +5,7 @@ using System;
using System.Reactive; using System.Reactive;
using System.Reactive.Linq; using System.Reactive.Linq;
using Avalonia.Data; using Avalonia.Data;
using Avalonia.Interactivity;
namespace Avalonia.Controls.Primitives namespace Avalonia.Controls.Primitives
{ {
@ -31,13 +32,18 @@ namespace Avalonia.Controls.Primitives
public static readonly StyledProperty<Orientation> OrientationProperty = public static readonly StyledProperty<Orientation> OrientationProperty =
AvaloniaProperty.Register<ScrollBar, Orientation>(nameof(Orientation)); AvaloniaProperty.Register<ScrollBar, Orientation>(nameof(Orientation));
private Button _lineUpButton;
private Button _lineDownButton;
private Button _pageUpButton;
private Button _pageDownButton;
/// <summary> /// <summary>
/// Initializes static members of the <see cref="ScrollBar"/> class. /// Initializes static members of the <see cref="ScrollBar"/> class.
/// </summary> /// </summary>
static ScrollBar() static ScrollBar()
{ {
PseudoClass(OrientationProperty, o => o == Avalonia.Controls.Orientation.Vertical, ":vertical"); PseudoClass(OrientationProperty, o => o == Orientation.Vertical, ":vertical");
PseudoClass(OrientationProperty, o => o == Avalonia.Controls.Orientation.Horizontal, ":horizontal"); PseudoClass(OrientationProperty, o => o == Orientation.Horizontal, ":horizontal");
} }
/// <summary> /// <summary>
@ -97,12 +103,101 @@ namespace Avalonia.Controls.Primitives
return false; return false;
case ScrollBarVisibility.Auto: case ScrollBarVisibility.Auto:
var viewportSize = ViewportSize; return double.IsNaN(ViewportSize) || Maximum > 0;
return double.IsNaN(viewportSize) || viewportSize < Maximum - Minimum;
default: default:
throw new InvalidOperationException("Invalid value for ScrollBar.Visibility."); 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); } set { SetAndRaise(IsCheckedProperty, ref _isChecked, value); }
} }
protected override void OnClick(RoutedEventArgs e) protected override void OnClick()
{ {
Toggle(); Toggle();
base.OnClick(e); base.OnClick();
} }
protected virtual void Toggle() protected virtual void Toggle()

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

@ -10,7 +10,7 @@ namespace Avalonia.Controls.Primitives
public class Track : Control public class Track : Control
{ {
public static readonly DirectProperty<Track, double> MinimumProperty = 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 = public static readonly DirectProperty<Track, double> MaximumProperty =
RangeBase.MaximumProperty.AddOwner<Track>(o => o.Maximum, (o, v) => o.Maximum = v); RangeBase.MaximumProperty.AddOwner<Track>(o => o.Maximum, (o, v) => o.Maximum = v);
@ -25,7 +25,13 @@ namespace Avalonia.Controls.Primitives
ScrollBar.OrientationProperty.AddOwner<Track>(); ScrollBar.OrientationProperty.AddOwner<Track>();
public static readonly StyledProperty<Thumb> ThumbProperty = 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 _minimum;
private double _maximum = 100.0; private double _maximum = 100.0;
@ -34,6 +40,8 @@ namespace Avalonia.Controls.Primitives
static Track() static Track()
{ {
ThumbProperty.Changed.AddClassHandler<Track>(x => x.ThumbChanged); 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); AffectsArrange(MinimumProperty, MaximumProperty, ValueProperty, OrientationProperty);
} }
@ -74,6 +82,18 @@ namespace Avalonia.Controls.Primitives
set { SetValue(ThumbProperty, value); } 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) protected override Size MeasureOverride(Size availableSize)
{ {
var thumb = Thumb; var thumb = Thumb;
@ -98,34 +118,54 @@ namespace Avalonia.Controls.Primitives
protected override Size ArrangeOverride(Size finalSize) protected override Size ArrangeOverride(Size finalSize)
{ {
var thumb = Thumb; 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 thumbWidth = double.IsNaN(viewportSize) ? thumb?.DesiredSize.Width ?? 0 : finalSize.Width * viewportSize / extent;
var thumbFraction = ViewportSize / range; var remaining = finalSize.Width - thumbWidth;
var valueFraction = (Value - Minimum) / range; var firstWidth = range <= 0 ? 0 : remaining * offset / range;
if (double.IsNaN(valueFraction) || double.IsInfinity(valueFraction)) if (decreaseButton != null)
{ {
valueFraction = 0; decreaseButton.Arrange(new Rect(0, 0, firstWidth, finalSize.Height));
thumbFraction = 1;
} }
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); increaseButton.Arrange(new Rect(firstWidth + thumbWidth, 0, remaining - firstWidth, finalSize.Height));
var x = (finalSize.Width - width) * valueFraction;
thumb.Arrange(new Rect(x, 0, width, 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); decreaseButton.Arrange(new Rect(0, 0, finalSize.Width, firstHeight));
var y = (finalSize.Height - height) * valueFraction; }
thumb.Arrange(new Rect(0, y, finalSize.Width, height));
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) if (oldThumb != null)
{ {
oldThumb.DragDelta -= ThumbDragged; oldThumb.DragDelta -= ThumbDragged;
}
LogicalChildren.Clear(); LogicalChildren.Remove(oldThumb);
VisualChildren.Clear(); VisualChildren.Remove(oldThumb);
}
if (newThumb != null) 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) private void ThumbDragged(object sender, VectorEventArgs e)
{ {
double range = Maximum - Minimum; 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> /// </summary>
protected double HorizontalScrollBarViewportSize protected double HorizontalScrollBarViewportSize
{ {
get { return Max((_viewport.Width / _extent.Width) * (_extent.Width - _viewport.Width), 0); } get { return _viewport.Width; }
} }
/// <summary> /// <summary>
@ -308,7 +308,7 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
protected double VerticalScrollBarViewportSize protected double VerticalScrollBarViewportSize
{ {
get { return Max((_viewport.Height / _extent.Height) * (_extent.Height - _viewport.Height), 0); } get { return _viewport.Height; }
} }
/// <summary> /// <summary>

55
src/Avalonia.Controls/Slider.cs

@ -33,6 +33,8 @@ namespace Avalonia.Controls
// Slider required parts // Slider required parts
private Track _track; private Track _track;
private Button _decreaseButton;
private Button _increaseButton;
/// <summary> /// <summary>
/// Initializes static members of the <see cref="Slider"/> class. /// 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.DragStartedEvent.AddClassHandler<Slider>(x => x.OnThumbDragStarted, RoutingStrategies.Bubble);
Thumb.DragDeltaEvent.AddClassHandler<Slider>(x => x.OnThumbDragDelta, RoutingStrategies.Bubble); Thumb.DragDeltaEvent.AddClassHandler<Slider>(x => x.OnThumbDragDelta, RoutingStrategies.Bubble);
Thumb.DragCompletedEvent.AddClassHandler<Slider>(x => x.OnThumbDragCompleted, RoutingStrategies.Bubble); Thumb.DragCompletedEvent.AddClassHandler<Slider>(x => x.OnThumbDragCompleted, RoutingStrategies.Bubble);
SmallChangeProperty.OverrideDefaultValue<Slider>(1);
LargeChangeProperty.OverrideDefaultValue<Slider>(10);
} }
/// <summary> /// <summary>
@ -83,7 +87,54 @@ namespace Avalonia.Controls
/// <inheritdoc/> /// <inheritdoc/>
protected override void OnTemplateApplied(TemplateAppliedEventArgs e) 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> /// <summary>
@ -101,7 +152,7 @@ namespace Avalonia.Controls
protected virtual void OnThumbDragDelta(VectorEventArgs e) protected virtual void OnThumbDragDelta(VectorEventArgs e)
{ {
Thumb thumb = e.Source as Thumb; Thumb thumb = e.Source as Thumb;
if (thumb != null && _track.Thumb == thumb) if (thumb != null && _track?.Thumb == thumb)
{ {
MoveToNextTick(_track.Value); MoveToNextTick(_track.Value);
} }

2
src/Avalonia.Controls/SystemDialog.cs

@ -20,7 +20,7 @@ namespace Avalonia.Controls
{ {
public string DefaultExtension { get; set; } 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)) ?? ((await AvaloniaLocator.Current.GetService<ISystemDialogImpl>().ShowFileDialogAsync(this, window?.PlatformImpl)) ??
new string[0]).FirstOrDefault(); new string[0]).FirstOrDefault();

236
src/Avalonia.Controls/ToolTip.cs

@ -3,11 +3,7 @@
using System; using System;
using System.Reactive.Linq; using System.Reactive.Linq;
using System.Reactive.Subjects;
using Avalonia.Controls.Primitives; using Avalonia.Controls.Primitives;
using Avalonia.Input;
using Avalonia.Threading;
using Avalonia.VisualTree;
namespace Avalonia.Controls namespace Avalonia.Controls
{ {
@ -29,29 +25,50 @@ namespace Avalonia.Controls
AvaloniaProperty.RegisterAttached<ToolTip, Control, object>("Tip"); AvaloniaProperty.RegisterAttached<ToolTip, Control, object>("Tip");
/// <summary> /// <summary>
/// The popup window used to display the active tooltip. /// Defines the ToolTip.IsOpen attached property.
/// </summary> /// </summary>
private static PopupRoot s_popup; public static readonly AttachedProperty<bool> IsOpenProperty =
AvaloniaProperty.RegisterAttached<ToolTip, Control, bool>("IsOpen");
/// <summary> /// <summary>
/// The control that the currently visible tooltip is attached to. /// Defines the ToolTip.Placement property.
/// </summary> /// </summary>
private static Control s_current; public static readonly AttachedProperty<PlacementMode> PlacementProperty =
AvaloniaProperty.RegisterAttached<Popup, Control, PlacementMode>("Placement", defaultValue: PlacementMode.Pointer);
/// <summary> /// <summary>
/// Observable fired when a tooltip should be displayed for a control. The output from this /// Defines the ToolTip.HorizontalOffset property.
/// observable is throttled and calls <see cref="ShowToolTip(Control)"/> when the time
/// period expires.
/// </summary> /// </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> /// <summary>
/// Initializes static members of the <see cref="ToolTip"/> class. /// Initializes static members of the <see cref="ToolTip"/> class.
/// </summary> /// </summary>
static ToolTip() static ToolTip()
{ {
TipProperty.Changed.Subscribe(TipChanged); TipProperty.Changed.Subscribe(ToolTipService.Instance.TipChanged);
s_show.Throttle(TimeSpan.FromSeconds(0.5), AvaloniaScheduler.Instance).Subscribe(ShowToolTip); IsOpenProperty.Changed.Subscribe(IsOpenChanged);
} }
/// <summary> /// <summary>
@ -77,101 +94,160 @@ namespace Avalonia.Controls
} }
/// <summary> /// <summary>
/// called when the <see cref="TipProperty"/> property changes on a control. /// Gets the value of the ToolTip.IsOpen attached property.
/// </summary> /// </summary>
/// <param name="e">The event args.</param> /// <param name="element">The control to get the property from.</param>
private static void TipChanged(AvaloniaPropertyChangedEventArgs e) /// <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) /// <summary>
{ /// Sets the value of the ToolTip.IsOpen attached property.
control.PointerEnter -= ControlPointerEnter; /// </summary>
control.PointerLeave -= ControlPointerLeave; /// <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) /// <summary>
{ /// Gets the value of the ToolTip.Placement attached property.
control.PointerEnter += ControlPointerEnter; /// </summary>
control.PointerLeave += ControlPointerLeave; /// <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> /// <summary>
/// Shows a tooltip for the specified control. /// Sets the value of the ToolTip.Placement attached property.
/// </summary> /// </summary>
/// <param name="control">The control.</param> /// <param name="element">The control to get the property from.</param>
private static void ShowToolTip(Control control) /// <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) element.SetValue(PlacementProperty, value);
{ }
var cp = (control.GetVisualRoot() as IInputRoot)?.MouseDevice?.GetPosition(control);
if (cp.HasValue && control.IsVisible && new Rect(control.Bounds.Size).Contains(cp.Value)) /// <summary>
{ /// Gets the value of the ToolTip.HorizontalOffset attached property.
var position = control.PointToScreen(cp.Value) + new Vector(0, 22); /// </summary>
/// <param name="element">The control to get the property from.</param>
if (s_popup == null) /// <returns>
{ /// A value indicating how the tool tip is positioned.
s_popup = new PopupRoot(); /// </returns>
s_popup.Content = new ToolTip(); public static double GetHorizontalOffset(Control element)
} {
else return element.GetValue(HorizontalOffsetProperty);
{ }
((ISetLogicalParent)s_popup).SetParent(null);
} /// <summary>
/// Sets the value of the ToolTip.HorizontalOffset attached property.
((ISetLogicalParent)s_popup).SetParent(control); /// </summary>
((ToolTip)s_popup.Content).Content = GetTip(control); /// <param name="element">The control to get the property from.</param>
s_popup.Position = position; /// <param name="value">A value indicating how the tool tip is positioned.</param>
s_popup.Show(); public static void SetHorizontalOffset(Control element, double value)
{
s_current = control; 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> /// <summary>
/// Called when the pointer enters a control with an attached tooltip. /// Gets the value of the ToolTip.ShowDelay attached property.
/// </summary> /// </summary>
/// <param name="sender">The event sender.</param> /// <param name="element">The control to get the property from.</param>
/// <param name="e">The event args.</param> /// <returns>
private static void ControlPointerEnter(object sender, PointerEventArgs e) /// A value indicating the time, in milliseconds, before a tool tip opens.
/// </returns>
public static int GetShowDelay(Control element)
{ {
s_current = (Control)sender; return element.GetValue(ShowDelayProperty);
s_show.OnNext(s_current);
} }
/// <summary> /// <summary>
/// Called when the pointer leaves a control with an attached tooltip. /// Sets the value of the ToolTip.ShowDelay attached property.
/// </summary> /// </summary>
/// <param name="sender">The event sender.</param> /// <param name="element">The control to get the property from.</param>
/// <param name="e">The event args.</param> /// <param name="value">A value indicating the time, in milliseconds, before a tool tip opens.</param>
private static void ControlPointerLeave(object sender, PointerEventArgs e) 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(); toolTip?.Close();
s_show.OnNext(null);
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) Close();
{
// Clear the ToolTip's Content in case it has control content: this will _popup = new PopupRoot { Content = this };
// reset its visual parent allowing it to be used again. ((ISetLogicalParent)_popup).SetParent(control);
((ToolTip)s_popup.Content).Content = null; _popup.Position = Popup.GetPosition(control, GetPlacement(control), _popup,
GetHorizontalOffset(control), GetVerticalOffset(control));
_popup.Show();
}
// Dispose of the popup. private void Close()
s_popup.Dispose(); {
s_popup = null; 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.Input.Raw;
using Avalonia.Layout; using Avalonia.Layout;
using Avalonia.Logging; using Avalonia.Logging;
using Avalonia.LogicalTree;
using Avalonia.Platform; using Avalonia.Platform;
using Avalonia.Rendering; using Avalonia.Rendering;
using Avalonia.Styling; using Avalonia.Styling;
using Avalonia.Utilities;
using Avalonia.VisualTree; using Avalonia.VisualTree;
using JetBrains.Annotations; using JetBrains.Annotations;
@ -26,7 +28,13 @@ namespace Avalonia.Controls
/// It handles scheduling layout, styling and rendering as well as /// It handles scheduling layout, styling and rendering as well as
/// tracking the widget's <see cref="ClientSize"/>. /// tracking the widget's <see cref="ClientSize"/>.
/// </remarks> /// </remarks>
public abstract class TopLevel : ContentControl, IInputRoot, ILayoutRoot, IRenderRoot, ICloseable, IStyleRoot public abstract class TopLevel : ContentControl,
IInputRoot,
ILayoutRoot,
IRenderRoot,
ICloseable,
IStyleRoot,
IWeakSubscriber<ResourcesChangedEventArgs>
{ {
/// <summary> /// <summary>
/// Defines the <see cref="ClientSize"/> property. /// Defines the <see cref="ClientSize"/> property.
@ -100,7 +108,6 @@ namespace Avalonia.Controls
impl.Resized = HandleResized; impl.Resized = HandleResized;
impl.ScalingChanged = HandleScalingChanged; impl.ScalingChanged = HandleScalingChanged;
_keyboardNavigationHandler?.SetOwner(this); _keyboardNavigationHandler?.SetOwner(this);
_accessKeyHandler?.SetOwner(this); _accessKeyHandler?.SetOwner(this);
styler?.ApplyStyles(this); styler?.ApplyStyles(this);
@ -116,6 +123,14 @@ namespace Avalonia.Controls
{ {
_applicationLifecycle.OnExit += OnApplicationExiting; _applicationLifecycle.OnExit += OnApplicationExiting;
} }
if (((IStyleHost)this).StylingParent is IResourceProvider applicationResources)
{
WeakSubscriptionManager.Subscribe(
applicationResources,
nameof(IResourceProvider.ResourcesChanged),
this);
}
} }
/// <summary> /// <summary>
@ -165,6 +180,11 @@ namespace Avalonia.Controls
/// <inheritdoc/> /// <inheritdoc/>
IMouseDevice IInputRoot.MouseDevice => PlatformImpl?.MouseDevice; IMouseDevice IInputRoot.MouseDevice => PlatformImpl?.MouseDevice;
void IWeakSubscriber<ResourcesChangedEventArgs>.OnEvent(object sender, ResourcesChangedEventArgs e)
{
((ILogical)this).NotifyResourcesChanged(e);
}
/// <summary> /// <summary>
/// Gets or sets a value indicating whether access keys are shown in the window. /// Gets or sets a value indicating whether access keys are shown in the window.
/// </summary> /// </summary>

24
src/Avalonia.Controls/Window.cs

@ -61,11 +61,17 @@ namespace Avalonia.Controls
AvaloniaProperty.Register<Window, SizeToContent>(nameof(SizeToContent)); AvaloniaProperty.Register<Window, SizeToContent>(nameof(SizeToContent));
/// <summary> /// <summary>
/// Enables of disables system window decorations (title bar, buttons, etc) /// Enables or disables system window decorations (title bar, buttons, etc)
/// </summary> /// </summary>
public static readonly StyledProperty<bool> HasSystemDecorationsProperty = public static readonly StyledProperty<bool> HasSystemDecorationsProperty =
AvaloniaProperty.Register<Window, bool>(nameof(HasSystemDecorations), true); 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> /// <summary>
/// Defines the <see cref="Title"/> property. /// Defines the <see cref="Title"/> property.
/// </summary> /// </summary>
@ -92,6 +98,8 @@ namespace Avalonia.Controls
HasSystemDecorationsProperty.Changed.AddClassHandler<Window>( HasSystemDecorationsProperty.Changed.AddClassHandler<Window>(
(s, e) => s.PlatformImpl?.SetSystemDecorations((bool) e.NewValue)); (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)); IconProperty.Changed.AddClassHandler<Window>((s, e) => s.PlatformImpl?.SetIcon(((WindowIcon)e.NewValue).PlatformImpl));
} }
@ -152,7 +160,7 @@ namespace Avalonia.Controls
} }
/// <summary> /// <summary>
/// Enables of disables system window decorations (title bar, buttons, etc) /// Enables or disables system window decorations (title bar, buttons, etc)
/// </summary> /// </summary>
/// ///
public bool HasSystemDecorations public bool HasSystemDecorations
@ -160,6 +168,16 @@ namespace Avalonia.Controls
get { return GetValue(HasSystemDecorationsProperty); } get { return GetValue(HasSystemDecorationsProperty); }
set { SetValue(HasSystemDecorationsProperty, value); } set { SetValue(HasSystemDecorationsProperty, value); }
} }
/// <summary>
/// Enables or disables the taskbar icon
/// </summary>
///
public bool ShowInTaskbar
{
get { return GetValue(ShowInTaskbarProperty); }
set { SetValue(ShowInTaskbarProperty, value); }
}
/// <summary> /// <summary>
/// Gets or sets the minimized/maximized state of the window. /// 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"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netstandard1.3</TargetFramework> <TargetFramework>netstandard2.0</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo> <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' "> <PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">

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

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

6
src/Avalonia.DotNetCoreRuntime/AppBuilder.cs

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

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

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

13
src/Avalonia.DotNetFrameworkRuntime/AppBuilder.cs

@ -59,7 +59,11 @@ namespace Avalonia
from attribute in assembly.GetCustomAttributes<ExportWindowingSubsystemAttribute>() from attribute in assembly.GetCustomAttributes<ExportWindowingSubsystemAttribute>()
where attribute.RequiredOS == os && CheckEnvironment(attribute.EnvironmentChecker) where attribute.RequiredOS == os && CheckEnvironment(attribute.EnvironmentChecker)
orderby attribute.Priority ascending 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() var renderingSubsystemAttribute = (from assembly in RuntimePlatform.GetLoadedAssemblies()
from attribute in assembly.GetCustomAttributes<ExportRenderingSubsystemAttribute>() from attribute in assembly.GetCustomAttributes<ExportRenderingSubsystemAttribute>()
@ -67,7 +71,12 @@ namespace Avalonia
where attribute.RequiresWindowingSubsystem == null where attribute.RequiresWindowingSubsystem == null
|| attribute.RequiresWindowingSubsystem == windowingSubsystemAttribute.Name || attribute.RequiresWindowingSubsystem == windowingSubsystemAttribute.Name
orderby attribute.Priority ascending 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 UseWindowingSubsystem(() => windowingSubsystemAttribute.InitializationType
.GetRuntimeMethod(windowingSubsystemAttribute.InitializationMethod, Type.EmptyTypes).Invoke(null, null), .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 Sdk="Microsoft.NET.Sdk">
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <PropertyGroup>
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> <TargetFramework>net461</TargetFramework>
<PropertyGroup> <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <DocumentationFile>bin\$(Configuration)\Avalonia.DotNetFrameworkRuntime.xml</DocumentationFile>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> <DefineConstants>$(DefineConstants);FULLDOTNET</DefineConstants>
<ProjectGuid>{4A1ABB09-9047-4BD5-A4AD-A055E52C5EE0}</ProjectGuid> <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<OutputType>Library</OutputType> </PropertyGroup>
<AppDesignerFolder>Properties</AppDesignerFolder> <ItemGroup>
<RootNamespace>Avalonia.DotNetFrameworkRuntime</RootNamespace> <Compile Include="..\Shared\SharedAssemblyInfo.cs">
<AssemblyName>Avalonia.DotNetFrameworkRuntime</AssemblyName> <Link>Properties\SharedAssemblyInfo.cs</Link>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion> </Compile>
<FileAlignment>512</FileAlignment>
<TargetFrameworkProfile /> <ProjectReference Include="..\Avalonia.Base\Avalonia.Base.csproj" />
</PropertyGroup> <ProjectReference Include="..\Avalonia.Controls\Avalonia.Controls.csproj" />
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' "> <ProjectReference Include="..\Avalonia.Visuals\Avalonia.Visuals.csproj" />
<DebugSymbols>true</DebugSymbols> <ProjectReference Include="..\Avalonia.Styling\Avalonia.Styling.csproj" />
<DebugType>full</DebugType> </ItemGroup>
<Optimize>false</Optimize> <Import Project="..\Shared\PlatformSupport\PlatformSupport.projitems" Label="Shared" />
<OutputPath>bin\Debug\</OutputPath> <Import Project="..\..\build\Rx.props" />
<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> </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"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netstandard1.3</TargetFramework> <TargetFramework>netstandard2.0</TargetFramework>
<EnableDefaultCompileItems>False</EnableDefaultCompileItems> <EnableDefaultCompileItems>False</EnableDefaultCompileItems>
<EnableDefaultItems>False</EnableDefaultItems> <EnableDefaultItems>False</EnableDefaultItems>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo> <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<NoWarn>CS0436</NoWarn>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' "> <PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<DebugSymbols>true</DebugSymbols> <DebugSymbols>true</DebugSymbols>

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

@ -4,18 +4,6 @@ using System.Text;
namespace System.Net 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 class WebException : Exception
{ {
public object Response { get; set; } public object Response { get; set; }

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

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

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

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

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

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

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

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netstandard1.3</TargetFramework> <TargetFramework>netstandard2.0</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo> <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' "> <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"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netstandard1.3</TargetFramework> <TargetFramework>netstandard2.0</TargetFramework>
<EnableDefaultCompileItems>False</EnableDefaultCompileItems>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo> <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
</PropertyGroup> </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> <ItemGroup>
<Compile Include="..\Shared\SharedAssemblyInfo.cs" Link="Properties\SharedAssemblyInfo.cs" /> <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.Animation\Avalonia.Animation.csproj" />
<ProjectReference Include="..\Avalonia.Base\Avalonia.Base.csproj" /> <ProjectReference Include="..\Avalonia.Base\Avalonia.Base.csproj" />
<ProjectReference Include="..\Avalonia.Controls\Avalonia.Controls.csproj" /> <ProjectReference Include="..\Avalonia.Controls\Avalonia.Controls.csproj" />
@ -88,5 +15,6 @@
<ProjectReference Include="..\Avalonia.Visuals\Avalonia.Visuals.csproj" /> <ProjectReference Include="..\Avalonia.Visuals\Avalonia.Visuals.csproj" />
</ItemGroup> </ItemGroup>
<Import Project="..\..\build\Rx.props" /> <Import Project="..\..\build\Rx.props" />
<Import Project="..\..\build\ReactiveUI.props" />
<Import Project="..\..\build\Splat.props" /> <Import Project="..\..\build\Splat.props" />
</Project> </Project>

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

@ -3,7 +3,4 @@
using System.Reflection; 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")] [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"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netstandard1.3</TargetFramework> <TargetFramework>netstandard2.0</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo> <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<RootNamespace>Avalonia</RootNamespace>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' "> <PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<DebugSymbols>true</DebugSymbols> <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 System;
using Avalonia.Collections; using Avalonia.Collections;
using Avalonia.Controls;
namespace Avalonia.LogicalTree namespace Avalonia.LogicalTree
{ {
@ -55,5 +56,15 @@ namespace Avalonia.LogicalTree
/// this method yourself. /// this method yourself.
/// </remarks> /// </remarks>
void NotifyDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e); 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; using Avalonia.Metadata;
[assembly: AssemblyTitle("Avalonia.Styling")] [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: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Styling")]
[assembly: InternalsVisibleTo("Avalonia.Styling.UnitTests")] [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. // 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. // Licensed under the MIT license. See licence.md file in the project root for full license information.
using Avalonia.Controls;
namespace Avalonia.Styling namespace Avalonia.Styling
{ {
/// <summary> /// <summary>
/// Defines the interface for styles. /// Defines the interface for styles.
/// </summary> /// </summary>
public interface IStyle public interface IStyle : IResourceNode
{ {
/// <summary> /// <summary>
/// Attaches the style to a control if the style's selector matches. /// 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. /// The control that contains this style. May be null.
/// </param> /// </param>
void Attach(IStyleable control, IStyleHost container); 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;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Specialized;
using System.Reactive.Linq; using System.Reactive.Linq;
using Avalonia.Controls;
using Avalonia.Metadata; using Avalonia.Metadata;
namespace Avalonia.Styling namespace Avalonia.Styling
@ -11,12 +13,12 @@ namespace Avalonia.Styling
/// <summary> /// <summary>
/// Defines a style. /// Defines a style.
/// </summary> /// </summary>
public class Style : IStyle public class Style : IStyle, ISetStyleParent
{ {
private static Dictionary<IStyleable, List<IDisposable>> _applied = private static Dictionary<IStyleable, List<IDisposable>> _applied =
new Dictionary<IStyleable, List<IDisposable>>(); new Dictionary<IStyleable, List<IDisposable>>();
private IResourceNode _parent;
private StyleResources _resources; private IResourceDictionary _resources;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="Style"/> class. /// Initializes a new instance of the <see cref="Style"/> class.
@ -34,33 +36,33 @@ namespace Avalonia.Styling
Selector = selector(null); Selector = selector(null);
} }
/// <inheritdoc/>
public event EventHandler<ResourcesChangedEventArgs> ResourcesChanged;
/// <summary> /// <summary>
/// Gets or sets a dictionary of style resources. /// Gets or sets a dictionary of style resources.
/// </summary> /// </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 if (hadResources || _resources.Count > 0)
{
var resources = Resources;
if (!Equals(resources, value))
{ {
foreach (var i in value) ((ISetStyleParent)this).NotifyResourcesChanged(new ResourcesChangedEventArgs());
{
resources[i.Key] = i.Value;
//resources.Add(i.Key, i.Value);
//(resources as IDictionary<string,object>).Add(i);
}
} }
} }
} }
@ -76,6 +78,12 @@ namespace Avalonia.Styling
[Content] [Content]
public IList<ISetter> Setters { get; set; } = new List<ISetter>(); public IList<ISetter> Setters { get; set; } = new List<ISetter>();
/// <inheritdoc/>
IResourceNode IResourceNode.ResourceParent => _parent;
/// <inheritdoc/>
bool IResourceProvider.HasResources => _resources?.Count > 0;
/// <summary> /// <summary>
/// Attaches the style to a control if the style's selector matches. /// Attaches the style to a control if the style's selector matches.
/// </summary> /// </summary>
@ -112,25 +120,11 @@ namespace Avalonia.Styling
} }
} }
/// <summary> /// <inheritdoc/>
/// Tries to find a named resource within the style. public bool TryGetResource(string key, out object result)
/// </summary>
/// <param name="name">The resource name.</param>
/// <returns>
/// The resource if found, otherwise <see cref="AvaloniaProperty.UnsetValue"/>.
/// </returns>
public object FindResource(string name)
{ {
object result = null; result = null;
return _resources?.TryGetResource(key, out result) ?? false;
if (_resources?.TryGetValue(name, out result) == true)
{
return result;
}
else
{
return AvaloniaProperty.UnsetValue;
}
} }
/// <summary> /// <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) private static List<IDisposable> GetSubscriptions(IStyleable control)
{ {
List<IDisposable> subscriptions; List<IDisposable> subscriptions;
@ -179,5 +190,10 @@ namespace Avalonia.Styling
_applied.Remove(control); _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. // 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. // Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using System.Linq; using System.Linq;
using Avalonia.Collections; using Avalonia.Collections;
using Avalonia.Controls;
namespace Avalonia.Styling namespace Avalonia.Styling
{ {
/// <summary> /// <summary>
/// A style that consists of a number of child styles. /// A style that consists of a number of child styles.
/// </summary> /// </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> /// <summary>
/// Attaches the style to a control if the style's selector matches. /// Attaches the style to a control if the style's selector matches.
/// </summary> /// </summary>
@ -26,26 +105,68 @@ namespace Avalonia.Styling
} }
} }
/// <summary> /// <inheritdoc/>
/// Tries to find a named resource within the style. public bool TryGetResource(string key, out object value)
/// </summary> {
/// <param name="name">The resource name.</param> if (_resources != null && _resources.TryGetValue(key, out value))
/// <returns> {
/// The resource if found, otherwise <see cref="AvaloniaProperty.UnsetValue"/>. return true;
/// </returns> }
public object FindResource(string name)
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"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netstandard1.3</TargetFramework> <TargetFramework>netstandard2.0</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo> <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' "> <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.PopupRoot.xaml?assembly=Avalonia.Themes.Default"/>
<StyleInclude Source="resm:Avalonia.Themes.Default.ProgressBar.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.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.Separator.xaml?assembly=Avalonia.Themes.Default"/>
<StyleInclude Source="resm:Avalonia.Themes.Default.Slider.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"/> <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