Browse Source

Merge branch 'master' into appbuilder-setup-run

pull/1251/head
Jeremy Koritzinsky 8 years ago
committed by GitHub
parent
commit
a1eee28803
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      .ncrunch/Avalonia.Direct2D1.RenderTests.v3.ncrunchproject
  2. 3
      Avalonia.sln
  3. 2
      appveyor.yml
  4. 2
      build/MonoMac.props
  5. 5
      build/Serilog.Sinks.Trace.props
  6. 4
      build/Serilog.props
  7. 6
      packages.cake
  8. 13
      samples/BindingTest/App.xaml.cs
  9. 1
      samples/BindingTest/BindingTest.csproj
  10. 1
      samples/ControlCatalog.Desktop/ControlCatalog.Desktop.csproj
  11. 15
      samples/ControlCatalog.Desktop/Program.cs
  12. 6
      samples/ControlCatalog/ControlCatalog.csproj
  13. 1
      samples/ControlCatalog/MainView.xaml
  14. 47
      samples/ControlCatalog/Pages/CalendarPage.xaml
  15. 28
      samples/ControlCatalog/Pages/CalendarPage.xaml.cs
  16. 10
      samples/ControlCatalog/Pages/CarouselPage.xaml
  17. 5
      samples/ControlCatalog/Pages/CarouselPage.xaml.cs
  18. 25
      samples/ControlCatalog/Pages/CheckBoxPage.xaml
  19. 2
      samples/ControlCatalog/Pages/DialogsPage.xaml.cs
  20. 24
      samples/ControlCatalog/Pages/RadioButtonPage.xaml
  21. 14
      samples/RenderTest/Program.cs
  22. 1
      samples/RenderTest/RenderTest.csproj
  23. 13
      samples/VirtualizationTest/Program.cs
  24. 1
      samples/VirtualizationTest/VirtualizationTest.csproj
  25. 1
      samples/interop/Direct3DInteropSample/Direct3DInteropSample.csproj
  26. 1
      scripts/ReplaceNugetCache.ps1
  27. 2
      scripts/ReplaceNugetCache.sh
  28. 3
      scripts/ReplaceNugetCacheRelease.ps1
  29. 2
      src/Android/Avalonia.AndroidTestApplication/MainActivity.cs
  30. 4
      src/Avalonia.Animation/Avalonia.Animation.csproj
  31. 4
      src/Avalonia.Base/Avalonia.Base.csproj
  32. 151
      src/Avalonia.Base/AvaloniaObject.cs
  33. 24
      src/Avalonia.Base/AvaloniaObjectExtensions.cs
  34. 5
      src/Avalonia.Base/Collections/AvaloniaList.cs
  35. 15
      src/Avalonia.Base/Collections/IAvaloniaList.cs
  36. 7
      src/Avalonia.Base/IAvaloniaObject.cs
  37. 46
      src/Avalonia.Base/PriorityBindingEntry.cs
  38. 63
      src/Avalonia.Base/PriorityValue.cs
  39. 16
      src/Avalonia.Base/Reactive/AnonymousSubject`1.cs
  40. 49
      src/Avalonia.Base/Reactive/AnonymousSubject`2.cs
  41. 156
      src/Avalonia.Base/Utilities/DeferredSetter.cs
  42. 4
      src/Avalonia.Controls/Avalonia.Controls.csproj
  43. 2132
      src/Avalonia.Controls/Calendar/Calendar.cs
  44. 215
      src/Avalonia.Controls/Calendar/CalendarBlackoutDatesCollection.cs
  45. 193
      src/Avalonia.Controls/Calendar/CalendarButton.cs
  46. 79
      src/Avalonia.Controls/Calendar/CalendarDateRange.cs
  47. 253
      src/Avalonia.Controls/Calendar/CalendarDayButton.cs
  48. 21
      src/Avalonia.Controls/Calendar/CalendarExtensions.cs
  49. 1264
      src/Avalonia.Controls/Calendar/CalendarItem.cs
  50. 155
      src/Avalonia.Controls/Calendar/DateTimeHelper.cs
  51. 361
      src/Avalonia.Controls/Calendar/SelectedDatesCollection.cs
  52. 2
      src/Avalonia.Controls/Classes.cs
  53. 1
      src/Avalonia.Controls/ContentControl.cs
  54. 15
      src/Avalonia.Controls/Control.cs
  55. 33
      src/Avalonia.Controls/DropDown.cs
  56. 4
      src/Avalonia.Controls/IPanel.cs
  57. 11
      src/Avalonia.Controls/ItemsControl.cs
  58. 43
      src/Avalonia.Controls/Panel.cs
  59. 15
      src/Avalonia.Controls/Presenters/ContentPresenter.cs
  60. 31
      src/Avalonia.Controls/Primitives/PopupRoot.cs
  61. 4
      src/Avalonia.Controls/Primitives/RangeBase.cs
  62. 72
      src/Avalonia.Controls/Primitives/SelectingItemsControl.cs
  63. 36
      src/Avalonia.Controls/Primitives/ToggleButton.cs
  64. 12
      src/Avalonia.Controls/Primitives/Track.cs
  65. 5
      src/Avalonia.Controls/ProgressBar.cs
  66. 9
      src/Avalonia.Controls/RadioButton.cs
  67. 20
      src/Avalonia.Controls/TextBox.cs
  68. 5
      src/Avalonia.Controls/TreeView.cs
  69. 8
      src/Avalonia.Controls/VirtualizingStackPanel.cs
  70. 4
      src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj
  71. 6
      src/Avalonia.Diagnostics/Views/TreePage.xaml.cs
  72. 2
      src/Avalonia.DotNetCoreRuntime/Avalonia.DotNetCoreRuntime.csproj
  73. 4
      src/Avalonia.Input/Avalonia.Input.csproj
  74. 17
      src/Avalonia.Input/KeyGesture.cs
  75. 4
      src/Avalonia.Interactivity/Avalonia.Interactivity.csproj
  76. 4
      src/Avalonia.Layout/Avalonia.Layout.csproj
  77. 5
      src/Avalonia.Logging.Serilog/Avalonia.Logging.Serilog.csproj
  78. 51
      src/Avalonia.Logging.Serilog/SerilogExtensions.cs
  79. 3
      src/Avalonia.Remote.Protocol/BsonStreamTransport.cs
  80. 4
      src/Avalonia.Styling/Avalonia.Styling.csproj
  81. 2
      src/Avalonia.Styling/Styling/Setter.cs
  82. 4
      src/Avalonia.Themes.Default/Avalonia.Themes.Default.csproj
  83. 2
      src/Avalonia.Themes.Default/Button.xaml
  84. 30
      src/Avalonia.Themes.Default/Calendar.xaml
  85. 80
      src/Avalonia.Themes.Default/CalendarButton.xaml
  86. 116
      src/Avalonia.Themes.Default/CalendarDayButton.xaml
  87. 183
      src/Avalonia.Themes.Default/CalendarItem.xaml
  88. 33
      src/Avalonia.Themes.Default/CheckBox.xaml
  89. 2
      src/Avalonia.Themes.Default/ContentControl.xaml
  90. 4
      src/Avalonia.Themes.Default/DefaultTheme.xaml
  91. 2
      src/Avalonia.Themes.Default/DropDownItem.xaml
  92. 2
      src/Avalonia.Themes.Default/EmbeddableControlRoot.xaml
  93. 8
      src/Avalonia.Themes.Default/Expander.xaml
  94. 2
      src/Avalonia.Themes.Default/LayoutTransformControl.xaml
  95. 2
      src/Avalonia.Themes.Default/ListBoxItem.xaml
  96. 2
      src/Avalonia.Themes.Default/PopupRoot.xaml
  97. 16
      src/Avalonia.Themes.Default/RadioButton.xaml
  98. 2
      src/Avalonia.Themes.Default/RepeatButton.xaml
  99. 2
      src/Avalonia.Themes.Default/TabStripItem.xaml
  100. 2
      src/Avalonia.Themes.Default/ToggleButton.xaml

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

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

3
Avalonia.sln

@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26730.10
VisualStudioVersion = 15.0.27004.2008
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Base", "src\Avalonia.Base\Avalonia.Base.csproj", "{B09B78D8-9B26-48B0-9149-D64A2F120F3F}"
EndProject
@ -155,7 +155,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Props", "Props", "{F3AC8BC1
build\ReactiveUI.props = build\ReactiveUI.props
build\Rx.props = build\Rx.props
build\Serilog.props = build\Serilog.props
build\Serilog.Sinks.Trace.props = build\Serilog.Sinks.Trace.props
build\SharpDX.props = build\SharpDX.props
build\SkiaSharp.Desktop.props = build\SkiaSharp.Desktop.props
build\SkiaSharp.props = build\SkiaSharp.props

2
appveyor.yml

@ -1,4 +1,4 @@
os: Visual Studio 2017
os: Previous Visual Studio 2017
platform:
- Any CPU
skip_branch_with_pr: true

2
build/MonoMac.props

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

5
build/Serilog.Sinks.Trace.props

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

4
build/Serilog.props

@ -1,5 +1,7 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<PackageReference Include="Serilog" Version="2.4.0" />
<PackageReference Include="Serilog" Version="2.5.0" />
<PackageReference Include="Serilog.Sinks.Trace" Version="2.1.0" />
<PackageReference Include="Serilog.Sinks.Debug" Version="1.0.0" />
</ItemGroup>
</Project>

6
packages.cake

@ -107,6 +107,8 @@ public class Packages
context.Information("Setting NuGet package dependencies versions:");
var SerilogVersion = packageVersions["Serilog"].FirstOrDefault().Item1;
var SerilogSinksDebugVersion = packageVersions["Serilog.Sinks.Debug"].FirstOrDefault().Item1;
var SerilogSinksTraceVersion = packageVersions["Serilog.Sinks.Trace"].FirstOrDefault().Item1;
var SpracheVersion = packageVersions["Sprache"].FirstOrDefault().Item1;
var SystemReactiveVersion = packageVersions["System.Reactive"].FirstOrDefault().Item1;
var ReactiveUIVersion = packageVersions["reactiveui"].FirstOrDefault().Item1;
@ -243,6 +245,8 @@ public class Packages
Dependencies = new DependencyBuilder(this)
{
new NuSpecDependency() { Id = "Serilog", Version = SerilogVersion },
new NuSpecDependency() { Id = "Serilog.Sinks.Debug", Version = SerilogSinksDebugVersion },
new NuSpecDependency() { Id = "Serilog.Sinks.Trace", Version = SerilogSinksTraceVersion },
new NuSpecDependency() { Id = "Sprache", Version = SpracheVersion },
new NuSpecDependency() { Id = "System.Reactive", Version = SystemReactiveVersion },
new NuSpecDependency() { Id = "Avalonia.Remote.Protocol", Version = parameters.Version },
@ -251,6 +255,8 @@ public class Packages
new NuSpecDependency() { Id = "Microsoft.Extensions.DependencyModel", TargetFramework = "netcoreapp2.0", Version = "1.1.0" },
new NuSpecDependency() { Id = "NETStandard.Library", TargetFramework = "netcoreapp2.0", Version = "1.6.0" },
new NuSpecDependency() { Id = "Serilog", TargetFramework = "netcoreapp2.0", Version = SerilogVersion },
new NuSpecDependency() { Id = "Serilog.Sinks.Debug", TargetFramework = "netcoreapp2.0", Version = SerilogSinksDebugVersion },
new NuSpecDependency() { Id = "Serilog.Sinks.Trace", TargetFramework = "netcoreapp2.0", Version = SerilogSinksTraceVersion },
new NuSpecDependency() { Id = "Sprache", TargetFramework = "netcoreapp2.0", Version = SpracheVersion },
new NuSpecDependency() { Id = "System.Reactive", TargetFramework = "netcoreapp2.0", Version = SystemReactiveVersion },
new NuSpecDependency() { Id = "Avalonia.Remote.Protocol", TargetFramework = "netcoreapp2.0", Version = parameters.Version },

13
samples/BindingTest/App.xaml.cs

@ -16,22 +16,11 @@ namespace BindingTest
private static void Main()
{
InitializeLogging();
AppBuilder.Configure<App>()
.UsePlatformDetect()
.UseReactiveUI()
.LogToDebug()
.Start<MainWindow>();
}
private static void InitializeLogging()
{
#if DEBUG
SerilogLogger.Initialize(new LoggerConfiguration()
.MinimumLevel.Warning()
.WriteTo.Trace(outputTemplate: "{Area}: {Message}")
.CreateLogger());
#endif
}
}
}

1
samples/BindingTest/BindingTest.csproj

@ -151,7 +151,6 @@
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="..\..\build\Serilog.props" />
<Import Project="..\..\build\Serilog.Sinks.Trace.props" />
<Import Project="..\..\build\Rx.props" />
<Import Project="..\..\build\ReactiveUI.props" />
</Project>

1
samples/ControlCatalog.Desktop/ControlCatalog.Desktop.csproj

@ -135,5 +135,4 @@
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="..\..\build\Serilog.props" />
<Import Project="..\..\build\SkiaSharp.props" />
<Import Project="..\..\build\Serilog.Sinks.Trace.props" />
</Project>

15
samples/ControlCatalog.Desktop/Program.cs

