Browse Source

Merge branch 'master' into RelativeSourceSyntaxSugar

pull/1209/head
Jeremy Koritzinsky 9 years ago
committed by GitHub
parent
commit
dfb1c3886c
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      Avalonia.sln
  2. 2
      appveyor.yml
  3. 2
      build/MonoMac.props
  4. 5
      build/Serilog.Sinks.Trace.props
  5. 4
      build/Serilog.props
  6. 6
      packages.cake
  7. 13
      samples/BindingTest/App.xaml.cs
  8. 1
      samples/BindingTest/BindingTest.csproj
  9. 1
      samples/ControlCatalog.Desktop/ControlCatalog.Desktop.csproj
  10. 15
      samples/ControlCatalog.Desktop/Program.cs
  11. 6
      samples/ControlCatalog/ControlCatalog.csproj
  12. 1
      samples/ControlCatalog/MainView.xaml
  13. 47
      samples/ControlCatalog/Pages/CalendarPage.xaml
  14. 28
      samples/ControlCatalog/Pages/CalendarPage.xaml.cs
  15. 10
      samples/ControlCatalog/Pages/CarouselPage.xaml
  16. 5
      samples/ControlCatalog/Pages/CarouselPage.xaml.cs
  17. 25
      samples/ControlCatalog/Pages/CheckBoxPage.xaml
  18. 2
      samples/ControlCatalog/Pages/DialogsPage.xaml.cs
  19. 24
      samples/ControlCatalog/Pages/RadioButtonPage.xaml
  20. 14
      samples/RenderTest/Program.cs
  21. 1
      samples/RenderTest/RenderTest.csproj
  22. 13
      samples/VirtualizationTest/Program.cs
  23. 1
      samples/VirtualizationTest/VirtualizationTest.csproj
  24. 1
      samples/interop/Direct3DInteropSample/Direct3DInteropSample.csproj
  25. 2132
      src/Avalonia.Controls/Calendar/Calendar.cs
  26. 215
      src/Avalonia.Controls/Calendar/CalendarBlackoutDatesCollection.cs
  27. 193
      src/Avalonia.Controls/Calendar/CalendarButton.cs
  28. 79
      src/Avalonia.Controls/Calendar/CalendarDateRange.cs
  29. 253
      src/Avalonia.Controls/Calendar/CalendarDayButton.cs
  30. 21
      src/Avalonia.Controls/Calendar/CalendarExtensions.cs
  31. 1264
      src/Avalonia.Controls/Calendar/CalendarItem.cs
  32. 155
      src/Avalonia.Controls/Calendar/DateTimeHelper.cs
  33. 361
      src/Avalonia.Controls/Calendar/SelectedDatesCollection.cs
  34. 2
      src/Avalonia.Controls/Classes.cs
  35. 36
      src/Avalonia.Controls/Primitives/ToggleButton.cs
  36. 2
      src/Avalonia.Controls/ProgressBar.cs
  37. 9
      src/Avalonia.Controls/RadioButton.cs
  38. 12
      src/Avalonia.Controls/TextBox.cs
  39. 1
      src/Avalonia.Logging.Serilog/Avalonia.Logging.Serilog.csproj
  40. 51
      src/Avalonia.Logging.Serilog/SerilogExtensions.cs
  41. 2
      src/Avalonia.Styling/Styling/Setter.cs
  42. 30
      src/Avalonia.Themes.Default/Calendar.xaml
  43. 80
      src/Avalonia.Themes.Default/CalendarButton.xaml
  44. 116
      src/Avalonia.Themes.Default/CalendarDayButton.xaml
  45. 183
      src/Avalonia.Themes.Default/CalendarItem.xaml
  46. 31
      src/Avalonia.Themes.Default/CheckBox.xaml
  47. 4
      src/Avalonia.Themes.Default/DefaultTheme.xaml
  48. 14
      src/Avalonia.Themes.Default/RadioButton.xaml
  49. 25
      src/Avalonia.Visuals/Animation/PageSlide.cs
  50. 2
      src/Markup/Avalonia.Markup.Xaml/PortableXaml/portable.xaml.github
  51. 4
      tests/Avalonia.Base.UnitTests/AvaloniaPropertyTests.cs
  52. 4
      tests/Avalonia.Base.UnitTests/PriorityValueTests.cs
  53. 277
      tests/Avalonia.Controls.UnitTests/CalendarTests.cs
  54. 8
      tests/Avalonia.Controls.UnitTests/CarouselTests.cs
  55. 10
      tests/Avalonia.Controls.UnitTests/ClassesTests.cs
  56. 2
      tests/Avalonia.Controls.UnitTests/ControlTests.cs
  57. 6
      tests/Avalonia.Controls.UnitTests/ItemsControlTests.cs
  58. 40
      tests/Avalonia.Controls.UnitTests/Presenters/CarouselPresenterTests.cs
  59. 6
      tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_Standalone.cs
  60. 2
      tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests.cs
  61. 2
      tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization_Simple.cs
  62. 2
      tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs
  63. 6
      tests/Avalonia.Controls.UnitTests/Primitives/ScrollBarTests.cs
  64. 57
      tests/Avalonia.Controls.UnitTests/Primitives/ToggleButtonTests.cs
  65. 36
      tests/Avalonia.Controls.UnitTests/RadioButtonTests.cs
  66. 8
      tests/Avalonia.Controls.UnitTests/TextBoxTests_DataValidation.cs
  67. 2
      tests/Avalonia.Layout.UnitTests/FullLayoutTests.cs
  68. 4
      tests/Avalonia.LeakTests/ControlTests.cs
  69. 6
      tests/Avalonia.Markup.UnitTests/Data/ExpressionObserverTests_DataValidation.cs
  70. 10
      tests/Avalonia.Markup.UnitTests/Data/ExpressionObserverTests_Negation.cs
  71. 2
      tests/Avalonia.Markup.UnitTests/Data/ExpressionObserverTests_Task.cs
  72. 2
      tests/Avalonia.Markup.Xaml.UnitTests/Data/BindingTests_Source.cs
  73. 35
      tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs
  74. 2
      tests/Avalonia.Markup.Xaml.UnitTests/Xaml/StyleTests.cs
  75. 2
      tests/Avalonia.Styling.UnitTests/SelectorTests_Template.cs
  76. 71
      tests/Avalonia.Styling.UnitTests/SetterTests.cs
  77. 8
      tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneBuilderTests_Layers.cs
  78. 2
      tests/Avalonia.Visuals.UnitTests/VisualTests.cs

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>

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);
}

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;
}
}
}

2
src/Avalonia.Controls/ProgressBar.cs

@ -112,8 +112,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;
}
}
}

12
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()
{
@ -280,6 +281,7 @@ namespace Avalonia.Controls
{
if (!IsReadOnly)
{
input = RemoveInvalidCharacters(input);
string text = Text ?? string.Empty;
int caretIndex = CaretIndex;
if (!string.IsNullOrEmpty(input))
@ -295,6 +297,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)))

1
src/Avalonia.Logging.Serilog/Avalonia.Logging.Serilog.csproj

@ -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;
}
}
}

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);
}
}
}

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"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
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"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
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>

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

@ -12,14 +12,23 @@
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}"
@ -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>

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>

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

@ -20,6 +20,14 @@
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}"
@ -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>

25
src/Avalonia.Visuals/Animation/PageSlide.cs

@ -15,6 +15,15 @@ namespace Avalonia.Animation
/// </summary>
public class PageSlide : IPageTransition
{
/// <summary>
/// The axis on which the PageSlide should occur
/// </summary>
public enum SlideAxis
{
Horizontal,
Vertical
}
/// <summary>
/// Initializes a new instance of the <see cref="PageSlide"/> class.
/// </summary>
@ -26,9 +35,11 @@ namespace Avalonia.Animation
/// Initializes a new instance of the <see cref="PageSlide"/> class.
/// </summary>
/// <param name="duration">The duration of the animation.</param>
public PageSlide(TimeSpan duration)
/// <param name="orientation">The axis on which the animation should occur</param>
public PageSlide(TimeSpan duration, SlideAxis orientation = SlideAxis.Horizontal)
{
Duration = duration;
Orientation = orientation;
}
/// <summary>
@ -36,6 +47,11 @@ namespace Avalonia.Animation
/// </summary>
public TimeSpan Duration { get; set; }
/// <summary>
/// Gets the duration of the animation.
/// </summary>
public SlideAxis Orientation { get; set; }
/// <summary>
/// Starts the animation.
/// </summary>
@ -55,7 +71,8 @@ namespace Avalonia.Animation
{
var tasks = new List<Task>();
var parent = GetVisualParent(from, to);
var distance = parent.Bounds.Width;
var distance = Orientation == SlideAxis.Horizontal ? parent.Bounds.Width : parent.Bounds.Height;
var translateProperty = Orientation == SlideAxis.Horizontal ? TranslateTransform.XProperty : TranslateTransform.YProperty;
if (from != null)
{
@ -63,7 +80,7 @@ namespace Avalonia.Animation
from.RenderTransform = transform;
tasks.Add(Animate.Property(
transform,
TranslateTransform.XProperty,
translateProperty,
0.0,
forward ? -distance : distance,
LinearEasing.For<double>(),
@ -77,7 +94,7 @@ namespace Avalonia.Animation
to.IsVisible = true;
tasks.Add(Animate.Property(
transform,
TranslateTransform.XProperty,
translateProperty,
forward ? distance : -distance,
0.0,
LinearEasing.For<double>(),

2
src/Markup/Avalonia.Markup.Xaml/PortableXaml/portable.xaml.github

@ -1 +1 @@
Subproject commit f226a516fe2bc191145c1fbf732f5a087f121a33
Subproject commit d50ae8335eb50d4b9606de6f5fa1cbbc78bfd72f

4
tests/Avalonia.Base.UnitTests/AvaloniaPropertyTests.cs

@ -113,8 +113,8 @@ namespace Avalonia.Base.UnitTests
{
var p1 = new TestProperty<string>("p1", typeof(Class1));
Assert.NotEqual(p1, null);
Assert.NotEqual(null, p1);
Assert.NotNull(p1);
Assert.NotNull(p1);
Assert.False(p1 == null);
Assert.False(null == p1);
Assert.False(p1.Equals(null));

4
tests/Avalonia.Base.UnitTests/PriorityValueTests.cs

@ -206,7 +206,7 @@ namespace Avalonia.Base.UnitTests
Assert.Equal(2, target.GetBindings().Count());
disposable.Dispose();
Assert.Equal(1, target.GetBindings().Count());
Assert.Single(target.GetBindings());
}
[Fact]
@ -248,7 +248,7 @@ namespace Avalonia.Base.UnitTests
Assert.Equal(2, target.GetBindings().Count());
subject.OnCompleted();
Assert.Equal(1, target.GetBindings().Count());
Assert.Single(target.GetBindings());
}
[Fact]

277
tests/Avalonia.Controls.UnitTests/CalendarTests.cs

@ -0,0 +1,277 @@
// 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 Xunit;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
namespace Avalonia.Controls.UnitTests
{
public class CalendarTests
{
private static bool CompareDates(DateTime first, DateTime second)
{
return first.Year == second.Year &&
first.Month == second.Month &&
first.Day == second.Day;
}
[Fact]
public void SelectedDatesChanged_Should_Fire_When_SelectedDate_Set()
{
bool handled = false;
Calendar calendar = new Calendar();
calendar.SelectionMode = CalendarSelectionMode.SingleDate;
calendar.SelectedDatesChanged += new EventHandler<SelectionChangedEventArgs>(delegate
{
handled = true;
});
DateTime value = new DateTime(2000, 10, 10);
calendar.SelectedDate = value;
Assert.True(handled);
}
[Fact]
public void DisplayDateChanged_Should_Fire_When_DisplayDate_Set()
{
bool handled = false;
Calendar calendar = new Calendar();
calendar.SelectionMode = CalendarSelectionMode.SingleDate;
calendar.DisplayDateChanged += new EventHandler<CalendarDateChangedEventArgs>(delegate
{
handled = true;
});
DateTime value = new DateTime(2000, 10, 10);
calendar.DisplayDate = value;
Assert.True(handled);
}
[Fact]
public void Setting_Selected_Date_To_Blackout_Date_Should_Throw()
{
Calendar calendar = new Calendar();
calendar.BlackoutDates.AddDatesInPast();
Assert.ThrowsAny<ArgumentOutOfRangeException>(
() => calendar.SelectedDate = DateTime.Today.AddDays(-1));
}
[Fact]
public void Setting_Selected_Date_To_Blackout_Date_Should_Throw_Range()
{
Calendar calendar = new Calendar();
calendar.BlackoutDates.Add(new CalendarDateRange(DateTime.Today, DateTime.Today.AddDays(10)));
calendar.SelectedDate = DateTime.Today.AddDays(-1);
Assert.True(CompareDates(calendar.SelectedDate.Value, DateTime.Today.AddDays(-1)));
Assert.True(CompareDates(calendar.SelectedDate.Value, calendar.SelectedDates[0]));
calendar.SelectedDate = DateTime.Today.AddDays(11);
Assert.True(CompareDates(calendar.SelectedDate.Value, DateTime.Today.AddDays(11)));
Assert.True(CompareDates(calendar.SelectedDate.Value, calendar.SelectedDates[0]));
Assert.ThrowsAny<ArgumentOutOfRangeException>(
() => calendar.SelectedDate = DateTime.Today.AddDays(5));
}
[Fact]
public void Adding_Blackout_Dates_Containing_Selected_Date_Should_Throw()
{
Calendar calendar = new Calendar();
calendar.SelectedDate = DateTime.Today.AddDays(5);
Assert.ThrowsAny<ArgumentOutOfRangeException>(
() => calendar.BlackoutDates.Add(new CalendarDateRange(DateTime.Today, DateTime.Today.AddDays(10))));
}
[Fact]
public void DisplayDateStartEnd_Should_Constrain_Display_Date()
{
Calendar calendar = new Calendar();
calendar.SelectionMode = CalendarSelectionMode.SingleDate;
calendar.DisplayDateStart = new DateTime(2005, 12, 30);
DateTime value = new DateTime(2005, 12, 15);
calendar.DisplayDate = value;
Assert.True(CompareDates(calendar.DisplayDate, calendar.DisplayDateStart.Value));
value = new DateTime(2005, 12, 30);
calendar.DisplayDate = value;
Assert.True(CompareDates(calendar.DisplayDate, value));
value = DateTime.MaxValue;
calendar.DisplayDate = value;
Assert.True(CompareDates(calendar.DisplayDate, value));
calendar.DisplayDateEnd = new DateTime(2010, 12, 30);
Assert.True(CompareDates(calendar.DisplayDate, calendar.DisplayDateEnd.Value));
}
[Fact]
public void Setting_DisplayDateEnd_Should_Alter_DispalyDate_And_DisplayDateStart()
{
Calendar calendar = new Calendar();
DateTime value = new DateTime(2000, 1, 30);
calendar.DisplayDate = value;
calendar.DisplayDateEnd = value;
calendar.DisplayDateStart = value;
Assert.True(CompareDates(calendar.DisplayDateStart.Value, value));
Assert.True(CompareDates(calendar.DisplayDateEnd.Value, value));
value = value.AddMonths(2);
calendar.DisplayDateStart = value;
Assert.True(CompareDates(calendar.DisplayDateStart.Value, value));
Assert.True(CompareDates(calendar.DisplayDateEnd.Value, value));
Assert.True(CompareDates(calendar.DisplayDate, value));
}
[Fact]
public void Display_Date_Range_End_Will_Contain_SelectedDate()
{
Calendar calendar = new Calendar();
calendar.SelectionMode = CalendarSelectionMode.SingleDate;
calendar.SelectedDate = DateTime.MaxValue;
Assert.True(CompareDates((DateTime)calendar.SelectedDate, DateTime.MaxValue));
calendar.DisplayDateEnd = DateTime.MaxValue.AddDays(-1);
Assert.True(CompareDates((DateTime)calendar.DisplayDateEnd, DateTime.MaxValue));
}
/// <summary>
/// The days added to the SelectedDates collection.
/// </summary>
private IList<object> _selectedDatesChangedAddedDays;
/// <summary>
/// The days removed from the SelectedDates collection.
/// </summary>
private IList<object> _selectedDateChangedRemovedDays;
/// <summary>
/// The number of times the SelectedDatesChanged event has been fired.
/// </summary>
private int _selectedDatesChangedCount;
/// <summary>
/// Handle the SelectedDatesChanged event.
/// </summary>
/// <param name="sender">The calendar.</param>
/// <param name="e">Event arguments.</param>
private void OnSelectedDatesChanged(object sender, SelectionChangedEventArgs e)
{
_selectedDatesChangedAddedDays =
e.AddedItems
.Cast<object>()
.ToList();
_selectedDateChangedRemovedDays =
e.RemovedItems
.Cast<object>()
.ToList();
_selectedDatesChangedCount++;
}
/// <summary>
/// Clear the variables used to track the SelectedDatesChanged event.
/// </summary>
private void ResetSelectedDatesChanged()
{
if (_selectedDatesChangedAddedDays != null)
{
_selectedDatesChangedAddedDays.Clear();
}
if (_selectedDateChangedRemovedDays != null)
{
_selectedDateChangedRemovedDays.Clear();
}
_selectedDatesChangedCount = 0;
}
[Fact]
public void SingleDate_Selection_Behavior()
{
ResetSelectedDatesChanged();
Calendar calendar = new Calendar();
calendar.SelectedDatesChanged += new EventHandler<SelectionChangedEventArgs>(OnSelectedDatesChanged);
calendar.SelectionMode = CalendarSelectionMode.SingleDate;
calendar.SelectedDate = DateTime.Today;
Assert.True(CompareDates(calendar.SelectedDate.Value, DateTime.Today));
Assert.True(calendar.SelectedDates.Count == 1);
Assert.True(CompareDates(calendar.SelectedDates[0], DateTime.Today));
Assert.True(_selectedDatesChangedCount == 1);
Assert.True(_selectedDatesChangedAddedDays.Count == 1);
Assert.True(_selectedDateChangedRemovedDays.Count == 0);
ResetSelectedDatesChanged();
calendar.SelectedDate = DateTime.Today;
Assert.True(CompareDates(calendar.SelectedDate.Value, DateTime.Today));
Assert.True(calendar.SelectedDates.Count == 1);
Assert.True(CompareDates(calendar.SelectedDates[0], DateTime.Today));
Assert.True(_selectedDatesChangedCount == 0);
calendar.ClearValue(Calendar.SelectedDateProperty);
calendar.SelectionMode = CalendarSelectionMode.None;
Assert.True(calendar.SelectedDates.Count == 0);
Assert.Null(calendar.SelectedDate);
calendar.SelectionMode = CalendarSelectionMode.SingleDate;
calendar.SelectedDates.Add(DateTime.Today.AddDays(1));
Assert.True(CompareDates(calendar.SelectedDate.Value, DateTime.Today.AddDays(1)));
Assert.True(calendar.SelectedDates.Count == 1);
Assert.ThrowsAny<InvalidOperationException>(
() => calendar.SelectedDates.Add(DateTime.Today.AddDays(2)));
}
[Fact]
public void SingleRange_Selection_Behavior()
{
ResetSelectedDatesChanged();
Calendar calendar = new Calendar();
calendar.SelectedDatesChanged += new EventHandler<SelectionChangedEventArgs>(OnSelectedDatesChanged);
calendar.SelectionMode = CalendarSelectionMode.SingleRange;
calendar.SelectedDate = DateTime.Today;
Assert.True(CompareDates(calendar.SelectedDate.Value, DateTime.Today));
Assert.True(calendar.SelectedDates.Count == 1);
Assert.True(CompareDates(calendar.SelectedDates[0], DateTime.Today));
Assert.True(_selectedDatesChangedCount == 1);
Assert.True(_selectedDatesChangedAddedDays.Count == 1);
Assert.True(_selectedDateChangedRemovedDays.Count == 0);
ResetSelectedDatesChanged();
calendar.SelectedDates.Clear();
Assert.Null(calendar.SelectedDate);
ResetSelectedDatesChanged();
calendar.SelectedDates.AddRange(DateTime.Today, DateTime.Today.AddDays(10));
Assert.True(CompareDates(calendar.SelectedDate.Value, DateTime.Today));
Assert.True(calendar.SelectedDates.Count == 11);
ResetSelectedDatesChanged();
calendar.SelectedDates.AddRange(DateTime.Today, DateTime.Today.AddDays(10));
Assert.True(calendar.SelectedDates.Count == 11);
Assert.True(_selectedDatesChangedCount == 0);
ResetSelectedDatesChanged();
calendar.SelectedDates.AddRange(DateTime.Today.AddDays(-20), DateTime.Today);
Assert.True(CompareDates(calendar.SelectedDate.Value, DateTime.Today.AddDays(-20)));
Assert.True(calendar.SelectedDates.Count == 21);
Assert.True(_selectedDatesChangedCount == 1);
Assert.True(_selectedDatesChangedAddedDays.Count == 21);
Assert.True(_selectedDateChangedRemovedDays.Count == 11);
ResetSelectedDatesChanged();
calendar.SelectedDates.Add(DateTime.Today.AddDays(100));
Assert.True(CompareDates(calendar.SelectedDate.Value, DateTime.Today.AddDays(100)));
Assert.True(calendar.SelectedDates.Count == 1);
}
}
}

8
tests/Avalonia.Controls.UnitTests/CarouselTests.cs

@ -47,7 +47,7 @@ namespace Avalonia.Controls.UnitTests
target.ApplyTemplate();
target.Presenter.ApplyTemplate();
Assert.Equal(1, target.GetLogicalChildren().Count());
Assert.Single(target.GetLogicalChildren());
var child = target.GetLogicalChildren().Single();
@ -69,9 +69,9 @@ namespace Avalonia.Controls.UnitTests
target.ApplyTemplate();
target.Presenter.ApplyTemplate();
Assert.Equal(1, target.ItemContainerGenerator.Containers.Count());
Assert.Single(target.ItemContainerGenerator.Containers);
target.SelectedIndex = 1;
Assert.Equal(1, target.ItemContainerGenerator.Containers.Count());
Assert.Single(target.ItemContainerGenerator.Containers);
}
[Fact]
@ -88,7 +88,7 @@ namespace Avalonia.Controls.UnitTests
target.ApplyTemplate();
target.Presenter.ApplyTemplate();
Assert.Equal(1, target.ItemContainerGenerator.Containers.Count());
Assert.Single(target.ItemContainerGenerator.Containers);
target.SelectedIndex = 1;
Assert.Equal(2, target.ItemContainerGenerator.Containers.Count());
}

10
tests/Avalonia.Controls.UnitTests/ClassesTests.cs

@ -161,5 +161,15 @@ namespace Avalonia.Controls.UnitTests
Assert.Equal(new[] { ":baz" }, target);
}
[Fact]
public void RemoveAll_Should_Remove_Classes()
{
var target = new Classes("foo", "bar", "baz");
target.RemoveAll(new[] { "bar", "baz" });
Assert.Equal(new[] { "foo" }, target);
}
}
}

2
tests/Avalonia.Controls.UnitTests/ControlTests.cs

@ -19,7 +19,7 @@ namespace Avalonia.Controls.UnitTests
{
var target = new Control();
Assert.Equal(0, target.Classes.Count);
Assert.Empty(target.Classes);
}
[Fact]

6
tests/Avalonia.Controls.UnitTests/ItemsControlTests.cs

@ -323,7 +323,7 @@ namespace Avalonia.Controls.UnitTests
Template = GetTemplate(),
};
Assert.True(target.Classes.Contains(":empty"));
Assert.Contains(":empty", target.Classes);
}
[Fact]
@ -335,7 +335,7 @@ namespace Avalonia.Controls.UnitTests
Items = new[] { 1, 2, 3 },
};
Assert.False(target.Classes.Contains(":empty"));
Assert.DoesNotContain(":empty", target.Classes);
}
[Fact]
@ -349,7 +349,7 @@ namespace Avalonia.Controls.UnitTests
target.Items = new int[0];
Assert.True(target.Classes.Contains(":empty"));
Assert.Contains(":empty", target.Classes);
}
[Fact]