@ -12,8 +12,6 @@ namespace ControlCatalog
{
static void Main(string[] args)
{
InitializeLogging();
// TODO: Make this work with GTK/Skia/Cairo depending on command-line args
// again.
BuildAvaloniaApp().Start<MainWindow>();
@ -23,18 +21,7 @@ namespace ControlCatalog
/// This method is needed for IDE previewer infrastructure
/// </summary>
public static AppBuilder BuildAvaloniaApp()
=> AppBuilder.Configure<App>().UsePlatformDetect();
// This will be made into a runtime configuration extension soon!
private static void InitializeLogging()
{
#if DEBUG
SerilogLogger.Initialize(new LoggerConfiguration()
.MinimumLevel.Warning()
.WriteTo.Trace(outputTemplate: "{Area}: {Message}")
.CreateLogger());
#endif
}
=> AppBuilder.Configure<App>().LogToDebug().UsePlatformDetect();
private static void ConfigureAssetAssembly(AppBuilder builder)
{

6
samples/ControlCatalog/ControlCatalog.csproj

@ -44,6 +44,9 @@
<EmbeddedResource Include="Pages\ButtonPage.xaml">
<SubType>Designer</SubType>
</EmbeddedResource>
<EmbeddedResource Include="Pages\CalendarPage.xaml">
<SubType>Designer</SubType>
</EmbeddedResource>
<EmbeddedResource Include="Pages\CanvasPage.xaml">
<SubType>Designer</SubType>
</EmbeddedResource>
@ -107,6 +110,9 @@
<Compile Include="Pages\ButtonPage.xaml.cs">
<DependentUpon>ButtonPage.xaml</DependentUpon>
</Compile>
<Compile Include="Pages\CalendarPage.xaml.cs">
<DependentUpon>CalendarPage.xaml</DependentUpon>
</Compile>
<Compile Include="Pages\CanvasPage.xaml.cs">
<DependentUpon>CanvasPage.xaml</DependentUpon>
</Compile>

1
samples/ControlCatalog/MainView.xaml

@ -7,6 +7,7 @@
</TabControl.Transition>
<TabItem Header="Border"><pages:BorderPage/></TabItem>
<TabItem Header="Button"><pages:ButtonPage/></TabItem>
<TabItem Header="Calendar"><pages:CalendarPage/></TabItem>
<TabItem Header="Canvas"><pages:CanvasPage/></TabItem>
<TabItem Header="Carousel"><pages:CarouselPage/></TabItem>
<TabItem Header="CheckBox"><pages:CheckBoxPage/></TabItem>

47
samples/ControlCatalog/Pages/CalendarPage.xaml

@ -0,0 +1,47 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel Orientation="Vertical" Gap="4">
<TextBlock Classes="h1">Calendar</TextBlock>
<TextBlock Classes="h2">A calendar control for selecting dates</TextBlock>
<StackPanel Orientation="Horizontal"
Margin="0,16,0,0"
HorizontalAlignment="Center"
Gap="16">
<StackPanel Orientation="Vertical">
<TextBlock Text="SelectionMode: None"/>
<Calendar SelectionMode="None"
Margin="0,0,0,8"/>
<TextBlock Text="SelectionMode: SingleDate"/>
<Calendar SelectionMode="SingleDate"
Margin="0,0,0,8"/>
<TextBlock Text="Disabled"/>
<Calendar IsEnabled="False"
SelectionMode="SingleDate"/>
</StackPanel>
<StackPanel Orientation="Vertical">
<TextBlock Text="SelectionMode: SingleRange"/>
<Calendar SelectionMode="SingleRange"
Margin="0,0,0,8"/>
<TextBlock Text="SelectionMode: MultipleRange"/>
<Calendar SelectionMode="MultipleRange"/>
</StackPanel>
<StackPanel Orientation="Vertical">
<TextBlock Text="DisplayDates"/>
<Calendar Name="DisplayDatesCalendar"
SelectionMode="SingleDate"
Margin="0,0,0,8"/>
<TextBlock Text="BlackoutDates"/>
<Calendar Name="BlackoutDatesCalendar"
SelectionMode="SingleDate" />
</StackPanel>
</StackPanel>
</StackPanel>
</UserControl>

28
samples/ControlCatalog/Pages/CalendarPage.xaml.cs

@ -0,0 +1,28 @@
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using System;
namespace ControlCatalog.Pages
{
public class CalendarPage : UserControl
{
public CalendarPage()
{
this.InitializeComponent();
var today = DateTime.Today;
var cal1 = this.FindControl<Calendar>("DisplayDatesCalendar");
cal1.DisplayDateStart = today.AddDays(-25);
cal1.DisplayDateEnd = today.AddDays(25);
var cal2 = this.FindControl<Calendar>("BlackoutDatesCalendar");
cal2.BlackoutDates.AddDatesInPast();
cal2.BlackoutDates.Add(new CalendarDateRange(today.AddDays(6)));
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}
}

10
samples/ControlCatalog/Pages/CarouselPage.xaml

@ -9,7 +9,7 @@
</Button>
<Carousel Name="carousel">
<Carousel.Transition>
<PageSlide Duration="0.25"/>
<PageSlide Duration="0.25" Orientation="Vertical" />
</Carousel.Transition>
<Image Source="resm:ControlCatalog.Assets.delicate-arch-896885_640.jpg"/>
<Image Source="resm:ControlCatalog.Assets.hirsch-899118_640.jpg"/>
@ -28,6 +28,14 @@
<DropDownItem>Crossfade</DropDownItem>
</DropDown>
</StackPanel>
<StackPanel Orientation="Horizontal" Gap="4">
<TextBlock VerticalAlignment="Center">Orientation</TextBlock>
<DropDown Name="orientation" SelectedIndex="1" VerticalAlignment="Center">
<DropDownItem>Horizontal</DropDownItem>
<DropDownItem>Vertical</DropDownItem>
</DropDown>
</StackPanel>
</StackPanel>
</UserControl>

5
samples/ControlCatalog/Pages/CarouselPage.xaml.cs

@ -11,6 +11,7 @@ namespace ControlCatalog.Pages
private Button _left;
private Button _right;
private DropDown _transition;
private DropDown _orientation;
public CarouselPage()
{
@ -18,6 +19,7 @@ namespace ControlCatalog.Pages
_left.Click += (s, e) => _carousel.Previous();
_right.Click += (s, e) => _carousel.Next();
_transition.SelectionChanged += TransitionChanged;
_orientation.SelectionChanged += TransitionChanged;
}
private void InitializeComponent()
@ -27,6 +29,7 @@ namespace ControlCatalog.Pages
_left = this.FindControl<Button>("left");
_right = this.FindControl<Button>("right");
_transition = this.FindControl<DropDown>("transition");
_orientation = this.FindControl<DropDown>("orientation");
}
private void TransitionChanged(object sender, SelectionChangedEventArgs e)
@ -37,7 +40,7 @@ namespace ControlCatalog.Pages
_carousel.Transition = null;
break;
case 1:
_carousel.Transition = new PageSlide(TimeSpan.FromSeconds(0.25));
_carousel.Transition = new PageSlide(TimeSpan.FromSeconds(0.25), _orientation.SelectedIndex == 0 ? PageSlide.SlideAxis.Horizontal : PageSlide.SlideAxis.Vertical);
break;
case 2:
_carousel.Transition = new CrossFade(TimeSpan.FromSeconds(0.25));

25
samples/ControlCatalog/Pages/CheckBoxPage.xaml

@ -1,15 +1,28 @@
<UserControl xmlns="https://github.com/avaloniaui">
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel Orientation="Vertical" Gap="4">
<TextBlock Classes="h1">CheckBox</TextBlock>
<TextBlock Classes="h2">A check box control</TextBlock>
<StackPanel Orientation="Vertical"
<StackPanel Orientation="Horizontal"
Margin="0,16,0,0"
HorizontalAlignment="Center"
Gap="16">
<CheckBox>Unchecked</CheckBox>
<CheckBox IsChecked="True">Checked</CheckBox>
<CheckBox IsChecked="True" IsEnabled="False">Disabled</CheckBox>
</StackPanel>
<StackPanel Orientation="Vertical"
Gap="16">
<CheckBox>Unchecked</CheckBox>
<CheckBox IsChecked="True">Checked</CheckBox>
<CheckBox IsChecked="{x:Null}">Indeterminate</CheckBox>
<CheckBox IsChecked="True" IsEnabled="False">Disabled</CheckBox>
</StackPanel>
<StackPanel Orientation="Vertical"
HorizontalAlignment="Center"
Gap="16">
<CheckBox IsChecked="False" IsThreeState="True">Three State: Unchecked</CheckBox>
<CheckBox IsChecked="True" IsThreeState="True">Three State: Checked</CheckBox>
<CheckBox IsChecked="{x:Null}" IsThreeState="True">Three State: Indeterminate</CheckBox>
<CheckBox IsChecked="{x:Null}" IsThreeState="True" IsEnabled="False">Three State: Disabled</CheckBox>
</StackPanel>
</StackPanel>
</StackPanel>
</UserControl>

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

@ -36,7 +36,7 @@ namespace ControlCatalog.Pages
};
}
Window GetWindow() => this.FindControl<CheckBox>("IsModal").IsChecked ? (Window)this.VisualRoot : null;
Window GetWindow() => this.FindControl<CheckBox>("IsModal").IsChecked.Value ? (Window)this.VisualRoot : null;
private void InitializeComponent()
{

24
samples/ControlCatalog/Pages/RadioButtonPage.xaml

@ -1,15 +1,27 @@
<UserControl xmlns="https://github.com/avaloniaui">
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel Orientation="Vertical" Gap="4">
<TextBlock Classes="h1">RadioButton</TextBlock>
<TextBlock Classes="h2">Allows the selection of a single option of many</TextBlock>
<StackPanel Orientation="Vertical"
<StackPanel Orientation="Horizontal"
Margin="0,16,0,0"
HorizontalAlignment="Center"
Gap="16">
<RadioButton IsChecked="True">Option 1</RadioButton>
<RadioButton>Option 2</RadioButton>
<RadioButton IsEnabled="False">Disabled</RadioButton>
</StackPanel>
<StackPanel Orientation="Vertical"
Gap="16">
<RadioButton IsChecked="True">Option 1</RadioButton>
<RadioButton>Option 2</RadioButton>
<RadioButton IsChecked="{x:Null}">Option 3</RadioButton>
<RadioButton IsEnabled="False">Disabled</RadioButton>
</StackPanel>
<StackPanel Orientation="Vertical"
Gap="16">
<RadioButton IsChecked="True" IsThreeState="True">Three States: Option 1</RadioButton>
<RadioButton IsChecked="False" IsThreeState="True">Three States: Option 2</RadioButton>
<RadioButton IsChecked="{x:Null}" IsThreeState="True">Three States: Option 3</RadioButton>
<RadioButton IsChecked="{x:Null}" IsThreeState="True" IsEnabled="False">Disabled</RadioButton>
</StackPanel>
</StackPanel>
</StackPanel>
</UserControl>

14
samples/RenderTest/Program.cs

@ -12,25 +12,13 @@ namespace RenderTest
{
static void Main(string[] args)
{
InitializeLogging();
// TODO: Make this work with GTK/Skia/Cairo depending on command-line args
// again.
AppBuilder.Configure<App>()
.UsePlatformDetect()
.UseReactiveUI()
.LogToDebug()
.Start<MainWindow>();
}
// This will be made into a runtime configuration extension soon!
private static void InitializeLogging()
{
#if DEBUG
SerilogLogger.Initialize(new LoggerConfiguration()
.MinimumLevel.Warning()
.WriteTo.Trace(outputTemplate: "{Area}: {Message}")
.CreateLogger());
#endif
}
}
}

1
samples/RenderTest/RenderTest.csproj

@ -180,7 +180,6 @@
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="..\..\build\Serilog.props" />
<Import Project="..\..\build\Serilog.Sinks.Trace.props" />
<Import Project="..\..\build\Rx.props" />
<Import Project="..\..\build\ReactiveUI.props" />
</Project>

13
samples/VirtualizationTest/Program.cs

@ -13,22 +13,11 @@ namespace VirtualizationTest
{
static void Main(string[] args)
{
InitializeLogging();
AppBuilder.Configure<App>()
.UsePlatformDetect()
.UseReactiveUI()
.LogToDebug()
.Start<MainWindow>();
}
private static void InitializeLogging()
{
#if DEBUG
SerilogLogger.Initialize(new LoggerConfiguration()
.MinimumLevel.Warning()
.WriteTo.Trace(outputTemplate: "{Area}: {Message}")
.CreateLogger());
#endif
}
}
}

1
samples/VirtualizationTest/VirtualizationTest.csproj

@ -147,7 +147,6 @@
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="..\..\build\Serilog.props" />
<Import Project="..\..\build\Serilog.Sinks.Trace.props" />
<Import Project="..\..\build\Rx.props" />
<Import Project="..\..\build\ReactiveUI.props" />
</Project>

1
samples/interop/Direct3DInteropSample/Direct3DInteropSample.csproj

@ -28,6 +28,5 @@
<ProjectReference Include="..\..\..\src\Windows\Avalonia.Win32\Avalonia.Win32.csproj" />
</ItemGroup>
<Import Project="..\..\..\build\Serilog.props" />
<Import Project="..\..\..\build\Serilog.Sinks.Trace.props" />
<Import Project="..\..\..\build\Rx.props" />
</Project>

1
scripts/ReplaceNugetCache.ps1

@ -2,3 +2,4 @@ copy ..\samples\ControlCatalog.NetCore\bin\Debug\netcoreapp2.0\Avalonia**.dll ~\
copy ..\samples\ControlCatalog.NetCore.\bin\Debug\netcoreapp2.0\Avalonia**.dll ~\.nuget\packages\avalonia\$args\lib\netstandard2.0\
copy ..\samples\ControlCatalog.NetCore.\bin\Debug\netcoreapp2.0\Avalonia**.dll ~\.nuget\packages\avalonia.gtk3\$args\lib\netstandard2.0\
copy ..\samples\ControlCatalog.NetCore.\bin\Debug\netcoreapp2.0\Avalonia**.dll ~\.nuget\packages\avalonia.win32\$args\lib\netstandard2.0\
copy ..\samples\ControlCatalog.NetCore.\bin\Debug\netcoreapp2.0\Avalonia**.dll ~\.nuget\packages\avalonia.skia\$args\lib\netstandard2.0\

2
scripts/ReplaceNugetCache.sh

@ -3,4 +3,6 @@
cp ../samples/ControlCatalog.NetCore/bin/Debug/netcoreapp2.0/Avalonia**.dll ~/.nuget/packages/avalonia/$1/lib/netcoreapp2.0/
cp ../samples/ControlCatalog.NetCore/bin/Debug/netcoreapp2.0/Avalonia**.dll ~/.nuget/packages/avalonia/$1/lib/netstandard2.0/
cp ../samples/ControlCatalog.NetCore/bin/Debug/netcoreapp2.0/Avalonia**.dll ~/.nuget/packages/avalonia.gtk3/$1/lib/netstandard2.0/
cp ../samples/ControlCatalog.NetCore/bin/Debug/netcoreapp2.0/Avalonia**.dll ~/.nuget/packages/avalonia.skia/$1/lib/netstandard2.0/

3
scripts/ReplaceNugetCacheRelease.ps1

@ -1,4 +1,5 @@
copy ..\samples\ControlCatalog.NetCore\bin\Release\netcoreapp2.0\Avalonia**.dll ~\.nuget\packages\avalonia\$args\lib\netcoreapp2.0\
copy ..\samples\ControlCatalog.NetCore.\bin\Release\netcoreapp2.0\Avalonia**.dll ~\.nuget\packages\avalonia\$args\lib\netstandard2.0\
copy ..\samples\ControlCatalog.NetCore.\bin\Release\netcoreapp2.0\Avalonia**.dll ~\.nuget\packages\avalonia.gtk3\$args\lib\netstandard2.0\
copy ..\samples\ControlCatalog.NetCore.\bin\Release\netcoreapp2.0\Avalonia**.dll ~\.nuget\packages\avalonia.win32\$args\lib\netstandard2.0\
copy ..\samples\ControlCatalog.NetCore.\bin\Release\netcoreapp2.0\Avalonia**.dll ~\.nuget\packages\avalonia.win32\$args\lib\netstandard2.0\
copy ..\samples\ControlCatalog.NetCore.\bin\Release\netcoreapp2.0\Avalonia**.dll ~\.nuget\packages\avalonia.skia\$args\lib\netstandard2.0\

2
src/Android/Avalonia.AndroidTestApplication/MainActivity.cs