40
tests/Avalonia.Controls.UnitTests/Presenters/CarouselPresenterTests.cs

@ -91,9 +91,9 @@ namespace Avalonia.Controls.UnitTests.Presenters
};
target.ApplyTemplate();
Assert.Equal(1, target.ItemContainerGenerator.Containers.Count());
Assert.Single(target.ItemContainerGenerator.Containers);
target.SelectedIndex = 1;
Assert.Equal(1, target.ItemContainerGenerator.Containers.Count());
Assert.Single(target.ItemContainerGenerator.Containers);
}
[Fact]
@ -107,8 +107,8 @@ namespace Avalonia.Controls.UnitTests.Presenters
};
target.ApplyTemplate();
Assert.Equal(1, target.ItemContainerGenerator.Containers.Count());
Assert.Equal(1, target.Panel.Children.Count);
Assert.Single(target.ItemContainerGenerator.Containers);
Assert.Single(target.Panel.Children);
target.SelectedIndex = 1;
Assert.Equal(2, target.ItemContainerGenerator.Containers.Count());
Assert.Equal(2, target.Panel.Children.Count);
@ -134,24 +134,24 @@ namespace Avalonia.Controls.UnitTests.Presenters
items.Add("foo");
target.SelectedIndex = 0;
Assert.Equal(1, target.ItemContainerGenerator.Containers.Count());
Assert.Equal(1, target.Panel.Children.Count);
Assert.Single(target.ItemContainerGenerator.Containers);
Assert.Single(target.Panel.Children);
items.Add("bar");
Assert.Equal(1, target.ItemContainerGenerator.Containers.Count());
Assert.Equal(1, target.Panel.Children.Count);
Assert.Single(target.ItemContainerGenerator.Containers);
Assert.Single(target.Panel.Children);
target.SelectedIndex = 1;
Assert.Equal(2, target.ItemContainerGenerator.Containers.Count());
Assert.Equal(2, target.Panel.Children.Count);
items.Remove(items[0]);
Assert.Equal(1, target.ItemContainerGenerator.Containers.Count());
Assert.Equal(1, target.Panel.Children.Count);
Assert.Single(target.ItemContainerGenerator.Containers);
Assert.Single(target.Panel.Children);
items.Remove(items[0]);
Assert.Equal(0, target.ItemContainerGenerator.Containers.Count());
Assert.Equal(0, target.Panel.Children.Count);
Assert.Empty(target.ItemContainerGenerator.Containers);
Assert.Empty(target.Panel.Children);
}
[Fact]
@ -171,12 +171,12 @@ namespace Avalonia.Controls.UnitTests.Presenters
items.Add("foo");
target.SelectedIndex = 0;
Assert.Equal(1, target.ItemContainerGenerator.Containers.Count());
Assert.Equal(1, target.Panel.Children.Count);
Assert.Single(target.ItemContainerGenerator.Containers);
Assert.Single(target.Panel.Children);
items.Add("bar");
Assert.Equal(1, target.ItemContainerGenerator.Containers.Count());
Assert.Equal(1, target.Panel.Children.Count);
Assert.Single(target.ItemContainerGenerator.Containers);
Assert.Single(target.Panel.Children);
target.SelectedIndex = 1;
Assert.Equal(2, target.ItemContainerGenerator.Containers.Count());
@ -184,13 +184,13 @@ namespace Avalonia.Controls.UnitTests.Presenters
Assert.Equal(0, target.ItemContainerGenerator.Containers.First().Index);
items.Remove(items[0]);
Assert.Equal(1, target.ItemContainerGenerator.Containers.Count());
Assert.Equal(1, target.Panel.Children.Count);
Assert.Single(target.ItemContainerGenerator.Containers);
Assert.Single(target.Panel.Children);
Assert.Equal(0, target.ItemContainerGenerator.Containers.First().Index);
items.Remove(items[0]);
Assert.Equal(0, target.ItemContainerGenerator.Containers.Count());
Assert.Equal(0, target.Panel.Children.Count);
Assert.Empty(target.ItemContainerGenerator.Containers);
Assert.Empty(target.Panel.Children);
}
private class TestItem : ContentControl

6
tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_Standalone.cs

@ -42,7 +42,7 @@ namespace Avalonia.Controls.UnitTests.Presenters
var logicalChildren = target.GetLogicalChildren();
Assert.Equal(1, logicalChildren.Count());
Assert.Single(logicalChildren);
Assert.Equal(content, logicalChildren.First());
}
@ -190,7 +190,7 @@ namespace Avalonia.Controls.UnitTests.Presenters
var logicalChildren = target.GetLogicalChildren();
Assert.Equal(1, logicalChildren.Count());
Assert.Single(logicalChildren);
target.Content = "bar";
target.UpdateChild();
@ -199,7 +199,7 @@ namespace Avalonia.Controls.UnitTests.Presenters
logicalChildren = target.GetLogicalChildren();
Assert.Equal(1, logicalChildren.Count());
Assert.Single(logicalChildren);
Assert.NotEqual(foo, logicalChildren.First());
}

2
tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests.cs

@ -87,7 +87,7 @@ namespace Avalonia.Controls.UnitTests.Presenters
target.ApplyTemplate();
items.RemoveAt(0);
Assert.Equal(1, target.Panel.Children.Count);
Assert.Single(target.Panel.Children);
Assert.Equal("bar", ((ContentPresenter)target.Panel.Children[0]).Content);
Assert.Equal("bar", ((ContentPresenter)target.ItemContainerGenerator.ContainerFromIndex(0)).Content);
}

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