@ -56,7 +56,7 @@ namespace Avalonia.AndroidTestApplication
{
Margin = new Thickness(30),
Background = Brushes.Yellow,
Children = new Avalonia.Controls.Controls
Children =
{
new TextBlock
{

4
src/Avalonia.Animation/Avalonia.Animation.csproj

@ -11,7 +11,7 @@
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<DocumentationFile>bin\Debug\Avalonia.Animation.XML</DocumentationFile>
<DocumentationFile>bin\Debug\Avalonia.Animation.xml</DocumentationFile>
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
@ -21,7 +21,7 @@
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<DocumentationFile>bin\Release\Avalonia.Animation.XML</DocumentationFile>
<DocumentationFile>bin\Release\Avalonia.Animation.xml</DocumentationFile>
<NoWarn>CS1591</NoWarn>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>

4
src/Avalonia.Base/Avalonia.Base.csproj

@ -12,7 +12,7 @@
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<DocumentationFile>bin\Debug\Avalonia.Base.XML</DocumentationFile>
<DocumentationFile>bin\Debug\Avalonia.Base.xml</DocumentationFile>
<NoWarn>CS1591</NoWarn>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
@ -22,7 +22,7 @@
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<DocumentationFile>bin\Release\Avalonia.Base.XML</DocumentationFile>
<DocumentationFile>bin\Release\Avalonia.Base.xml</DocumentationFile>
<NoWarn>CS1591</NoWarn>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>

151
src/Avalonia.Base/AvaloniaObject.cs

@ -51,6 +51,21 @@ namespace Avalonia
/// </summary>
private EventHandler<AvaloniaPropertyChangedEventArgs> _propertyChanged;
private DeferredSetter<AvaloniaProperty, object> _directDeferredSetter;
/// <summary>
/// Delayed setter helper for direct properties. Used to fix #855.
/// </summary>
private DeferredSetter<AvaloniaProperty, object> DirectPropertyDeferredSetter
{
get
{
return _directDeferredSetter ??
(_directDeferredSetter = new DeferredSetter<AvaloniaProperty, object>());
}
}
/// <summary>
/// Initializes a new instance of the <see cref="AvaloniaObject"/> class.
/// </summary>
@ -225,6 +240,19 @@ namespace Avalonia
return (T)GetValue((AvaloniaProperty)property);
}
/// <summary>
/// Checks whether a <see cref="AvaloniaProperty"/> is animating.
/// </summary>
/// <param name="property">The property.</param>
/// <returns>True if the property is animating, otherwise false.</returns>
public bool IsAnimating(AvaloniaProperty property)
{
Contract.Requires<ArgumentNullException>(property != null);
VerifyAccess();
return _values.TryGetValue(property, out PriorityValue value) ? value.IsAnimating : false;
}
/// <summary>
/// Checks whether a <see cref="AvaloniaProperty"/> is set on this object.
/// </summary>
@ -311,9 +339,6 @@ namespace Avalonia
var description = GetDescription(source);
var scheduler = AvaloniaLocator.Current.GetService<IScheduler>() ?? ImmediateScheduler.Instance;
source = source.ObserveOn(scheduler);
if (property.IsDirect)
{
if (property.IsReadOnly)
@ -539,6 +564,45 @@ namespace Avalonia
}
}
/// <summary>
/// A callback type for encapsulating complex logic for setting direct properties.
/// </summary>
/// <typeparam name="T">The type of the property.</typeparam>
/// <param name="value">The value to which to set the property.</param>
/// <param name="field">The backing field for the property.</param>
/// <param name="notifyWrapper">A wrapper for the property-changed notification.</param>
protected delegate void SetAndRaiseCallback<T>(T value, ref T field, Action<Action> notifyWrapper);
/// <summary>
/// Sets the backing field for a direct avalonia property, raising the
/// <see cref="PropertyChanged"/> event if the value has changed.
/// </summary>
/// <typeparam name="T">The type of the property.</typeparam>
/// <param name="property">The property.</param>
/// <param name="field">The backing field.</param>
/// <param name="setterCallback">A callback called to actually set the value to the backing field.</param>
/// <param name="value">The value.</param>
/// <returns>
/// True if the value changed, otherwise false.
/// </returns>
protected bool SetAndRaise<T>(
AvaloniaProperty<T> property,
ref T field,
SetAndRaiseCallback<T> setterCallback,
T value)
{
Contract.Requires<ArgumentNullException>(setterCallback != null);
return DirectPropertyDeferredSetter.SetAndNotify(
property,
ref field,
(object val, ref T backing, Action<Action> notify) =>
{
setterCallback((T)val, ref backing, notify);
return true;
},
value);
}
/// <summary>
/// Sets the backing field for a direct avalonia property, raising the
/// <see cref="PropertyChanged"/> event if the value has changed.
@ -553,17 +617,32 @@ namespace Avalonia
protected bool SetAndRaise<T>(AvaloniaProperty<T> property, ref T field, T value)
{
VerifyAccess();
if (!object.Equals(field, value))
{
var old = field;
field = value;
RaisePropertyChanged(property, old, value, BindingPriority.LocalValue);
return true;
}
else
{
return false;
}
return SetAndRaise(
property,
ref field,
(T val, ref T backing, Action<Action> notifyWrapper)
=> SetAndRaiseCore(property, ref backing, val, notifyWrapper),
value);
}
/// <summary>
/// Default assignment logic for SetAndRaise.
/// </summary>
/// <typeparam name="T">The type of the property.</typeparam>
/// <param name="property">The property.</param>
/// <param name="field">The backing field.</param>
/// <param name="value">The value.</param>
/// <param name="notifyWrapper">A wrapper for the property-changed notification.</param>
/// <returns>
/// True if the value changed, otherwise false.
/// </returns>
private bool SetAndRaiseCore<T>(AvaloniaProperty property, ref T field, T value, Action<Action> notifyWrapper)
{
var old = field;
field = value;
notifyWrapper(() => RaisePropertyChanged(property, old, value, BindingPriority.LocalValue));
return true;
}
/// <summary>
@ -661,29 +740,41 @@ namespace Avalonia
/// <param name="value">The value.</param>
private void SetDirectValue(AvaloniaProperty property, object value)
{
var notification = value as BindingNotification;
if (notification != null)
void Set()
{
notification.LogIfError(this, property);
value = notification.Value;
}
var notification = value as BindingNotification;
if (notification == null || notification.ErrorType == BindingErrorType.Error || notification.HasValue)
{
var metadata = (IDirectPropertyMetadata)property.GetMetadata(GetType());
var accessor = (IDirectPropertyAccessor)GetRegistered(property);
var finalValue = value == AvaloniaProperty.UnsetValue ?
metadata.UnsetValue : value;
if (notification != null)
{
notification.LogIfError(this, property);
value = notification.Value;
}
LogPropertySet(property, value, BindingPriority.LocalValue);
if (notification == null || notification.ErrorType == BindingErrorType.Error || notification.HasValue)
{
var metadata = (IDirectPropertyMetadata)property.GetMetadata(GetType());
var accessor = (IDirectPropertyAccessor)GetRegistered(property);
var finalValue = value == AvaloniaProperty.UnsetValue ?
metadata.UnsetValue : value;
accessor.SetValue(this, finalValue);
LogPropertySet(property, value, BindingPriority.LocalValue);
accessor.SetValue(this, finalValue);
}
if (notification != null)
{
UpdateDataValidation(property, notification);
}
}
if (notification != null)
if (Dispatcher.UIThread.CheckAccess())
{
Set();
}
else
{
UpdateDataValidation(property, notification);
Dispatcher.UIThread.InvokeAsync(Set);
}
}

24
src/Avalonia.Base/AvaloniaObjectExtensions.cs

@ -138,17 +138,9 @@ namespace Avalonia
AvaloniaProperty property,
BindingPriority priority = BindingPriority.LocalValue)
{
// TODO: Subject.Create<T> is not yet in stable Rx : once it is, remove the
// AnonymousSubject classes and use Subject.Create<T>.
var output = new Subject<object>();
var result = new AnonymousSubject<object>(
Observer.Create<object>(
x => output.OnNext(x),
e => output.OnError(e),
() => output.OnCompleted()),
return Subject.Create<object>(
Observer.Create<object>(x => o.SetValue(property, x, priority)),
o.GetObservable(property));
o.Bind(property, output, priority);
return result;
}
/// <summary>
@ -169,17 +161,9 @@ namespace Avalonia
AvaloniaProperty<T> property,
BindingPriority priority = BindingPriority.LocalValue)
{
// TODO: Subject.Create<T> is not yet in stable Rx : once it is, remove the
// AnonymousSubject classes from this file and use Subject.Create<T>.
var output = new Subject<T>();
var result = new AnonymousSubject<T>(
Observer.Create<T>(
x => output.OnNext(x),
e => output.OnError(e),
() => output.OnCompleted()),
return Subject.Create<T>(
Observer.Create<T>(x => o.SetValue(property, x, priority)),
o.GetObservable(property));
o.Bind(property, output, priority);
return result;
}
/// <summary>

5
src/Avalonia.Base/Collections/AvaloniaList.cs

@ -350,14 +350,15 @@ namespace Avalonia.Collections
public void MoveRange(int oldIndex, int count, int newIndex)
{
var items = _inner.GetRange(oldIndex, count);
var modifiedNewIndex = newIndex;
_inner.RemoveRange(oldIndex, count);
if (newIndex > oldIndex)
{
newIndex -= count;
modifiedNewIndex -= count;
}
_inner.InsertRange(newIndex, items);
_inner.InsertRange(modifiedNewIndex, items);
if (_collectionChanged != null)
{

15
src/Avalonia.Base/Collections/IAvaloniaList.cs

@ -36,6 +36,21 @@ namespace Avalonia.Collections
/// <param name="items">The items.</param>
void InsertRange(int index, IEnumerable<T> items);
/// <summary>
/// Moves an item to a new index.
/// </summary>
/// <param name="oldIndex">The index of the item to move.</param>
/// <param name="newIndex">The index to move the item to.</param>
void Move(int oldIndex, int newIndex);
/// <summary>
/// Moves multiple items to a new index.
/// </summary>
/// <param name="oldIndex">The first index of the items to move.</param>
/// <param name="count">The number of items to move.</param>
/// <param name="newIndex">The index to move the items to.</param>
void MoveRange(int oldIndex, int count, int newIndex);
/// <summary>
/// Removes multiple items from the collection.
/// </summary>

7
src/Avalonia.Base/IAvaloniaObject.cs

@ -31,6 +31,13 @@ namespace Avalonia
/// <returns>The value.</returns>
T GetValue<T>(AvaloniaProperty<T> property);
/// <summary>
/// Checks whether a <see cref="AvaloniaProperty"/> is animating.
/// </summary>
/// <param name="property">The property.</param>
/// <returns>True if the property is animating, otherwise false.</returns>
bool IsAnimating(AvaloniaProperty property);
/// <summary>
/// Checks whether a <see cref="AvaloniaProperty"/> is set on this object.
/// </summary>

46
src/Avalonia.Base/PriorityBindingEntry.cs

@ -3,6 +3,7 @@
using System;
using Avalonia.Data;
using Avalonia.Threading;
namespace Avalonia
{
@ -92,33 +93,50 @@ namespace Avalonia
private void ValueChanged(object value)
{
_owner.Owner.Owner?.VerifyAccess();
var notification = value as BindingNotification;
if (notification != null)
void Signal()
{
if (notification.HasValue || notification.ErrorType == BindingErrorType.Error)
var notification = value as BindingNotification;
if (notification != null)
{
Value = notification.Value;
_owner.Changed(this);
if (notification.HasValue || notification.ErrorType == BindingErrorType.Error)
{
Value = notification.Value;
_owner.Changed(this);
}
if (notification.ErrorType != BindingErrorType.None)
{
_owner.Error(this, notification);
}
}
if (notification.ErrorType != BindingErrorType.None)
else
{
_owner.Error(this, notification);
Value = value;
_owner.Changed(this);
}
}
if (Dispatcher.UIThread.CheckAccess())
{
Signal();
}
else
{
Value = value;
_owner.Changed(this);
Dispatcher.UIThread.InvokeAsync(Signal);
}
}
private void Completed()
{
_owner.Completed(this);
if (Dispatcher.UIThread.CheckAccess())
{
_owner.Completed(this);
}
else
{
Dispatcher.UIThread.InvokeAsync(() => _owner.Completed(this));
}
}
}
}

63
src/Avalonia.Base/PriorityValue.cs

@ -28,8 +28,10 @@ namespace Avalonia
{
private readonly Type _valueType;
private readonly SingleOrDictionary<int, PriorityLevel> _levels = new SingleOrDictionary<int, PriorityLevel>();
private object _value;
private readonly Func<object, object> _validate;
private static readonly DeferredSetter<PriorityValue, (object value, int priority)> delayedSetter = new DeferredSetter<PriorityValue, (object, int)>();
private (object value, int priority) _value;
/// <summary>
/// Initializes a new instance of the <see cref="PriorityValue"/> class.
@ -47,11 +49,22 @@ namespace Avalonia
Owner = owner;
Property = property;
_valueType = valueType;
_value = AvaloniaProperty.UnsetValue;
ValuePriority = int.MaxValue;
_value = (AvaloniaProperty.UnsetValue, int.MaxValue);
_validate = validate;
}
/// <summary>
/// Gets a value indicating whether the property is animating.
/// </summary>
public bool IsAnimating
{
get
{
return ValuePriority <= (int)BindingPriority.Animation &&
GetLevel(ValuePriority).ActiveBindingIndex != -1;
}
}
/// <summary>
/// Gets the owner of the value.
/// </summary>
@ -65,16 +78,12 @@ namespace Avalonia
/// <summary>
/// Gets the current value.
/// </summary>
public object Value => _value;
public object Value => _value.value;
/// <summary>
/// Gets the priority of the binding that is currently active.
/// </summary>
public int ValuePriority
{
get;
private set;
}
public int ValuePriority => _value.priority;
/// <summary>
/// Adds a new binding.
@ -234,25 +243,36 @@ namespace Avalonia
/// <param name="priority">The priority level that the value came from.</param>
private void UpdateValue(object value, int priority)
{
var notification = value as BindingNotification;
delayedSetter.SetAndNotify(this,
ref _value,
UpdateCore,
(value, priority));
}
private bool UpdateCore(
(object value, int priority) update,
ref (object value, int priority) backing,
Action<Action> notify)
{
var val = update.value;
var notification = val as BindingNotification;
object castValue;
if (notification != null)
{
value = (notification.HasValue) ? notification.Value : null;
val = (notification.HasValue) ? notification.Value : null;
}
if (TypeUtilities.TryConvertImplicit(_valueType, value, out castValue))
if (TypeUtilities.TryConvertImplicit(_valueType, val, out castValue))
{
var old = _value;
var old = backing.value;
if (_validate != null && castValue != AvaloniaProperty.UnsetValue)
{
castValue = _validate(castValue);
}
ValuePriority = priority;
_value = castValue;
backing = (castValue, update.priority);
if (notification?.HasValue == true)
{
@ -261,7 +281,7 @@ namespace Avalonia
if (notification == null || notification.HasValue)
{
Owner?.Changed(this, old, _value);
notify(() => Owner?.Changed(this, old, Value));
}
if (notification != null)
@ -272,14 +292,15 @@ namespace Avalonia
else
{
Logger.Error(
LogArea.Binding,
LogArea.Binding,
Owner,
"Binding produced invalid value for {$Property} ({$PropertyType}): {$Value} ({$ValueType})",
Property.Name,
_valueType,
value,
value?.GetType());
Property.Name,
_valueType,
val,
val?.GetType());
}
return true;
}
}
}

16
src/Avalonia.Base/Reactive/AnonymousSubject`1.cs

@ -1,16 +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.Subjects;
namespace Avalonia.Reactive
{
public class AnonymousSubject<T> : AnonymousSubject<T, T>, ISubject<T>
{
public AnonymousSubject(IObserver<T> observer, IObservable<T> observable)
: base(observer, observable)
{
}
}
}

49
src/Avalonia.Base/Reactive/AnonymousSubject`2.cs

@ -1,49 +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.Subjects;
namespace Avalonia.Reactive
{
public class AnonymousSubject<T, U> : ISubject<T, U>
{
private readonly IObserver<T> _observer;
private readonly IObservable<U> _observable;
public AnonymousSubject(IObserver<T> observer, IObservable<U> observable)
{
_observer = observer;
_observable = observable;
}
public void OnCompleted()
{
_observer.OnCompleted();
}
public void OnError(Exception error)
{
if (error == null)
throw new ArgumentNullException("error");
_observer.OnError(error);
}
public void OnNext(T value)
{
_observer.OnNext(value);
}
public IDisposable Subscribe(IObserver<U> observer)
{
if (observer == null)
throw new ArgumentNullException("observer");
//
// [OK] Use of unsafe Subscribe: non-pretentious wrapping of an observable sequence.
//
return _observable.Subscribe/*Unsafe*/(observer);
}
}
}

156
src/Avalonia.Base/Utilities/DeferredSetter.cs

@ -0,0 +1,156 @@
using System;
using System.Collections.Generic;
using System.Reactive.Disposables;
using System.Runtime.CompilerServices;
using System.Text;
namespace Avalonia.Utilities
{
/// <summary>
/// A utility class to enable deferring assignment until after property-changed notifications are sent.
/// </summary>
/// <typeparam name="TProperty">The type of the object that represents the property.</typeparam>
/// <typeparam name="TSetRecord">The type of value with which to track the delayed assignment.</typeparam>
class DeferredSetter<TProperty, TSetRecord>
where TProperty: class
{
private struct NotifyDisposable : IDisposable
{
private readonly SettingStatus status;
internal NotifyDisposable(SettingStatus status)
{
this.status = status;
status.Notifying = true;
}
public void Dispose()
{
status.Notifying = false;
}
}
/// <summary>
/// Information on current setting/notification status of a property.
/// </summary>
private class SettingStatus
{
public bool Notifying { get; set; }
private Queue<TSetRecord> pendingValues;
public Queue<TSetRecord> PendingValues
{
get
{
return pendingValues ?? (pendingValues = new Queue<TSetRecord>());
}
}
}
private readonly ConditionalWeakTable<TProperty, SettingStatus> setRecords = new ConditionalWeakTable<TProperty, SettingStatus>();
/// <summary>
/// Mark the property as currently notifying.
/// </summary>
/// <param name="property">The property to mark as notifying.</param>
/// <returns>Returns a disposable that when disposed, marks the property as done notifying.</returns>
private NotifyDisposable MarkNotifying(TProperty property)
{
Contract.Requires<InvalidOperationException>(!IsNotifying(property));
return new NotifyDisposable(setRecords.GetOrCreateValue(property));
}
/// <summary>
/// Check if the property is currently notifying listeners.
/// </summary>
/// <param name="property">The property.</param>
/// <returns>If the property is currently notifying listeners.</returns>
private bool IsNotifying(TProperty property)
=> setRecords.TryGetValue(property, out var value) && value.Notifying;
/// <summary>
/// Add a pending assignment for the property.
/// </summary>
/// <param name="property">The property.</param>
/// <param name="value">The value to assign.</param>
private void AddPendingSet(TProperty property, TSetRecord value)
{
Contract.Requires<InvalidOperationException>(IsNotifying(property));
setRecords.GetOrCreateValue(property).PendingValues.Enqueue(value);
}
/// <summary>
/// Checks if there are any pending assignments for the property.
/// </summary>
/// <param name="property">The property to check.</param>
/// <returns>If the property has any pending assignments.</returns>
private bool HasPendingSet(TProperty property)
{
return setRecords.TryGetValue(property, out var status) && status.PendingValues.Count != 0;
}
/// <summary>
/// Gets the first pending assignment for the property.
/// </summary>
/// <param name="property">The property to check.</param>
/// <returns>The first pending assignment for the property.</returns>
private TSetRecord GetFirstPendingSet(TProperty property)
{
return setRecords.GetOrCreateValue(property).PendingValues.Dequeue();
}
public delegate bool SetterDelegate<TValue>(TSetRecord record, ref TValue backing, Action<Action> notifyCallback);
/// <summary>
/// Set the property and notify listeners while ensuring we don't get into a stack overflow as happens with #855 and #824
/// </summary>
/// <param name="property">The property to set.</param>
/// <param name="backing">The backing field for the property</param>
/// <param name="setterCallback">
/// A callback that actually sets the property.
/// The first parameter is the value to set, and the second is a wrapper that takes a callback that sends the property-changed notification.
/// </param>
/// <param name="value">The value to try to set.</param>
public bool SetAndNotify<TValue>(
TProperty property,
ref TValue backing,
SetterDelegate<TValue> setterCallback,
TSetRecord value)
{
Contract.Requires<ArgumentNullException>(setterCallback != null);
if (!IsNotifying(property))
{
bool updated = false;
if (!object.Equals(value, backing))
{
updated = setterCallback(value, ref backing, notification =>
{
using (MarkNotifying(property))
{
notification();
}
});
}
while (HasPendingSet(property))
{
updated |= setterCallback(GetFirstPendingSet(property), ref backing, notification =>
{
using (MarkNotifying(property))
{
notification();
}
});
}
return updated;
}
else if(!object.Equals(value, backing))
{
AddPendingSet(property, value);
}
return false;
}
}
}

4
src/Avalonia.Controls/Avalonia.Controls.csproj

@ -11,7 +11,7 @@
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<DocumentationFile>bin\Debug\Avalonia.Controls.XML</DocumentationFile>
<DocumentationFile>bin\Debug\Avalonia.Controls.xml</DocumentationFile>
<NoWarn>CS1591;CS0067</NoWarn>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
@ -21,7 +21,7 @@
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<DocumentationFile>bin\Release\Avalonia.Controls.XML</DocumentationFile>
<DocumentationFile>bin\Release\Avalonia.Controls.xml</DocumentationFile>
<NoWarn>CS1591</NoWarn>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>

2132
src/Avalonia.Controls/Calendar/Calendar.cs

File diff suppressed because it is too large

215
src/Avalonia.Controls/Calendar/CalendarBlackoutDatesCollection.cs

@ -0,0 +1,215 @@
// (c) Copyright Microsoft Corporation.
// This source is subject to the Microsoft Public License (Ms-PL).
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved.
using Avalonia.Threading;
using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading;
namespace Avalonia.Controls.Primitives
{
public sealed class CalendarBlackoutDatesCollection : ObservableCollection<CalendarDateRange>
{
/// <summary>
/// The Calendar whose dates this object represents.
/// </summary>
private Calendar _owner;
/// <summary>
/// Initializes a new instance of the
/// <see cref="T:Avalonia.Controls.Primitives.CalendarBlackoutDatesCollection" />
/// class.
/// </summary>
/// <param name="owner">
/// The <see cref="T:Avalonia.Controls.Calendar" /> whose dates
/// this object represents.
/// </param>
public CalendarBlackoutDatesCollection(Calendar owner)
{
_owner = owner ?? throw new ArgumentNullException(nameof(owner));
}
/// <summary>
/// Adds all dates before <see cref="P:System.DateTime.Today" /> to the
/// collection.
/// </summary>
public void AddDatesInPast()
{
Add(new CalendarDateRange(DateTime.MinValue, DateTime.Today.AddDays(-1)));
}
/// <summary>
/// Returns a value that represents whether this collection contains the
/// specified date.
/// </summary>
/// <param name="date">The date to search for.</param>
/// <returns>
/// True if the collection contains the specified date; otherwise,
/// false.
/// </returns>
public bool Contains(DateTime date)
{
int count = Count;
for (int i = 0; i < count; i++)
{
if (DateTimeHelper.InRange(date, this[i]))
{
return true;
}
}
return false;
}
/// <summary>
/// Returns a value that represents whether this collection contains the
/// specified range of dates.
/// </summary>
/// <param name="start">The start of the date range.</param>
/// <param name="end">The end of the date range.</param>
/// <returns>
/// True if all dates in the range are contained in the collection;
/// otherwise, false.
/// </returns>
public bool Contains(DateTime start, DateTime end)
{
DateTime rangeStart;
DateTime rangeEnd;
if (DateTime.Compare(end, start) > -1)
{
rangeStart = DateTimeHelper.DiscardTime(start).Value;
rangeEnd = DateTimeHelper.DiscardTime(end).Value;
}
else
{
rangeStart = DateTimeHelper.DiscardTime(end).Value;
rangeEnd = DateTimeHelper.DiscardTime(start).Value;
}
int count = Count;
for (int i = 0; i < count; i++)
{
CalendarDateRange range = this[i];
if (DateTime.Compare(range.Start, rangeStart) == 0 && DateTime.Compare(range.End, rangeEnd) == 0)
{
return true;
}
}
return false;
}
/// <summary>
/// Returns a value that represents whether this collection contains any
/// date in the specified range.
/// </summary>
/// <param name="range">The range of dates to search for.</param>
/// <returns>
/// True if any date in the range is contained in the collection;
/// otherwise, false.
/// </returns>
public bool ContainsAny(CalendarDateRange range)
{
return this.Any(r => r.ContainsAny(range));
}
/// <summary>
/// Removes all items from the collection.
/// </summary>
/// <remarks>
/// This implementation raises the CollectionChanged event.
/// </remarks>
protected override void ClearItems()
{
EnsureValidThread();
base.ClearItems();
_owner.UpdateMonths();
}
/// <summary>
/// Inserts an item into the collection at the specified index.
/// </summary>
/// <param name="index">
/// The zero-based index at which item should be inserted.
/// </param>
/// <param name="item">The object to insert.</param>
/// <remarks>
/// This implementation raises the CollectionChanged event.
/// </remarks>
protected override void InsertItem(int index, CalendarDateRange item)
{
EnsureValidThread();
if (!IsValid(item))
{
throw new ArgumentOutOfRangeException("Value is not valid.");
}
base.InsertItem(index, item);
_owner.UpdateMonths();
}
/// <summary>
/// Removes the item at the specified index of the collection.
/// </summary>
/// <param name="index">
/// The zero-based index of the element to remove.
/// </param>
/// <remarks>
/// This implementation raises the CollectionChanged event.
/// </remarks>
protected override void RemoveItem(int index)
{
EnsureValidThread();
base.RemoveItem(index);
_owner.UpdateMonths();
}
/// <summary>
/// Replaces the element at the specified index.
/// </summary>
/// <param name="index">
/// The zero-based index of the element to replace.
/// </param>
/// <param name="item">
/// The new value for the element at the specified index.
/// </param>
/// <remarks>
/// This implementation raises the CollectionChanged event.
/// </remarks>
protected override void SetItem(int index, CalendarDateRange item)
{
EnsureValidThread();
if (!IsValid(item))
{
throw new ArgumentOutOfRangeException("Value is not valid.");
}
base.SetItem(index, item);
_owner.UpdateMonths();
}
private bool IsValid(CalendarDateRange item)
{
foreach (DateTime day in _owner.SelectedDates)
{
if (DateTimeHelper.InRange(day, item))
{
return false;
}
}
return true;
}
private void EnsureValidThread()
{
Dispatcher.UIThread.VerifyAccess();
}
}
}

193
src/Avalonia.Controls/Calendar/CalendarButton.cs

@ -0,0 +1,193 @@
// (c) Copyright Microsoft Corporation.
// This source is subject to the Microsoft Public License (Ms-PL).
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved.
using Avalonia.Input;
using System;
namespace Avalonia.Controls.Primitives
{
/// <summary>
/// Represents a button on a
/// <see cref="T:Avalonia.Controls.Calendar" />.
/// </summary>
public sealed class CalendarButton : Button
{
/// <summary>
/// A value indicating whether the button is focused.
/// </summary>
private bool _isCalendarButtonFocused;
/// <summary>
/// A value indicating whether the button is inactive.
/// </summary>
private bool _isInactive;
/// <summary>
/// A value indicating whether the button is selected.
/// </summary>
private bool _isSelected;
/// <summary>
/// Initializes a new instance of the
/// <see cref="T:Avalonia.Controls.Primitives.CalendarButton" />
/// class.
/// </summary>
public CalendarButton()
: base()
{
Content = DateTimeHelper.GetCurrentDateFormat().AbbreviatedMonthNames[0];
}
/// <summary>
/// Gets or sets the Calendar associated with this button.
/// </summary>
internal Calendar Owner { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the button is focused.
/// </summary>
internal bool IsCalendarButtonFocused
{
get { return _isCalendarButtonFocused; }
set
{
if (_isCalendarButtonFocused != value)
{
_isCalendarButtonFocused = value;
SetPseudoClasses();
}
}
}
/// <summary>
/// Gets or sets a value indicating whether the button is inactive.
/// </summary>
internal bool IsInactive
{
get { return _isInactive; }
set
{
if (_isInactive != value)
{
_isInactive = value;
SetPseudoClasses();
}
}
}
/// <summary>
/// Gets or sets a value indicating whether the button is selected.
/// </summary>
internal bool IsSelected
{
get { return _isSelected; }
set
{
if (_isSelected != value)
{
_isSelected = value;
SetPseudoClasses();
}
}
}
/// <summary>
/// Builds the visual tree for the
/// <see cref="T:System.Windows.Controls.Primitives.CalendarButton" />
/// when a new template is applied.
/// </summary>
protected override void OnTemplateApplied(TemplateAppliedEventArgs e)
{
base.OnTemplateApplied(e);
SetPseudoClasses();
}
/// <summary>
/// Sets PseudoClasses based on current state.
/// </summary>
private void SetPseudoClasses()
{
PseudoClasses.Set(":selected", IsSelected);
PseudoClasses.Set(":inactive", IsInactive);
PseudoClasses.Set(":btnfocused", IsCalendarButtonFocused && IsEnabled);
}
/// <summary>
/// Occurs when the left mouse button is pressed (or when the tip of the
/// stylus touches the tablet PC) while the mouse pointer is over a
/// UIElement.
/// </summary>
public event EventHandler<PointerPressedEventArgs> CalendarLeftMouseButtonDown;
/// <summary>
/// Occurs when the left mouse button is released (or the tip of the
/// stylus is removed from the tablet PC) while the mouse (or the
/// stylus) is over a UIElement (or while a UIElement holds mouse
/// capture).
/// </summary>
public event EventHandler<PointerReleasedEventArgs> CalendarLeftMouseButtonUp;
/// <summary>
/// Provides class handling for the MouseLeftButtonDown event that
/// occurs when the left mouse button is pressed while the mouse pointer
/// is over this control.
/// </summary>
/// <param name="e">The event data. </param>
/// <exception cref="System.ArgumentNullException">
/// e is a null reference (Nothing in Visual Basic).
/// </exception>
/// <remarks>
/// This method marks the MouseLeftButtonDown event as handled by
/// setting the MouseButtonEventArgs.Handled property of the event data
/// to true when the button is enabled and its ClickMode is not set to
/// Hover. Since this method marks the MouseLeftButtonDown event as
/// handled in some situations, you should use the Click event instead
/// to detect a button click.
/// </remarks>
protected override void OnPointerPressed(PointerPressedEventArgs e)
{
base.OnPointerPressed(e);
if (e.MouseButton == MouseButton.Left)
CalendarLeftMouseButtonDown?.Invoke(this, e);
}
/// <summary>
/// Provides handling for the MouseLeftButtonUp event that occurs when
/// the left mouse button is released while the mouse pointer is over
/// this control.
/// </summary>
/// <param name="e">The event data.</param>
/// <exception cref="System.ArgumentNullException">
/// e is a null reference (Nothing in Visual Basic).
/// </exception>
/// <remarks>
/// This method marks the MouseLeftButtonUp event as handled by setting
/// the MouseButtonEventArgs.Handled property of the event data to true
/// when the button is enabled and its ClickMode is not set to Hover.
/// Since this method marks the MouseLeftButtonUp event as handled in
/// some situations, you should use the Click event instead to detect a
/// button click.
/// </remarks>
protected override void OnPointerReleased(PointerReleasedEventArgs e)
{
base.OnPointerReleased(e);
if (e.MouseButton == MouseButton.Left)
CalendarLeftMouseButtonUp?.Invoke(this, e);
}
/// <summary>
/// We need to simulate the MouseLeftButtonUp event for the
/// CalendarButton that stays in Pressed state after MouseCapture is
/// released since there is no actual MouseLeftButtonUp event for the
/// release.
/// </summary>
/// <param name="e">Event arguments.</param>
internal void SendMouseLeftButtonUp(PointerReleasedEventArgs e)
{
e.Handled = false;
base.OnPointerReleased(e);
}
}
}

79
src/Avalonia.Controls/Calendar/CalendarDateRange.cs

@ -0,0 +1,79 @@
// (c) Copyright Microsoft Corporation.
// This source is subject to the Microsoft Public License (Ms-PL).
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved.
using System;
using System.Diagnostics;
namespace Avalonia.Controls
{
public sealed class CalendarDateRange
{
/// <summary>
/// Gets the first date in the represented range.
/// </summary>
/// <value>The first date in the represented range.</value>
public DateTime Start { get; private set; }
/// <summary>
/// Gets the last date in the represented range.
/// </summary>
/// <value>The last date in the represented range.</value>
public DateTime End { get; private set; }
/// <summary>
/// Initializes a new instance of the
/// <see cref="T:System.Windows.Controls.CalendarDateRange" /> class
/// with a single date.
/// </summary>
/// <param name="day">The date to be represented by the range.</param>
public CalendarDateRange(DateTime day)
{
Start = day;
End = day;
}
/// <summary>
/// Initializes a new instance of the
/// <see cref="T:System.Windows.Controls.CalendarDateRange" /> class
/// with a range of dates.
/// </summary>
/// <param name="start">
/// The start of the range to be represented.
/// </param>
/// <param name="end">The end of the range to be represented.</param>
public CalendarDateRange(DateTime start, DateTime end)
{
if (DateTime.Compare(end, start) >= 0)
{
Start = start;
End = end;
}
else
{
// Always use the start for ranges on the same day
Start = start;
End = start;
}
}
/// <summary>
/// Returns true if any day in the given DateTime range is contained in
/// the current CalendarDateRange.
/// </summary>
/// <param name="range">Inherited code: Requires comment 1.</param>
/// <returns>Inherited code: Requires comment 2.</returns>
internal bool ContainsAny(CalendarDateRange range)
{
Debug.Assert(range != null, "range should not be null!");
int start = DateTime.Compare(Start, range.Start);
// Check if any part of the supplied range is contained by this
// range or if the supplied range completely covers this range.
return (start <= 0 && DateTime.Compare(End, range.Start) >= 0) ||
(start >= 0 && DateTime.Compare(Start, range.End) <= 0);
}
}
}

253
src/Avalonia.Controls/Calendar/CalendarDayButton.cs

@ -0,0 +1,253 @@
// (c) Copyright Microsoft Corporation.
// This source is subject to the Microsoft Public License (Ms-PL).
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved.
using Avalonia.Input;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
namespace Avalonia.Controls.Primitives
{
public sealed class CalendarDayButton : Button
{
/// <summary>
/// Default content for the CalendarDayButton.
/// </summary>
private const int DefaultContent = 1;
private bool _isCurrent;
private bool _ignoringMouseOverState;
private bool _isBlackout;
private bool _isToday;
private bool _isInactive;
private bool _isSelected;
/// <summary>
/// Initializes a new instance of the
/// <see cref="T:Avalonia.Controls.Primitives.CalendarDayButton" />
/// class.
/// </summary>
public CalendarDayButton()
: base()
{
//Focusable = false;
Content = DefaultContent.ToString(CultureInfo.CurrentCulture);
}
/// <summary>
/// Gets or sets the Calendar associated with this button.
/// </summary>
internal Calendar Owner { get; set; }
internal int Index { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the button is the focused
/// element on the Calendar control.
/// </summary>
internal bool IsCurrent
{
get { return _isCurrent; }
set
{
if (_isCurrent != value)
{
_isCurrent = value;
SetPseudoClasses();
}
}
}
/// <summary>
/// Ensure the button is not in the MouseOver state.
/// </summary>
/// <remarks>
/// If a button is in the MouseOver state when a Popup is closed (as is
/// the case when you select a date in the DatePicker control), it will
/// continue to think it's in the mouse over state even when the Popup
/// opens again and it's not. This method is used to forcibly clear the
/// state by changing the CommonStates state group.
/// </remarks>
internal void IgnoreMouseOverState()
{
// TODO: Investigate whether this needs to be done by changing the
// state everytime we change any state, or if it can be done once
// to properly reset the control.
_ignoringMouseOverState = false;
// If the button thinks it's in the MouseOver state (which can
// happen when a Popup is closed before the button can change state)
// we will override the state so it shows up as normal.
if (IsPointerOver)
{
_ignoringMouseOverState = true;
SetPseudoClasses();
}
}
/// <summary>
/// Gets or sets a value indicating whether this is a blackout date.
/// </summary>
internal bool IsBlackout
{
get { return _isBlackout; }
set
{
if (_isBlackout != value)
{
_isBlackout = value;
SetPseudoClasses();
}
}
}
/// <summary>
/// Gets or sets a value indicating whether this button represents
/// today.
/// </summary>
internal bool IsToday
{
get { return _isToday; }
set
{
if (_isToday != value)
{
_isToday = value;
SetPseudoClasses();
}
}
}
/// <summary>
/// Gets or sets a value indicating whether the button is inactive.
/// </summary>
internal bool IsInactive
{
get { return _isInactive; }
set
{
if (_isInactive != value)
{
_isInactive = value;
SetPseudoClasses();
}
}
}
/// <summary>
/// Gets or sets a value indicating whether the button is selected.
/// </summary>
internal bool IsSelected
{
get { return _isSelected; }
set
{
if (_isSelected != value)
{
_isSelected = value;
SetPseudoClasses();
}
}
}
protected override void OnTemplateApplied(TemplateAppliedEventArgs e)
{
base.OnTemplateApplied(e);
SetPseudoClasses();
}
private void SetPseudoClasses()
{
if (_ignoringMouseOverState)
{
PseudoClasses.Set(":pressed", IsPressed);
PseudoClasses.Set(":disabled", !IsEnabled);
}
PseudoClasses.Set(":selected", IsSelected);
PseudoClasses.Set(":inactive", IsInactive);
PseudoClasses.Set(":today", IsToday);
PseudoClasses.Set(":blackout", IsBlackout);
PseudoClasses.Set(":dayfocused", IsCurrent && IsEnabled);
}
/// <summary>
/// Occurs when the left mouse button is pressed (or when the tip of the
/// stylus touches the tablet PC) while the mouse pointer is over a
/// UIElement.
/// </summary>
public event EventHandler<PointerPressedEventArgs> CalendarDayButtonMouseDown;
/// <summary>
/// Occurs when the left mouse button is released (or the tip of the
/// stylus is removed from the tablet PC) while the mouse (or the
/// stylus) is over a UIElement (or while a UIElement holds mouse
/// capture).
/// </summary>
public event EventHandler<PointerReleasedEventArgs> CalendarDayButtonMouseUp;
/// <summary>
/// Provides class handling for the MouseLeftButtonDown event that
/// occurs when the left mouse button is pressed while the mouse pointer
/// is over this control.
/// </summary>
/// <param name="e">The event data. </param>
/// <exception cref="System.ArgumentNullException">
/// e is a null reference (Nothing in Visual Basic).
/// </exception>
/// <remarks>
/// This method marks the MouseLeftButtonDown event as handled by
/// setting the MouseButtonEventArgs.Handled property of the event data
/// to true when the button is enabled and its ClickMode is not set to
/// Hover. Since this method marks the MouseLeftButtonDown event as
/// handled in some situations, you should use the Click event instead
/// to detect a button click.
/// </remarks>
protected override void OnPointerPressed(PointerPressedEventArgs e)
{
base.OnPointerPressed(e);
if (e.MouseButton == MouseButton.Left)
CalendarDayButtonMouseDown?.Invoke(this, e);
}
/// <summary>
/// Provides handling for the MouseLeftButtonUp event that occurs when
/// the left mouse button is released while the mouse pointer is over
/// this control.
/// </summary>
/// <param name="e">The event data.</param>
/// <exception cref="System.ArgumentNullException">
/// e is a null reference (Nothing in Visual Basic).
/// </exception>
/// <remarks>
/// This method marks the MouseLeftButtonUp event as handled by setting
/// the MouseButtonEventArgs.Handled property of the event data to true
/// when the button is enabled and its ClickMode is not set to Hover.
/// Since this method marks the MouseLeftButtonUp event as handled in
/// some situations, you should use the Click event instead to detect a
/// button click.
/// </remarks>
protected override void OnPointerReleased(PointerReleasedEventArgs e)
{
base.OnPointerReleased(e);
if (e.MouseButton == MouseButton.Left)
CalendarDayButtonMouseUp?.Invoke(this, e);
}
/// <summary>
/// We need to simulate the MouseLeftButtonUp event for the
/// CalendarDayButton that stays in Pressed state after MouseCapture is
/// released since there is no actual MouseLeftButtonUp event for the
/// release.
/// </summary>
/// <param name="e">Event arguments.</param>
internal void SendMouseLeftButtonUp(PointerReleasedEventArgs e)
{
e.Handled = false;
base.OnPointerReleased(e);
}
}
}

21
src/Avalonia.Controls/Calendar/CalendarExtensions.cs

@ -0,0 +1,21 @@
// (c) Copyright Microsoft Corporation.
// This source is subject to the Microsoft Public License (Ms-PL).
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved.
using System;
using System.Collections.Generic;
using Avalonia.Input;
using System.Diagnostics;
namespace Avalonia.Controls.Primitives
{
internal static class CalendarExtensions
{
public static void GetMetaKeyState(InputModifiers modifiers, out bool ctrl, out bool shift)
{
ctrl = (modifiers & InputModifiers.Control) == InputModifiers.Control;
shift = (modifiers & InputModifiers.Shift) == InputModifiers.Shift;
}
}
}

1264
src/Avalonia.Controls/Calendar/CalendarItem.cs

File diff suppressed because it is too large

155
src/Avalonia.Controls/Calendar/DateTimeHelper.cs

@ -0,0 +1,155 @@
// (c) Copyright Microsoft Corporation.
// This source is subject to the Microsoft Public License (Ms-PL).
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved.
using System;
using System.Diagnostics;
using System.Globalization;
namespace Avalonia.Controls
{
internal static class DateTimeHelper
{
public static DateTime? AddDays(DateTime time, int days)
{
System.Globalization.Calendar cal = new GregorianCalendar();
try
{
return cal.AddDays(time, days);
}
catch (ArgumentException)
{
return null;
}
}
public static DateTime? AddMonths(DateTime time, int months)
{
System.Globalization.Calendar cal = new GregorianCalendar();
try
{
return cal.AddMonths(time, months);
}
catch (ArgumentException)
{
return null;
}
}
public static DateTime? AddYears(DateTime time, int years)
{
System.Globalization.Calendar cal = new GregorianCalendar();
try
{
return cal.AddYears(time, years);
}
catch (ArgumentException)
{
return null;
}
}
public static int CompareDays(DateTime dt1, DateTime dt2)
{
return DateTime.Compare(DiscardTime(dt1).Value, DiscardTime(dt2).Value);
}
public static int CompareYearMonth(DateTime dt1, DateTime dt2)
{
return (dt1.Year - dt2.Year) * 12 + (dt1.Month - dt2.Month);
}
public static int DecadeOfDate(DateTime date)
{
return date.Year - (date.Year % 10);
}
public static DateTime DiscardDayTime(DateTime d)
{
int year = d.Year;
int month = d.Month;
DateTime newD = new DateTime(year, month, 1, 0, 0, 0);
return newD;
}
public static DateTime? DiscardTime(DateTime? d)
{
if (d == null)
{
return null;
}
return d.Value.Date;
}
public static int EndOfDecade(DateTime date)
{
return DecadeOfDate(date) + 9;
}
public static DateTimeFormatInfo GetCurrentDateFormat()
{
if (CultureInfo.CurrentCulture.Calendar is GregorianCalendar)
{
return CultureInfo.CurrentCulture.DateTimeFormat;
}
else
{
foreach (System.Globalization.Calendar cal in CultureInfo.CurrentCulture.OptionalCalendars)
{
if (cal is GregorianCalendar)
{
// if the default calendar is not Gregorian, return the
// first supported GregorianCalendar dtfi
DateTimeFormatInfo dtfi = new CultureInfo(CultureInfo.CurrentCulture.Name).DateTimeFormat;
dtfi.Calendar = cal;
return dtfi;
}
}
// if there are no GregorianCalendars in the OptionalCalendars
// list, use the invariant dtfi
DateTimeFormatInfo dt = new CultureInfo(CultureInfo.InvariantCulture.Name).DateTimeFormat;
dt.Calendar = new GregorianCalendar();
return dt;
}
}
public static bool InRange(DateTime date, CalendarDateRange range)
{
Debug.Assert(DateTime.Compare(range.Start, range.End) < 1, "The range should start before it ends!");
if (CompareDays(date, range.Start) > -1 && CompareDays(date, range.End) < 1)
{
return true;
}
return false;
}
public static string ToYearMonthPatternString(DateTime date)
{
string result = string.Empty;
DateTimeFormatInfo format = GetCurrentDateFormat();
if (format != null)
{
result = date.ToString(format.YearMonthPattern, format);
}
return result;
}
public static string ToYearString(DateTime date)
{
string result = string.Empty;
DateTimeFormatInfo format = GetCurrentDateFormat();
if (format != null)
{
result = date.Year.ToString(format);
}
return result;
}
}
}

361
src/Avalonia.Controls/Calendar/SelectedDatesCollection.cs

@ -0,0 +1,361 @@
// (c) Copyright Microsoft Corporation.
// This source is subject to the Microsoft Public License (Ms-PL).
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved.
using Avalonia.Threading;
using System;
using System.Collections.ObjectModel;
using System.Threading;
namespace Avalonia.Controls.Primitives
{
public sealed class SelectedDatesCollection : ObservableCollection<DateTime>
{
/// <summary>
/// Inherited code: Requires comment.
/// </summary>
private Collection<DateTime> _addedItems;
/// <summary>
/// Inherited code: Requires comment.
/// </summary>
private bool _isCleared;
/// <summary>
/// Inherited code: Requires comment.
/// </summary>
private bool _isRangeAdded;
/// <summary>
/// Inherited code: Requires comment.
/// </summary>
private Calendar _owner;
/// <summary>
/// Initializes a new instance of the
/// <see cref="T:Avalonia.Controls.Primitives.SelectedDatesCollection" />
/// class.
/// </summary>
/// <param name="owner">
/// The <see cref="T:Avalonia.Controls.Calendar" /> associated
/// with this object.
/// </param>
public SelectedDatesCollection(Calendar owner)
{
_owner = owner;
_addedItems = new Collection<DateTime>();
}
private void InvokeCollectionChanged(System.Collections.IList removedItems, System.Collections.IList addedItems)
{
_owner.OnSelectedDatesCollectionChanged(new SelectionChangedEventArgs(null, addedItems, removedItems));
}
/// <summary>
/// Adds all the dates in the specified range, which includes the first
/// and last dates, to the collection.
/// </summary>
/// <param name="start">The first date to add to the collection.</param>
/// <param name="end">The last date to add to the collection.</param>
public void AddRange(DateTime start, DateTime end)
{
DateTime? rangeStart;
// increment parameter specifies if the Days were selected in
// Descending order or Ascending order based on this value, we add
// the days in the range either in Ascending order or in Descending
// order
int increment = (DateTime.Compare(end, start) >= 0) ? 1 : -1;
_addedItems.Clear();
rangeStart = start;
_isRangeAdded = true;
if (_owner.IsMouseSelection)
{
// In Mouse Selection we allow the user to be able to add
// multiple ranges in one action in MultipleRange Mode. In
// SingleRange Mode, we only add the first selected range.
while (rangeStart.HasValue && DateTime.Compare(end, rangeStart.Value) != -increment)
{
if (Calendar.IsValidDateSelection(_owner, rangeStart))
{
Add(rangeStart.Value);
}
else
{
if (_owner.SelectionMode == CalendarSelectionMode.SingleRange)
{
_owner.HoverEnd = rangeStart.Value.AddDays(-increment);
break;
}
}
rangeStart = DateTimeHelper.AddDays(rangeStart.Value, increment);
}
}
else
{
// If CalendarSelectionMode.SingleRange and a user
// programmatically tries to add multiple ranges, we will throw
// away the old range and replace it with the new one. In order
// to provide the removed items without an additional event, we
// are calling ClearInternal
if (_owner.SelectionMode == CalendarSelectionMode.SingleRange && Count > 0)
{
foreach (DateTime item in this)
{
_owner.RemovedItems.Add(item);
}
ClearInternal();
}
while (rangeStart.HasValue && DateTime.Compare(end, rangeStart.Value) != -increment)
{
Add(rangeStart.Value);
rangeStart = DateTimeHelper.AddDays(rangeStart.Value, increment);
}
}
_owner.OnSelectedDatesCollectionChanged(new SelectionChangedEventArgs(null, _addedItems, _owner.RemovedItems));
_owner.RemovedItems.Clear();
_owner.UpdateMonths();
_isRangeAdded = false;
}
/// <summary>
/// Removes all items from the collection.
/// </summary>
/// <remarks>
/// This implementation raises the CollectionChanged event.
/// </remarks>
protected override void ClearItems()
{
EnsureValidThread();
Collection<DateTime> addedItems = new Collection<DateTime>();
Collection<DateTime> removedItems = new Collection<DateTime>();
foreach (DateTime item in this)
{
removedItems.Add(item);
}
base.ClearItems();
// The event fires after SelectedDate changes
if (_owner.SelectionMode != CalendarSelectionMode.None && _owner.SelectedDate != null)
{
_owner.SelectedDate = null;
}
if (removedItems.Count != 0)
{
InvokeCollectionChanged(removedItems, addedItems);
}
_owner.UpdateMonths();
}
/// <summary>
/// Inserts an item into the collection at the specified index.
/// </summary>
/// <param name="index">
/// The zero-based index at which item should be inserted.
/// </param>
/// <param name="item">The object to insert.</param>
/// <remarks>
/// This implementation raises the CollectionChanged event.
/// </remarks>
protected override void InsertItem(int index, DateTime item)
{
EnsureValidThread();
if (!Contains(item))
{
Collection<DateTime> addedItems = new Collection<DateTime>();
if (CheckSelectionMode())
{
if (Calendar.IsValidDateSelection(_owner, item))
{
// If the Collection is cleared since it is SingleRange
// and it had another range set the index to 0
if (_isCleared)
{
index = 0;
_isCleared = false;
}
base.InsertItem(index, item);
// The event fires after SelectedDate changes
if (index == 0 && !(_owner.SelectedDate.HasValue && DateTime.Compare(_owner.SelectedDate.Value, item) == 0))
{
_owner.SelectedDate = item;
}
if (!_isRangeAdded)
{
addedItems.Add(item);
InvokeCollectionChanged(_owner.RemovedItems, addedItems);
_owner.RemovedItems.Clear();
int monthDifference = DateTimeHelper.CompareYearMonth(item, _owner.DisplayDateInternal);
if (monthDifference < 2 && monthDifference > -2)
{
_owner.UpdateMonths();
}
}
else
{
_addedItems.Add(item);
}
}
else
{
throw new ArgumentOutOfRangeException("SelectedDate value is not valid.");
}
}
}
}
/// <summary>
/// Removes the item at the specified index of the collection.
/// </summary>
/// <param name="index">
/// The zero-based index of the element to remove.
/// </param>
/// <remarks>
/// This implementation raises the CollectionChanged event.
/// </remarks>
protected override void RemoveItem(int index)
{
EnsureValidThread();
if (index >= Count)
{
base.RemoveItem(index);
}
else
{
Collection<DateTime> addedItems = new Collection<DateTime>();
Collection<DateTime> removedItems = new Collection<DateTime>();
int monthDifference = DateTimeHelper.CompareYearMonth(this[index], _owner.DisplayDateInternal);
removedItems.Add(this[index]);
base.RemoveItem(index);
// The event fires after SelectedDate changes
if (index == 0)
{
if (Count > 0)
{
_owner.SelectedDate = this[0];
}
else
{
_owner.SelectedDate = null;
}
}
InvokeCollectionChanged(removedItems, addedItems);
if (monthDifference < 2 && monthDifference > -2)
{
_owner.UpdateMonths();
}
}
}
/// <summary>
/// Replaces the element at the specified index.
/// </summary>
/// <param name="index">
/// The zero-based index of the element to replace.
/// </param>
/// <param name="item">
/// The new value for the element at the specified index.
/// </param>
/// <remarks>
/// This implementation raises the CollectionChanged event.
/// </remarks>
protected override void SetItem(int index, DateTime item)
{
EnsureValidThread();
if (!Contains(item))
{
Collection<DateTime> addedItems = new Collection<DateTime>();
Collection<DateTime> removedItems = new Collection<DateTime>();
if (index >= Count)
{
base.SetItem(index, item);
}
else
{
if (item != null && DateTime.Compare(this[index], item) != 0 && Calendar.IsValidDateSelection(_owner, item))
{
removedItems.Add(this[index]);
base.SetItem(index, item);
addedItems.Add(item);
// The event fires after SelectedDate changes
if (index == 0 && !(_owner.SelectedDate.HasValue && DateTime.Compare(_owner.SelectedDate.Value, item) == 0))
{
_owner.SelectedDate = item;
}
InvokeCollectionChanged(removedItems, addedItems);
int monthDifference = DateTimeHelper.CompareYearMonth(item, _owner.DisplayDateInternal);
if (monthDifference < 2 && monthDifference > -2)
{
_owner.UpdateMonths();
}
}
}
}
}
internal void ClearInternal()
{
base.ClearItems();
}
private bool CheckSelectionMode()
{
if (_owner.SelectionMode == CalendarSelectionMode.None)
{
throw new InvalidOperationException("The SelectedDate property cannot be set when the selection mode is None.");
}
if (_owner.SelectionMode == CalendarSelectionMode.SingleDate && Count > 0)
{
throw new InvalidOperationException("The SelectedDates collection can be changed only in a multiple selection mode. Use the SelectedDate in a single selection mode.");
}
// if user tries to add an item into the SelectedDates in
// SingleRange mode, we throw away the old range and replace it with
// the new one in order to provide the removed items without an
// additional event, we are calling ClearInternal
if (_owner.SelectionMode == CalendarSelectionMode.SingleRange && !_isRangeAdded && Count > 0)
{
foreach (DateTime item in this)
{
_owner.RemovedItems.Add(item);
}
ClearInternal();
_isCleared = true;
}
return true;
}
private void EnsureValidThread()
{
Dispatcher.UIThread.VerifyAccess();
}
}
}

2
src/Avalonia.Controls/Classes.cs

@ -179,7 +179,7 @@ namespace Avalonia.Controls
{
ThrowIfPseudoclass(name, "removed");
if (!Contains(name))
if (Contains(name))
{
c.Add(name);
}

1
src/Avalonia.Controls/ContentControl.cs

@ -51,6 +51,7 @@ namespace Avalonia.Controls
/// Gets or sets the content to display.
/// </summary>
[Content]
[DependsOn(nameof(ContentTemplate))]
public object Content
{
get { return GetValue(ContentProperty); }

15
src/Avalonia.Controls/Control.cs

@ -487,11 +487,6 @@ namespace Avalonia.Controls
void ILogical.NotifyResourcesChanged(ResourcesChangedEventArgs e)
{
ResourcesChanged?.Invoke(this, new ResourcesChangedEventArgs());
foreach (var child in LogicalChildren)
{
child.NotifyResourcesChanged(e);
}
}
/// <inheritdoc/>
@ -536,6 +531,15 @@ namespace Avalonia.Controls
}
_parent = (IControl)parent;
if (old != null)
{
old.ResourcesChanged -= ThisResourcesChanged;
}
if (_parent != null)
{
_parent.ResourcesChanged += ThisResourcesChanged;
}
((ILogical)this).NotifyResourcesChanged(new ResourcesChangedEventArgs());
if (_parent is IStyleRoot || _parent?.IsAttachedToLogicalTree == true || this is IStyleRoot)
@ -621,7 +625,6 @@ namespace Avalonia.Controls
Contract.Requires<ArgumentNullException>(property != null);
Contract.Requires<ArgumentNullException>(selector != null);
Contract.Requires<ArgumentNullException>(className != null);
Contract.Requires<ArgumentNullException>(property != null);
if (string.IsNullOrWhiteSpace(className))
{

33
src/Avalonia.Controls/DropDown.cs

@ -96,6 +96,16 @@ namespace Avalonia.Controls
this.UpdateSelectionBoxItem(this.SelectedItem);
}
protected override void OnGotFocus(GotFocusEventArgs e)
{
base.OnGotFocus(e);
if (!e.Handled && e.NavigationMethod == NavigationMethod.Directional)
{
e.Handled = UpdateSelectionFromEventSource(e.Source);
}
}
/// <inheritdoc/>
protected override void OnKeyDown(KeyEventArgs e)
{
@ -104,7 +114,7 @@ namespace Avalonia.Controls
if (!e.Handled)
{
if (e.Key == Key.F4 ||
(e.Key == Key.Down && ((e.Modifiers & InputModifiers.Alt) != 0)))
((e.Key == Key.Down || e.Key == Key.Up) && ((e.Modifiers & InputModifiers.Alt) != 0)))
{
IsDropDownOpen = !IsDropDownOpen;
e.Handled = true;
@ -114,6 +124,27 @@ namespace Avalonia.Controls
IsDropDownOpen = false;
e.Handled = true;
}
if (!IsDropDownOpen)
{
if (e.Key == Key.Down)
{
if (SelectedIndex == -1)
SelectedIndex = 0;
if (++SelectedIndex >= ItemCount)
SelectedIndex = 0;
e.Handled = true;
}
else if (e.Key == Key.Up)
{
if (--SelectedIndex < 0)
SelectedIndex = ItemCount - 1;
e.Handled = true;
}
}
}
}

4
src/Avalonia.Controls/IPanel.cs

@ -9,8 +9,8 @@ namespace Avalonia.Controls
public interface IPanel : IControl
{
/// <summary>
/// Gets or sets the children of the <see cref="Panel"/>.
/// Gets the children of the <see cref="Panel"/>.
/// </summary>
Controls Children { get; set; }
Controls Children { get; }
}
}

11
src/Avalonia.Controls/ItemsControl.cs

@ -11,6 +11,7 @@ using Avalonia.Controls.Presenters;
using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates;
using Avalonia.Controls.Utils;
using Avalonia.Input;
using Avalonia.LogicalTree;
using Avalonia.Metadata;
@ -106,6 +107,12 @@ namespace Avalonia.Controls
set { SetAndRaise(ItemsProperty, ref _items, value); }
}
public int ItemCount
{
get;
private set;
}
/// <summary>
/// Gets or sets the panel used to display the items.
/// </summary>
@ -352,6 +359,10 @@ namespace Avalonia.Controls
RemoveControlItemsFromLogicalChildren(e.OldItems);
break;
}
int? count = (Items as IList)?.Count;
if (count != null)
ItemCount = (int)count;
var collection = sender as ICollection;
PseudoClasses.Set(":empty", collection == null || collection.Count == 0);

43
src/Avalonia.Controls/Panel.cs

@ -25,8 +25,6 @@ namespace Avalonia.Controls
public static readonly StyledProperty<IBrush> BackgroundProperty =
Border.BackgroundProperty.AddOwner<Panel>();
private readonly Controls _children = new Controls();
/// <summary>
/// Initializes static members of the <see cref="Panel"/> class.
/// </summary>
@ -40,38 +38,14 @@ namespace Avalonia.Controls
/// </summary>
public Panel()
{
_children.CollectionChanged += ChildrenChanged;
Children.CollectionChanged += ChildrenChanged;
}
/// <summary>
/// Gets or sets the children of the <see cref="Panel"/>.
/// Gets the children of the <see cref="Panel"/>.
/// </summary>
/// <remarks>
/// Even though this property can be set, the setter is only intended for use in object
/// initializers. Assigning to this property does not change the underlying collection,
/// it simply clears the existing collection and adds the contents of the assigned
/// collection.
/// </remarks>
[Content]
public Controls Children
{
get
{
return _children;
}
set
{
Contract.Requires<ArgumentNullException>(value != null);
if (_children != value)
{
VisualChildren.Clear();
_children.Clear();
_children.AddRange(value);
}
}
}
public Controls Children { get; } = new Controls();
/// <summary>
/// Gets or Sets Panel background brush.
@ -115,6 +89,11 @@ namespace Avalonia.Controls
VisualChildren.AddRange(e.NewItems.OfType<Visual>());
break;
case NotifyCollectionChangedAction.Move:
LogicalChildren.MoveRange(e.OldStartingIndex, e.OldItems.Count, e.NewStartingIndex);
VisualChildren.MoveRange(e.OldStartingIndex, e.OldItems.Count, e.NewStartingIndex);
break;
case NotifyCollectionChangedAction.Remove:
controls = e.OldItems.OfType<Control>().ToList();
LogicalChildren.RemoveAll(controls);
@ -132,11 +111,7 @@ namespace Avalonia.Controls
break;
case NotifyCollectionChangedAction.Reset:
controls = e.OldItems.OfType<Control>().ToList();
LogicalChildren.Clear();
VisualChildren.Clear();
VisualChildren.AddRange(_children);
break;
throw new NotSupportedException();
}
InvalidateMeasure();