@ -704,7 +704,7 @@ namespace Avalonia.Controls.UnitTests.Presenters
target.Measure(new Size(100, 100));
target.Arrange(new Rect(target.DesiredSize));
Assert.Equal(0, target.Panel.Children.Count);
Assert.Empty(target.Panel.Children);
items.AddRange(defaultItems.Select(s => s + " new"));

2
tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs

@ -194,7 +194,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
target.Open();
Assert.Equal(1, target.PopupRoot.GetVisualChildren().Count());
Assert.Single(target.PopupRoot.GetVisualChildren());
var templatedChild = target.PopupRoot.GetVisualChildren().Single();
Assert.IsType<ContentPresenter>(templatedChild);

6
tests/Avalonia.Controls.UnitTests/Primitives/ScrollBarTests.cs

@ -24,7 +24,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
var track = (Track)target.GetTemplateChildren().First(x => x.Name == "track");
target.Value = 50;
Assert.Equal(track.Value, 50);
Assert.Equal(50, track.Value);
}
[Fact]
@ -39,7 +39,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
var track = (Track)target.GetTemplateChildren().First(x => x.Name == "track");
track.Value = 50;
Assert.Equal(target.Value, 50);
Assert.Equal(50, target.Value);
}
[Fact]
@ -56,7 +56,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
target.Value = 25;
track.Value = 50;
Assert.Equal(target.Value, 50);
Assert.Equal(50, target.Value);
}
[Fact]