15
src/Avalonia.Controls/Presenters/ContentPresenter.cs

@ -8,6 +8,7 @@ using Avalonia.Controls.Templates;
using Avalonia.Layout;
using Avalonia.LogicalTree;
using Avalonia.Media;
using Avalonia.Metadata;
using Avalonia.VisualTree;
namespace Avalonia.Controls.Presenters
@ -139,6 +140,7 @@ namespace Avalonia.Controls.Presenters
/// <summary>
/// Gets or sets the content to be displayed by the presenter.
/// </summary>
[DependsOn(nameof(ContentTemplate))]
public object Content
{
get { return GetValue(ContentProperty); }
@ -255,18 +257,9 @@ namespace Avalonia.Controls.Presenters
LogicalChildren.Remove(oldChild);
}
if (newChild.Parent == null)
if (newChild.Parent == null && TemplatedParent == null)
{
var templatedLogicalParent = TemplatedParent as ILogical;
if (templatedLogicalParent != null)
{
((ISetLogicalParent)newChild).SetParent(templatedLogicalParent);
}
else
{
LogicalChildren.Add(newChild);
}
LogicalChildren.Add(newChild);
}
VisualChildren.Add(newChild);

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

@ -8,6 +8,7 @@ using Avalonia.Interactivity;
using Avalonia.Layout;
using Avalonia.Media;
using Avalonia.Platform;
using Avalonia.Styling;
using Avalonia.VisualTree;
using JetBrains.Annotations;
@ -16,7 +17,7 @@ namespace Avalonia.Controls.Primitives
/// <summary>
/// The root window of a <see cref="Popup"/>.
/// </summary>
public class PopupRoot : WindowBase, IInteractive, IHostedVisualTreeRoot, IDisposable
public class PopupRoot : WindowBase, IInteractive, IHostedVisualTreeRoot, IDisposable, IStyleHost
{
private IDisposable _presenterSubscription;
@ -66,6 +67,11 @@ namespace Avalonia.Controls.Primitives
/// </summary>
IVisual IHostedVisualTreeRoot.Host => Parent;
/// <summary>
/// Gets the styling parent of the popup root.
/// </summary>
IStyleHost IStyleHost.StylingParent => Parent;
/// <inheritdoc/>
public void Dispose() => PlatformImpl?.Dispose();
@ -90,20 +96,23 @@ namespace Avalonia.Controls.Primitives
private void SetTemplatedParentAndApplyChildTemplates(IControl control)
{
var templatedParent = Parent.TemplatedParent;
if (control.TemplatedParent == null)
if (control != null)
{
control.SetValue(TemplatedParentProperty, templatedParent);
}
var templatedParent = Parent.TemplatedParent;
control.ApplyTemplate();
if (control.TemplatedParent == null)
{
control.SetValue(TemplatedParentProperty, templatedParent);
}
if (!(control is IPresenter) && control.TemplatedParent == templatedParent)
{
foreach (IControl child in control.GetVisualChildren())
control.ApplyTemplate();
if (!(control is IPresenter) && control.TemplatedParent == templatedParent)
{
SetTemplatedParentAndApplyChildTemplates(child);
foreach (IControl child in control.GetVisualChildren())
{
SetTemplatedParentAndApplyChildTemplates(child);
}
}
}
}

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

@ -2,6 +2,7 @@
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using Avalonia.Data;
using Avalonia.Utilities;
namespace Avalonia.Controls.Primitives
@ -36,7 +37,8 @@ namespace Avalonia.Controls.Primitives
AvaloniaProperty.RegisterDirect<RangeBase, double>(
nameof(Value),
o => o.Value,
(o, v) => o.Value = v);
(o, v) => o.Value = v,
defaultBindingMode: BindingMode.TwoWay);
/// <summary>
/// Defines the <see cref="SmallChange"/> property.

72
src/Avalonia.Controls/Primitives/SelectingItemsControl.cs

@ -151,15 +151,23 @@ namespace Avalonia.Controls.Primitives
{
if (_updateCount == 0)
{
var old = SelectedIndex;
var effective = (value >= 0 && value < Items?.Cast<object>().Count()) ? value : -1;
if (old != effective)
SetAndRaise(SelectedIndexProperty, ref _selectedIndex, (int val, ref int backing, Action<Action> notifyWrapper) =>
{
_selectedIndex = effective;
RaisePropertyChanged(SelectedIndexProperty, old, effective, BindingPriority.LocalValue);
SelectedItem = ElementAt(Items, effective);
}
var old = backing;
var effective = (val >= 0 && val < Items?.Cast<object>().Count()) ? val : -1;
if (old != effective)
{
backing = effective;
notifyWrapper(() =>
RaisePropertyChanged(
SelectedIndexProperty,
old,
effective,
BindingPriority.LocalValue));
SelectedItem = ElementAt(Items, effective);
}
}, value);
}
else
{
@ -183,31 +191,41 @@ namespace Avalonia.Controls.Primitives
{
if (_updateCount == 0)
{
var old = SelectedItem;
var index = IndexOf(Items, value);
var effective = index != -1 ? value : null;
if (!object.Equals(effective, old))
SetAndRaise(SelectedItemProperty, ref _selectedItem, (object val, ref object backing, Action<Action> notifyWrapper) =>
{
_selectedItem = effective;
RaisePropertyChanged(SelectedItemProperty, old, effective, BindingPriority.LocalValue);
SelectedIndex = index;
var old = backing;
var index = IndexOf(Items, val);
var effective = index != -1 ? val : null;
if (effective != null)
if (!object.Equals(effective, old))
{
if (SelectedItems.Count != 1 || SelectedItems[0] != effective)
backing = effective;
notifyWrapper(() =>
RaisePropertyChanged(
SelectedItemProperty,
old,
effective,
BindingPriority.LocalValue));
SelectedIndex = index;
if (effective != null)
{
if (SelectedItems.Count != 1 || SelectedItems[0] != effective)
{
_syncingSelectedItems = true;
SelectedItems.Clear();
SelectedItems.Add(effective);
_syncingSelectedItems = false;
}
}
else if (SelectedItems.Count > 0)
{
_syncingSelectedItems = true;
SelectedItems.Clear();
SelectedItems.Add(effective);
_syncingSelectedItems = false;
}
}
else if (SelectedItems.Count > 0)
{
SelectedItems.Clear();
}
}
}, value);
}
else
{
@ -297,7 +315,7 @@ namespace Avalonia.Controls.Primitives
.OfType<IControl>()
.FirstOrDefault(x => x.LogicalParent == this && ItemContainerGenerator?.IndexFromContainer(x) != -1);
return item as IControl;
return item;
}
/// <inheritdoc/>

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

@ -9,26 +9,37 @@ namespace Avalonia.Controls.Primitives
{
public class ToggleButton : Button
{
public static readonly DirectProperty<ToggleButton, bool> IsCheckedProperty =
AvaloniaProperty.RegisterDirect<ToggleButton, bool>(
"IsChecked",
public static readonly DirectProperty<ToggleButton, bool?> IsCheckedProperty =
AvaloniaProperty.RegisterDirect<ToggleButton, bool?>(
nameof(IsChecked),
o => o.IsChecked,
(o,v) => o.IsChecked = v,
(o, v) => o.IsChecked = v,
defaultBindingMode: BindingMode.TwoWay);
private bool _isChecked;
public static readonly StyledProperty<bool> IsThreeStateProperty =
AvaloniaProperty.Register<ToggleButton, bool>(nameof(IsThreeState));
private bool? _isChecked = false;
static ToggleButton()
{
PseudoClass(IsCheckedProperty, ":checked");
PseudoClass(IsCheckedProperty, c => c == true, ":checked");
PseudoClass(IsCheckedProperty, c => c == false, ":unchecked");
PseudoClass(IsCheckedProperty, c => c == null, ":indeterminate");
}
public bool IsChecked
public bool? IsChecked
{
get { return _isChecked; }
set { SetAndRaise(IsCheckedProperty, ref _isChecked, value); }
}
public bool IsThreeState
{
get => GetValue(IsThreeStateProperty);
set => SetValue(IsThreeStateProperty, value);
}
protected override void OnClick()
{
Toggle();
@ -37,7 +48,16 @@ namespace Avalonia.Controls.Primitives
protected virtual void Toggle()
{
IsChecked = !IsChecked;
if (IsChecked.HasValue)
if (IsChecked.Value)
if (IsThreeState)
IsChecked = null;
else
IsChecked = false;
else
IsChecked = true;
else
IsChecked = false;
}
}
}

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

@ -154,7 +154,11 @@ namespace Avalonia.Controls.Primitives
if (increaseButton != null)
{
increaseButton.Arrange(new Rect(firstWidth + thumbWidth, 0, remaining - firstWidth, finalSize.Height));
increaseButton.Arrange(new Rect(
firstWidth + thumbWidth,
0,
Math.Max(0, remaining - firstWidth),
finalSize.Height));
}
}
else
@ -185,7 +189,11 @@ namespace Avalonia.Controls.Primitives
if (increaseButton != null)
{
increaseButton.Arrange(new Rect(0, firstHeight + thumbHeight, finalSize.Width, Math.Max(remaining - firstHeight, 0)));
increaseButton.Arrange(new Rect(
0,
firstHeight + thumbHeight,
finalSize.Width,
Math.Max(remaining - firstHeight, 0)));
}
}

5
src/Avalonia.Controls/ProgressBar.cs

@ -28,9 +28,6 @@ namespace Avalonia.Controls
{
ValueProperty.Changed.AddClassHandler<ProgressBar>(x => x.ValueChanged);
HorizontalAlignmentProperty.OverrideDefaultValue<ProgressBar>(HorizontalAlignment.Left);
VerticalAlignmentProperty.OverrideDefaultValue<ProgressBar>(VerticalAlignment.Top);
IsIndeterminateProperty.Changed.AddClassHandler<ProgressBar>(
(p, e) => { if (p._indicator != null) p.UpdateIsIndeterminate((bool)e.NewValue); });
OrientationProperty.Changed.AddClassHandler<ProgressBar>(
@ -112,8 +109,10 @@ namespace Avalonia.Controls
private void UpdateIsIndeterminate(bool isIndeterminate)
{
if (isIndeterminate)
{
if (_indeterminateAnimation == null || _indeterminateAnimation.Disposed)
_indeterminateAnimation = IndeterminateAnimation.StartAnimation(this);
}
else
_indeterminateAnimation?.Dispose();
}

9
src/Avalonia.Controls/RadioButton.cs

@ -17,17 +17,17 @@ namespace Avalonia.Controls
protected override void Toggle()
{
if (!IsChecked)
if (!IsChecked.GetValueOrDefault())
{
IsChecked = true;
}
}
private void IsCheckedChanged(bool value)
private void IsCheckedChanged(bool? value)
{
var parent = this.GetVisualParent();
if (value && parent != null)
if (value.GetValueOrDefault() && parent != null)
{
var siblings = parent
.GetVisualChildren()
@ -36,7 +36,8 @@ namespace Avalonia.Controls
foreach (var sibling in siblings)
{
sibling.IsChecked = false;
if (sibling.IsChecked.GetValueOrDefault())
sibling.IsChecked = false;
}
}
}

20
src/Avalonia.Controls/TextBox.cs

@ -97,6 +97,7 @@ namespace Avalonia.Controls
private UndoRedoHelper<UndoRedoState> _undoRedoHelper;
private bool _ignoreTextChanges;
private IEnumerable<Exception> _dataValidationErrors;
private static readonly string[] invalidCharacters = new String[1]{"\u007f"};
static TextBox()
{
@ -177,6 +178,10 @@ namespace Avalonia.Controls
{
value = CoerceCaretIndex(value);
SetAndRaise(SelectionStartProperty, ref _selectionStart, value);
if (SelectionStart == SelectionEnd)
{
CaretIndex = SelectionStart;
}
}
}
@ -191,6 +196,10 @@ namespace Avalonia.Controls
{
value = CoerceCaretIndex(value);
SetAndRaise(SelectionEndProperty, ref _selectionEnd, value);
if (SelectionStart == SelectionEnd)
{
CaretIndex = SelectionEnd;
}
}
}
@ -280,6 +289,7 @@ namespace Avalonia.Controls
{
if (!IsReadOnly)
{
input = RemoveInvalidCharacters(input);
string text = Text ?? string.Empty;
int caretIndex = CaretIndex;
if (!string.IsNullOrEmpty(input))
@ -295,6 +305,16 @@ namespace Avalonia.Controls
}
}
public string RemoveInvalidCharacters(string text)
{
for (var i = 0; i < invalidCharacters.Length; i++)
{
text = text.Replace(invalidCharacters[i], string.Empty);
}
return text;
}
private async void Copy()
{
await ((IClipboard)AvaloniaLocator.Current.GetService(typeof(IClipboard)))

5
src/Avalonia.Controls/TreeView.cs

@ -176,10 +176,7 @@ namespace Avalonia.Controls
SelectedItem = item;
if (SelectedItem != null)
{
MarkContainerSelected(container, true);
}
MarkContainerSelected(container, true);
}
}

8
src/Avalonia.Controls/VirtualizingStackPanel.cs

@ -134,12 +134,14 @@ namespace Avalonia.Controls
protected override IInputElement GetControlInDirection(NavigationDirection direction, IControl from)
{
if (from == null)
return null;
var logicalScrollable = Parent as ILogicalScrollable;
var fromControl = from as IControl;
if (logicalScrollable?.IsLogicalScrollEnabled == true && fromControl != null)
if (logicalScrollable?.IsLogicalScrollEnabled == true)
{
return logicalScrollable.GetControlInDirection(direction, fromControl);
return logicalScrollable.GetControlInDirection(direction, from);
}
else
{

4
src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj

@ -11,7 +11,7 @@
<DefineConstants>TRACE;DEBUG</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<DocumentationFile>bin\Debug\Avalonia.Diagnostics.XML</DocumentationFile>
<DocumentationFile>bin\Debug\Avalonia.Diagnostics.xml</DocumentationFile>
<NoWarn>CS1591</NoWarn>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
@ -21,7 +21,7 @@
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<DocumentationFile>bin\Release\Avalonia.Diagnostics.XML</DocumentationFile>
<DocumentationFile>bin\Release\Avalonia.Diagnostics.xml</DocumentationFile>
<NoWarn>CS1591</NoWarn>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>

6
src/Avalonia.Diagnostics/Views/TreePage.xaml.cs

@ -27,6 +27,12 @@ namespace Avalonia.Diagnostics.Views
if (layer != null)
{
if (_adorner != null)
{
((Panel)_adorner.Parent).Children.Remove(_adorner);
_adorner = null;
}
_adorner = new Rectangle
{
Fill = new SolidColorBrush(0x80a0c5e8),

2
src/Avalonia.DotNetCoreRuntime/Avalonia.DotNetCoreRuntime.csproj

@ -5,7 +5,7 @@
<DefineConstants>$(DefineConstants);DOTNETCORE</DefineConstants>
</PropertyGroup>
<PropertyGroup>
<DocumentationFile>bin\$(Configuration)\Avalonia.DotNetCoreRuntime.XML</DocumentationFile>
<DocumentationFile>bin\$(Configuration)\Avalonia.DotNetCoreRuntime.xml</DocumentationFile>
</PropertyGroup>
<ItemGroup>
<Compile Include="..\Shared\SharedAssemblyInfo.cs">

4
src/Avalonia.Input/Avalonia.Input.csproj

@ -11,7 +11,7 @@
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<DocumentationFile>bin\Debug\Avalonia.Input.XML</DocumentationFile>
<DocumentationFile>bin\Debug\Avalonia.Input.xml</DocumentationFile>
<NoWarn>CS1591</NoWarn>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
@ -21,7 +21,7 @@
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<DocumentationFile>bin\Release\Avalonia.Input.XML</DocumentationFile>
<DocumentationFile>bin\Release\Avalonia.Input.xml</DocumentationFile>
<NoWarn>CS1591</NoWarn>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>

17
src/Avalonia.Input/KeyGesture.cs

@ -111,6 +111,21 @@ namespace Avalonia.Input
return string.Join(" + ", parts);
}
public bool Matches(KeyEventArgs keyEvent) => keyEvent.Key == Key && keyEvent.Modifiers == Modifiers;
public bool Matches(KeyEventArgs keyEvent) => ResolveNumPadOperationKey(keyEvent.Key) == Key && keyEvent.Modifiers == Modifiers;
private Key ResolveNumPadOperationKey(Key key)
{
switch (key)
{
case Key.Add:
return Key.OemPlus;
case Key.Subtract:
return Key.OemMinus;
case Key.Decimal:
return Key.OemPeriod;
default:
return key;
}
}
}
}

4
src/Avalonia.Interactivity/Avalonia.Interactivity.csproj

@ -11,7 +11,7 @@
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<DocumentationFile>bin\Debug\Avalonia.Interactivity.XML</DocumentationFile>
<DocumentationFile>bin\Debug\Avalonia.Interactivity.xml</DocumentationFile>
<NoWarn>CS1591</NoWarn>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
@ -21,7 +21,7 @@
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<DocumentationFile>bin\Release\Avalonia.Interactivity.XML</DocumentationFile>
<DocumentationFile>bin\Release\Avalonia.Interactivity.xml</DocumentationFile>
<NoWarn>CS1591</NoWarn>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>

4
src/Avalonia.Layout/Avalonia.Layout.csproj

@ -11,7 +11,7 @@
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<DocumentationFile>bin\Debug\Avalonia.Layout.XML</DocumentationFile>
<DocumentationFile>bin\Debug\Avalonia.Layout.xml</DocumentationFile>
<NoWarn>CS1591</NoWarn>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
@ -21,7 +21,7 @@
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<DocumentationFile>bin\Release\Avalonia.Layout.XML</DocumentationFile>
<DocumentationFile>bin\Release\Avalonia.Layout.xml</DocumentationFile>
<NoWarn>CS1591</NoWarn>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>

5
src/Avalonia.Logging.Serilog/Avalonia.Logging.Serilog.csproj

@ -11,7 +11,7 @@
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<DocumentationFile>bin\Debug\Avalonia.Logging.Serilog.XML</DocumentationFile>
<DocumentationFile>bin\Debug\Avalonia.Logging.Serilog.xml</DocumentationFile>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<DebugType>pdbonly</DebugType>
@ -20,7 +20,7 @@
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<DocumentationFile>bin\Release\Avalonia.Logging.Serilog.XML</DocumentationFile>
<DocumentationFile>bin\Release\Avalonia.Logging.Serilog.xml</DocumentationFile>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<ItemGroup>
@ -28,6 +28,7 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Avalonia.Base\Avalonia.Base.csproj" />
<ProjectReference Include="..\Avalonia.Controls\Avalonia.Controls.csproj" />
</ItemGroup>
<Import Project="..\..\build\Serilog.props" />
</Project>

51
src/Avalonia.Logging.Serilog/SerilogExtensions.cs

@ -0,0 +1,51 @@
using System;
using Avalonia.Controls;
using Serilog;
using SerilogLevel = Serilog.Events.LogEventLevel;
namespace Avalonia.Logging.Serilog
{
/// <summary>
/// Extension methods for Serilog logging.
/// </summary>
public static class SerilogExtensions
{
/// <summary>
/// Logs Avalonia events to the <see cref="System.Diagnostics.Debug"/> sink.
/// </summary>
/// <typeparam name="T">The application class type.</typeparam>
/// <param name="builder">The app builder instance.</param>
/// <param name="level">The minimum level to log.</param>
/// <returns>The app builder instance.</returns>
public static T LogToDebug<T>(
this T builder,
LogEventLevel level = LogEventLevel.Warning)
where T : AppBuilderBase<T>, new()
{
SerilogLogger.Initialize(new LoggerConfiguration()
.MinimumLevel.Is((SerilogLevel)level)
.WriteTo.Debug(outputTemplate: "{Area}: {Message}")
.CreateLogger());
return builder;
}
/// <summary>
/// Logs Avalonia events to the <see cref="System.Diagnostics.Trace"/> sink.
/// </summary>
/// <typeparam name="T">The application class type.</typeparam>
/// <param name="builder">The app builder instance.</param>
/// <param name="level">The minimum level to log.</param>
/// <returns>The app builder instance.</returns>
public static T LogToTrace<T>(
this T builder,
LogEventLevel level = LogEventLevel.Warning)
where T : AppBuilderBase<T>, new()
{
SerilogLogger.Initialize(new LoggerConfiguration()
.MinimumLevel.Is((SerilogLevel)level)
.WriteTo.Trace(outputTemplate: "{Area}: {Message}")
.CreateLogger());
return builder;
}
}
}

3
src/Avalonia.Remote.Protocol/BsonStreamTransport.cs

@ -64,7 +64,6 @@ namespace Avalonia.Remote.Protocol
async Task Reader()
{
Task.Yield();
try
{
while (true)
@ -147,4 +146,4 @@ namespace Avalonia.Remote.Protocol
public event Action<IAvaloniaRemoteTransportConnection, object> OnMessage;
public event Action<IAvaloniaRemoteTransportConnection, Exception> OnException;
}
}
}

4
src/Avalonia.Styling/Avalonia.Styling.csproj

@ -12,7 +12,7 @@
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<DocumentationFile>bin\Debug\Avalonia.Styling.XML</DocumentationFile>
<DocumentationFile>bin\Debug\Avalonia.Styling.xml</DocumentationFile>
<NoWarn>CS1591</NoWarn>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
@ -22,7 +22,7 @@
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<DocumentationFile>bin\Release\Avalonia.Styling.XML</DocumentationFile>
<DocumentationFile>bin\Release\Avalonia.Styling.xml</DocumentationFile>
<NoWarn>CS1591</NoWarn>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>

2
src/Avalonia.Styling/Styling/Setter.cs

@ -174,7 +174,7 @@ namespace Avalonia.Styling
}
else
{
return sourceInstance.WithPriority(BindingPriority.StyleTrigger);
return sourceInstance.WithPriority(BindingPriority.Style);
}
}
}