57
tests/Avalonia.Controls.UnitTests/Primitives/ToggleButtonTests.cs

@ -0,0 +1,57 @@
using Avalonia.Markup.Xaml.Data;
using Avalonia.UnitTests;
using Xunit;
namespace Avalonia.Controls.Primitives.UnitTests
{
public class ToggleButtonTests
{
private const string uncheckedClass = ":unchecked";
private const string checkedClass = ":checked";
private const string indeterminateClass = ":indeterminate";
[Theory]
[InlineData(false, uncheckedClass, false)]
[InlineData(false, uncheckedClass, true)]
[InlineData(true, checkedClass, false)]
[InlineData(true, checkedClass, true)]
[InlineData(null, indeterminateClass, false)]
[InlineData(null, indeterminateClass, true)]
public void ToggleButton_Has_Correct_Class_According_To_Is_Checked(bool? isChecked, string expectedClass, bool isThreeState)
{
var toggleButton = new ToggleButton();
toggleButton.IsThreeState = isThreeState;
toggleButton.IsChecked = isChecked;
Assert.Contains(expectedClass, toggleButton.Classes);
}
[Fact]
public void ToggleButton_Is_Checked_Binds_To_Bool()
{
var toggleButton = new ToggleButton();
var source = new Class1();
toggleButton.DataContext = source;
toggleButton.Bind(ToggleButton.IsCheckedProperty, new Binding("Foo"));
source.Foo = true;
Assert.True(toggleButton.IsChecked);
source.Foo = false;
Assert.False(toggleButton.IsChecked);
}
private class Class1 : NotifyingBase
{
private bool _foo;
public bool Foo
{
get { return _foo; }
set { _foo = value; RaisePropertyChanged(); }
}
}
}
}

36
tests/Avalonia.Controls.UnitTests/RadioButtonTests.cs

@ -0,0 +1,36 @@
using Avalonia.Markup.Xaml.Data;
using Avalonia.UnitTests;
using Xunit;
namespace Avalonia.Controls.UnitTests
{
public class RadioButtonTests
{
[Theory]
[InlineData(false)]
[InlineData(true)]
public void Indeterminate_RadioButton_Is_Not_Unchecked_After_Checking_Other_Radio_Button(bool isThreeState)
{
var panel = new Panel();
var radioButton1 = new RadioButton();
radioButton1.IsThreeState = false;
radioButton1.IsChecked = false;
var radioButton2 = new RadioButton();
radioButton2.IsThreeState = isThreeState;
radioButton2.IsChecked = null;
panel.Children.Add(radioButton1);
panel.Children.Add(radioButton2);
Assert.Null(radioButton2.IsChecked);
radioButton1.IsChecked = true;
Assert.True(radioButton1.IsChecked);
Assert.Null(radioButton2.IsChecked);
}
}
}

8
tests/Avalonia.Controls.UnitTests/TextBoxTests_DataValidation.cs

@ -33,11 +33,11 @@ namespace Avalonia.Controls.UnitTests
target.ApplyTemplate();
Assert.False(target.Classes.Contains(":error"));
Assert.DoesNotContain(":error", target.Classes);
target.Text = "20";
Assert.True(target.Classes.Contains(":error"));
Assert.Contains(":error", target.Classes);
target.Text = "1";
Assert.False(target.Classes.Contains(":error"));
Assert.DoesNotContain(":error", target.Classes);
}
}
@ -57,7 +57,7 @@ namespace Avalonia.Controls.UnitTests
Assert.Null(target.DataValidationErrors);
target.Text = "20";
Assert.Equal(1, target.DataValidationErrors.Count());
Assert.Single(target.DataValidationErrors);
Assert.IsType<InvalidOperationException>(target.DataValidationErrors.Single());
target.Text = "1";
Assert.Null(target.DataValidationErrors);

2
tests/Avalonia.Layout.UnitTests/FullLayoutTests.cs

@ -109,7 +109,7 @@ namespace Avalonia.Layout.UnitTests
var presenters = scrollViewer.GetTemplateChildren().OfType<ScrollContentPresenter>().ToList();
Assert.Equal(2, scrollBars.Count);
Assert.Equal(1, presenters.Count);
Assert.Single(presenters);
var presenter = presenters[0];
Assert.Equal(new Size(190, 190), presenter.Bounds.Size);