4
src/Avalonia.Themes.Default/Avalonia.Themes.Default.csproj

@ -11,7 +11,7 @@
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<DocumentationFile>bin\Debug\Avalonia.Themes.Default.XML</DocumentationFile>
<DocumentationFile>bin\Debug\Avalonia.Themes.Default.xml</DocumentationFile>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<DebugType>pdbonly</DebugType>
@ -20,7 +20,7 @@
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<DocumentationFile>bin\Release\Avalonia.Themes.Default.XML</DocumentationFile>
<DocumentationFile>bin\Release\Avalonia.Themes.Default.xml</DocumentationFile>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<ItemGroup>

2
src/Avalonia.Themes.Default/Button.xaml

@ -13,8 +13,8 @@
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
Content="{TemplateBinding Content}"
Padding="{TemplateBinding Padding}"
TextBlock.Foreground="{TemplateBinding Foreground}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"

30
src/Avalonia.Themes.Default/Calendar.xaml

@ -0,0 +1,30 @@
<!--
// (c) Copyright Microsoft Corporation.
// This source is subject to the Microsoft Public License (Ms-PL).
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved.
-->
<Styles xmlns="https://github.com/avaloniaui">
<Style Selector="Calendar">
<!--<Setter Property="Focusable" Value="False" />-->
<Setter Property="BorderThickness" Value="{DynamicResource ThemeBorderThickness}" />
<Setter Property="BorderBrush" Value="{DynamicResource ThemeBorderDarkBrush}" />
<Setter Property="Background" Value="{DynamicResource ThemeBackgroundBrush}" />
<Setter Property="HeaderBackground" Value="{DynamicResource ThemeAccentBrush2}" />
<Setter Property="Template">
<ControlTemplate>
<StackPanel Name="Root"
HorizontalAlignment="Center">
<CalendarItem Name="CalendarItem"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
HeaderBackground="{TemplateBinding HeaderBackground}"/>
</StackPanel>
</ControlTemplate>
</Setter>
</Style>
</Styles>

80
src/Avalonia.Themes.Default/CalendarButton.xaml

@ -0,0 +1,80 @@
<!--
// (c) Copyright Microsoft Corporation.
// This source is subject to the Microsoft Public License (Ms-PL).
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved.
-->
<Styles xmlns="https://github.com/avaloniaui">
<Style Selector="CalendarButton">
<Setter Property="Background" Value="{DynamicResource ThemeAccentBrush2}" />
<Setter Property="FontSize" Value="{DynamicResource FontSizeSmall}" />
<Setter Property="HorizontalContentAlignment" Value="Center" />
<Setter Property="VerticalContentAlignment" Value="Center" />
<Setter Property="MinWidth" Value="37" />
<Setter Property="MinHeight" Value="38" />
<Setter Property="Focusable" Value="False"/>
<Setter Property="Template">
<ControlTemplate>
<Grid>
<Rectangle Name="SelectedBackground"
Opacity="0.75"
Fill="{TemplateBinding Background}"/>
<Rectangle Name="Background"
Opacity="0.5"
Fill="{TemplateBinding Background}"/>
<!--Focusable="False"-->
<ContentControl Name="Content"
Foreground="#FF333333"
ContentTemplate="{TemplateBinding ContentTemplate}"
Content="{TemplateBinding Content}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
FontSize="{TemplateBinding FontSize}"
Margin="1,0,1,1"/>
<Rectangle Name="FocusVisual"
StrokeThickness="1"
Stroke="{DynamicResource HighlightBrush}"
IsHitTestVisible="False"/>
</Grid>
</ControlTemplate>
</Setter>
</Style>
<Style Selector="CalendarButton /template/ Rectangle#Background">
<Setter Property="IsVisible" Value="False"/>
</Style>
<Style Selector="CalendarButton:pointerover /template/ Rectangle#Background">
<Setter Property="IsVisible" Value="True"/>
</Style>
<Style Selector="CalendarButton:pressed /template/ Rectangle#Background">
<Setter Property="IsVisible" Value="True"/>
</Style>
<Style Selector="CalendarButton /template/ Rectangle#SelectedBackground">
<Setter Property="IsVisible" Value="False"/>
</Style>
<Style Selector="CalendarButton:selected /template/ Rectangle#SelectedBackground">
<Setter Property="IsVisible" Value="True"/>
</Style>
<Style Selector="CalendarButton /template/ ContentControl#Content">
<Setter Property="Foreground" Value="{DynamicResource ThemeForegroundBrush}"/>
</Style>
<Style Selector="CalendarButton:inactive /template/ ContentControl#Content">
<Setter Property="Foreground" Value="{DynamicResource ThemeForegroundLightBrush}"/>
</Style>
<Style Selector="CalendarButton /template/ Rectangle#FocusVisual">
<Setter Property="IsVisible" Value="False"/>
</Style>
<Style Selector="CalendarButton:btnfocused /template/ Rectangle#FocusVisual">
<Setter Property="IsVisible" Value="True"/>
</Style>
</Styles>

116
src/Avalonia.Themes.Default/CalendarDayButton.xaml