4
tests/Avalonia.LeakTests/ControlTests.cs

@ -155,7 +155,7 @@ namespace Avalonia.LeakTests
// template applied.
LayoutManager.Instance.ExecuteInitialLayoutPass(window);
Assert.IsType<TextBox>(window.Presenter.Child);
Assert.NotEqual(0, window.Presenter.Child.GetVisualChildren().Count());
Assert.NotEmpty(window.Presenter.Child.GetVisualChildren());
// Clear the content and ensure the TextBox is removed.
window.Content = null;
@ -290,7 +290,7 @@ namespace Avalonia.LeakTests
// Do a layout and make sure that TreeViewItems get realized.
LayoutManager.Instance.ExecuteInitialLayoutPass(window);
Assert.Equal(1, target.ItemContainerGenerator.Containers.Count());
Assert.Single(target.ItemContainerGenerator.Containers);
// Clear the content and ensure the TreeView is removed.
window.Content = null;

6
tests/Avalonia.Markup.UnitTests/Data/ExpressionObserverTests_DataValidation.cs

@ -78,6 +78,10 @@ namespace Avalonia.Markup.UnitTests.Data
var data = new IndeiTest();
var observer = new ExpressionObserver(data, nameof(data.MustBePositive), true);
var result = new List<object>();
var errmsg = string.Empty;
try { typeof(IndeiTest).GetProperty(nameof(IndeiTest.MustBePositive)).SetValue(data, "foo"); }
catch(Exception e) { errmsg = e.Message; }
observer.Subscribe(x => result.Add(x));
observer.SetValue(5);
@ -99,7 +103,7 @@ namespace Avalonia.Markup.UnitTests.Data
// Exception is thrown by trying to set value to "foo".
new BindingNotification(
new ArgumentException("Object of type 'System.String' cannot be converted to type 'System.Int32'."),
new ArgumentException(errmsg),
BindingErrorType.DataValidationError),
// Value is set then validation is updated.

10
tests/Avalonia.Markup.UnitTests/Data/ExpressionObserverTests_Negation.cs

@ -19,7 +19,7 @@ namespace Avalonia.Markup.UnitTests.Data
var target = new ExpressionObserver(data, "!Foo");
var result = await target.Take(1);
Assert.Equal(false, result);
Assert.False((bool)result);
GC.KeepAlive(data);
}
@ -31,7 +31,7 @@ namespace Avalonia.Markup.UnitTests.Data
var target = new ExpressionObserver(data, "!Foo");
var result = await target.Take(1);
Assert.Equal(true, result);
Assert.True((bool)result);
GC.KeepAlive(data);
}
@ -43,7 +43,7 @@ namespace Avalonia.Markup.UnitTests.Data
var target = new ExpressionObserver(data, "!Foo");
var result = await target.Take(1);
Assert.Equal(false, result);
Assert.False((bool)result);
GC.KeepAlive(data);
}
@ -55,7 +55,7 @@ namespace Avalonia.Markup.UnitTests.Data
var target = new ExpressionObserver(data, "!Foo");
var result = await target.Take(1);
Assert.Equal(true, result);
Assert.True((bool)result);
GC.KeepAlive(data);
}
@ -67,7 +67,7 @@ namespace Avalonia.Markup.UnitTests.Data
var target = new ExpressionObserver(data, "!Foo");
var result = await target.Take(1);
Assert.Equal(false, result);
Assert.False((bool)result);
GC.KeepAlive(data);
}

2
tests/Avalonia.Markup.UnitTests/Data/ExpressionObserverTests_Task.cs

@ -28,7 +28,7 @@ namespace Avalonia.Markup.UnitTests.Data
tcs.SetResult("foo");
sync.ExecutePostedCallbacks();
Assert.Equal(1, result.Count);
Assert.Single(result);
Assert.IsType<Task<string>>(result[0]);
GC.KeepAlive(data);

2
tests/Avalonia.Markup.Xaml.UnitTests/Data/BindingTests_Source.cs

@ -23,7 +23,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Data
target.Bind(TextBlock.TextProperty, binding);
Assert.Equal(target.Text, "foo");
Assert.Equal("foo", target.Text);
}
public class Source : INotifyPropertyChanged

35
tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs

@ -79,6 +79,20 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
Assert.Equal(21.0, TextBlock.GetFontSize(target));
}
[Fact]
public void Attached_Property_With_Namespace_Is_Set()
{
var xaml =
@"<ContentControl xmlns='https://github.com/avaloniaui'
xmlns:test='clr-namespace:Avalonia.Markup.Xaml.UnitTests.Xaml;assembly=Avalonia.Markup.Xaml.UnitTests'
test:BasicTestsAttachedPropertyHolder.Foo='Bar'/>";
var target = AvaloniaXamlLoader.Parse<ContentControl>(xaml);
Assert.NotNull(target);
Assert.Equal("Bar", BasicTestsAttachedPropertyHolder.GetFoo(target));
}
[Fact]
public void Attached_Property_Supports_Binding()
{
@ -105,7 +119,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
var target = AvaloniaXamlLoader.Parse<Panel>(xaml);
Assert.Equal(0, target.Children.Count);
Assert.Empty(target.Children);
Assert.Equal("Foo", ToolTip.GetTip(target));
}
@ -363,7 +377,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
var styles = AvaloniaXamlLoader.Parse<Styles>(xaml);
Assert.Equal(1, styles.Count);
Assert.Single(styles);
var style = (Style)styles[0];
@ -391,13 +405,13 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
var styles = AvaloniaXamlLoader.Parse<Styles>(xaml);
Assert.Equal(1, styles.Count);
Assert.Single(styles);
var style = (Style)styles[0];
var setters = style.Setters.Cast<Setter>().ToArray();
Assert.Equal(1, setters.Length);
Assert.Single(setters);
Assert.Equal(TextBlock.FontSizeProperty, setters[0].Property);
Assert.Equal(21.0, setters[0].Value);
@ -445,7 +459,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
";
var styles = AvaloniaXamlLoader.Parse<Styles>(xaml);
Assert.Equal(1, styles.Count);
Assert.Single(styles);
var style = (Style)styles[0];
@ -674,7 +688,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
var style = AvaloniaXamlLoader.Parse<Style>(xaml);
Assert.Equal(1, style.Setters.Count());
Assert.Single(style.Setters);
var setter = (Setter)style.Setters.First();
@ -873,4 +887,13 @@ do we need it?")]
}
}
}
public class BasicTestsAttachedPropertyHolder
{
public static AvaloniaProperty<string> FooProperty =
AvaloniaProperty.RegisterAttached<BasicTestsAttachedPropertyHolder, AvaloniaObject, string>("Foo");
public static void SetFoo(AvaloniaObject target, string value) => target.SetValue(FooProperty, value);
public static string GetFoo(AvaloniaObject target) => (string)target.GetValue(FooProperty);
}
}

2
tests/Avalonia.Markup.Xaml.UnitTests/Xaml/StyleTests.cs

@ -76,7 +76,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
var window = AvaloniaXamlLoader.Parse<ContentControl>(xaml);
Assert.Equal(1, window.Styles.Count);
Assert.Single(window.Styles);
var styleInclude = window.Styles[0] as StyleInclude;

2
tests/Avalonia.Styling.UnitTests/SelectorTests_Template.cs

@ -133,7 +133,7 @@ namespace Avalonia.Styling.UnitTests
using (activator.Subscribe(_ => { }))
{
Assert.Equal(1, inccDebug.GetCollectionChangedSubscribers().Length);
Assert.Single(inccDebug.GetCollectionChangedSubscribers());
}
Assert.Null(inccDebug.GetCollectionChangedSubscribers());

71
tests/Avalonia.Styling.UnitTests/SetterTests.cs

@ -86,6 +86,77 @@ namespace Avalonia.Styling.UnitTests
Assert.Null(control.Tag);
}
[Fact]
public void Setter_Should_Apply_Value_Without_Activator_With_Style_Priority()
{
var control = new Mock<IStyleable>();
var style = Mock.Of<Style>();
var setter = new Setter(TextBlock.TextProperty, "foo");
setter.Apply(style, control.Object, null);
control.Verify(x => x.Bind(
TextBlock.TextProperty,
It.IsAny<IObservable<object>>(),
BindingPriority.Style));
}
[Fact]
public void Setter_Should_Apply_Value_With_Activator_With_StyleTrigger_Priority()
{
var control = new Mock<IStyleable>();
var style = Mock.Of<Style>();
var setter = new Setter(TextBlock.TextProperty, "foo");
var activator = new Subject<bool>();
setter.Apply(style, control.Object, activator);
control.Verify(x => x.Bind(
TextBlock.TextProperty,
It.IsAny<IObservable<object>>(),
BindingPriority.StyleTrigger));
}
[Fact]
public void Setter_Should_Apply_Binding_Without_Activator_With_Style_Priority()
{
var control = new Mock<IStyleable>();
var style = Mock.Of<Style>();
var setter = new Setter(TextBlock.TextProperty, CreateMockBinding(TextBlock.TextProperty));
setter.Apply(style, control.Object, null);
control.Verify(x => x.Bind(
TextBlock.TextProperty,
It.IsAny<IObservable<object>>(),
BindingPriority.Style));
}
[Fact]
public void Setter_Should_Apply_Binding_With_Activator_With_StyleTrigger_Priority()
{
var control = new Mock<IStyleable>();
var style = Mock.Of<Style>();
var setter = new Setter(TextBlock.TextProperty, CreateMockBinding(TextBlock.TextProperty));
var activator = new Subject<bool>();
setter.Apply(style, control.Object, activator);
control.Verify(x => x.Bind(
TextBlock.TextProperty,
It.IsAny<IObservable<object>>(),
BindingPriority.StyleTrigger));
}
private IBinding CreateMockBinding(AvaloniaProperty property)
{
var subject = new Subject<object>();
var descriptor = InstancedBinding.OneWay(subject);
var binding = Mock.Of<IBinding>(x =>
x.Initiate(It.IsAny<IAvaloniaObject>(), property, null, false) == descriptor);
return binding;
}
private class TestConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)

8
tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneBuilderTests_Layers.cs

@ -70,11 +70,11 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
Assert.Same(tree, rootNode.LayerRoot);
Assert.Same(tree, borderNode.LayerRoot);
Assert.Same(tree, canvasNode.LayerRoot);
Assert.Equal(1, scene.Layers.Count());
Assert.Single(scene.Layers);
var rootDirty = scene.Layers[tree].Dirty;
Assert.Equal(1, rootDirty.Count());
Assert.Single(rootDirty);
Assert.Equal(new Rect(21, 21, 58, 78), rootDirty.Single());
}
}
@ -136,11 +136,11 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
Assert.Same(tree, rootNode.LayerRoot);
Assert.Same(tree, borderNode.LayerRoot);
Assert.Same(tree, canvasNode.LayerRoot);
Assert.Equal(1, scene.Layers.Count());
Assert.Single(scene.Layers);
var rootDirty = scene.Layers[tree].Dirty;
Assert.Equal(1, rootDirty.Count());
Assert.Single(rootDirty);
Assert.Equal(new Rect(21, 21, 58, 78), rootDirty.Single());
}
}

2
tests/Avalonia.Visuals.UnitTests/VisualTests.cs

@ -190,7 +190,7 @@ namespace Avalonia.Visuals.UnitTests
root1.Child = child;
Assert.Throws<InvalidOperationException>(() => root2.Child = child);
Assert.Equal(0, root2.GetVisualChildren().Count());
Assert.Empty(root2.GetVisualChildren());
}
}
}

Loading…
Cancel
Save