@ -0,0 +1,116 @@
<!--
// (c) Copyright Microsoft Corporation.
// This source is subject to the Microsoft Public License (Ms-PL).
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved.
-->
<Styles xmlns="https://github.com/avaloniaui">
<Style Selector="CalendarDayButton">
<Setter Property="Background" Value="{DynamicResource ThemeAccentBrush2}" />
<Setter Property="FontSize" Value="{DynamicResource FontSizeSmall}" />
<Setter Property="HorizontalContentAlignment" Value="Center" />
<Setter Property="VerticalContentAlignment" Value="Center" />
<Setter Property="MinWidth" Value="5" />
<Setter Property="MinHeight" Value="5" />
<Setter Property="Focusable" Value="False"/>
<Setter Property="Template">
<ControlTemplate>
<Panel Background="Transparent">
<Rectangle Name="TodayBackground"
Fill="{DynamicResource HighlightBrush}"/>
<Rectangle Name="SelectedBackground"
Opacity="0.75"
Fill="{TemplateBinding Background}"/>
<Rectangle Name="Background"
Opacity="0.5"
Fill="{TemplateBinding Background}"/>
<ContentControl Name="Content"
ContentTemplate="{TemplateBinding ContentTemplate}"
Content="{TemplateBinding Content}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
FontSize="{TemplateBinding FontSize}"
Margin="5,1,5,1"/>
<Path Name="BlackoutVisual"
Margin="3"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
RenderTransformOrigin="0.5,0.5"
Fill="#FF000000"
Stretch="Fill"
Data="M8.1772461,11.029181 L10.433105,11.029181 L11.700684,12.801641 L12.973633,11.029181 L15.191895,11.029181 L12.844727,13.999395 L15.21875,17.060919 L12.962891,17.060919 L11.673828,15.256231 L10.352539,17.060919 L8.1396484,17.060919 L10.519043,14.042364 z" />
<Rectangle Name="FocusVisual"
StrokeThickness="1"
Stroke="{DynamicResource HighlightBrush}"
IsHitTestVisible="False"/>
</Panel>
</ControlTemplate>
</Setter>
</Style>
<Style Selector="CalendarDayButton /template/ Rectangle#Background">
<Setter Property="IsVisible" Value="False"/>
</Style>
<Style Selector="CalendarDayButton:pointerover /template/ Rectangle#Background">
<Setter Property="IsVisible" Value="True"/>
</Style>
<Style Selector="CalendarDayButton:pressed /template/ Rectangle#Background">
<Setter Property="IsVisible" Value="True"/>
</Style>
<Style Selector="CalendarDayButton /template/ Rectangle#SelectedBackground">
<Setter Property="IsVisible" Value="False"/>
</Style>
<Style Selector="CalendarDayButton:selected /template/ Rectangle#SelectedBackground">
<Setter Property="IsVisible" Value="True"/>
</Style>
<Style Selector="CalendarDayButton /template/ ContentControl#Content">
<Setter Property="Opacity" Value="1"/>
<Setter Property="Foreground" Value="{DynamicResource ThemeForegroundBrush}"/>
</Style>
<Style Selector="CalendarDayButton:disabled /template/ Rectangle#Background">
<Setter Property="IsVisible" Value="False"/>
</Style>
<Style Selector="CalendarDayButton:disabled /template/ ContentControl#Content">
<Setter Property="Opacity" Value="0.3"/>
</Style>
<Style Selector="CalendarDayButton /template/ Rectangle#FocusVisual">
<Setter Property="IsVisible" Value="False"/>
</Style>
<Style Selector="CalendarDayButton:dayfocused /template/ Rectangle#FocusVisual">
<Setter Property="IsVisible" Value="True"/>
</Style>
<Style Selector="CalendarDayButton /template/ Rectangle#TodayBackground">
<Setter Property="IsVisible" Value="False"/>
</Style>
<Style Selector="CalendarDayButton:today /template/ Rectangle#TodayBackground">
<Setter Property="IsVisible" Value="True"/>
</Style>
<Style Selector="CalendarDayButton:inactive /template/ ContentControl#Content">
<Setter Property="Foreground" Value="{DynamicResource ThemeForegroundLightBrush}"/>
</Style>
<Style Selector="CalendarDayButton:today /template/ ContentControl#Content">
<Setter Property="Foreground" Value="#FFFFFFFF"/>
</Style>
<Style Selector="CalendarDayButton /template/ Path#BlackoutVisual">
<Setter Property="Opacity" Value="0"/>
</Style>
<Style Selector="CalendarDayButton:blackout /template/ Path#BlackoutVisual">
<Setter Property="Opacity" Value="0.3"/>
</Style>
</Styles>

183
src/Avalonia.Themes.Default/CalendarItem.xaml

@ -0,0 +1,183 @@
<!--
// (c) Copyright Microsoft Corporation.
// This source is subject to the Microsoft Public License (Ms-PL).
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved.
-->
<Styles xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style Selector="CalendarItem">
<Setter Property="Template">
<ControlTemplate>
<Panel>
<Border BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="{TemplateBinding BorderBrush}"
Background="{TemplateBinding Background}"
Margin="0,2,0,2"
CornerRadius="1">
<Border CornerRadius="1"
BorderBrush="{DynamicResource ThemeBackgroundBrush}"
BorderThickness="2">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.Styles>
<Style Selector="Button.CalendarHeader">
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderThickness" Value="0"/>
</Style>
<Style Selector="Button.CalendarHeader:pressed /template/ ContentPresenter">
<Setter Property="Background" Value="Transparent"/>
</Style>
<Style Selector="Button.CalendarNavigation">
<Setter Property="Height" Value="20"/>
<Setter Property="Width" Value="28"/>
</Style>
<Style Selector="Button.CalendarNavigation > Path">
<Setter Property="Fill" Value="{DynamicResource ThemeForegroundBrush}"/>
</Style>
<Style Selector="Button.CalendarNavigation:pointerover > Path">
<Setter Property="Fill" Value="{DynamicResource HighlightBrush}"/>
</Style>
<Style Selector="Button#HeaderButton:pointerover">
<Setter Property="Foreground" Value="{DynamicResource HighlightBrush}"/>
</Style>
</Grid.Styles>
<Rectangle Grid.ColumnSpan="3"
Fill="{TemplateBinding HeaderBackground}"
Stretch="Fill"
VerticalAlignment="Top"
Height="22"/>
<Button Name="PreviousButton"
Classes="CalendarHeader CalendarNavigation"
IsVisible="False"
HorizontalAlignment="Left">
<Path Margin="14,-6,0,0"
Height="10"
Width="6"
VerticalAlignment="Center"
HorizontalAlignment="Left"
Stretch="Fill"
Data="M288.75,232.25 L288.75,240.625 L283,236.625 z" />
</Button>
<Button Name="HeaderButton"
Classes="CalendarHeader"
Grid.Column="1"
FontWeight="Bold"
FontSize="10.5"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Padding="1,5,1,9"/>
<Button Name="NextButton"
Classes="CalendarHeader CalendarNavigation"
Grid.Column="2"
IsVisible="False"
HorizontalAlignment="Right" >
<Path Margin="0,-6,14,0"
Height="10"
Width="6"
Stretch="Fill"
VerticalAlignment="Center"
HorizontalAlignment="Right"
Data="M282.875,231.875 L282.875,240.375 L288.625,236 z" />
</Button>
<Grid Name="MonthView"
Grid.Row="1"
Grid.ColumnSpan="3"
IsVisible="False"
Margin="6,-1,6,6">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
</Grid>
<Grid Name="YearView"
Grid.Row="1"
Grid.ColumnSpan="3"
IsVisible="False"
Margin="6,-3,7,6">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
</Grid>
</Grid>
</Border>
</Border>
<Rectangle Name="DisabledVisual"
Stretch="Fill"
Fill="#FFFFFFFF"
Opacity="{DynamicResource ThemeDisabledOpacity}"
Margin="0,2,0,2" />
</Panel>
</ControlTemplate>
</Setter>
<Setter Property="DayTitleTemplate">
<Template>
<TextBlock FontWeight="Bold"
FontSize="9.5"
Margin="0,4,0,4"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Text="{Binding}" />
</Template>
</Setter>
</Style>
<Style Selector="CalendarItem /template/ Rectangle#DisabledVisual">
<Setter Property="IsVisible" Value="False"/>
</Style>
<Style Selector="CalendarItem:calendardisabled /template/ Rectangle#DisabledVisual">
<Setter Property="IsVisible" Value="True"/>
</Style>
</Styles>

33
src/Avalonia.Themes.Default/CheckBox.xaml

@ -12,18 +12,27 @@
Width="18"
Height="18"
VerticalAlignment="Center">
<Path Name="checkMark"
Fill="{DynamicResource HighlightBrush}"
Width="11"
Height="10"
Stretch="Uniform"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Data="M 1145.607177734375,430 C1145.607177734375,430 1141.449951171875,435.0772705078125 1141.449951171875,435.0772705078125 1141.449951171875,435.0772705078125 1139.232177734375,433.0999755859375 1139.232177734375,433.0999755859375 1139.232177734375,433.0999755859375 1138,434.5538330078125 1138,434.5538330078125 1138,434.5538330078125 1141.482177734375,438 1141.482177734375,438 1141.482177734375,438 1141.96875,437.9375 1141.96875,437.9375 1141.96875,437.9375 1147,431.34619140625 1147,431.34619140625 1147,431.34619140625 1145.607177734375,430 1145.607177734375,430 z"/>
<Panel>
<Path Name="checkMark"
Fill="{DynamicResource HighlightBrush}"
Width="11"
Height="10"
Stretch="Uniform"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Data="M 1145.607177734375,430 C1145.607177734375,430 1141.449951171875,435.0772705078125 1141.449951171875,435.0772705078125 1141.449951171875,435.0772705078125 1139.232177734375,433.0999755859375 1139.232177734375,433.0999755859375 1139.232177734375,433.0999755859375 1138,434.5538330078125 1138,434.5538330078125 1138,434.5538330078125 1141.482177734375,438 1141.482177734375,438 1141.482177734375,438 1141.96875,437.9375 1141.96875,437.9375 1141.96875,437.9375 1147,431.34619140625 1147,431.34619140625 1147,431.34619140625 1145.607177734375,430 1145.607177734375,430 z"/>
<Rectangle Name="indeterminateMark"
Fill="{DynamicResource HighlightBrush}"
Width="10"
Height="10"
Stretch="Uniform"
HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Panel>
</Border>
<ContentPresenter Name="PART_ContentPresenter"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
Content="{TemplateBinding Content}"
Margin="4,0,0,0"
VerticalAlignment="Center"
Grid.Column="1"/>
@ -37,9 +46,15 @@
<Style Selector="CheckBox /template/ Path#checkMark">
<Setter Property="IsVisible" Value="False"/>
</Style>
<Style Selector="CheckBox /template/ Rectangle#indeterminateMark">
<Setter Property="IsVisible" Value="False"/>
</Style>
<Style Selector="CheckBox:checked /template/ Path#checkMark">
<Setter Property="IsVisible" Value="True"/>
</Style>
<Style Selector="CheckBox:indeterminate /template/ Rectangle#indeterminateMark">
<Setter Property="IsVisible" Value="True"/>
</Style>
<Style Selector="CheckBox:disabled /template/ Border#border">
<Setter Property="Opacity" Value="{DynamicResource ThemeDisabledOpacity}"/>
</Style>

2
src/Avalonia.Themes.Default/ContentControl.xaml

@ -5,8 +5,8 @@
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
Content="{TemplateBinding Content}"
Padding="{TemplateBinding Padding}"/>
</ControlTemplate>
</Setter>

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

@ -36,4 +36,8 @@
<StyleInclude Source="resm:Avalonia.Themes.Default.TreeViewItem.xaml?assembly=Avalonia.Themes.Default"/>
<StyleInclude Source="resm:Avalonia.Themes.Default.Window.xaml?assembly=Avalonia.Themes.Default"/>
<StyleInclude Source="resm:Avalonia.Themes.Default.EmbeddableControlRoot.xaml?assembly=Avalonia.Themes.Default"/>
<StyleInclude Source="resm:Avalonia.Themes.Default.CalendarButton.xaml?assembly=Avalonia.Themes.Default"/>
<StyleInclude Source="resm:Avalonia.Themes.Default.CalendarDayButton.xaml?assembly=Avalonia.Themes.Default"/>
<StyleInclude Source="resm:Avalonia.Themes.Default.CalendarItem.xaml?assembly=Avalonia.Themes.Default"/>
<StyleInclude Source="resm:Avalonia.Themes.Default.Calendar.xaml?assembly=Avalonia.Themes.Default"/>
</Styles>

2
src/Avalonia.Themes.Default/DropDownItem.xaml

@ -10,8 +10,8 @@
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
Content="{TemplateBinding Content}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
Padding="{TemplateBinding Padding}"/>

2
src/Avalonia.Themes.Default/EmbeddableControlRoot.xaml

@ -7,8 +7,8 @@
<Border Background="{TemplateBinding Background}">
<AdornerDecorator>
<ContentPresenter Name="PART_ContentPresenter"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
Content="{TemplateBinding Content}"
Margin="{TemplateBinding Padding}"/>
</AdornerDecorator>
</Border>

8
src/Avalonia.Themes.Default/Expander.xaml

@ -16,8 +16,8 @@
<ContentPresenter Name="PART_ContentPresenter"
Grid.Row="1"
IsVisible="{TemplateBinding IsExpanded}"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
Content="{TemplateBinding Content}"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" />
</Grid>
@ -34,8 +34,8 @@
<ContentPresenter Name="PART_ContentPresenter"
Grid.Row="0"
IsVisible="{TemplateBinding IsExpanded}"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
Content="{TemplateBinding Content}"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" />
</Grid>
@ -52,8 +52,8 @@
<ContentPresenter Name="PART_ContentPresenter"
Grid.Column="1"
IsVisible="{TemplateBinding IsExpanded}"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
Content="{TemplateBinding Content}"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" />
</Grid>
@ -70,8 +70,8 @@
<ContentPresenter Name="PART_ContentPresenter"
Grid.Column="0"
IsVisible="{TemplateBinding IsExpanded}"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
Content="{TemplateBinding Content}"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" />
</Grid>

2
src/Avalonia.Themes.Default/LayoutTransformControl.xaml

@ -5,8 +5,8 @@
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
Content="{TemplateBinding Content}"
Padding="{TemplateBinding Padding}"/>
</ControlTemplate>
</Setter>

2
src/Avalonia.Themes.Default/ListBoxItem.xaml

@ -7,8 +7,8 @@
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
Content="{TemplateBinding Content}"
Padding="{TemplateBinding Padding}"/>
</ControlTemplate>
</Setter>

2
src/Avalonia.Themes.Default/PopupRoot.xaml

@ -4,8 +4,8 @@
<ControlTemplate>
<ContentPresenter Name="PART_ContentPresenter"
Background="{TemplateBinding Background}"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
Content="{TemplateBinding Content}"
Padding="{TemplateBinding Padding}"/>
</ControlTemplate>
</Setter>

16
src/Avalonia.Themes.Default/RadioButton.xaml

@ -20,9 +20,17 @@
UseLayoutRounding="False"
HorizontalAlignment="Center"
VerticalAlignment="Center"/>
<Ellipse Name="indeterminateMark"
Fill="{DynamicResource ThemeAccentBrush}"
Width="10"
Height="10"
Stretch="Uniform"
UseLayoutRounding="False"
HorizontalAlignment="Center"
VerticalAlignment="Center"/>
<ContentPresenter Name="PART_ContentPresenter"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
Content="{TemplateBinding Content}"
Margin="4,0,0,0"
VerticalAlignment="Center"
Grid.Column="1"/>
@ -36,9 +44,15 @@
<Style Selector="RadioButton /template/ Ellipse#checkMark">
<Setter Property="IsVisible" Value="False"/>
</Style>
<Style Selector="RadioButton /template/ Ellipse#indeterminateMark">
<Setter Property="IsVisible" Value="False"/>
</Style>
<Style Selector="RadioButton:checked /template/ Ellipse#checkMark">
<Setter Property="IsVisible" Value="True"/>
</Style>
<Style Selector="RadioButton:indeterminate /template/ Ellipse#indeterminateMark">
<Setter Property="IsVisible" Value="True"/>
</Style>
<Style Selector="RadioButton:disabled /template/ Ellipse#border">
<Setter Property="Opacity" Value="{DynamicResource ThemeDisabledOpacity}"/>
</Style>

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

@ -20,8 +20,8 @@
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
Content="{TemplateBinding Content}"
Padding="{TemplateBinding Padding}"
TextBlock.Foreground="{TemplateBinding Foreground}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"

2
src/Avalonia.Themes.Default/TabStripItem.xaml

@ -9,8 +9,8 @@
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
Content="{TemplateBinding Content}"
Padding="{TemplateBinding Padding}"/>
</ControlTemplate>
</Setter>

2
src/Avalonia.Themes.Default/ToggleButton.xaml

@ -13,8 +13,8 @@
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
Content="{TemplateBinding Content}"
Padding="{TemplateBinding Padding}"
TextBlock.Foreground="{TemplateBinding Foreground}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"

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

Loading…
Cancel
Save