committed by
GitHub
312 changed files with 13912 additions and 4732 deletions
@ -0,0 +1,17 @@ |
|||||
|
This template is not intended to be prescriptive, but to help us review pull requests it would be useful if you included as much of the following information as possible: |
||||
|
|
||||
|
- What does the pull request do? |
||||
|
- What is the current behavior? |
||||
|
- What is the updated/expected behavior with this PR? |
||||
|
- How was the solution implemented (if it's not obvious)? |
||||
|
|
||||
|
Checklist: |
||||
|
|
||||
|
- [ ] Added unit tests (if possible)? |
||||
|
- [ ] Added XML documentation to any related classes? |
||||
|
- [ ] Consider submitting a PR to https://github.com/AvaloniaUI/Avaloniaui.net with user documentation |
||||
|
|
||||
|
If the pull request fixes issue(s) list them like this: |
||||
|
|
||||
|
Fixes #123 |
||||
|
Fixes #456 |
||||
@ -0,0 +1,11 @@ |
|||||
|
<Project DefaultTargets="Build" |
||||
|
xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
||||
|
<ItemGroup> |
||||
|
<Compile Update="**\*.xaml.cs"> |
||||
|
<DependentUpon>%(Filename)</DependentUpon> |
||||
|
</Compile> |
||||
|
<EmbeddedResource Include="**\*.xaml"> |
||||
|
<SubType>Designer</SubType> |
||||
|
</EmbeddedResource> |
||||
|
</ItemGroup> |
||||
|
</Project> |
||||
@ -0,0 +1,13 @@ |
|||||
|
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
||||
|
<PropertyGroup Condition="'$(TargetFramework)'=='net461'" > |
||||
|
<OutputType>WinExe</OutputType> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
<!-- Should be a Condition="'$(TargetFramework)'=='net461'" here but that doesn't work due |
||||
|
to https://github.com/dotnet/sdk/issues/1227 --> |
||||
|
<ItemGroup> |
||||
|
<ProjectReference Include="..\..\src\Windows\Avalonia.Win32\Avalonia.Win32.csproj" /> |
||||
|
<ProjectReference Include="..\..\src\Windows\Avalonia.Direct2D1\Avalonia.Direct2D1.csproj" /> |
||||
|
</ItemGroup> |
||||
|
<Import Condition="'$(TargetFramework)'=='net461'" Project="SharpDX.props" /> |
||||
|
</Project> |
||||
@ -0,0 +1,13 @@ |
|||||
|
<Project DefaultTargets="Build" |
||||
|
xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
||||
|
<PropertyGroup> |
||||
|
<Product>Avalonia</Product> |
||||
|
<Version>0.6.2</Version> |
||||
|
<Copyright>Copyright 2016 © The AvaloniaUI Project</Copyright> |
||||
|
<PackageLicenseUrl>https://github.com/AvaloniaUI/Avalonia/blob/master/licence.md</PackageLicenseUrl> |
||||
|
<PackageProjectUrl>https://github.com/AvaloniaUI/Avalonia/</PackageProjectUrl> |
||||
|
<RepositoryUrl>https://github.com/AvaloniaUI/Avalonia/</RepositoryUrl> |
||||
|
<GenerateDocumentationFile>true</GenerateDocumentationFile> |
||||
|
<NoWarn>CS1591</NoWarn> |
||||
|
</PropertyGroup> |
||||
|
</Project> |
||||
@ -0,0 +1,5 @@ |
|||||
|
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
||||
|
<ItemGroup> |
||||
|
<PackageReference Include="System.Drawing.Common" Version="4.5.0-preview1-25914-04" /> |
||||
|
</ItemGroup> |
||||
|
</Project> |
||||
@ -1,155 +1,33 @@ |
|||||
<?xml version="1.0" encoding="utf-8"?> |
<Project Sdk="Microsoft.NET.Sdk"> |
||||
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> |
|
||||
<PropertyGroup> |
<PropertyGroup> |
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> |
<OutputType>Exe</OutputType> |
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> |
<TargetFrameworks>netcoreapp2.0;net461</TargetFrameworks> |
||||
<ProjectGuid>{08B3E6B9-1CD5-443C-9F61-6D49D1C5F162}</ProjectGuid> |
|
||||
<OutputType>WinExe</OutputType> |
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder> |
|
||||
<RootNamespace>BindingTest</RootNamespace> |
|
||||
<AssemblyName>BindingTest</AssemblyName> |
|
||||
<TargetFrameworkVersion>v4.7</TargetFrameworkVersion> |
|
||||
<FileAlignment>512</FileAlignment> |
|
||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects> |
|
||||
<TargetFrameworkProfile /> |
|
||||
<RestoreProjectStyle>PackageReference</RestoreProjectStyle> |
|
||||
</PropertyGroup> |
</PropertyGroup> |
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> |
|
||||
<PlatformTarget>AnyCPU</PlatformTarget> |
|
||||
<DebugSymbols>true</DebugSymbols> |
|
||||
<DebugType>full</DebugType> |
|
||||
<Optimize>false</Optimize> |
|
||||
<OutputPath>bin\Debug\</OutputPath> |
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants> |
|
||||
<ErrorReport>prompt</ErrorReport> |
|
||||
<WarningLevel>4</WarningLevel> |
|
||||
</PropertyGroup> |
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> |
|
||||
<PlatformTarget>AnyCPU</PlatformTarget> |
|
||||
<DebugType>pdbonly</DebugType> |
|
||||
<Optimize>true</Optimize> |
|
||||
<OutputPath>bin\Release\</OutputPath> |
|
||||
<DefineConstants>TRACE</DefineConstants> |
|
||||
<ErrorReport>prompt</ErrorReport> |
|
||||
<WarningLevel>4</WarningLevel> |
|
||||
</PropertyGroup> |
|
||||
<PropertyGroup> |
|
||||
<StartupObject /> |
|
||||
</PropertyGroup> |
|
||||
<ItemGroup> |
|
||||
<Reference Include="System" /> |
|
||||
<Reference Include="System.ComponentModel.DataAnnotations" /> |
|
||||
<Reference Include="System.Core" /> |
|
||||
<Reference Include="System.Xml.Linq" /> |
|
||||
<Reference Include="System.Data.DataSetExtensions" /> |
|
||||
<Reference Include="Microsoft.CSharp" /> |
|
||||
<Reference Include="System.Data" /> |
|
||||
<Reference Include="System.Xml" /> |
|
||||
</ItemGroup> |
|
||||
<ItemGroup> |
|
||||
<EmbeddedResource Include="App.xaml"> |
|
||||
<SubType>Designer</SubType> |
|
||||
</EmbeddedResource> |
|
||||
<Compile Include="App.xaml.cs"> |
|
||||
<DependentUpon>App.xaml</DependentUpon> |
|
||||
</Compile> |
|
||||
<Compile Include="MainWindow.xaml.cs"> |
|
||||
<DependentUpon>MainWindow.xaml</DependentUpon> |
|
||||
</Compile> |
|
||||
<Compile Include="Properties\AssemblyInfo.cs" /> |
|
||||
<Compile Include="TestItemView.xaml.cs"> |
|
||||
<DependentUpon>TestItemView.xaml</DependentUpon> |
|
||||
</Compile> |
|
||||
<Compile Include="ViewModels\DataAnnotationsErrorViewModel.cs" /> |
|
||||
<Compile Include="ViewModels\IndeiErrorViewModel.cs" /> |
|
||||
<Compile Include="ViewModels\ExceptionErrorViewModel.cs" /> |
|
||||
<Compile Include="ViewModels\MainWindowViewModel.cs" /> |
|
||||
<Compile Include="ViewModels\NestedCommandViewModel.cs" /> |
|
||||
<Compile Include="ViewModels\TestItem.cs" /> |
|
||||
</ItemGroup> |
|
||||
<ItemGroup> |
|
||||
<None Include="App.config" /> |
|
||||
<EmbeddedResource Include="MainWindow.xaml"> |
|
||||
<SubType>Designer</SubType> |
|
||||
</EmbeddedResource> |
|
||||
<EmbeddedResource Include="TestItemView.xaml" /> |
|
||||
</ItemGroup> |
|
||||
<ItemGroup> |
<ItemGroup> |
||||
<ProjectReference Include="..\..\src\Avalonia.DotNetFrameworkRuntime\Avalonia.DotNetFrameworkRuntime.csproj"> |
<ProjectReference Include="..\..\src\Avalonia.DesignerSupport\Avalonia.DesignerSupport.csproj" /> |
||||
<Project>{4A1ABB09-9047-4BD5-A4AD-A055E52C5EE0}</Project> |
|
||||
<Name>Avalonia.DotNetFrameworkRuntime</Name> |
<ProjectReference Condition="'$(TargetFramework)'=='netcoreapp2.0'" Include="..\..\src\Avalonia.DotNetCoreRuntime\Avalonia.DotNetCoreRuntime.csproj" /> |
||||
</ProjectReference> |
|
||||
<ProjectReference Include="..\..\src\Markup\Avalonia.Markup.Xaml\Avalonia.Markup.Xaml.csproj"> |
<ProjectReference Condition="'$(TargetFramework)'=='net461'" Include="..\..\src\Avalonia.DotNetFrameworkRuntime\Avalonia.DotNetFrameworkRuntime.csproj" /> |
||||
<Project>{3e53a01a-b331-47f3-b828-4a5717e77a24}</Project> |
|
||||
<Name>Avalonia.Markup.Xaml</Name> |
<ProjectReference Include="..\..\src\Linux\Avalonia.LinuxFramebuffer\Avalonia.LinuxFramebuffer.csproj" /> |
||||
</ProjectReference> |
<ProjectReference Include="..\..\src\Avalonia.Base\Avalonia.Base.csproj" /> |
||||
<ProjectReference Include="..\..\src\Markup\Avalonia.Markup\Avalonia.Markup.csproj"> |
<ProjectReference Include="..\..\src\Markup\Avalonia.Markup.Xaml\Avalonia.Markup.Xaml.csproj" /> |
||||
<Project>{6417e941-21bc-467b-a771-0de389353ce6}</Project> |
<ProjectReference Include="..\..\src\Markup\Avalonia.Markup\Avalonia.Markup.csproj" /> |
||||
<Name>Avalonia.Markup</Name> |
<ProjectReference Include="..\..\src\Avalonia.Animation\Avalonia.Animation.csproj" /> |
||||
</ProjectReference> |
<ProjectReference Include="..\..\src\Avalonia.Controls\Avalonia.Controls.csproj" /> |
||||
<ProjectReference Include="..\..\src\Avalonia.Animation\Avalonia.Animation.csproj"> |
<ProjectReference Include="..\..\src\Avalonia.Diagnostics\Avalonia.Diagnostics.csproj" /> |
||||
<Project>{d211e587-d8bc-45b9-95a4-f297c8fa5200}</Project> |
<ProjectReference Include="..\..\src\Avalonia.Input\Avalonia.Input.csproj" /> |
||||
<Name>Avalonia.Animation</Name> |
<ProjectReference Include="..\..\src\Avalonia.Interactivity\Avalonia.Interactivity.csproj" /> |
||||
</ProjectReference> |
<ProjectReference Include="..\..\src\Avalonia.Layout\Avalonia.Layout.csproj" /> |
||||
<ProjectReference Include="..\..\src\Avalonia.Base\Avalonia.Base.csproj"> |
<ProjectReference Include="..\..\src\Avalonia.ReactiveUI\Avalonia.ReactiveUI.csproj" /> |
||||
<Project>{b09b78d8-9b26-48b0-9149-d64a2f120f3f}</Project> |
<ProjectReference Include="..\..\src\Avalonia.Visuals\Avalonia.Visuals.csproj" /> |
||||
<Name>Avalonia.Base</Name> |
<ProjectReference Include="..\..\src\Avalonia.Styling\Avalonia.Styling.csproj" /> |
||||
</ProjectReference> |
<ProjectReference Include="..\..\src\Avalonia.Themes.Default\Avalonia.Themes.Default.csproj" /> |
||||
<ProjectReference Include="..\..\src\Avalonia.Controls\Avalonia.Controls.csproj"> |
<ProjectReference Include="..\..\src\Avalonia.Logging.Serilog\Avalonia.Logging.Serilog.csproj" /> |
||||
<Project>{d2221c82-4a25-4583-9b43-d791e3f6820c}</Project> |
|
||||
<Name>Avalonia.Controls</Name> |
|
||||
</ProjectReference> |
|
||||
<ProjectReference Include="..\..\src\Avalonia.DesignerSupport\Avalonia.DesignerSupport.csproj"> |
|
||||
<Project>{799a7bb5-3c2c-48b6-85a7-406a12c420da}</Project> |
|
||||
<Name>Avalonia.DesignerSupport</Name> |
|
||||
</ProjectReference> |
|
||||
<ProjectReference Include="..\..\src\Avalonia.Diagnostics\Avalonia.Diagnostics.csproj"> |
|
||||
<Project>{7062ae20-5dcc-4442-9645-8195bdece63e}</Project> |
|
||||
<Name>Avalonia.Diagnostics</Name> |
|
||||
</ProjectReference> |
|
||||
<ProjectReference Include="..\..\src\Avalonia.Input\Avalonia.Input.csproj"> |
|
||||
<Project>{62024b2d-53eb-4638-b26b-85eeaa54866e}</Project> |
|
||||
<Name>Avalonia.Input</Name> |
|
||||
</ProjectReference> |
|
||||
<ProjectReference Include="..\..\src\Avalonia.Interactivity\Avalonia.Interactivity.csproj"> |
|
||||
<Project>{6b0ed19d-a08b-461c-a9d9-a9ee40b0c06b}</Project> |
|
||||
<Name>Avalonia.Interactivity</Name> |
|
||||
</ProjectReference> |
|
||||
<ProjectReference Include="..\..\src\Avalonia.Layout\Avalonia.Layout.csproj"> |
|
||||
<Project>{42472427-4774-4c81-8aff-9f27b8e31721}</Project> |
|
||||
<Name>Avalonia.Layout</Name> |
|
||||
</ProjectReference> |
|
||||
<ProjectReference Include="..\..\src\Avalonia.Logging.Serilog\Avalonia.Logging.Serilog.csproj"> |
|
||||
<Project>{b61b66a3-b82d-4875-8001-89d3394fe0c9}</Project> |
|
||||
<Name>Avalonia.Logging.Serilog</Name> |
|
||||
</ProjectReference> |
|
||||
<ProjectReference Include="..\..\src\Avalonia.ReactiveUI\Avalonia.ReactiveUI.csproj"> |
|
||||
<Project>{6417b24e-49c2-4985-8db2-3ab9d898ec91}</Project> |
|
||||
<Name>Avalonia.ReactiveUI</Name> |
|
||||
</ProjectReference> |
|
||||
<ProjectReference Include="..\..\src\Avalonia.Visuals\Avalonia.Visuals.csproj"> |
|
||||
<Project>{eb582467-6abb-43a1-b052-e981ba910e3a}</Project> |
|
||||
<Name>Avalonia.Visuals</Name> |
|
||||
</ProjectReference> |
|
||||
<ProjectReference Include="..\..\src\Avalonia.Styling\Avalonia.Styling.csproj"> |
|
||||
<Project>{f1baa01a-f176-4c6a-b39d-5b40bb1b148f}</Project> |
|
||||
<Name>Avalonia.Styling</Name> |
|
||||
</ProjectReference> |
|
||||
<ProjectReference Include="..\..\src\Avalonia.Themes.Default\Avalonia.Themes.Default.csproj"> |
|
||||
<Project>{3e10a5fa-e8da-48b1-ad44-6a5b6cb7750f}</Project> |
|
||||
<Name>Avalonia.Themes.Default</Name> |
|
||||
</ProjectReference> |
|
||||
<ProjectReference Include="..\..\src\Windows\Avalonia.Direct2D1\Avalonia.Direct2D1.csproj" Condition="'$(Platform)'!='Mono'"> |
|
||||
<Project>{3e908f67-5543-4879-a1dc-08eace79b3cd}</Project> |
|
||||
<Name>Avalonia.Direct2D1</Name> |
|
||||
</ProjectReference> |
|
||||
<ProjectReference Include="..\..\src\Windows\Avalonia.Win32\Avalonia.Win32.csproj" Condition="'$(Platform)'!='Mono'"> |
|
||||
<Project>{811a76cf-1cf6-440f-963b-bbe31bd72a82}</Project> |
|
||||
<Name>Avalonia.Win32</Name> |
|
||||
</ProjectReference> |
|
||||
</ItemGroup> |
</ItemGroup> |
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> |
<Import Project="..\..\build\SampleApp.props" /> |
||||
|
<Import Project="..\..\build\EmbedXaml.props" /> |
||||
<Import Project="..\..\build\Serilog.props" /> |
<Import Project="..\..\build\Serilog.props" /> |
||||
<Import Project="..\..\build\Rx.props" /> |
<Import Project="..\..\build\Rx.props" /> |
||||
<Import Project="..\..\build\ReactiveUI.props" /> |
<Import Project="..\..\build\ReactiveUI.props" /> |
||||
|
|||||
@ -1,36 +0,0 @@ |
|||||
using System.Reflection; |
|
||||
using System.Runtime.CompilerServices; |
|
||||
using System.Runtime.InteropServices; |
|
||||
|
|
||||
// General Information about an assembly is controlled through the following
|
|
||||
// set of attributes. Change these attribute values to modify the information
|
|
||||
// associated with an assembly.
|
|
||||
[assembly: AssemblyTitle("BindingTest")] |
|
||||
[assembly: AssemblyDescription("")] |
|
||||
[assembly: AssemblyConfiguration("")] |
|
||||
[assembly: AssemblyCompany("")] |
|
||||
[assembly: AssemblyProduct("BindingTest")] |
|
||||
[assembly: AssemblyCopyright("Copyright © 2015")] |
|
||||
[assembly: AssemblyTrademark("")] |
|
||||
[assembly: AssemblyCulture("")] |
|
||||
|
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
|
||||
// to COM components. If you need to access a type in this assembly from
|
|
||||
// COM, set the ComVisible attribute to true on that type.
|
|
||||
[assembly: ComVisible(false)] |
|
||||
|
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
|
||||
[assembly: Guid("08b3e6b9-1cd5-443c-9f61-6d49d1c5f162")] |
|
||||
|
|
||||
// Version information for an assembly consists of the following four values:
|
|
||||
//
|
|
||||
// Major Version
|
|
||||
// Minor Version
|
|
||||
// Build Number
|
|
||||
// Revision
|
|
||||
//
|
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
|
||||
// by using the '*' as shown below:
|
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
|
||||
[assembly: AssemblyVersion("1.0.0.0")] |
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")] |
|
||||
@ -1,138 +1,21 @@ |
|||||
<?xml version="1.0" encoding="utf-8"?> |
<Project Sdk="Microsoft.NET.Sdk"> |
||||
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> |
|
||||
<PropertyGroup> |
<PropertyGroup> |
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> |
<OutputType>Exe</OutputType> |
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> |
<TargetFramework>net461</TargetFramework> |
||||
<ProjectGuid>{2B888490-D14A-4BCA-AB4B-48676FA93C9B}</ProjectGuid> |
|
||||
<OutputType>WinExe</OutputType> |
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder> |
|
||||
<RootNamespace>ControlCatalog.Desktop</RootNamespace> |
|
||||
<AssemblyName>ControlCatalog.Desktop</AssemblyName> |
|
||||
<TargetFrameworkVersion>v4.7</TargetFrameworkVersion> |
|
||||
<FileAlignment>512</FileAlignment> |
|
||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects> |
|
||||
<TargetFrameworkProfile /> |
|
||||
<RestoreProjectStyle>PackageReference</RestoreProjectStyle> |
|
||||
</PropertyGroup> |
</PropertyGroup> |
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> |
|
||||
<PlatformTarget>AnyCPU</PlatformTarget> |
|
||||
<DebugSymbols>true</DebugSymbols> |
|
||||
<DebugType>full</DebugType> |
|
||||
<Optimize>false</Optimize> |
|
||||
<OutputPath>bin\Debug\</OutputPath> |
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants> |
|
||||
<ErrorReport>prompt</ErrorReport> |
|
||||
<WarningLevel>4</WarningLevel> |
|
||||
</PropertyGroup> |
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> |
|
||||
<PlatformTarget>x86</PlatformTarget> |
|
||||
<DebugType>pdbonly</DebugType> |
|
||||
<Optimize>true</Optimize> |
|
||||
<OutputPath>bin\Release\</OutputPath> |
|
||||
<DefineConstants>TRACE</DefineConstants> |
|
||||
<ErrorReport>prompt</ErrorReport> |
|
||||
<WarningLevel>4</WarningLevel> |
|
||||
</PropertyGroup> |
|
||||
<PropertyGroup> |
|
||||
<StartupObject /> |
|
||||
</PropertyGroup> |
|
||||
<ItemGroup> |
|
||||
<Reference Include="System" /> |
|
||||
<Reference Include="System.Core" /> |
|
||||
<Reference Include="System.Xml.Linq" /> |
|
||||
<Reference Include="System.Data.DataSetExtensions" /> |
|
||||
<Reference Include="Microsoft.CSharp" /> |
|
||||
<Reference Include="System.Data" /> |
|
||||
<Reference Include="System.Net.Http" /> |
|
||||
<Reference Include="System.Xml" /> |
|
||||
</ItemGroup> |
|
||||
<ItemGroup> |
|
||||
<Compile Include="Program.cs" /> |
|
||||
<Compile Include="Properties\AssemblyInfo.cs" /> |
|
||||
</ItemGroup> |
|
||||
<ItemGroup> |
<ItemGroup> |
||||
<None Include="App.config" /> |
<ProjectReference Include="..\..\src\Avalonia.DesignerSupport\Avalonia.DesignerSupport.csproj" /> |
||||
|
<ProjectReference Include="..\..\src\Avalonia.DotNetFrameworkRuntime\Avalonia.DotNetFrameworkRuntime.csproj" /> |
||||
|
<ProjectReference Include="..\..\src\Avalonia.Logging.Serilog\Avalonia.Logging.Serilog.csproj" /> |
||||
|
<ProjectReference Include="..\ControlCatalog\ControlCatalog.csproj" /> |
||||
</ItemGroup> |
</ItemGroup> |
||||
|
|
||||
<ItemGroup> |
<ItemGroup> |
||||
<ProjectReference Include="..\..\src\Avalonia.DesignerSupport\Avalonia.DesignerSupport.csproj"> |
<Folder Include="Properties\" /> |
||||
<Project>{799a7bb5-3c2c-48b6-85a7-406a12c420da}</Project> |
|
||||
<Name>Avalonia.DesignerSupport</Name> |
|
||||
</ProjectReference> |
|
||||
<ProjectReference Include="..\..\src\Avalonia.DotNetFrameworkRuntime\Avalonia.DotNetFrameworkRuntime.csproj"> |
|
||||
<Project>{4A1ABB09-9047-4BD5-A4AD-A055E52C5EE0}</Project> |
|
||||
<Name>Avalonia.DotNetFrameworkRuntime</Name> |
|
||||
</ProjectReference> |
|
||||
<ProjectReference Include="..\..\src\Gtk\Avalonia.Gtk3\Avalonia.Gtk3.csproj"> |
|
||||
<Project>{bb1f7bb5-6ad4-4776-94d9-c09d0a972658}</Project> |
|
||||
<Name>Avalonia.Gtk3</Name> |
|
||||
</ProjectReference> |
|
||||
<ProjectReference Include="..\..\src\Markup\Avalonia.Markup.Xaml\Avalonia.Markup.Xaml.csproj"> |
|
||||
<Project>{3E53A01A-B331-47F3-B828-4A5717E77A24}</Project> |
|
||||
<Name>Avalonia.Markup.Xaml</Name> |
|
||||
</ProjectReference> |
|
||||
<ProjectReference Include="..\..\src\Markup\Avalonia.Markup\Avalonia.Markup.csproj"> |
|
||||
<Project>{6417E941-21BC-467B-A771-0DE389353CE6}</Project> |
|
||||
<Name>Avalonia.Markup</Name> |
|
||||
</ProjectReference> |
|
||||
<ProjectReference Include="..\..\src\Avalonia.Animation\Avalonia.Animation.csproj"> |
|
||||
<Project>{d211e587-d8bc-45b9-95a4-f297c8fa5200}</Project> |
|
||||
<Name>Avalonia.Animation</Name> |
|
||||
</ProjectReference> |
|
||||
<ProjectReference Include="..\..\src\Avalonia.Base\Avalonia.Base.csproj"> |
|
||||
<Project>{B09B78D8-9B26-48B0-9149-D64A2F120F3F}</Project> |
|
||||
<Name>Avalonia.Base</Name> |
|
||||
</ProjectReference> |
|
||||
<ProjectReference Include="..\..\src\Avalonia.Controls\Avalonia.Controls.csproj"> |
|
||||
<Project>{D2221C82-4A25-4583-9B43-D791E3F6820C}</Project> |
|
||||
<Name>Avalonia.Controls</Name> |
|
||||
</ProjectReference> |
|
||||
<ProjectReference Include="..\..\src\Avalonia.Input\Avalonia.Input.csproj"> |
|
||||
<Project>{62024b2d-53eb-4638-b26b-85eeaa54866e}</Project> |
|
||||
<Name>Avalonia.Input</Name> |
|
||||
</ProjectReference> |
|
||||
<ProjectReference Include="..\..\src\Avalonia.Interactivity\Avalonia.Interactivity.csproj"> |
|
||||
<Project>{6b0ed19d-a08b-461c-a9d9-a9ee40b0c06b}</Project> |
|
||||
<Name>Avalonia.Interactivity</Name> |
|
||||
</ProjectReference> |
|
||||
<ProjectReference Include="..\..\src\Avalonia.Layout\Avalonia.Layout.csproj"> |
|
||||
<Project>{42472427-4774-4c81-8aff-9f27b8e31721}</Project> |
|
||||
<Name>Avalonia.Layout</Name> |
|
||||
</ProjectReference> |
|
||||
<ProjectReference Include="..\..\src\Avalonia.Logging.Serilog\Avalonia.Logging.Serilog.csproj"> |
|
||||
<Project>{B61B66A3-B82D-4875-8001-89D3394FE0C9}</Project> |
|
||||
<Name>Avalonia.Logging.Serilog</Name> |
|
||||
</ProjectReference> |
|
||||
<ProjectReference Include="..\..\src\Avalonia.Visuals\Avalonia.Visuals.csproj"> |
|
||||
<Project>{eb582467-6abb-43a1-b052-e981ba910e3a}</Project> |
|
||||
<Name>Avalonia.Visuals</Name> |
|
||||
</ProjectReference> |
|
||||
<ProjectReference Include="..\..\src\Avalonia.Styling\Avalonia.Styling.csproj"> |
|
||||
<Project>{F1BAA01A-F176-4C6A-B39D-5B40BB1B148F}</Project> |
|
||||
<Name>Avalonia.Styling</Name> |
|
||||
</ProjectReference> |
|
||||
<ProjectReference Include="..\..\src\Avalonia.Themes.Default\Avalonia.Themes.Default.csproj"> |
|
||||
<Project>{3E10A5FA-E8DA-48B1-AD44-6A5B6CB7750F}</Project> |
|
||||
<Name>Avalonia.Themes.Default</Name> |
|
||||
</ProjectReference> |
|
||||
<ProjectReference Include="..\..\src\Skia\Avalonia.Skia\Avalonia.Skia.csproj"> |
|
||||
<Project>{7d2d3083-71dd-4cc9-8907-39a0d86fb322}</Project> |
|
||||
<Name>Avalonia.Skia</Name> |
|
||||
</ProjectReference> |
|
||||
<ProjectReference Include="..\..\src\Windows\Avalonia.Direct2D1\Avalonia.Direct2D1.csproj" Condition="'$(Platform)'!='Mono'"> |
|
||||
<Project>{3E908F67-5543-4879-A1DC-08EACE79B3CD}</Project> |
|
||||
<Name>Avalonia.Direct2D1</Name> |
|
||||
</ProjectReference> |
|
||||
<ProjectReference Include="..\..\src\Windows\Avalonia.Win32\Avalonia.Win32.csproj" Condition="'$(Platform)'!='Mono'"> |
|
||||
<Project>{811A76CF-1CF6-440F-963B-BBE31BD72A82}</Project> |
|
||||
<Name>Avalonia.Win32</Name> |
|
||||
</ProjectReference> |
|
||||
<ProjectReference Include="..\ControlCatalog\ControlCatalog.csproj"> |
|
||||
<Project>{d0a739b9-3c68-4ba6-a328-41606954b6bd}</Project> |
|
||||
<Name>ControlCatalog</Name> |
|
||||
</ProjectReference> |
|
||||
</ItemGroup> |
</ItemGroup> |
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> |
|
||||
|
<Import Project="..\..\build\SampleApp.props" /> |
||||
<Import Project="..\..\build\Serilog.props" /> |
<Import Project="..\..\build\Serilog.props" /> |
||||
<Import Project="..\..\build\SkiaSharp.props" /> |
|
||||
</Project> |
</Project> |
||||
@ -1,36 +0,0 @@ |
|||||
using System.Reflection; |
|
||||
using System.Runtime.CompilerServices; |
|
||||
using System.Runtime.InteropServices; |
|
||||
|
|
||||
// General Information about an assembly is controlled through the following
|
|
||||
// set of attributes. Change these attribute values to modify the information
|
|
||||
// associated with an assembly.
|
|
||||
[assembly: AssemblyTitle("ControlCatalog.Desktop")] |
|
||||
[assembly: AssemblyDescription("")] |
|
||||
[assembly: AssemblyConfiguration("")] |
|
||||
[assembly: AssemblyCompany("")] |
|
||||
[assembly: AssemblyProduct("ControlCatalog.Desktop")] |
|
||||
[assembly: AssemblyCopyright("Copyright © 2016")] |
|
||||
[assembly: AssemblyTrademark("")] |
|
||||
[assembly: AssemblyCulture("")] |
|
||||
|
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
|
||||
// to COM components. If you need to access a type in this assembly from
|
|
||||
// COM, set the ComVisible attribute to true on that type.
|
|
||||
[assembly: ComVisible(false)] |
|
||||
|
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
|
||||
[assembly: Guid("2b888490-d14a-4bca-ab4b-48676fa93c9b")] |
|
||||
|
|
||||
// Version information for an assembly consists of the following four values:
|
|
||||
//
|
|
||||
// Major Version
|
|
||||
// Minor Version
|
|
||||
// Build Number
|
|
||||
// Revision
|
|
||||
//
|
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
|
||||
// by using the '*' as shown below:
|
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
|
||||
[assembly: AssemblyVersion("1.0.0.0")] |
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")] |
|
||||
@ -1,6 +1,6 @@ |
|||||
<Window xmlns="https://github.com/avaloniaui" MinWidth="500" MinHeight="300" |
<Window xmlns="https://github.com/avaloniaui" MinWidth="500" MinHeight="300" |
||||
Title="Avalonia Control Gallery" |
Title="Avalonia Control Gallery" |
||||
Icon="resm:ControlCatalog.Assets.test_icon.ico?assembly=ControlCatalog" |
Icon="resm:ControlCatalog.Assets.test_icon.ico?assembly=ControlCatalog" |
||||
xmlns:local="clr-namespace:ControlCatalog;assembly=ControlCatalog"> |
xmlns:local="clr-namespace:ControlCatalog"> |
||||
<local:MainView/> |
<local:MainView/> |
||||
</Window> |
</Window> |
||||
@ -0,0 +1,59 @@ |
|||||
|
<UserControl xmlns="https://github.com/avaloniaui"> |
||||
|
<StackPanel Orientation="Vertical" Gap="4"> |
||||
|
<TextBlock Classes="h1">AutoCompleteBox</TextBlock> |
||||
|
<TextBlock Classes="h2">A control into which the user can input text</TextBlock> |
||||
|
|
||||
|
<StackPanel Orientation="Horizontal" |
||||
|
Margin="0,16,0,0" |
||||
|
HorizontalAlignment="Center" |
||||
|
Gap="8"> |
||||
|
<StackPanel Orientation="Vertical"> |
||||
|
<TextBlock Text="MinimumPrefixLength: 1"/> |
||||
|
<AutoCompleteBox Width="200" |
||||
|
Margin="0,0,0,8" |
||||
|
MinimumPrefixLength="1"/> |
||||
|
<TextBlock Text="MinimumPrefixLength: 3"/> |
||||
|
<AutoCompleteBox Width="200" |
||||
|
Margin="0,0,0,8" |
||||
|
MinimumPrefixLength="3"/> |
||||
|
<TextBlock Text="MinimumPopulateDelay: 1 Second"/> |
||||
|
<AutoCompleteBox Width="200" |
||||
|
Margin="0,0,0,8" |
||||
|
MinimumPopulateDelay="1"/> |
||||
|
<TextBlock Text="MaxDropDownHeight: 60"/> |
||||
|
<AutoCompleteBox Width="200" |
||||
|
Margin="0,0,0,8" |
||||
|
MaxDropDownHeight="60"/> |
||||
|
<AutoCompleteBox Width="200" |
||||
|
Margin="0,0,0,8" |
||||
|
Watermark="Watermark"/> |
||||
|
<TextBlock Text="Disabled"/> |
||||
|
<AutoCompleteBox Width="200" |
||||
|
IsEnabled="False"/> |
||||
|
</StackPanel> |
||||
|
|
||||
|
|
||||
|
<StackPanel Orientation="Vertical"> |
||||
|
|
||||
|
<TextBlock Text="ValueMemeberSelector"/> |
||||
|
<AutoCompleteBox Width="200" |
||||
|
Margin="0,0,0,8" |
||||
|
ValueMemberSelector="Capital"/> |
||||
|
<TextBlock Text="ValueMemberBinding"/> |
||||
|
<AutoCompleteBox Width="200" |
||||
|
Margin="0,0,0,8" |
||||
|
ValueMemberBinding="{Binding Capital}"/> |
||||
|
<TextBlock Text="Multi-Binding"/> |
||||
|
<AutoCompleteBox Name="MultiBindingBox" |
||||
|
Width="200" |
||||
|
Margin="0,0,0,8" |
||||
|
FilterMode="Contains"/> |
||||
|
<TextBlock Text="Async Populate"/> |
||||
|
<AutoCompleteBox Name="AsyncBox" |
||||
|
Width="200" |
||||
|
Margin="0,0,0,8" |
||||
|
FilterMode="None"/> |
||||
|
</StackPanel> |
||||
|
</StackPanel> |
||||
|
</StackPanel> |
||||
|
</UserControl> |
||||
@ -0,0 +1,143 @@ |
|||||
|
using Avalonia.Controls; |
||||
|
using Avalonia.LogicalTree; |
||||
|
using Avalonia.Markup; |
||||
|
using Avalonia.Markup.Xaml; |
||||
|
using Avalonia.Markup.Xaml.Data; |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Threading; |
||||
|
using System.Threading.Tasks; |
||||
|
|
||||
|
namespace ControlCatalog.Pages |
||||
|
{ |
||||
|
public class AutoCompleteBoxPage : UserControl |
||||
|
{ |
||||
|
public class StateData |
||||
|
{ |
||||
|
public string Name { get; private set; } |
||||
|
public string Abbreviation { get; private set; } |
||||
|
public string Capital { get; private set; } |
||||
|
|
||||
|
public StateData(string name, string abbreviatoin, string capital) |
||||
|
{ |
||||
|
Name = name; |
||||
|
Abbreviation = abbreviatoin; |
||||
|
Capital = capital; |
||||
|
} |
||||
|
|
||||
|
public override string ToString() |
||||
|
{ |
||||
|
return Name; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private StateData[] BuildAllStates() |
||||
|
{ |
||||
|
return new StateData[] |
||||
|
{ |
||||
|
new StateData("Alabama","AL","Montgomery"), |
||||
|
new StateData("Alaska","AK","Juneau"), |
||||
|
new StateData("Arizona","AZ","Phoenix"), |
||||
|
new StateData("Arkansas","AR","Little Rock"), |
||||
|
new StateData("California","CA","Sacramento"), |
||||
|
new StateData("Colorado","CO","Denver"), |
||||
|
new StateData("Connecticut","CT","Hartford"), |
||||
|
new StateData("Delaware","DE","Dover"), |
||||
|
new StateData("Florida","FL","Tallahassee"), |
||||
|
new StateData("Georgia","GA","Atlanta"), |
||||
|
new StateData("Hawaii","HI","Honolulu"), |
||||
|
new StateData("Idaho","ID","Boise"), |
||||
|
new StateData("Illinois","IL","Springfield"), |
||||
|
new StateData("Indiana","IN","Indianapolis"), |
||||
|
new StateData("Iowa","IA","Des Moines"), |
||||
|
new StateData("Kansas","KS","Topeka"), |
||||
|
new StateData("Kentucky","KY","Frankfort"), |
||||
|
new StateData("Louisiana","LA","Baton Rouge"), |
||||
|
new StateData("Maine","ME","Augusta"), |
||||
|
new StateData("Maryland","MD","Annapolis"), |
||||
|
new StateData("Massachusetts","MA","Boston"), |
||||
|
new StateData("Michigan","MI","Lansing"), |
||||
|
new StateData("Minnesota","MN","St. Paul"), |
||||
|
new StateData("Mississippi","MS","Jackson"), |
||||
|
new StateData("Missouri","MO","Jefferson City"), |
||||
|
new StateData("Montana","MT","Helena"), |
||||
|
new StateData("Nebraska","NE","Lincoln"), |
||||
|
new StateData("Nevada","NV","Carson City"), |
||||
|
new StateData("New Hampshire","NH","Concord"), |
||||
|
new StateData("New Jersey","NJ","Trenton"), |
||||
|
new StateData("New Mexico","NM","Santa Fe"), |
||||
|
new StateData("New York","NY","Albany"), |
||||
|
new StateData("North Carolina","NC","Raleigh"), |
||||
|
new StateData("North Dakota","ND","Bismarck"), |
||||
|
new StateData("Ohio","OH","Columbus"), |
||||
|
new StateData("Oklahoma","OK","Oklahoma City"), |
||||
|
new StateData("Oregon","OR","Salem"), |
||||
|
new StateData("Pennsylvania","PA","Harrisburg"), |
||||
|
new StateData("Rhode Island","RI","Providence"), |
||||
|
new StateData("South Carolina","SC","Columbia"), |
||||
|
new StateData("South Dakota","SD","Pierre"), |
||||
|
new StateData("Tennessee","TN","Nashville"), |
||||
|
new StateData("Texas","TX","Austin"), |
||||
|
new StateData("Utah","UT","Salt Lake City"), |
||||
|
new StateData("Vermont","VT","Montpelier"), |
||||
|
new StateData("Virginia","VA","Richmond"), |
||||
|
new StateData("Washington","WA","Olympia"), |
||||
|
new StateData("West Virginia","WV","Charleston"), |
||||
|
new StateData("Wisconsin","WI","Madison"), |
||||
|
new StateData("Wyoming","WY","Cheyenne"), |
||||
|
}; |
||||
|
} |
||||
|
public StateData[] States { get; private set; } |
||||
|
|
||||
|
public AutoCompleteBoxPage() |
||||
|
{ |
||||
|
this.InitializeComponent(); |
||||
|
|
||||
|
States = BuildAllStates(); |
||||
|
|
||||
|
foreach (AutoCompleteBox box in GetAllAutoCompleteBox()) |
||||
|
{ |
||||
|
box.Items = States; |
||||
|
} |
||||
|
|
||||
|
var converter = new FuncMultiValueConverter<string, string>(parts => |
||||
|
{ |
||||
|
return String.Format("{0} ({1})", parts.ToArray()); |
||||
|
}); |
||||
|
var binding = new MultiBinding { Converter = converter }; |
||||
|
binding.Bindings.Add(new Binding("Name")); |
||||
|
binding.Bindings.Add(new Binding("Abbreviation")); |
||||
|
|
||||
|
var multibindingBox = this.FindControl<AutoCompleteBox>("MultiBindingBox"); |
||||
|
multibindingBox.ValueMemberBinding = binding; |
||||
|
|
||||
|
var asyncBox = this.FindControl<AutoCompleteBox>("AsyncBox"); |
||||
|
asyncBox.AsyncPopulator = PopulateAsync; |
||||
|
} |
||||
|
private IEnumerable<AutoCompleteBox> GetAllAutoCompleteBox() |
||||
|
{ |
||||
|
return |
||||
|
this.GetLogicalDescendants() |
||||
|
.OfType<AutoCompleteBox>(); |
||||
|
} |
||||
|
|
||||
|
private bool StringContains(string str, string query) |
||||
|
{ |
||||
|
return str.IndexOf(query, StringComparison.OrdinalIgnoreCase) >= 0; |
||||
|
} |
||||
|
private async Task<IEnumerable<object>> PopulateAsync(string searchText, CancellationToken cancellationToken) |
||||
|
{ |
||||
|
await Task.Delay(TimeSpan.FromSeconds(1.5), cancellationToken); |
||||
|
|
||||
|
return |
||||
|
States.Where(data => StringContains(data.Name, searchText) || StringContains(data.Capital, searchText)) |
||||
|
.ToList(); |
||||
|
} |
||||
|
|
||||
|
private void InitializeComponent() |
||||
|
{ |
||||
|
AvaloniaXamlLoader.Load(this); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,24 @@ |
|||||
|
<UserControl xmlns="https://github.com/avaloniaui" |
||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> |
||||
|
|
||||
|
<StackPanel Orientation="Vertical" Gap="4"> |
||||
|
<TextBlock Classes="h1">ButtonSpinner</TextBlock> |
||||
|
<TextBlock Classes="h2">The ButtonSpinner control allows you to add button spinners to any element and then respond to the Spin event to manipulate that element.</TextBlock> |
||||
|
|
||||
|
<StackPanel Orientation="Vertical" Gap="8" Width="200" Margin="0,20,0,0"> |
||||
|
<CheckBox Name="allowSpinCheck" IsChecked="True">AllowSpin</CheckBox> |
||||
|
<CheckBox Name="showSpinCheck" IsChecked="True">ShowButtonSpinner</CheckBox> |
||||
|
<ButtonSpinner Spin="OnSpin" Height="30" |
||||
|
AllowSpin="{Binding #allowSpinCheck.IsChecked}" |
||||
|
ShowButtonSpinner="{Binding #showSpinCheck.IsChecked}"> |
||||
|
<TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Text="Everest"/> |
||||
|
</ButtonSpinner> |
||||
|
<ButtonSpinner Spin="OnSpin" Height="30" ButtonSpinnerLocation="Left" |
||||
|
AllowSpin="{Binding #allowSpinCheck.IsChecked}" |
||||
|
ShowButtonSpinner="{Binding #showSpinCheck.IsChecked}"> |
||||
|
<TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Text="Everest"/> |
||||
|
</ButtonSpinner> |
||||
|
</StackPanel> |
||||
|
</StackPanel> |
||||
|
|
||||
|
</UserControl> |
||||
@ -0,0 +1,54 @@ |
|||||
|
using System; |
||||
|
using Avalonia; |
||||
|
using Avalonia.Controls; |
||||
|
using Avalonia.Interactivity; |
||||
|
using Avalonia.Markup.Xaml; |
||||
|
|
||||
|
namespace ControlCatalog.Pages |
||||
|
{ |
||||
|
public class ButtonSpinnerPage : UserControl |
||||
|
{ |
||||
|
public ButtonSpinnerPage() |
||||
|
{ |
||||
|
this.InitializeComponent(); |
||||
|
} |
||||
|
|
||||
|
private void InitializeComponent() |
||||
|
{ |
||||
|
AvaloniaXamlLoader.Load(this); |
||||
|
} |
||||
|
|
||||
|
private void OnSpin(object sender, SpinEventArgs e) |
||||
|
{ |
||||
|
var spinner = (ButtonSpinner)sender; |
||||
|
var txtBox = (TextBlock)spinner.Content; |
||||
|
|
||||
|
int value = Array.IndexOf(_mountains, txtBox.Text); |
||||
|
if (e.Direction == SpinDirection.Increase) |
||||
|
value++; |
||||
|
else |
||||
|
value--; |
||||
|
|
||||
|
if (value < 0) |
||||
|
value = _mountains.Length - 1; |
||||
|
else if (value >= _mountains.Length) |
||||
|
value = 0; |
||||
|
|
||||
|
txtBox.Text = _mountains[value]; |
||||
|
} |
||||
|
|
||||
|
private readonly string[] _mountains = new[] |
||||
|
{ |
||||
|
"Everest", |
||||
|
"K2 (Mount Godwin Austen)", |
||||
|
"Kangchenjunga", |
||||
|
"Lhotse", |
||||
|
"Makalu", |
||||
|
"Cho Oyu", |
||||
|
"Dhaulagiri", |
||||
|
"Manaslu", |
||||
|
"Nanga Parbat", |
||||
|
"Annapurna" |
||||
|
}; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,19 @@ |
|||||
|
<UserControl xmlns="https://github.com/avaloniaui"> |
||||
|
<StackPanel Orientation="Vertical" Gap="4"> |
||||
|
<TextBlock Classes="h1">Drag+Drop</TextBlock> |
||||
|
<TextBlock Classes="h2">Example of Drag+Drop capabilities</TextBlock> |
||||
|
|
||||
|
<StackPanel Orientation="Horizontal" |
||||
|
Margin="0,16,0,0" |
||||
|
HorizontalAlignment="Center" |
||||
|
Gap="16"> |
||||
|
<Border BorderBrush="{DynamicResource ThemeAccentBrush}" BorderThickness="2" Padding="16" Name="DragMe"> |
||||
|
<TextBlock Name="DragState">Drag Me</TextBlock> |
||||
|
</Border> |
||||
|
<Border Background="{DynamicResource ThemeAccentBrush2}" Padding="16" |
||||
|
DragDrop.AllowDrop="True"> |
||||
|
<TextBlock Name="DropState">Drop some text or files here</TextBlock> |
||||
|
</Border> |
||||
|
</StackPanel> |
||||
|
</StackPanel> |
||||
|
</UserControl> |
||||
@ -0,0 +1,71 @@ |
|||||
|
using Avalonia.Controls; |
||||
|
using Avalonia.Input; |
||||
|
using Avalonia.Markup.Xaml; |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Text; |
||||
|
|
||||
|
namespace ControlCatalog.Pages |
||||
|
{ |
||||
|
public class DragAndDropPage : UserControl |
||||
|
{ |
||||
|
private TextBlock _DropState; |
||||
|
private TextBlock _DragState; |
||||
|
private Border _DragMe; |
||||
|
private int DragCount = 0; |
||||
|
|
||||
|
public DragAndDropPage() |
||||
|
{ |
||||
|
this.InitializeComponent(); |
||||
|
|
||||
|
_DragMe.PointerPressed += DoDrag; |
||||
|
|
||||
|
AddHandler(DragDrop.DropEvent, Drop); |
||||
|
AddHandler(DragDrop.DragOverEvent, DragOver); |
||||
|
} |
||||
|
|
||||
|
private async void DoDrag(object sender, Avalonia.Input.PointerPressedEventArgs e) |
||||
|
{ |
||||
|
DataObject dragData = new DataObject(); |
||||
|
dragData.Set(DataFormats.Text, $"You have dragged text {++DragCount} times"); |
||||
|
|
||||
|
var result = await DragDrop.DoDragDrop(dragData, DragDropEffects.Copy); |
||||
|
switch(result) |
||||
|
{ |
||||
|
case DragDropEffects.Copy: |
||||
|
_DragState.Text = "The text was copied"; break; |
||||
|
case DragDropEffects.Link: |
||||
|
_DragState.Text = "The text was linked"; break; |
||||
|
case DragDropEffects.None: |
||||
|
_DragState.Text = "The drag operation was canceled"; break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void DragOver(object sender, DragEventArgs e) |
||||
|
{ |
||||
|
// Only allow Copy or Link as Drop Operations.
|
||||
|
e.DragEffects = e.DragEffects & (DragDropEffects.Copy | DragDropEffects.Link); |
||||
|
|
||||
|
// Only allow if the dragged data contains text or filenames.
|
||||
|
if (!e.Data.Contains(DataFormats.Text) && !e.Data.Contains(DataFormats.FileNames)) |
||||
|
e.DragEffects = DragDropEffects.None; |
||||
|
} |
||||
|
|
||||
|
private void Drop(object sender, DragEventArgs e) |
||||
|
{ |
||||
|
if (e.Data.Contains(DataFormats.Text)) |
||||
|
_DropState.Text = e.Data.GetText(); |
||||
|
else if (e.Data.Contains(DataFormats.FileNames)) |
||||
|
_DropState.Text = string.Join(Environment.NewLine, e.Data.GetFileNames()); |
||||
|
} |
||||
|
|
||||
|
private void InitializeComponent() |
||||
|
{ |
||||
|
AvaloniaXamlLoader.Load(this); |
||||
|
|
||||
|
_DropState = this.Find<TextBlock>("DropState"); |
||||
|
_DragState = this.Find<TextBlock>("DragState"); |
||||
|
_DragMe = this.Find<Border>("DragMe"); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,80 @@ |
|||||
|
<UserControl xmlns="https://github.com/avaloniaui" |
||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> |
||||
|
<StackPanel Orientation="Vertical" Gap="4"> |
||||
|
<TextBlock Margin="2" Classes="h1">Numeric up-down control</TextBlock> |
||||
|
<TextBlock Margin="2" Classes="h2" TextWrapping="Wrap">Numeric up-down control provides a TextBox with button spinners that allow incrementing and decrementing numeric values by using the spinner buttons, keyboard up/down arrows, or mouse wheel.</TextBlock> |
||||
|
|
||||
|
<TextBlock Margin="2,5,2,2" FontSize="14" FontWeight="Bold">Features:</TextBlock> |
||||
|
<Grid Margin="2" ColumnDefinitions="Auto,Auto,Auto,Auto" RowDefinitions="Auto,Auto"> |
||||
|
<Grid Grid.Row="0" Grid.Column="0" ColumnDefinitions="Auto, Auto" RowDefinitions="35,35,35,35,35"> |
||||
|
<TextBlock Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" Margin="2">ShowButtonSpinner:</TextBlock> |
||||
|
<CheckBox Grid.Row="0" Grid.Column="1" IsChecked="{Binding #upDown.ShowButtonSpinner}" VerticalAlignment="Center" Margin="2"/> |
||||
|
|
||||
|
<TextBlock Grid.Row="1" Grid.Column="0" VerticalAlignment="Center" Margin="2">IsReadOnly:</TextBlock> |
||||
|
<CheckBox Grid.Row="1" Grid.Column="1" IsChecked="{Binding #upDown.IsReadOnly}" VerticalAlignment="Center" Margin="2"/> |
||||
|
|
||||
|
<TextBlock Grid.Row="2" Grid.Column="0" VerticalAlignment="Center" Margin="2">AllowSpin:</TextBlock> |
||||
|
<CheckBox Grid.Row="2" Grid.Column="1" IsChecked="{Binding #upDown.AllowSpin}" IsEnabled="{Binding #upDown.!IsReadOnly}" VerticalAlignment="Center" Margin="2"/> |
||||
|
|
||||
|
<TextBlock Grid.Row="3" Grid.Column="0" VerticalAlignment="Center" Margin="2">ClipValueToMinMax:</TextBlock> |
||||
|
<CheckBox Grid.Row="3" Grid.Column="1" IsChecked="{Binding #upDown.ClipValueToMinMax}" VerticalAlignment="Center" Margin="2"/> |
||||
|
|
||||
|
</Grid> |
||||
|
<Grid Grid.Row="0" Grid.Column="1" Margin="10,2,2,2" ColumnDefinitions="Auto, 120" RowDefinitions="35,35,35,35,35"> |
||||
|
<TextBlock Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" Margin="2">FormatString:</TextBlock> |
||||
|
<DropDown Grid.Row="0" Grid.Column="1" Items="{Binding Formats}" SelectedItem="{Binding SelectedFormat}" |
||||
|
VerticalAlignment="Center" Margin="2"> |
||||
|
<DropDown.ItemTemplate> |
||||
|
<DataTemplate> |
||||
|
<StackPanel Orientation="Horizontal" Gap="2"> |
||||
|
<TextBlock Text="{Binding Name}"/> |
||||
|
<TextBlock Text="-"/> |
||||
|
<TextBlock Text="{Binding Value}"/> |
||||
|
</StackPanel> |
||||
|
</DataTemplate> |
||||
|
</DropDown.ItemTemplate> |
||||
|
</DropDown> |
||||
|
|
||||
|
<TextBlock Grid.Row="1" Grid.Column="0" VerticalAlignment="Center" Margin="2">ButtonSpinnerLocation:</TextBlock> |
||||
|
<DropDown Grid.Row="1" Grid.Column="1" Items="{Binding SpinnerLocations}" SelectedItem="{Binding #upDown.ButtonSpinnerLocation}" |
||||
|
VerticalAlignment="Center" Margin="2"/> |
||||
|
|
||||
|
<TextBlock Grid.Row="2" Grid.Column="0" VerticalAlignment="Center" Margin="2">CultureInfo:</TextBlock> |
||||
|
<DropDown Grid.Row="2" Grid.Column="1" Items="{Binding Cultures}" SelectedItem="{Binding #upDown.CultureInfo}" |
||||
|
VerticalAlignment="Center" Margin="2"/> |
||||
|
|
||||
|
<TextBlock Grid.Row="3" Grid.Column="0" VerticalAlignment="Center" Margin="2">Watermark:</TextBlock> |
||||
|
<TextBox Grid.Row="3" Grid.Column="1" Text="{Binding #upDown.Watermark}" VerticalAlignment="Center" Margin="2" /> |
||||
|
|
||||
|
<TextBlock Grid.Row="4" Grid.Column="0" VerticalAlignment="Center" Margin="2">Text:</TextBlock> |
||||
|
<TextBox Grid.Row="4" Grid.Column="1" Text="{Binding #upDown.Text}" VerticalAlignment="Center" Margin="2" /> |
||||
|
</Grid> |
||||
|
<Grid Grid.Row="0" Grid.Column="2" Margin="10,2,2,2" RowDefinitions="35,35,35,35,35" ColumnDefinitions="Auto, 120"> |
||||
|
<TextBlock Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" Margin="10,2,2,2">Minimum:</TextBlock> |
||||
|
<NumericUpDown Grid.Row="0" Grid.Column="1" Value="{Binding #upDown.Minimum}" |
||||
|
CultureInfo="{Binding #upDown.CultureInfo}" VerticalAlignment="Center" Height="25" Margin="2" Width="70" HorizontalAlignment="Center"/> |
||||
|
|
||||
|
<TextBlock Grid.Row="1" Grid.Column="0" VerticalAlignment="Center" Margin="10,2,2,2">Maximum:</TextBlock> |
||||
|
<NumericUpDown Grid.Row="1" Grid.Column="1" Value="{Binding #upDown.Maximum}" |
||||
|
CultureInfo="{Binding #upDown.CultureInfo}" VerticalAlignment="Center" Height="25" Margin="2" Width="70" HorizontalAlignment="Center"/> |
||||
|
|
||||
|
<TextBlock Grid.Row="2" Grid.Column="0" VerticalAlignment="Center" Margin="10,2,2,2">Increment:</TextBlock> |
||||
|
<NumericUpDown Grid.Row="2" Grid.Column="1" Value="{Binding #upDown.Increment}" VerticalAlignment="Center" |
||||
|
Height="25" Margin="2" Width="70" HorizontalAlignment="Center"/> |
||||
|
|
||||
|
<TextBlock Grid.Row="3" Grid.Column="0" VerticalAlignment="Center" Margin="10,2,2,2">Value:</TextBlock> |
||||
|
<NumericUpDown Grid.Row="3" Grid.Column="1" Value="{Binding #upDown.Value}" VerticalAlignment="Center" |
||||
|
Height="25" Margin="2" Width="70" HorizontalAlignment="Center"/> |
||||
|
|
||||
|
</Grid> |
||||
|
</Grid> |
||||
|
|
||||
|
<StackPanel Margin="2,10,2,2" Orientation="Horizontal" Gap="10"> |
||||
|
<TextBlock FontSize="14" FontWeight="Bold" VerticalAlignment="Center">Usage of NumericUpDown:</TextBlock> |
||||
|
<NumericUpDown Name="upDown" Minimum="0" Maximum="10" Increment="0.5" |
||||
|
CultureInfo="en-US" VerticalAlignment="Center" Height="25" Width="100" |
||||
|
Watermark="Enter text" FormatString="{Binding SelectedFormat.Value}"/> |
||||
|
</StackPanel> |
||||
|
|
||||
|
</StackPanel> |
||||
|
</UserControl> |
||||
@ -0,0 +1,94 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Globalization; |
||||
|
using System.Linq; |
||||
|
using Avalonia; |
||||
|
using Avalonia.Controls; |
||||
|
using Avalonia.Controls.Primitives; |
||||
|
using Avalonia.Markup.Xaml; |
||||
|
using ReactiveUI; |
||||
|
|
||||
|
namespace ControlCatalog.Pages |
||||
|
{ |
||||
|
public class NumericUpDownPage : UserControl |
||||
|
{ |
||||
|
public NumericUpDownPage() |
||||
|
{ |
||||
|
this.InitializeComponent(); |
||||
|
var viewModel = new NumbersPageViewModel(); |
||||
|
DataContext = viewModel; |
||||
|
} |
||||
|
|
||||
|
private void InitializeComponent() |
||||
|
{ |
||||
|
AvaloniaXamlLoader.Load(this); |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
|
||||
|
public class NumbersPageViewModel : ReactiveObject |
||||
|
{ |
||||
|
private IList<FormatObject> _formats; |
||||
|
private FormatObject _selectedFormat; |
||||
|
private IList<Location> _spinnerLocations; |
||||
|
|
||||
|
public NumbersPageViewModel() |
||||
|
{ |
||||
|
SelectedFormat = Formats.FirstOrDefault(); |
||||
|
} |
||||
|
|
||||
|
public IList<FormatObject> Formats |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return _formats ?? (_formats = new List<FormatObject>() |
||||
|
{ |
||||
|
new FormatObject() {Name = "Currency", Value = "C2"}, |
||||
|
new FormatObject() {Name = "Fixed point", Value = "F2"}, |
||||
|
new FormatObject() {Name = "General", Value = "G"}, |
||||
|
new FormatObject() {Name = "Number", Value = "N"}, |
||||
|
new FormatObject() {Name = "Percent", Value = "P"}, |
||||
|
new FormatObject() {Name = "Degrees", Value = "{0:N2} °"}, |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public IList<Location> SpinnerLocations |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
if (_spinnerLocations == null) |
||||
|
{ |
||||
|
_spinnerLocations = new List<Location>(); |
||||
|
foreach (Location value in Enum.GetValues(typeof(Location))) |
||||
|
{ |
||||
|
_spinnerLocations.Add(value); |
||||
|
} |
||||
|
} |
||||
|
return _spinnerLocations ; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public IList<CultureInfo> Cultures { get; } = new List<CultureInfo>() |
||||
|
{ |
||||
|
new CultureInfo("en-US"), |
||||
|
new CultureInfo("en-GB"), |
||||
|
new CultureInfo("fr-FR"), |
||||
|
new CultureInfo("ar-DZ"), |
||||
|
new CultureInfo("zh-CN"), |
||||
|
new CultureInfo("cs-CZ") |
||||
|
}; |
||||
|
|
||||
|
public FormatObject SelectedFormat |
||||
|
{ |
||||
|
get { return _selectedFormat; } |
||||
|
set { this.RaiseAndSetIfChanged(ref _selectedFormat, value); } |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public class FormatObject |
||||
|
{ |
||||
|
public string Value { get; set; } |
||||
|
public string Name { get; set; } |
||||
|
} |
||||
|
} |
||||
@ -1,36 +0,0 @@ |
|||||
using System.Reflection; |
|
||||
using System.Runtime.CompilerServices; |
|
||||
using System.Runtime.InteropServices; |
|
||||
|
|
||||
// General Information about an assembly is controlled through the following
|
|
||||
// set of attributes. Change these attribute values to modify the information
|
|
||||
// associated with an assembly.
|
|
||||
[assembly: AssemblyTitle("ControlCatalog")] |
|
||||
[assembly: AssemblyDescription("")] |
|
||||
[assembly: AssemblyConfiguration("")] |
|
||||
[assembly: AssemblyCompany("")] |
|
||||
[assembly: AssemblyProduct("ControlCatalog")] |
|
||||
[assembly: AssemblyCopyright("Copyright © 2015")] |
|
||||
[assembly: AssemblyTrademark("")] |
|
||||
[assembly: AssemblyCulture("")] |
|
||||
|
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
|
||||
// to COM components. If you need to access a type in this assembly from
|
|
||||
// COM, set the ComVisible attribute to true on that type.
|
|
||||
[assembly: ComVisible(false)] |
|
||||
|
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
|
||||
[assembly: Guid("61bec86c-f307-4295-b5b8-9428610d7d55")] |
|
||||
|
|
||||
// Version information for an assembly consists of the following four values:
|
|
||||
//
|
|
||||
// Major Version
|
|
||||
// Minor Version
|
|
||||
// Build Number
|
|
||||
// Revision
|
|
||||
//
|
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
|
||||
// by using the '*' as shown below:
|
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
|
||||
[assembly: AssemblyVersion("1.0.0.0")] |
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")] |
|
||||
@ -0,0 +1,3 @@ |
|||||
|
<Project> |
||||
|
<Import Project="..\build\SharedVersion.props" /> |
||||
|
</Project> |
||||
@ -1,36 +0,0 @@ |
|||||
using System.Reflection; |
|
||||
using System.Runtime.CompilerServices; |
|
||||
using System.Runtime.InteropServices; |
|
||||
|
|
||||
// General Information about an assembly is controlled through the following
|
|
||||
// set of attributes. Change these attribute values to modify the information
|
|
||||
// associated with an assembly.
|
|
||||
[assembly: AssemblyTitle("RenderTest")] |
|
||||
[assembly: AssemblyDescription("")] |
|
||||
[assembly: AssemblyConfiguration("")] |
|
||||
[assembly: AssemblyCompany("")] |
|
||||
[assembly: AssemblyProduct("RenderTest")] |
|
||||
[assembly: AssemblyCopyright("Copyright © 2016")] |
|
||||
[assembly: AssemblyTrademark("")] |
|
||||
[assembly: AssemblyCulture("")] |
|
||||
|
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
|
||||
// to COM components. If you need to access a type in this assembly from
|
|
||||
// COM, set the ComVisible attribute to true on that type.
|
|
||||
[assembly: ComVisible(false)] |
|
||||
|
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
|
||||
[assembly: Guid("f1fdc5b0-4654-416f-ae69-e3e9bbd87801")] |
|
||||
|
|
||||
// Version information for an assembly consists of the following four values:
|
|
||||
//
|
|
||||
// Major Version
|
|
||||
// Minor Version
|
|
||||
// Build Number
|
|
||||
// Revision
|
|
||||
//
|
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
|
||||
// by using the '*' as shown below:
|
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
|
||||
[assembly: AssemblyVersion("1.0.0.0")] |
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")] |
|
||||
@ -1,184 +1,33 @@ |
|||||
<?xml version="1.0" encoding="utf-8"?> |
<Project Sdk="Microsoft.NET.Sdk"> |
||||
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> |
|
||||
<PropertyGroup> |
<PropertyGroup> |
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> |
<OutputType>Exe</OutputType> |
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> |
<TargetFrameworks>netcoreapp2.0;net461</TargetFrameworks> |
||||
<ProjectGuid>{F1FDC5B0-4654-416F-AE69-E3E9BBD87801}</ProjectGuid> |
|
||||
<OutputType>WinExe</OutputType> |
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder> |
|
||||
<RootNamespace>RenderTest</RootNamespace> |
|
||||
<AssemblyName>RenderTest</AssemblyName> |
|
||||
<TargetFrameworkVersion>v4.7</TargetFrameworkVersion> |
|
||||
<FileAlignment>512</FileAlignment> |
|
||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects> |
|
||||
<TargetFrameworkProfile /> |
|
||||
</PropertyGroup> |
</PropertyGroup> |
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> |
|
||||
<PlatformTarget>AnyCPU</PlatformTarget> |
|
||||
<DebugSymbols>true</DebugSymbols> |
|
||||
<DebugType>full</DebugType> |
|
||||
<Optimize>false</Optimize> |
|
||||
<OutputPath>bin\Debug\</OutputPath> |
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants> |
|
||||
<ErrorReport>prompt</ErrorReport> |
|
||||
<WarningLevel>4</WarningLevel> |
|
||||
</PropertyGroup> |
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> |
|
||||
<PlatformTarget>AnyCPU</PlatformTarget> |
|
||||
<DebugType>pdbonly</DebugType> |
|
||||
<Optimize>true</Optimize> |
|
||||
<OutputPath>bin\Release\</OutputPath> |
|
||||
<DefineConstants>TRACE</DefineConstants> |
|
||||
<ErrorReport>prompt</ErrorReport> |
|
||||
<WarningLevel>4</WarningLevel> |
|
||||
</PropertyGroup> |
|
||||
<PropertyGroup> |
|
||||
<StartupObject /> |
|
||||
</PropertyGroup> |
|
||||
<ItemGroup> |
|
||||
<Reference Include="System" /> |
|
||||
<Reference Include="System.Core" /> |
|
||||
<Reference Include="System.Xml.Linq" /> |
|
||||
<Reference Include="System.Data.DataSetExtensions" /> |
|
||||
<Reference Include="Microsoft.CSharp" /> |
|
||||
<Reference Include="System.Data" /> |
|
||||
<Reference Include="System.Net.Http" /> |
|
||||
<Reference Include="System.Xml" /> |
|
||||
<Reference Include="WindowsBase" /> |
|
||||
</ItemGroup> |
|
||||
<ItemGroup> |
|
||||
<Compile Include="App.xaml.cs"> |
|
||||
<DependentUpon>App.xaml</DependentUpon> |
|
||||
</Compile> |
|
||||
<Compile Include="Pages\DrawingPage.xaml.cs"> |
|
||||
<DependentUpon>DrawingPage.xaml</DependentUpon> |
|
||||
</Compile> |
|
||||
<Compile Include="Pages\ClippingPage.xaml.cs"> |
|
||||
<DependentUpon>ClippingPage.xaml</DependentUpon> |
|
||||
</Compile> |
|
||||
<Compile Include="Pages\AnimationsPage.xaml.cs"> |
|
||||
<DependentUpon>AnimationsPage.xaml</DependentUpon> |
|
||||
</Compile> |
|
||||
<Compile Include="Program.cs" /> |
|
||||
<Compile Include="Properties\AssemblyInfo.cs" /> |
|
||||
<Compile Include="MainWindow.xaml.cs"> |
|
||||
<DependentUpon>MainWindow.xaml</DependentUpon> |
|
||||
</Compile> |
|
||||
<Compile Include="ViewModels\MainWindowViewModel.cs" /> |
|
||||
</ItemGroup> |
|
||||
<ItemGroup> |
|
||||
<None Include="App.config" /> |
|
||||
</ItemGroup> |
|
||||
<ItemGroup> |
|
||||
<EmbeddedResource Include="App.xaml"> |
|
||||
<SubType>Designer</SubType> |
|
||||
</EmbeddedResource> |
|
||||
</ItemGroup> |
|
||||
<ItemGroup> |
<ItemGroup> |
||||
<ProjectReference Include="..\..\src\Avalonia.Animation\Avalonia.Animation.csproj"> |
<ProjectReference Include="..\..\src\Avalonia.DesignerSupport\Avalonia.DesignerSupport.csproj" /> |
||||
<Project>{d211e587-d8bc-45b9-95a4-f297c8fa5200}</Project> |
|
||||
<Name>Avalonia.Animation</Name> |
<ProjectReference Condition="'$(TargetFramework)'=='netcoreapp2.0'" Include="..\..\src\Avalonia.DotNetCoreRuntime\Avalonia.DotNetCoreRuntime.csproj" /> |
||||
</ProjectReference> |
|
||||
<ProjectReference Include="..\..\src\Avalonia.Base\Avalonia.Base.csproj"> |
<ProjectReference Condition="'$(TargetFramework)'=='net461'" Include="..\..\src\Avalonia.DotNetFrameworkRuntime\Avalonia.DotNetFrameworkRuntime.csproj" /> |
||||
<Project>{b09b78d8-9b26-48b0-9149-d64a2f120f3f}</Project> |
|
||||
<Name>Avalonia.Base</Name> |
<ProjectReference Include="..\..\src\Linux\Avalonia.LinuxFramebuffer\Avalonia.LinuxFramebuffer.csproj" /> |
||||
</ProjectReference> |
<ProjectReference Include="..\..\src\Avalonia.Base\Avalonia.Base.csproj" /> |
||||
<ProjectReference Include="..\..\src\Avalonia.Controls\Avalonia.Controls.csproj"> |
<ProjectReference Include="..\..\src\Markup\Avalonia.Markup.Xaml\Avalonia.Markup.Xaml.csproj" /> |
||||
<Project>{d2221c82-4a25-4583-9b43-d791e3f6820c}</Project> |
<ProjectReference Include="..\..\src\Markup\Avalonia.Markup\Avalonia.Markup.csproj" /> |
||||
<Name>Avalonia.Controls</Name> |
<ProjectReference Include="..\..\src\Avalonia.Animation\Avalonia.Animation.csproj" /> |
||||
</ProjectReference> |
<ProjectReference Include="..\..\src\Avalonia.Controls\Avalonia.Controls.csproj" /> |
||||
<ProjectReference Include="..\..\src\Avalonia.DesignerSupport\Avalonia.DesignerSupport.csproj"> |
<ProjectReference Include="..\..\src\Avalonia.Diagnostics\Avalonia.Diagnostics.csproj" /> |
||||
<Project>{799a7bb5-3c2c-48b6-85a7-406a12c420da}</Project> |
<ProjectReference Include="..\..\src\Avalonia.Input\Avalonia.Input.csproj" /> |
||||
<Name>Avalonia.DesignerSupport</Name> |
<ProjectReference Include="..\..\src\Avalonia.Interactivity\Avalonia.Interactivity.csproj" /> |
||||
</ProjectReference> |
<ProjectReference Include="..\..\src\Avalonia.Layout\Avalonia.Layout.csproj" /> |
||||
<ProjectReference Include="..\..\src\Avalonia.Diagnostics\Avalonia.Diagnostics.csproj"> |
<ProjectReference Include="..\..\src\Avalonia.ReactiveUI\Avalonia.ReactiveUI.csproj" /> |
||||
<Project>{7062ae20-5dcc-4442-9645-8195bdece63e}</Project> |
<ProjectReference Include="..\..\src\Avalonia.Visuals\Avalonia.Visuals.csproj" /> |
||||
<Name>Avalonia.Diagnostics</Name> |
<ProjectReference Include="..\..\src\Avalonia.Styling\Avalonia.Styling.csproj" /> |
||||
</ProjectReference> |
<ProjectReference Include="..\..\src\Avalonia.Themes.Default\Avalonia.Themes.Default.csproj" /> |
||||
<ProjectReference Include="..\..\src\Avalonia.DotNetFrameworkRuntime\Avalonia.DotNetFrameworkRuntime.csproj"> |
<ProjectReference Include="..\..\src\Avalonia.Logging.Serilog\Avalonia.Logging.Serilog.csproj" /> |
||||
<Project>{4a1abb09-9047-4bd5-a4ad-a055e52c5ee0}</Project> |
</ItemGroup> |
||||
<Name>Avalonia.DotNetFrameworkRuntime</Name> |
<Import Project="..\..\build\SampleApp.props" /> |
||||
</ProjectReference> |
<Import Project="..\..\build\EmbedXaml.props" /> |
||||
<ProjectReference Include="..\..\src\Avalonia.Input\Avalonia.Input.csproj"> |
|
||||
<Project>{62024b2d-53eb-4638-b26b-85eeaa54866e}</Project> |
|
||||
<Name>Avalonia.Input</Name> |
|
||||
</ProjectReference> |
|
||||
<ProjectReference Include="..\..\src\Avalonia.Interactivity\Avalonia.Interactivity.csproj"> |
|
||||
<Project>{6b0ed19d-a08b-461c-a9d9-a9ee40b0c06b}</Project> |
|
||||
<Name>Avalonia.Interactivity</Name> |
|
||||
</ProjectReference> |
|
||||
<ProjectReference Include="..\..\src\Avalonia.Layout\Avalonia.Layout.csproj"> |
|
||||
<Project>{42472427-4774-4c81-8aff-9f27b8e31721}</Project> |
|
||||
<Name>Avalonia.Layout</Name> |
|
||||
</ProjectReference> |
|
||||
<ProjectReference Include="..\..\src\Avalonia.Logging.Serilog\Avalonia.Logging.Serilog.csproj"> |
|
||||
<Project>{b61b66a3-b82d-4875-8001-89d3394fe0c9}</Project> |
|
||||
<Name>Avalonia.Logging.Serilog</Name> |
|
||||
</ProjectReference> |
|
||||
<ProjectReference Include="..\..\src\Avalonia.ReactiveUI\Avalonia.ReactiveUI.csproj"> |
|
||||
<Project>{6417b24e-49c2-4985-8db2-3ab9d898ec91}</Project> |
|
||||
<Name>Avalonia.ReactiveUI</Name> |
|
||||
</ProjectReference> |
|
||||
<ProjectReference Include="..\..\src\Avalonia.Visuals\Avalonia.Visuals.csproj"> |
|
||||
<Project>{eb582467-6abb-43a1-b052-e981ba910e3a}</Project> |
|
||||
<Name>Avalonia.Visuals</Name> |
|
||||
</ProjectReference> |
|
||||
<ProjectReference Include="..\..\src\Avalonia.Styling\Avalonia.Styling.csproj"> |
|
||||
<Project>{f1baa01a-f176-4c6a-b39d-5b40bb1b148f}</Project> |
|
||||
<Name>Avalonia.Styling</Name> |
|
||||
</ProjectReference> |
|
||||
<ProjectReference Include="..\..\src\Avalonia.Themes.Default\Avalonia.Themes.Default.csproj"> |
|
||||
<Project>{3e10a5fa-e8da-48b1-ad44-6a5b6cb7750f}</Project> |
|
||||
<Name>Avalonia.Themes.Default</Name> |
|
||||
</ProjectReference> |
|
||||
<ProjectReference Include="..\..\src\Markup\Avalonia.Markup.Xaml\Avalonia.Markup.Xaml.csproj"> |
|
||||
<Project>{3e53a01a-b331-47f3-b828-4a5717e77a24}</Project> |
|
||||
<Name>Avalonia.Markup.Xaml</Name> |
|
||||
</ProjectReference> |
|
||||
<ProjectReference Include="..\..\src\Markup\Avalonia.Markup\Avalonia.Markup.csproj"> |
|
||||
<Project>{6417e941-21bc-467b-a771-0de389353ce6}</Project> |
|
||||
<Name>Avalonia.Markup</Name> |
|
||||
</ProjectReference> |
|
||||
<ProjectReference Include="..\..\src\Skia\Avalonia.Skia\Avalonia.Skia.csproj"> |
|
||||
<Project>{7d2d3083-71dd-4cc9-8907-39a0d86fb322}</Project> |
|
||||
<Name>Avalonia.Skia</Name> |
|
||||
</ProjectReference> |
|
||||
<ProjectReference Include="..\..\src\Windows\Avalonia.Direct2D1\Avalonia.Direct2D1.csproj"> |
|
||||
<Project>{3e908f67-5543-4879-a1dc-08eace79b3cd}</Project> |
|
||||
<Name>Avalonia.Direct2D1</Name> |
|
||||
</ProjectReference> |
|
||||
<ProjectReference Include="..\..\src\Windows\Avalonia.Win32\Avalonia.Win32.csproj"> |
|
||||
<Project>{811a76cf-1cf6-440f-963b-bbe31bd72a82}</Project> |
|
||||
<Name>Avalonia.Win32</Name> |
|
||||
</ProjectReference> |
|
||||
</ItemGroup> |
|
||||
<ItemGroup> |
|
||||
<EmbeddedResource Include="MainWindow.xaml"> |
|
||||
<SubType>Designer</SubType> |
|
||||
</EmbeddedResource> |
|
||||
</ItemGroup> |
|
||||
<ItemGroup> |
|
||||
<EmbeddedResource Include="SideBar.xaml"> |
|
||||
<SubType>Designer</SubType> |
|
||||
</EmbeddedResource> |
|
||||
</ItemGroup> |
|
||||
<ItemGroup> |
|
||||
<EmbeddedResource Include="Pages\AnimationsPage.xaml"> |
|
||||
<SubType>Designer</SubType> |
|
||||
</EmbeddedResource> |
|
||||
</ItemGroup> |
|
||||
<ItemGroup> |
|
||||
<EmbeddedResource Include="Pages\ClippingPage.xaml"> |
|
||||
<SubType>Designer</SubType> |
|
||||
</EmbeddedResource> |
|
||||
</ItemGroup> |
|
||||
<ItemGroup> |
|
||||
<EmbeddedResource Include="Pages\DrawingPage.xaml"> |
|
||||
<SubType>Designer</SubType> |
|
||||
</EmbeddedResource> |
|
||||
</ItemGroup> |
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> |
|
||||
<Import Project="..\..\build\Serilog.props" /> |
<Import Project="..\..\build\Serilog.props" /> |
||||
<Import Project="..\..\build\Rx.props" /> |
<Import Project="..\..\build\Rx.props" /> |
||||
<Import Project="..\..\build\ReactiveUI.props" /> |
<Import Project="..\..\build\ReactiveUI.props" /> |
||||
|
|||||
@ -1,36 +0,0 @@ |
|||||
using System.Reflection; |
|
||||
using System.Runtime.CompilerServices; |
|
||||
using System.Runtime.InteropServices; |
|
||||
|
|
||||
// General Information about an assembly is controlled through the following
|
|
||||
// set of attributes. Change these attribute values to modify the information
|
|
||||
// associated with an assembly.
|
|
||||
[assembly: AssemblyTitle("VirtualizationTest")] |
|
||||
[assembly: AssemblyDescription("")] |
|
||||
[assembly: AssemblyConfiguration("")] |
|
||||
[assembly: AssemblyCompany("")] |
|
||||
[assembly: AssemblyProduct("VirtualizationTest")] |
|
||||
[assembly: AssemblyCopyright("Copyright © 2016")] |
|
||||
[assembly: AssemblyTrademark("")] |
|
||||
[assembly: AssemblyCulture("")] |
|
||||
|
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
|
||||
// to COM components. If you need to access a type in this assembly from
|
|
||||
// COM, set the ComVisible attribute to true on that type.
|
|
||||
[assembly: ComVisible(false)] |
|
||||
|
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
|
||||
[assembly: Guid("fbcaf3d0-2808-4934-8e96-3f607594517b")] |
|
||||
|
|
||||
// Version information for an assembly consists of the following four values:
|
|
||||
//
|
|
||||
// Major Version
|
|
||||
// Minor Version
|
|
||||
// Build Number
|
|
||||
// Revision
|
|
||||
//
|
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
|
||||
// by using the '*' as shown below:
|
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
|
||||
[assembly: AssemblyVersion("1.0.0.0")] |
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")] |
|
||||
@ -1,151 +1,33 @@ |
|||||
<?xml version="1.0" encoding="utf-8"?> |
<Project Sdk="Microsoft.NET.Sdk"> |
||||
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> |
|
||||
<PropertyGroup> |
<PropertyGroup> |
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> |
<OutputType>Exe</OutputType> |
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> |
<TargetFrameworks>netcoreapp2.0;net461</TargetFrameworks> |
||||
<ProjectGuid>{FBCAF3D0-2808-4934-8E96-3F607594517B}</ProjectGuid> |
|
||||
<OutputType>WinExe</OutputType> |
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder> |
|
||||
<RootNamespace>VirtualizationTest</RootNamespace> |
|
||||
<AssemblyName>VirtualizationTest</AssemblyName> |
|
||||
<TargetFrameworkVersion>v4.7</TargetFrameworkVersion> |
|
||||
<FileAlignment>512</FileAlignment> |
|
||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects> |
|
||||
<TargetFrameworkProfile /> |
|
||||
</PropertyGroup> |
</PropertyGroup> |
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> |
|
||||
<PlatformTarget>AnyCPU</PlatformTarget> |
|
||||
<DebugSymbols>true</DebugSymbols> |
|
||||
<DebugType>full</DebugType> |
|
||||
<Optimize>false</Optimize> |
|
||||
<OutputPath>bin\Debug\</OutputPath> |
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants> |
|
||||
<ErrorReport>prompt</ErrorReport> |
|
||||
<WarningLevel>4</WarningLevel> |
|
||||
</PropertyGroup> |
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> |
|
||||
<PlatformTarget>AnyCPU</PlatformTarget> |
|
||||
<DebugType>pdbonly</DebugType> |
|
||||
<Optimize>true</Optimize> |
|
||||
<OutputPath>bin\Release\</OutputPath> |
|
||||
<DefineConstants>TRACE</DefineConstants> |
|
||||
<ErrorReport>prompt</ErrorReport> |
|
||||
<WarningLevel>4</WarningLevel> |
|
||||
</PropertyGroup> |
|
||||
<PropertyGroup> |
|
||||
<StartupObject /> |
|
||||
</PropertyGroup> |
|
||||
<ItemGroup> |
|
||||
<Reference Include="System" /> |
|
||||
<Reference Include="System.Core" /> |
|
||||
<Reference Include="System.Xml.Linq" /> |
|
||||
<Reference Include="System.Data.DataSetExtensions" /> |
|
||||
<Reference Include="Microsoft.CSharp" /> |
|
||||
<Reference Include="System.Data" /> |
|
||||
<Reference Include="System.Net.Http" /> |
|
||||
<Reference Include="System.Xml" /> |
|
||||
</ItemGroup> |
|
||||
<ItemGroup> |
|
||||
<Compile Include="App.xaml.cs"> |
|
||||
<DependentUpon>App.xaml</DependentUpon> |
|
||||
</Compile> |
|
||||
<Compile Include="MainWindow.xaml.cs"> |
|
||||
<DependentUpon>MainWindow.xaml</DependentUpon> |
|
||||
</Compile> |
|
||||
<Compile Include="Program.cs" /> |
|
||||
<Compile Include="Properties\AssemblyInfo.cs" /> |
|
||||
<Compile Include="ViewModels\ItemViewModel.cs" /> |
|
||||
<Compile Include="ViewModels\MainWindowViewModel.cs" /> |
|
||||
</ItemGroup> |
|
||||
<ItemGroup> |
|
||||
<None Include="App.config" /> |
|
||||
</ItemGroup> |
|
||||
<ItemGroup> |
|
||||
<ProjectReference Include="..\..\src\Avalonia.Animation\Avalonia.Animation.csproj"> |
|
||||
<Project>{d211e587-d8bc-45b9-95a4-f297c8fa5200}</Project> |
|
||||
<Name>Avalonia.Animation</Name> |
|
||||
</ProjectReference> |
|
||||
<ProjectReference Include="..\..\src\Avalonia.Base\Avalonia.Base.csproj"> |
|
||||
<Project>{b09b78d8-9b26-48b0-9149-d64a2f120f3f}</Project> |
|
||||
<Name>Avalonia.Base</Name> |
|
||||
</ProjectReference> |
|
||||
<ProjectReference Include="..\..\src\Avalonia.Controls\Avalonia.Controls.csproj"> |
|
||||
<Project>{d2221c82-4a25-4583-9b43-d791e3f6820c}</Project> |
|
||||
<Name>Avalonia.Controls</Name> |
|
||||
</ProjectReference> |
|
||||
<ProjectReference Include="..\..\src\Avalonia.DesignerSupport\Avalonia.DesignerSupport.csproj"> |
|
||||
<Project>{799a7bb5-3c2c-48b6-85a7-406a12c420da}</Project> |
|
||||
<Name>Avalonia.DesignerSupport</Name> |
|
||||
</ProjectReference> |
|
||||
<ProjectReference Include="..\..\src\Avalonia.Diagnostics\Avalonia.Diagnostics.csproj"> |
|
||||
<Project>{7062ae20-5dcc-4442-9645-8195bdece63e}</Project> |
|
||||
<Name>Avalonia.Diagnostics</Name> |
|
||||
</ProjectReference> |
|
||||
<ProjectReference Include="..\..\src\Avalonia.DotNetFrameworkRuntime\Avalonia.DotNetFrameworkRuntime.csproj"> |
|
||||
<Project>{4A1ABB09-9047-4BD5-A4AD-A055E52C5EE0}</Project> |
|
||||
<Name>Avalonia.DotNetFrameworkRuntime</Name> |
|
||||
</ProjectReference> |
|
||||
<ProjectReference Include="..\..\src\Avalonia.Input\Avalonia.Input.csproj"> |
|
||||
<Project>{62024b2d-53eb-4638-b26b-85eeaa54866e}</Project> |
|
||||
<Name>Avalonia.Input</Name> |
|
||||
</ProjectReference> |
|
||||
<ProjectReference Include="..\..\src\Avalonia.Interactivity\Avalonia.Interactivity.csproj"> |
|
||||
<Project>{6b0ed19d-a08b-461c-a9d9-a9ee40b0c06b}</Project> |
|
||||
<Name>Avalonia.Interactivity</Name> |
|
||||
</ProjectReference> |
|
||||
<ProjectReference Include="..\..\src\Avalonia.Layout\Avalonia.Layout.csproj"> |
|
||||
<Project>{42472427-4774-4c81-8aff-9f27b8e31721}</Project> |
|
||||
<Name>Avalonia.Layout</Name> |
|
||||
</ProjectReference> |
|
||||
<ProjectReference Include="..\..\src\Avalonia.Logging.Serilog\Avalonia.Logging.Serilog.csproj"> |
|
||||
<Project>{B61B66A3-B82D-4875-8001-89D3394FE0C9}</Project> |
|
||||
<Name>Avalonia.Logging.Serilog</Name> |
|
||||
</ProjectReference> |
|
||||
<ProjectReference Include="..\..\src\Avalonia.ReactiveUI\Avalonia.ReactiveUI.csproj"> |
|
||||
<Project>{6417b24e-49c2-4985-8db2-3ab9d898ec91}</Project> |
|
||||
<Name>Avalonia.ReactiveUI</Name> |
|
||||
</ProjectReference> |
|
||||
<ProjectReference Include="..\..\src\Avalonia.Visuals\Avalonia.Visuals.csproj"> |
|
||||
<Project>{eb582467-6abb-43a1-b052-e981ba910e3a}</Project> |
|
||||
<Name>Avalonia.Visuals</Name> |
|
||||
</ProjectReference> |
|
||||
<ProjectReference Include="..\..\src\Avalonia.Styling\Avalonia.Styling.csproj"> |
|
||||
<Project>{f1baa01a-f176-4c6a-b39d-5b40bb1b148f}</Project> |
|
||||
<Name>Avalonia.Styling</Name> |
|
||||
</ProjectReference> |
|
||||
<ProjectReference Include="..\..\src\Avalonia.Themes.Default\Avalonia.Themes.Default.csproj"> |
|
||||
<Project>{3e10a5fa-e8da-48b1-ad44-6a5b6cb7750f}</Project> |
|
||||
<Name>Avalonia.Themes.Default</Name> |
|
||||
</ProjectReference> |
|
||||
<ProjectReference Include="..\..\src\Markup\Avalonia.Markup.Xaml\Avalonia.Markup.Xaml.csproj"> |
|
||||
<Project>{3e53a01a-b331-47f3-b828-4a5717e77a24}</Project> |
|
||||
<Name>Avalonia.Markup.Xaml</Name> |
|
||||
</ProjectReference> |
|
||||
<ProjectReference Include="..\..\src\Markup\Avalonia.Markup\Avalonia.Markup.csproj"> |
|
||||
<Project>{6417e941-21bc-467b-a771-0de389353ce6}</Project> |
|
||||
<Name>Avalonia.Markup</Name> |
|
||||
</ProjectReference> |
|
||||
<ProjectReference Include="..\..\src\Windows\Avalonia.Direct2D1\Avalonia.Direct2D1.csproj" Condition="'$(Platform)'!='Mono'"> |
|
||||
<Project>{3e908f67-5543-4879-a1dc-08eace79b3cd}</Project> |
|
||||
<Name>Avalonia.Direct2D1</Name> |
|
||||
</ProjectReference> |
|
||||
<ProjectReference Include="..\..\src\Windows\Avalonia.Win32\Avalonia.Win32.csproj" Condition="'$(Platform)'!='Mono'"> |
|
||||
<Project>{811a76cf-1cf6-440f-963b-bbe31bd72a82}</Project> |
|
||||
<Name>Avalonia.Win32</Name> |
|
||||
</ProjectReference> |
|
||||
</ItemGroup> |
|
||||
<ItemGroup> |
|
||||
<EmbeddedResource Include="App.xaml"> |
|
||||
<SubType>Designer</SubType> |
|
||||
</EmbeddedResource> |
|
||||
</ItemGroup> |
|
||||
<ItemGroup> |
<ItemGroup> |
||||
<EmbeddedResource Include="MainWindow.xaml"> |
<ProjectReference Include="..\..\src\Avalonia.DesignerSupport\Avalonia.DesignerSupport.csproj" /> |
||||
<SubType>Designer</SubType> |
|
||||
</EmbeddedResource> |
<ProjectReference Condition="'$(TargetFramework)'=='netcoreapp2.0'" Include="..\..\src\Avalonia.DotNetCoreRuntime\Avalonia.DotNetCoreRuntime.csproj" /> |
||||
|
|
||||
|
<ProjectReference Condition="'$(TargetFramework)'=='net461'" Include="..\..\src\Avalonia.DotNetFrameworkRuntime\Avalonia.DotNetFrameworkRuntime.csproj" /> |
||||
|
|
||||
|
<ProjectReference Include="..\..\src\Linux\Avalonia.LinuxFramebuffer\Avalonia.LinuxFramebuffer.csproj" /> |
||||
|
<ProjectReference Include="..\..\src\Avalonia.Base\Avalonia.Base.csproj" /> |
||||
|
<ProjectReference Include="..\..\src\Markup\Avalonia.Markup.Xaml\Avalonia.Markup.Xaml.csproj" /> |
||||
|
<ProjectReference Include="..\..\src\Markup\Avalonia.Markup\Avalonia.Markup.csproj" /> |
||||
|
<ProjectReference Include="..\..\src\Avalonia.Animation\Avalonia.Animation.csproj" /> |
||||
|
<ProjectReference Include="..\..\src\Avalonia.Controls\Avalonia.Controls.csproj" /> |
||||
|
<ProjectReference Include="..\..\src\Avalonia.Diagnostics\Avalonia.Diagnostics.csproj" /> |
||||
|
<ProjectReference Include="..\..\src\Avalonia.Input\Avalonia.Input.csproj" /> |
||||
|
<ProjectReference Include="..\..\src\Avalonia.Interactivity\Avalonia.Interactivity.csproj" /> |
||||
|
<ProjectReference Include="..\..\src\Avalonia.Layout\Avalonia.Layout.csproj" /> |
||||
|
<ProjectReference Include="..\..\src\Avalonia.ReactiveUI\Avalonia.ReactiveUI.csproj" /> |
||||
|
<ProjectReference Include="..\..\src\Avalonia.Visuals\Avalonia.Visuals.csproj" /> |
||||
|
<ProjectReference Include="..\..\src\Avalonia.Styling\Avalonia.Styling.csproj" /> |
||||
|
<ProjectReference Include="..\..\src\Avalonia.Themes.Default\Avalonia.Themes.Default.csproj" /> |
||||
|
<ProjectReference Include="..\..\src\Avalonia.Logging.Serilog\Avalonia.Logging.Serilog.csproj" /> |
||||
</ItemGroup> |
</ItemGroup> |
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> |
<Import Project="..\..\build\SampleApp.props" /> |
||||
|
<Import Project="..\..\build\EmbedXaml.props" /> |
||||
<Import Project="..\..\build\Serilog.props" /> |
<Import Project="..\..\build\Serilog.props" /> |
||||
<Import Project="..\..\build\Rx.props" /> |
<Import Project="..\..\build\Rx.props" /> |
||||
<Import Project="..\..\build\ReactiveUI.props" /> |
<Import Project="..\..\build\ReactiveUI.props" /> |
||||
|
|||||
@ -1,6 +0,0 @@ |
|||||
// Copyright (c) The Avalonia Project. All rights reserved.
|
|
||||
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|
||||
|
|
||||
using System.Reflection; |
|
||||
|
|
||||
[assembly: AssemblyTitle("Avalonia.Animation")] |
|
||||
@ -1,36 +1,9 @@ |
|||||
<Project Sdk="Microsoft.NET.Sdk"> |
<Project Sdk="Microsoft.NET.Sdk"> |
||||
<PropertyGroup> |
<PropertyGroup> |
||||
<TargetFramework>netstandard2.0</TargetFramework> |
<TargetFramework>netstandard2.0</TargetFramework> |
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo> |
<AssemblyName>Avalonia.Base</AssemblyName> |
||||
<RootNamespace>Avalonia</RootNamespace> |
<RootNamespace>Avalonia</RootNamespace> |
||||
</PropertyGroup> |
</PropertyGroup> |
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' "> |
|
||||
<DebugSymbols>true</DebugSymbols> |
|
||||
<DebugType>full</DebugType> |
|
||||
<Optimize>false</Optimize> |
|
||||
<OutputPath>bin\Debug\</OutputPath> |
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants> |
|
||||
<ErrorReport>prompt</ErrorReport> |
|
||||
<WarningLevel>4</WarningLevel> |
|
||||
<DocumentationFile>bin\Debug\Avalonia.Base.xml</DocumentationFile> |
|
||||
<NoWarn>CS1591</NoWarn> |
|
||||
</PropertyGroup> |
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Release' "> |
|
||||
<DebugType>pdbonly</DebugType> |
|
||||
<Optimize>true</Optimize> |
|
||||
<OutputPath>bin\Release\</OutputPath> |
|
||||
<DefineConstants>TRACE</DefineConstants> |
|
||||
<ErrorReport>prompt</ErrorReport> |
|
||||
<WarningLevel>4</WarningLevel> |
|
||||
<DocumentationFile>bin\Release\Avalonia.Base.xml</DocumentationFile> |
|
||||
<NoWarn>CS1591</NoWarn> |
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors> |
|
||||
</PropertyGroup> |
|
||||
<ItemGroup> |
|
||||
<Compile Include="..\Shared\SharedAssemblyInfo.cs"> |
|
||||
<Link>Properties\SharedAssemblyInfo.cs</Link> |
|
||||
</Compile> |
|
||||
</ItemGroup> |
|
||||
<Import Project="..\..\build\Base.props" /> |
<Import Project="..\..\build\Base.props" /> |
||||
<Import Project="..\..\build\Rx.props" /> |
<Import Project="..\..\build\Rx.props" /> |
||||
<Import Project="..\..\build\JetBrains.Annotations.props" /> |
<Import Project="..\..\build\JetBrains.Annotations.props" /> |
||||
|
|||||
@ -0,0 +1,127 @@ |
|||||
|
// Copyright (c) The Avalonia Project. All rights reserved.
|
||||
|
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
||||
|
|
||||
|
using System; |
||||
|
using System.Collections; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Collections.Specialized; |
||||
|
using System.Reactive; |
||||
|
using System.Reactive.Disposables; |
||||
|
using System.Reactive.Linq; |
||||
|
using System.Reactive.Subjects; |
||||
|
using Avalonia.Utilities; |
||||
|
|
||||
|
namespace Avalonia.Collections |
||||
|
{ |
||||
|
public static class NotifyCollectionChangedExtensions |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Gets a weak observable for the CollectionChanged event.
|
||||
|
/// </summary>
|
||||
|
/// <param name="collection">The collection.</param>
|
||||
|
/// <returns>An observable.</returns>
|
||||
|
public static IObservable<NotifyCollectionChangedEventArgs> GetWeakCollectionChangedObservable( |
||||
|
this INotifyCollectionChanged collection) |
||||
|
{ |
||||
|
Contract.Requires<ArgumentNullException>(collection != null); |
||||
|
|
||||
|
return new WeakCollectionChangedObservable(new WeakReference<INotifyCollectionChanged>(collection)); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Subcribes to the CollectionChanged event using a weak subscription.
|
||||
|
/// </summary>
|
||||
|
/// <param name="collection">The collection.</param>
|
||||
|
/// <param name="handler">
|
||||
|
/// An action called when the collection event is raised.
|
||||
|
/// </param>
|
||||
|
/// <returns>A disposable used to terminate the subscription.</returns>
|
||||
|
public static IDisposable WeakSubscribe( |
||||
|
this INotifyCollectionChanged collection, |
||||
|
NotifyCollectionChangedEventHandler handler) |
||||
|
{ |
||||
|
Contract.Requires<ArgumentNullException>(collection != null); |
||||
|
Contract.Requires<ArgumentNullException>(handler != null); |
||||
|
|
||||
|
return |
||||
|
collection.GetWeakCollectionChangedObservable() |
||||
|
.Subscribe(e => handler.Invoke(collection, e)); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Subcribes to the CollectionChanged event using a weak subscription.
|
||||
|
/// </summary>
|
||||
|
/// <param name="collection">The collection.</param>
|
||||
|
/// <param name="handler">
|
||||
|
/// An action called when the collection event is raised.
|
||||
|
/// </param>
|
||||
|
/// <returns>A disposable used to terminate the subscription.</returns>
|
||||
|
public static IDisposable WeakSubscribe( |
||||
|
this INotifyCollectionChanged collection, |
||||
|
Action<NotifyCollectionChangedEventArgs> handler) |
||||
|
{ |
||||
|
Contract.Requires<ArgumentNullException>(collection != null); |
||||
|
Contract.Requires<ArgumentNullException>(handler != null); |
||||
|
|
||||
|
return |
||||
|
collection.GetWeakCollectionChangedObservable() |
||||
|
.Subscribe(handler); |
||||
|
} |
||||
|
|
||||
|
private class WeakCollectionChangedObservable : ObservableBase<NotifyCollectionChangedEventArgs>, |
||||
|
IWeakSubscriber<NotifyCollectionChangedEventArgs> |
||||
|
{ |
||||
|
private WeakReference<INotifyCollectionChanged> _sourceReference; |
||||
|
private readonly Subject<NotifyCollectionChangedEventArgs> _changed = new Subject<NotifyCollectionChangedEventArgs>(); |
||||
|
|
||||
|
private int _count; |
||||
|
|
||||
|
public WeakCollectionChangedObservable(WeakReference<INotifyCollectionChanged> source) |
||||
|
{ |
||||
|
_sourceReference = source; |
||||
|
} |
||||
|
|
||||
|
public void OnEvent(object sender, NotifyCollectionChangedEventArgs e) |
||||
|
{ |
||||
|
_changed.OnNext(e); |
||||
|
} |
||||
|
|
||||
|
protected override IDisposable SubscribeCore(IObserver<NotifyCollectionChangedEventArgs> observer) |
||||
|
{ |
||||
|
if (_sourceReference.TryGetTarget(out INotifyCollectionChanged instance)) |
||||
|
{ |
||||
|
if (_count++ == 0) |
||||
|
{ |
||||
|
WeakSubscriptionManager.Subscribe( |
||||
|
instance, |
||||
|
nameof(instance.CollectionChanged), |
||||
|
this); |
||||
|
} |
||||
|
|
||||
|
return Observable.Using(() => Disposable.Create(DecrementCount), _ => _changed) |
||||
|
.Subscribe(observer); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
_changed.OnCompleted(); |
||||
|
observer.OnCompleted(); |
||||
|
return Disposable.Empty; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void DecrementCount() |
||||
|
{ |
||||
|
if (--_count == 0) |
||||
|
{ |
||||
|
if (_sourceReference.TryGetTarget(out INotifyCollectionChanged instance)) |
||||
|
{ |
||||
|
WeakSubscriptionManager.Unsubscribe( |
||||
|
instance, |
||||
|
nameof(instance.CollectionChanged), |
||||
|
this); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,219 @@ |
|||||
|
using System; |
||||
|
using System.Globalization; |
||||
|
using static System.Char; |
||||
|
|
||||
|
namespace Avalonia.Utilities |
||||
|
{ |
||||
|
public struct StringTokenizer : IDisposable |
||||
|
{ |
||||
|
private const char DefaultSeparatorChar = ','; |
||||
|
|
||||
|
private readonly string _s; |
||||
|
private readonly int _length; |
||||
|
private readonly char _separator; |
||||
|
private readonly string _exceptionMessage; |
||||
|
private readonly IFormatProvider _formatProvider; |
||||
|
private int _index; |
||||
|
private int _tokenIndex; |
||||
|
private int _tokenLength; |
||||
|
|
||||
|
public StringTokenizer(string s, IFormatProvider formatProvider, string exceptionMessage = null) |
||||
|
: this(s, GetSeparatorFromFormatProvider(formatProvider), exceptionMessage) |
||||
|
{ |
||||
|
_formatProvider = formatProvider; |
||||
|
} |
||||
|
|
||||
|
public StringTokenizer(string s, char separator = DefaultSeparatorChar, string exceptionMessage = null) |
||||
|
{ |
||||
|
_s = s ?? throw new ArgumentNullException(nameof(s)); |
||||
|
_length = s?.Length ?? 0; |
||||
|
_separator = separator; |
||||
|
_exceptionMessage = exceptionMessage; |
||||
|
_formatProvider = CultureInfo.InvariantCulture; |
||||
|
_index = 0; |
||||
|
_tokenIndex = -1; |
||||
|
_tokenLength = 0; |
||||
|
|
||||
|
while (_index < _length && IsWhiteSpace(_s, _index)) |
||||
|
{ |
||||
|
_index++; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public string CurrentToken => _tokenIndex < 0 ? null : _s.Substring(_tokenIndex, _tokenLength); |
||||
|
|
||||
|
public void Dispose() |
||||
|
{ |
||||
|
if (_index != _length) |
||||
|
{ |
||||
|
throw GetFormatException(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public bool TryReadInt32(out Int32 result, char? separator = null) |
||||
|
{ |
||||
|
if (TryReadString(out var stringResult, separator) && |
||||
|
int.TryParse(stringResult, NumberStyles.Integer, _formatProvider, out result)) |
||||
|
{ |
||||
|
return true; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
result = default(Int32); |
||||
|
return false; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public int ReadInt32(char? separator = null) |
||||
|
{ |
||||
|
if (!TryReadInt32(out var result, separator)) |
||||
|
{ |
||||
|
throw GetFormatException(); |
||||
|
} |
||||
|
|
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
public bool TryReadDouble(out double result, char? separator = null) |
||||
|
{ |
||||
|
if (TryReadString(out var stringResult, separator) && |
||||
|
double.TryParse(stringResult, NumberStyles.Float, _formatProvider, out result)) |
||||
|
{ |
||||
|
return true; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
result = default(double); |
||||
|
return false; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public double ReadDouble(char? separator = null) |
||||
|
{ |
||||
|
if (!TryReadDouble(out var result, separator)) |
||||
|
{ |
||||
|
throw GetFormatException(); |
||||
|
} |
||||
|
|
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
public bool TryReadString(out string result, char? separator = null) |
||||
|
{ |
||||
|
var success = TryReadToken(separator ?? _separator); |
||||
|
result = CurrentToken; |
||||
|
return success; |
||||
|
} |
||||
|
|
||||
|
public string ReadString(char? separator = null) |
||||
|
{ |
||||
|
if (!TryReadString(out var result, separator)) |
||||
|
{ |
||||
|
throw GetFormatException(); |
||||
|
} |
||||
|
|
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
private bool TryReadToken(char separator) |
||||
|
{ |
||||
|
_tokenIndex = -1; |
||||
|
|
||||
|
if (_index >= _length) |
||||
|
{ |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
var c = _s[_index]; |
||||
|
|
||||
|
var index = _index; |
||||
|
var length = 0; |
||||
|
|
||||
|
while (_index < _length) |
||||
|
{ |
||||
|
c = _s[_index]; |
||||
|
|
||||
|
if (IsWhiteSpace(c) || c == separator) |
||||
|
{ |
||||
|
break; |
||||
|
} |
||||
|
|
||||
|
_index++; |
||||
|
length++; |
||||
|
} |
||||
|
|
||||
|
SkipToNextToken(separator); |
||||
|
|
||||
|
_tokenIndex = index; |
||||
|
_tokenLength = length; |
||||
|
|
||||
|
if (_tokenLength < 1) |
||||
|
{ |
||||
|
throw GetFormatException(); |
||||
|
} |
||||
|
|
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
private void SkipToNextToken(char separator) |
||||
|
{ |
||||
|
if (_index < _length) |
||||
|
{ |
||||
|
var c = _s[_index]; |
||||
|
|
||||
|
if (c != separator && !IsWhiteSpace(c)) |
||||
|
{ |
||||
|
throw GetFormatException(); |
||||
|
} |
||||
|
|
||||
|
var length = 0; |
||||
|
|
||||
|
while (_index < _length) |
||||
|
{ |
||||
|
c = _s[_index]; |
||||
|
|
||||
|
if (c == separator) |
||||
|
{ |
||||
|
length++; |
||||
|
_index++; |
||||
|
|
||||
|
if (length > 1) |
||||
|
{ |
||||
|
throw GetFormatException(); |
||||
|
} |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
if (!IsWhiteSpace(c)) |
||||
|
{ |
||||
|
break; |
||||
|
} |
||||
|
|
||||
|
_index++; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (length > 0 && _index >= _length) |
||||
|
{ |
||||
|
throw GetFormatException(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private FormatException GetFormatException() => |
||||
|
_exceptionMessage != null ? new FormatException(_exceptionMessage) : new FormatException(); |
||||
|
|
||||
|
private static char GetSeparatorFromFormatProvider(IFormatProvider provider) |
||||
|
{ |
||||
|
var c = DefaultSeparatorChar; |
||||
|
|
||||
|
var formatInfo = NumberFormatInfo.GetInstance(provider); |
||||
|
if (formatInfo.NumberDecimalSeparator.Length > 0 && c == formatInfo.NumberDecimalSeparator[0]) |
||||
|
{ |
||||
|
c = ';'; |
||||
|
} |
||||
|
|
||||
|
return c; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
File diff suppressed because it is too large
@ -0,0 +1,263 @@ |
|||||
|
using System; |
||||
|
using Avalonia.Controls.Primitives; |
||||
|
using Avalonia.Input; |
||||
|
using Avalonia.Interactivity; |
||||
|
|
||||
|
namespace Avalonia.Controls |
||||
|
{ |
||||
|
public enum Location |
||||
|
{ |
||||
|
Left, |
||||
|
Right |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Represents a spinner control that includes two Buttons.
|
||||
|
/// </summary>
|
||||
|
public class ButtonSpinner : Spinner |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Defines the <see cref="AllowSpin"/> property.
|
||||
|
/// </summary>
|
||||
|
public static readonly StyledProperty<bool> AllowSpinProperty = |
||||
|
AvaloniaProperty.Register<ButtonSpinner, bool>(nameof(AllowSpin), true); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Defines the <see cref="ShowButtonSpinner"/> property.
|
||||
|
/// </summary>
|
||||
|
public static readonly StyledProperty<bool> ShowButtonSpinnerProperty = |
||||
|
AvaloniaProperty.Register<ButtonSpinner, bool>(nameof(ShowButtonSpinner), true); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Defines the <see cref="ButtonSpinnerLocation"/> property.
|
||||
|
/// </summary>
|
||||
|
public static readonly StyledProperty<Location> ButtonSpinnerLocationProperty = |
||||
|
AvaloniaProperty.Register<ButtonSpinner, Location>(nameof(ButtonSpinnerLocation), Location.Right); |
||||
|
|
||||
|
private Button _decreaseButton; |
||||
|
/// <summary>
|
||||
|
/// Gets or sets the DecreaseButton template part.
|
||||
|
/// </summary>
|
||||
|
private Button DecreaseButton |
||||
|
{ |
||||
|
get { return _decreaseButton; } |
||||
|
set |
||||
|
{ |
||||
|
if (_decreaseButton != null) |
||||
|
{ |
||||
|
_decreaseButton.Click -= OnButtonClick; |
||||
|
} |
||||
|
_decreaseButton = value; |
||||
|
if (_decreaseButton != null) |
||||
|
{ |
||||
|
_decreaseButton.Click += OnButtonClick; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private Button _increaseButton; |
||||
|
/// <summary>
|
||||
|
/// Gets or sets the IncreaseButton template part.
|
||||
|
/// </summary>
|
||||
|
private Button IncreaseButton |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return _increaseButton; |
||||
|
} |
||||
|
set |
||||
|
{ |
||||
|
if (_increaseButton != null) |
||||
|
{ |
||||
|
_increaseButton.Click -= OnButtonClick; |
||||
|
} |
||||
|
_increaseButton = value; |
||||
|
if (_increaseButton != null) |
||||
|
{ |
||||
|
_increaseButton.Click += OnButtonClick; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes static members of the <see cref="ButtonSpinner"/> class.
|
||||
|
/// </summary>
|
||||
|
static ButtonSpinner() |
||||
|
{ |
||||
|
AllowSpinProperty.Changed.Subscribe(AllowSpinChanged); |
||||
|
PseudoClass(ButtonSpinnerLocationProperty, location => location == Location.Left, ":left"); |
||||
|
PseudoClass(ButtonSpinnerLocationProperty, location => location == Location.Right, ":right"); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets or sets a value indicating whether the <see cref="ButtonSpinner"/> should allow to spin.
|
||||
|
/// </summary>
|
||||
|
public bool AllowSpin |
||||
|
{ |
||||
|
get { return GetValue(AllowSpinProperty); } |
||||
|
set { SetValue(AllowSpinProperty, value); } |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets or sets a value indicating whether the spin buttons should be shown.
|
||||
|
/// </summary>
|
||||
|
public bool ShowButtonSpinner |
||||
|
{ |
||||
|
get { return GetValue(ShowButtonSpinnerProperty); } |
||||
|
set { SetValue(ShowButtonSpinnerProperty, value); } |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets or sets current location of the <see cref="ButtonSpinner"/>.
|
||||
|
/// </summary>
|
||||
|
public Location ButtonSpinnerLocation |
||||
|
{ |
||||
|
get { return GetValue(ButtonSpinnerLocationProperty); } |
||||
|
set { SetValue(ButtonSpinnerLocationProperty, value); } |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
protected override void OnTemplateApplied(TemplateAppliedEventArgs e) |
||||
|
{ |
||||
|
IncreaseButton = e.NameScope.Find<Button>("PART_IncreaseButton"); |
||||
|
DecreaseButton = e.NameScope.Find<Button>("PART_DecreaseButton"); |
||||
|
SetButtonUsage(); |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
protected override void OnPointerReleased(PointerReleasedEventArgs e) |
||||
|
{ |
||||
|
base.OnPointerReleased(e); |
||||
|
Point mousePosition; |
||||
|
if (IncreaseButton != null && IncreaseButton.IsEnabled == false) |
||||
|
{ |
||||
|
mousePosition = e.GetPosition(IncreaseButton); |
||||
|
if (mousePosition.X > 0 && mousePosition.X < IncreaseButton.Width && |
||||
|
mousePosition.Y > 0 && mousePosition.Y < IncreaseButton.Height) |
||||
|
{ |
||||
|
e.Handled = true; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (DecreaseButton != null && DecreaseButton.IsEnabled == false) |
||||
|
{ |
||||
|
mousePosition = e.GetPosition(DecreaseButton); |
||||
|
if (mousePosition.X > 0 && mousePosition.X < DecreaseButton.Width && |
||||
|
mousePosition.Y > 0 && mousePosition.Y < DecreaseButton.Height) |
||||
|
{ |
||||
|
e.Handled = true; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
protected override void OnKeyDown(KeyEventArgs e) |
||||
|
{ |
||||
|
switch (e.Key) |
||||
|
{ |
||||
|
case Key.Up: |
||||
|
{ |
||||
|
if (AllowSpin) |
||||
|
{ |
||||
|
OnSpin(new SpinEventArgs(SpinEvent, SpinDirection.Increase)); |
||||
|
e.Handled = true; |
||||
|
} |
||||
|
break; |
||||
|
} |
||||
|
case Key.Down: |
||||
|
{ |
||||
|
if (AllowSpin) |
||||
|
{ |
||||
|
OnSpin(new SpinEventArgs(SpinEvent, SpinDirection.Decrease)); |
||||
|
e.Handled = true; |
||||
|
} |
||||
|
break; |
||||
|
} |
||||
|
case Key.Enter: |
||||
|
{ |
||||
|
//Do not Spin on enter Key when spinners have focus
|
||||
|
if (((IncreaseButton != null) && (IncreaseButton.IsFocused)) |
||||
|
|| ((DecreaseButton != null) && DecreaseButton.IsFocused)) |
||||
|
{ |
||||
|
e.Handled = true; |
||||
|
} |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
protected override void OnPointerWheelChanged(PointerWheelEventArgs e) |
||||
|
{ |
||||
|
base.OnPointerWheelChanged(e); |
||||
|
if (!e.Handled && AllowSpin) |
||||
|
{ |
||||
|
if (e.Delta.Y != 0) |
||||
|
{ |
||||
|
var spinnerEventArgs = new SpinEventArgs(SpinEvent, (e.Delta.Y < 0) ? SpinDirection.Decrease : SpinDirection.Increase, true); |
||||
|
OnSpin(spinnerEventArgs); |
||||
|
e.Handled = spinnerEventArgs.Handled; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected override void OnValidSpinDirectionChanged(ValidSpinDirections oldValue, ValidSpinDirections newValue) |
||||
|
{ |
||||
|
SetButtonUsage(); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Called when the <see cref="AllowSpin"/> property value changed.
|
||||
|
/// </summary>
|
||||
|
/// <param name="oldValue">The old value.</param>
|
||||
|
/// <param name="newValue">The new value.</param>
|
||||
|
protected virtual void OnAllowSpinChanged(bool oldValue, bool newValue) |
||||
|
{ |
||||
|
SetButtonUsage(); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Called when the <see cref="AllowSpin"/> property value changed.
|
||||
|
/// </summary>
|
||||
|
/// <param name="e">The event args.</param>
|
||||
|
private static void AllowSpinChanged(AvaloniaPropertyChangedEventArgs e) |
||||
|
{ |
||||
|
if (e.Sender is ButtonSpinner spinner) |
||||
|
{ |
||||
|
var oldValue = (bool)e.OldValue; |
||||
|
var newValue = (bool)e.NewValue; |
||||
|
spinner.OnAllowSpinChanged(oldValue, newValue); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Disables or enables the buttons based on the valid spin direction.
|
||||
|
/// </summary>
|
||||
|
private void SetButtonUsage() |
||||
|
{ |
||||
|
if (IncreaseButton != null) |
||||
|
{ |
||||
|
IncreaseButton.IsEnabled = AllowSpin && ((ValidSpinDirection & ValidSpinDirections.Increase) == ValidSpinDirections.Increase); |
||||
|
} |
||||
|
|
||||
|
if (DecreaseButton != null) |
||||
|
{ |
||||
|
DecreaseButton.IsEnabled = AllowSpin && ((ValidSpinDirection & ValidSpinDirections.Decrease) == ValidSpinDirections.Decrease); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Called when user clicks one of the spin buttons.
|
||||
|
/// </summary>
|
||||
|
/// <param name="sender">The event sender.</param>
|
||||
|
/// <param name="e">The event args.</param>
|
||||
|
private void OnButtonClick(object sender, RoutedEventArgs e) |
||||
|
{ |
||||
|
if (AllowSpin) |
||||
|
{ |
||||
|
var direction = sender == IncreaseButton ? SpinDirection.Increase : SpinDirection.Decrease; |
||||
|
OnSpin(new SpinEventArgs(SpinEvent, direction)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,998 @@ |
|||||
|
using System; |
||||
|
using System.Globalization; |
||||
|
using System.IO; |
||||
|
using System.Linq; |
||||
|
using Avalonia.Controls.Primitives; |
||||
|
using Avalonia.Data; |
||||
|
using Avalonia.Input; |
||||
|
using Avalonia.Interactivity; |
||||
|
using Avalonia.Threading; |
||||
|
using Avalonia.Utilities; |
||||
|
|
||||
|
namespace Avalonia.Controls |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Control that represents a TextBox with button spinners that allow incrementing and decrementing numeric values.
|
||||
|
/// </summary>
|
||||
|
public class NumericUpDown : TemplatedControl |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Defines the <see cref="AllowSpin"/> property.
|
||||
|
/// </summary>
|
||||
|
public static readonly StyledProperty<bool> AllowSpinProperty = |
||||
|
ButtonSpinner.AllowSpinProperty.AddOwner<NumericUpDown>(); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Defines the <see cref="ButtonSpinnerLocation"/> property.
|
||||
|
/// </summary>
|
||||
|
public static readonly StyledProperty<Location> ButtonSpinnerLocationProperty = |
||||
|
ButtonSpinner.ButtonSpinnerLocationProperty.AddOwner<NumericUpDown>(); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Defines the <see cref="ShowButtonSpinner"/> property.
|
||||
|
/// </summary>
|
||||
|
public static readonly StyledProperty<bool> ShowButtonSpinnerProperty = |
||||
|
ButtonSpinner.ShowButtonSpinnerProperty.AddOwner<NumericUpDown>(); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Defines the <see cref="ClipValueToMinMax"/> property.
|
||||
|
/// </summary>
|
||||
|
public static readonly DirectProperty<NumericUpDown, bool> ClipValueToMinMaxProperty = |
||||
|
AvaloniaProperty.RegisterDirect<NumericUpDown, bool>(nameof(ClipValueToMinMax), |
||||
|
updown => updown.ClipValueToMinMax, (updown, b) => updown.ClipValueToMinMax = b); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Defines the <see cref="CultureInfo"/> property.
|
||||
|
/// </summary>
|
||||
|
public static readonly DirectProperty<NumericUpDown, CultureInfo> CultureInfoProperty = |
||||
|
AvaloniaProperty.RegisterDirect<NumericUpDown, CultureInfo>(nameof(CultureInfo), o => o.CultureInfo, |
||||
|
(o, v) => o.CultureInfo = v, CultureInfo.CurrentCulture); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Defines the <see cref="FormatString"/> property.
|
||||
|
/// </summary>
|
||||
|
public static readonly StyledProperty<string> FormatStringProperty = |
||||
|
AvaloniaProperty.Register<NumericUpDown, string>(nameof(FormatString), string.Empty); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Defines the <see cref="Increment"/> property.
|
||||
|
/// </summary>
|
||||
|
public static readonly StyledProperty<double> IncrementProperty = |
||||
|
AvaloniaProperty.Register<NumericUpDown, double>(nameof(Increment), 1.0d, validate: OnCoerceIncrement); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Defines the <see cref="IsReadOnly"/> property.
|
||||
|
/// </summary>
|
||||
|
public static readonly StyledProperty<bool> IsReadOnlyProperty = |
||||
|
AvaloniaProperty.Register<NumericUpDown, bool>(nameof(IsReadOnly)); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Defines the <see cref="Maximum"/> property.
|
||||
|
/// </summary>
|
||||
|
public static readonly StyledProperty<double> MaximumProperty = |
||||
|
AvaloniaProperty.Register<NumericUpDown, double>(nameof(Maximum), double.MaxValue, validate: OnCoerceMaximum); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Defines the <see cref="Minimum"/> property.
|
||||
|
/// </summary>
|
||||
|
public static readonly StyledProperty<double> MinimumProperty = |
||||
|
AvaloniaProperty.Register<NumericUpDown, double>(nameof(Minimum), double.MinValue, validate: OnCoerceMinimum); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Defines the <see cref="ParsingNumberStyle"/> property.
|
||||
|
/// </summary>
|
||||
|
public static readonly DirectProperty<NumericUpDown, NumberStyles> ParsingNumberStyleProperty = |
||||
|
AvaloniaProperty.RegisterDirect<NumericUpDown, NumberStyles>(nameof(ParsingNumberStyle), |
||||
|
updown => updown.ParsingNumberStyle, (updown, style) => updown.ParsingNumberStyle = style); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Defines the <see cref="Text"/> property.
|
||||
|
/// </summary>
|
||||
|
public static readonly DirectProperty<NumericUpDown, string> TextProperty = |
||||
|
AvaloniaProperty.RegisterDirect<NumericUpDown, string>(nameof(Text), o => o.Text, (o, v) => o.Text = v, |
||||
|
defaultBindingMode: BindingMode.TwoWay); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Defines the <see cref="Value"/> property.
|
||||
|
/// </summary>
|
||||
|
public static readonly DirectProperty<NumericUpDown, double> ValueProperty = |
||||
|
AvaloniaProperty.RegisterDirect<NumericUpDown, double>(nameof(Value), updown => updown.Value, |
||||
|
(updown, v) => updown.Value = v, defaultBindingMode: BindingMode.TwoWay); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Defines the <see cref="Watermark"/> property.
|
||||
|
/// </summary>
|
||||
|
public static readonly StyledProperty<string> WatermarkProperty = |
||||
|
AvaloniaProperty.Register<NumericUpDown, string>(nameof(Watermark)); |
||||
|
|
||||
|
private IDisposable _textBoxTextChangedSubscription; |
||||
|
|
||||
|
private double _value; |
||||
|
private string _text; |
||||
|
private bool _internalValueSet; |
||||
|
private bool _clipValueToMinMax; |
||||
|
private bool _isSyncingTextAndValueProperties; |
||||
|
private bool _isTextChangedFromUI; |
||||
|
private CultureInfo _cultureInfo; |
||||
|
private NumberStyles _parsingNumberStyle = NumberStyles.Any; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the Spinner template part.
|
||||
|
/// </summary>
|
||||
|
private Spinner Spinner { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the TextBox template part.
|
||||
|
/// </summary>
|
||||
|
private TextBox TextBox { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets or sets the ability to perform increment/decrement operations via the keyboard, button spinners, or mouse wheel.
|
||||
|
/// </summary>
|
||||
|
public bool AllowSpin |
||||
|
{ |
||||
|
get { return GetValue(AllowSpinProperty); } |
||||
|
set { SetValue(AllowSpinProperty, value); } |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets or sets current location of the <see cref="ButtonSpinner"/>.
|
||||
|
/// </summary>
|
||||
|
public Location ButtonSpinnerLocation |
||||
|
{ |
||||
|
get { return GetValue(ButtonSpinnerLocationProperty); } |
||||
|
set { SetValue(ButtonSpinnerLocationProperty, value); } |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets or sets a value indicating whether the spin buttons should be shown.
|
||||
|
/// </summary>
|
||||
|
public bool ShowButtonSpinner |
||||
|
{ |
||||
|
get { return GetValue(ShowButtonSpinnerProperty); } |
||||
|
set { SetValue(ShowButtonSpinnerProperty, value); } |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets or sets if the value should be clipped when minimum/maximum is reached.
|
||||
|
/// </summary>
|
||||
|
public bool ClipValueToMinMax |
||||
|
{ |
||||
|
get { return _clipValueToMinMax; } |
||||
|
set { SetAndRaise(ClipValueToMinMaxProperty, ref _clipValueToMinMax, value); } |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets or sets the current CultureInfo.
|
||||
|
/// </summary>
|
||||
|
public CultureInfo CultureInfo |
||||
|
{ |
||||
|
get { return _cultureInfo; } |
||||
|
set { SetAndRaise(CultureInfoProperty, ref _cultureInfo, value); } |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets or sets the display format of the <see cref="Value"/>.
|
||||
|
/// </summary>
|
||||
|
public string FormatString |
||||
|
{ |
||||
|
get { return GetValue(FormatStringProperty); } |
||||
|
set { SetValue(FormatStringProperty, value); } |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets or sets the amount in which to increment the <see cref="Value"/>.
|
||||
|
/// </summary>
|
||||
|
public double Increment |
||||
|
{ |
||||
|
get { return GetValue(IncrementProperty); } |
||||
|
set { SetValue(IncrementProperty, value); } |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets or sets if the control is read only.
|
||||
|
/// </summary>
|
||||
|
public bool IsReadOnly |
||||
|
{ |
||||
|
get { return GetValue(IsReadOnlyProperty); } |
||||
|
set { SetValue(IsReadOnlyProperty, value); } |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets or sets the maximum allowed value.
|
||||
|
/// </summary>
|
||||
|
public double Maximum |
||||
|
{ |
||||
|
get { return GetValue(MaximumProperty); } |
||||
|
set { SetValue(MaximumProperty, value); } |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets or sets the minimum allowed value.
|
||||
|
/// </summary>
|
||||
|
public double Minimum |
||||
|
{ |
||||
|
get { return GetValue(MinimumProperty); } |
||||
|
set { SetValue(MinimumProperty, value); } |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets or sets the parsing style (AllowLeadingWhite, Float, AllowHexSpecifier, ...). By default, Any.
|
||||
|
/// </summary>
|
||||
|
public NumberStyles ParsingNumberStyle |
||||
|
{ |
||||
|
get { return _parsingNumberStyle; } |
||||
|
set { SetAndRaise(ParsingNumberStyleProperty, ref _parsingNumberStyle, value); } |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets or sets the formatted string representation of the value.
|
||||
|
/// </summary>
|
||||
|
public string Text |
||||
|
{ |
||||
|
get { return _text; } |
||||
|
set { SetAndRaise(TextProperty, ref _text, value); } |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets or sets the value.
|
||||
|
/// </summary>
|
||||
|
public double Value |
||||
|
{ |
||||
|
get { return _value; } |
||||
|
set |
||||
|
{ |
||||
|
value = OnCoerceValue(value); |
||||
|
SetAndRaise(ValueProperty, ref _value, value); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets or sets the object to use as a watermark if the <see cref="Value"/> is null.
|
||||
|
/// </summary>
|
||||
|
public string Watermark |
||||
|
{ |
||||
|
get { return GetValue(WatermarkProperty); } |
||||
|
set { SetValue(WatermarkProperty, value); } |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes new instance of <see cref="NumericUpDown"/> class.
|
||||
|
/// </summary>
|
||||
|
public NumericUpDown() |
||||
|
{ |
||||
|
Initialized += (sender, e) => |
||||
|
{ |
||||
|
if (!_internalValueSet && IsInitialized) |
||||
|
{ |
||||
|
SyncTextAndValueProperties(false, null, true); |
||||
|
} |
||||
|
|
||||
|
SetValidSpinDirection(); |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes static members of the <see cref="NumericUpDown"/> class.
|
||||
|
/// </summary>
|
||||
|
static NumericUpDown() |
||||
|
{ |
||||
|
CultureInfoProperty.Changed.Subscribe(OnCultureInfoChanged); |
||||
|
FormatStringProperty.Changed.Subscribe(FormatStringChanged); |
||||
|
IncrementProperty.Changed.Subscribe(IncrementChanged); |
||||
|
IsReadOnlyProperty.Changed.Subscribe(OnIsReadOnlyChanged); |
||||
|
MaximumProperty.Changed.Subscribe(OnMaximumChanged); |
||||
|
MinimumProperty.Changed.Subscribe(OnMinimumChanged); |
||||
|
TextProperty.Changed.Subscribe(OnTextChanged); |
||||
|
ValueProperty.Changed.Subscribe(OnValueChanged); |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
protected override void OnTemplateApplied(TemplateAppliedEventArgs e) |
||||
|
{ |
||||
|
if (TextBox != null) |
||||
|
{ |
||||
|
TextBox.PointerPressed -= TextBoxOnPointerPressed; |
||||
|
_textBoxTextChangedSubscription?.Dispose(); |
||||
|
} |
||||
|
TextBox = e.NameScope.Find<TextBox>("PART_TextBox"); |
||||
|
if (TextBox != null) |
||||
|
{ |
||||
|
TextBox.Text = Text; |
||||
|
TextBox.PointerPressed += TextBoxOnPointerPressed; |
||||
|
_textBoxTextChangedSubscription = TextBox.GetObservable(TextBox.TextProperty).Subscribe(txt => TextBoxOnTextChanged()); |
||||
|
} |
||||
|
|
||||
|
if (Spinner != null) |
||||
|
{ |
||||
|
Spinner.Spin -= OnSpinnerSpin; |
||||
|
} |
||||
|
|
||||
|
Spinner = e.NameScope.Find<Spinner>("PART_Spinner"); |
||||
|
|
||||
|
if (Spinner != null) |
||||
|
{ |
||||
|
Spinner.Spin += OnSpinnerSpin; |
||||
|
} |
||||
|
|
||||
|
SetValidSpinDirection(); |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
protected override void OnKeyDown(KeyEventArgs e) |
||||
|
{ |
||||
|
switch (e.Key) |
||||
|
{ |
||||
|
case Key.Enter: |
||||
|
var commitSuccess = CommitInput(); |
||||
|
e.Handled = !commitSuccess; |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Called when the <see cref="CultureInfo"/> property value changed.
|
||||
|
/// </summary>
|
||||
|
/// <param name="oldValue">The old value.</param>
|
||||
|
/// <param name="newValue">The new value.</param>
|
||||
|
protected virtual void OnCultureInfoChanged(CultureInfo oldValue, CultureInfo newValue) |
||||
|
{ |
||||
|
if (IsInitialized) |
||||
|
{ |
||||
|
SyncTextAndValueProperties(false, null); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Called when the <see cref="FormatString"/> property value changed.
|
||||
|
/// </summary>
|
||||
|
/// <param name="oldValue">The old value.</param>
|
||||
|
/// <param name="newValue">The new value.</param>
|
||||
|
protected virtual void OnFormatStringChanged(string oldValue, string newValue) |
||||
|
{ |
||||
|
if (IsInitialized) |
||||
|
{ |
||||
|
SyncTextAndValueProperties(false, null); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Called when the <see cref="Increment"/> property value changed.
|
||||
|
/// </summary>
|
||||
|
/// <param name="oldValue">The old value.</param>
|
||||
|
/// <param name="newValue">The new value.</param>
|
||||
|
protected virtual void OnIncrementChanged(double oldValue, double newValue) |
||||
|
{ |
||||
|
if (IsInitialized) |
||||
|
{ |
||||
|
SetValidSpinDirection(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Called when the <see cref="IsReadOnly"/> property value changed.
|
||||
|
/// </summary>
|
||||
|
/// <param name="oldValue">The old value.</param>
|
||||
|
/// <param name="newValue">The new value.</param>
|
||||
|
protected virtual void OnIsReadOnlyChanged(bool oldValue, bool newValue) |
||||
|
{ |
||||
|
SetValidSpinDirection(); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Called when the <see cref="Maximum"/> property value changed.
|
||||
|
/// </summary>
|
||||
|
/// <param name="oldValue">The old value.</param>
|
||||
|
/// <param name="newValue">The new value.</param>
|
||||
|
protected virtual void OnMaximumChanged(double oldValue, double newValue) |
||||
|
{ |
||||
|
if (IsInitialized) |
||||
|
{ |
||||
|
SetValidSpinDirection(); |
||||
|
} |
||||
|
if (ClipValueToMinMax) |
||||
|
{ |
||||
|
Value = MathUtilities.Clamp(Value, Minimum, Maximum); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Called when the <see cref="Minimum"/> property value changed.
|
||||
|
/// </summary>
|
||||
|
/// <param name="oldValue">The old value.</param>
|
||||
|
/// <param name="newValue">The new value.</param>
|
||||
|
protected virtual void OnMinimumChanged(double oldValue, double newValue) |
||||
|
{ |
||||
|
if (IsInitialized) |
||||
|
{ |
||||
|
SetValidSpinDirection(); |
||||
|
} |
||||
|
if (ClipValueToMinMax) |
||||
|
{ |
||||
|
Value = MathUtilities.Clamp(Value, Minimum, Maximum); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Called when the <see cref="Text"/> property value changed.
|
||||
|
/// </summary>
|
||||
|
/// <param name="oldValue">The old value.</param>
|
||||
|
/// <param name="newValue">The new value.</param>
|
||||
|
protected virtual void OnTextChanged(string oldValue, string newValue) |
||||
|
{ |
||||
|
if (IsInitialized) |
||||
|
{ |
||||
|
SyncTextAndValueProperties(true, Text); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Called when the <see cref="Value"/> property value changed.
|
||||
|
/// </summary>
|
||||
|
/// <param name="oldValue">The old value.</param>
|
||||
|
/// <param name="newValue">The new value.</param>
|
||||
|
protected virtual void OnValueChanged(double oldValue, double newValue) |
||||
|
{ |
||||
|
if (!_internalValueSet && IsInitialized) |
||||
|
{ |
||||
|
SyncTextAndValueProperties(false, null, true); |
||||
|
} |
||||
|
|
||||
|
SetValidSpinDirection(); |
||||
|
|
||||
|
RaiseValueChangedEvent(oldValue, newValue); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Called when the <see cref="Increment"/> property has to be coerced.
|
||||
|
/// </summary>
|
||||
|
/// <param name="baseValue">The value.</param>
|
||||
|
protected virtual double OnCoerceIncrement(double baseValue) |
||||
|
{ |
||||
|
return baseValue; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Called when the <see cref="Maximum"/> property has to be coerced.
|
||||
|
/// </summary>
|
||||
|
/// <param name="baseValue">The value.</param>
|
||||
|
protected virtual double OnCoerceMaximum(double baseValue) |
||||
|
{ |
||||
|
return Math.Max(baseValue, Minimum); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Called when the <see cref="Minimum"/> property has to be coerced.
|
||||
|
/// </summary>
|
||||
|
/// <param name="baseValue">The value.</param>
|
||||
|
protected virtual double OnCoerceMinimum(double baseValue) |
||||
|
{ |
||||
|
return Math.Min(baseValue, Maximum); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Called when the <see cref="Value"/> property has to be coerced.
|
||||
|
/// </summary>
|
||||
|
/// <param name="baseValue">The value.</param>
|
||||
|
protected virtual double OnCoerceValue(double baseValue) |
||||
|
{ |
||||
|
return baseValue; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Raises the OnSpin event when spinning is initiated by the end-user.
|
||||
|
/// </summary>
|
||||
|
/// <param name="e">The event args.</param>
|
||||
|
protected virtual void OnSpin(SpinEventArgs e) |
||||
|
{ |
||||
|
if (e == null) |
||||
|
{ |
||||
|
throw new ArgumentNullException("e"); |
||||
|
} |
||||
|
|
||||
|
var handler = Spinned; |
||||
|
handler?.Invoke(this, e); |
||||
|
|
||||
|
if (e.Direction == SpinDirection.Increase) |
||||
|
{ |
||||
|
DoIncrement(); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
DoDecrement(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Raises the <see cref="ValueChanged"/> event.
|
||||
|
/// </summary>
|
||||
|
/// <param name="oldValue">The old value.</param>
|
||||
|
/// <param name="newValue">The new value.</param>
|
||||
|
protected virtual void RaiseValueChangedEvent(double oldValue, double newValue) |
||||
|
{ |
||||
|
var e = new NumericUpDownValueChangedEventArgs(ValueChangedEvent, oldValue, newValue); |
||||
|
RaiseEvent(e); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts the formatted text to a value.
|
||||
|
/// </summary>
|
||||
|
private double ConvertTextToValue(string text) |
||||
|
{ |
||||
|
double result = 0; |
||||
|
|
||||
|
if (string.IsNullOrEmpty(text)) |
||||
|
{ |
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
// Since the conversion from Value to text using a FormartString may not be parsable,
|
||||
|
// we verify that the already existing text is not the exact same value.
|
||||
|
var currentValueText = ConvertValueToText(); |
||||
|
if (Equals(currentValueText, text)) |
||||
|
{ |
||||
|
return Value; |
||||
|
} |
||||
|
|
||||
|
result = ConvertTextToValueCore(currentValueText, text); |
||||
|
|
||||
|
if (ClipValueToMinMax) |
||||
|
{ |
||||
|
return MathUtilities.Clamp(result, Minimum, Maximum); |
||||
|
} |
||||
|
|
||||
|
ValidateMinMax(result); |
||||
|
|
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts the value to formatted text.
|
||||
|
/// </summary>
|
||||
|
/// <returns></returns>
|
||||
|
private string ConvertValueToText() |
||||
|
{ |
||||
|
//Manage FormatString of type "{}{0:N2} °" (in xaml) or "{0:N2} °" in code-behind.
|
||||
|
if (FormatString.Contains("{0")) |
||||
|
{ |
||||
|
return string.Format(CultureInfo, FormatString, Value); |
||||
|
} |
||||
|
|
||||
|
return Value.ToString(FormatString, CultureInfo); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Called by OnSpin when the spin direction is SpinDirection.Increase.
|
||||
|
/// </summary>
|
||||
|
private void OnIncrement() |
||||
|
{ |
||||
|
var result = Value + Increment; |
||||
|
Value = MathUtilities.Clamp(result, Minimum, Maximum); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Called by OnSpin when the spin direction is SpinDirection.Descrease.
|
||||
|
/// </summary>
|
||||
|
private void OnDecrement() |
||||
|
{ |
||||
|
var result = Value - Increment; |
||||
|
Value = MathUtilities.Clamp(result, Minimum, Maximum); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Sets the valid spin directions.
|
||||
|
/// </summary>
|
||||
|
private void SetValidSpinDirection() |
||||
|
{ |
||||
|
var validDirections = ValidSpinDirections.None; |
||||
|
|
||||
|
// Zero increment always prevents spin.
|
||||
|
if (Increment != 0 && !IsReadOnly) |
||||
|
{ |
||||
|
if (Value < Maximum) |
||||
|
{ |
||||
|
validDirections = validDirections | ValidSpinDirections.Increase; |
||||
|
} |
||||
|
|
||||
|
if (Value > Minimum) |
||||
|
{ |
||||
|
validDirections = validDirections | ValidSpinDirections.Decrease; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (Spinner != null) |
||||
|
{ |
||||
|
Spinner.ValidSpinDirection = validDirections; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Called when the <see cref="CultureInfo"/> property value changed.
|
||||
|
/// </summary>
|
||||
|
/// <param name="e">The event args.</param>
|
||||
|
private static void OnCultureInfoChanged(AvaloniaPropertyChangedEventArgs e) |
||||
|
{ |
||||
|
if (e.Sender is NumericUpDown upDown) |
||||
|
{ |
||||
|
var oldValue = (CultureInfo)e.OldValue; |
||||
|
var newValue = (CultureInfo)e.NewValue; |
||||
|
upDown.OnCultureInfoChanged(oldValue, newValue); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Called when the <see cref="Increment"/> property value changed.
|
||||
|
/// </summary>
|
||||
|
/// <param name="e">The event args.</param>
|
||||
|
private static void IncrementChanged(AvaloniaPropertyChangedEventArgs e) |
||||
|
{ |
||||
|
if (e.Sender is NumericUpDown upDown) |
||||
|
{ |
||||
|
var oldValue = (double)e.OldValue; |
||||
|
var newValue = (double)e.NewValue; |
||||
|
upDown.OnIncrementChanged(oldValue, newValue); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Called when the <see cref="FormatString"/> property value changed.
|
||||
|
/// </summary>
|
||||
|
/// <param name="e">The event args.</param>
|
||||
|
private static void FormatStringChanged(AvaloniaPropertyChangedEventArgs e) |
||||
|
{ |
||||
|
if (e.Sender is NumericUpDown upDown) |
||||
|
{ |
||||
|
var oldValue = (string)e.OldValue; |
||||
|
var newValue = (string)e.NewValue; |
||||
|
upDown.OnFormatStringChanged(oldValue, newValue); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Called when the <see cref="IsReadOnly"/> property value changed.
|
||||
|
/// </summary>
|
||||
|
/// <param name="e">The event args.</param>
|
||||
|
private static void OnIsReadOnlyChanged(AvaloniaPropertyChangedEventArgs e) |
||||
|
{ |
||||
|
if (e.Sender is NumericUpDown upDown) |
||||
|
{ |
||||
|
var oldValue = (bool)e.OldValue; |
||||
|
var newValue = (bool)e.NewValue; |
||||
|
upDown.OnIsReadOnlyChanged(oldValue, newValue); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Called when the <see cref="Maximum"/> property value changed.
|
||||
|
/// </summary>
|
||||
|
/// <param name="e">The event args.</param>
|
||||
|
private static void OnMaximumChanged(AvaloniaPropertyChangedEventArgs e) |
||||
|
{ |
||||
|
if (e.Sender is NumericUpDown upDown) |
||||
|
{ |
||||
|
var oldValue = (double)e.OldValue; |
||||
|
var newValue = (double)e.NewValue; |
||||
|
upDown.OnMaximumChanged(oldValue, newValue); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Called when the <see cref="Minimum"/> property value changed.
|
||||
|
/// </summary>
|
||||
|
/// <param name="e">The event args.</param>
|
||||
|
private static void OnMinimumChanged(AvaloniaPropertyChangedEventArgs e) |
||||
|
{ |
||||
|
if (e.Sender is NumericUpDown upDown) |
||||
|
{ |
||||
|
var oldValue = (double)e.OldValue; |
||||
|
var newValue = (double)e.NewValue; |
||||
|
upDown.OnMinimumChanged(oldValue, newValue); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Called when the <see cref="Text"/> property value changed.
|
||||
|
/// </summary>
|
||||
|
/// <param name="e">The event args.</param>
|
||||
|
private static void OnTextChanged(AvaloniaPropertyChangedEventArgs e) |
||||
|
{ |
||||
|
if (e.Sender is NumericUpDown upDown) |
||||
|
{ |
||||
|
var oldValue = (string)e.OldValue; |
||||
|
var newValue = (string)e.NewValue; |
||||
|
upDown.OnTextChanged(oldValue, newValue); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Called when the <see cref="Value"/> property value changed.
|
||||
|
/// </summary>
|
||||
|
/// <param name="e">The event args.</param>
|
||||
|
private static void OnValueChanged(AvaloniaPropertyChangedEventArgs e) |
||||
|
{ |
||||
|
if (e.Sender is NumericUpDown upDown) |
||||
|
{ |
||||
|
var oldValue = (double)e.OldValue; |
||||
|
var newValue = (double)e.NewValue; |
||||
|
upDown.OnValueChanged(oldValue, newValue); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void SetValueInternal(double value) |
||||
|
{ |
||||
|
_internalValueSet = true; |
||||
|
try |
||||
|
{ |
||||
|
Value = value; |
||||
|
} |
||||
|
finally |
||||
|
{ |
||||
|
_internalValueSet = false; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private static double OnCoerceMaximum(NumericUpDown upDown, double value) |
||||
|
{ |
||||
|
return upDown.OnCoerceMaximum(value); |
||||
|
} |
||||
|
|
||||
|
private static double OnCoerceMinimum(NumericUpDown upDown, double value) |
||||
|
{ |
||||
|
return upDown.OnCoerceMinimum(value); |
||||
|
} |
||||
|
|
||||
|
private static double OnCoerceIncrement(NumericUpDown upDown, double value) |
||||
|
{ |
||||
|
return upDown.OnCoerceIncrement(value); |
||||
|
} |
||||
|
|
||||
|
private void TextBoxOnTextChanged() |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
_isTextChangedFromUI = true; |
||||
|
if (TextBox != null) |
||||
|
{ |
||||
|
Text = TextBox.Text; |
||||
|
} |
||||
|
} |
||||
|
finally |
||||
|
{ |
||||
|
_isTextChangedFromUI = false; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void OnSpinnerSpin(object sender, SpinEventArgs e) |
||||
|
{ |
||||
|
if (AllowSpin && !IsReadOnly) |
||||
|
{ |
||||
|
var spin = !e.UsingMouseWheel; |
||||
|
spin |= ((TextBox != null) && TextBox.IsFocused); |
||||
|
|
||||
|
if (spin) |
||||
|
{ |
||||
|
e.Handled = true; |
||||
|
OnSpin(e); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void DoDecrement() |
||||
|
{ |
||||
|
if (Spinner == null || (Spinner.ValidSpinDirection & ValidSpinDirections.Decrease) == ValidSpinDirections.Decrease) |
||||
|
{ |
||||
|
OnDecrement(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void DoIncrement() |
||||
|
{ |
||||
|
if (Spinner == null || (Spinner.ValidSpinDirection & ValidSpinDirections.Increase) == ValidSpinDirections.Increase) |
||||
|
{ |
||||
|
OnIncrement(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public event EventHandler<SpinEventArgs> Spinned; |
||||
|
|
||||
|
private void TextBoxOnPointerPressed(object sender, PointerPressedEventArgs e) |
||||
|
{ |
||||
|
if (e.Device.Captured != Spinner) |
||||
|
{ |
||||
|
Dispatcher.UIThread.InvokeAsync(() => { e.Device.Capture(Spinner); }, DispatcherPriority.Input); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Defines the <see cref="ValueChanged"/> event.
|
||||
|
/// </summary>
|
||||
|
public static readonly RoutedEvent<NumericUpDownValueChangedEventArgs> ValueChangedEvent = |
||||
|
RoutedEvent.Register<NumericUpDown, NumericUpDownValueChangedEventArgs>(nameof(ValueChanged), RoutingStrategies.Bubble); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Raised when the <see cref="Value"/> changes.
|
||||
|
/// </summary>
|
||||
|
public event EventHandler<SpinEventArgs> ValueChanged |
||||
|
{ |
||||
|
add { AddHandler(ValueChangedEvent, value); } |
||||
|
remove { RemoveHandler(ValueChangedEvent, value); } |
||||
|
} |
||||
|
|
||||
|
private bool CommitInput() |
||||
|
{ |
||||
|
return SyncTextAndValueProperties(true, Text); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Synchronize <see cref="Text"/> and <see cref="Value"/> properties.
|
||||
|
/// </summary>
|
||||
|
/// <param name="updateValueFromText">If value should be updated from text.</param>
|
||||
|
/// <param name="text">The text.</param>
|
||||
|
private bool SyncTextAndValueProperties(bool updateValueFromText, string text) |
||||
|
{ |
||||
|
return SyncTextAndValueProperties(updateValueFromText, text, false); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Synchronize <see cref="Text"/> and <see cref="Value"/> properties.
|
||||
|
/// </summary>
|
||||
|
/// <param name="updateValueFromText">If value should be updated from text.</param>
|
||||
|
/// <param name="text">The text.</param>
|
||||
|
/// <param name="forceTextUpdate">Force text update.</param>
|
||||
|
private bool SyncTextAndValueProperties(bool updateValueFromText, string text, bool forceTextUpdate) |
||||
|
{ |
||||
|
if (_isSyncingTextAndValueProperties) |
||||
|
return true; |
||||
|
|
||||
|
_isSyncingTextAndValueProperties = true; |
||||
|
var parsedTextIsValid = true; |
||||
|
try |
||||
|
{ |
||||
|
if (updateValueFromText) |
||||
|
{ |
||||
|
if (!string.IsNullOrEmpty(text)) |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
var newValue = ConvertTextToValue(text); |
||||
|
if (!Equals(newValue, Value)) |
||||
|
{ |
||||
|
SetValueInternal(newValue); |
||||
|
} |
||||
|
} |
||||
|
catch |
||||
|
{ |
||||
|
parsedTextIsValid = false; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Do not touch the ongoing text input from user.
|
||||
|
if (!_isTextChangedFromUI) |
||||
|
{ |
||||
|
var keepEmpty = !forceTextUpdate && string.IsNullOrEmpty(Text); |
||||
|
if (!keepEmpty) |
||||
|
{ |
||||
|
var newText = ConvertValueToText(); |
||||
|
if (!Equals(Text, newText)) |
||||
|
{ |
||||
|
Text = newText; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Sync Text and textBox
|
||||
|
if (TextBox != null) |
||||
|
{ |
||||
|
TextBox.Text = Text; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (_isTextChangedFromUI && !parsedTextIsValid) |
||||
|
{ |
||||
|
// Text input was made from the user and the text
|
||||
|
// repesents an invalid value. Disable the spinner in this case.
|
||||
|
if (Spinner != null) |
||||
|
{ |
||||
|
Spinner.ValidSpinDirection = ValidSpinDirections.None; |
||||
|
} |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
SetValidSpinDirection(); |
||||
|
} |
||||
|
} |
||||
|
finally |
||||
|
{ |
||||
|
_isSyncingTextAndValueProperties = false; |
||||
|
} |
||||
|
return parsedTextIsValid; |
||||
|
} |
||||
|
|
||||
|
private double ConvertTextToValueCore(string currentValueText, string text) |
||||
|
{ |
||||
|
double result; |
||||
|
|
||||
|
if (IsPercent(FormatString)) |
||||
|
{ |
||||
|
result = decimal.ToDouble(ParsePercent(text, CultureInfo)); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
// Problem while converting new text
|
||||
|
if (!double.TryParse(text, ParsingNumberStyle, CultureInfo, out var outputValue)) |
||||
|
{ |
||||
|
var shouldThrow = true; |
||||
|
|
||||
|
// Check if CurrentValueText is also failing => it also contains special characters. ex : 90°
|
||||
|
if (!double.TryParse(currentValueText, ParsingNumberStyle, CultureInfo, out var _)) |
||||
|
{ |
||||
|
// extract non-digit characters
|
||||
|
var currentValueTextSpecialCharacters = currentValueText.Where(c => !char.IsDigit(c)); |
||||
|
var textSpecialCharacters = text.Where(c => !char.IsDigit(c)); |
||||
|
// same non-digit characters on currentValueText and new text => remove them on new Text to parse it again.
|
||||
|
if (currentValueTextSpecialCharacters.Except(textSpecialCharacters).ToList().Count == 0) |
||||
|
{ |
||||
|
foreach (var character in textSpecialCharacters) |
||||
|
{ |
||||
|
text = text.Replace(character.ToString(), string.Empty); |
||||
|
} |
||||
|
// if without the special characters, parsing is good, do not throw
|
||||
|
if (double.TryParse(text, ParsingNumberStyle, CultureInfo, out outputValue)) |
||||
|
{ |
||||
|
shouldThrow = false; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (shouldThrow) |
||||
|
{ |
||||
|
throw new InvalidDataException("Input string was not in a correct format."); |
||||
|
} |
||||
|
} |
||||
|
result = outputValue; |
||||
|
} |
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
private void ValidateMinMax(double value) |
||||
|
{ |
||||
|
if (value < Minimum) |
||||
|
{ |
||||
|
throw new ArgumentOutOfRangeException(nameof(Minimum), string.Format("Value must be greater than Minimum value of {0}", Minimum)); |
||||
|
} |
||||
|
else if (value > Maximum) |
||||
|
{ |
||||
|
throw new ArgumentOutOfRangeException(nameof(Maximum), string.Format("Value must be less than Maximum value of {0}", Maximum)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Parse percent format text
|
||||
|
/// </summary>
|
||||
|
/// <param name="text">Text to parse.</param>
|
||||
|
/// <param name="cultureInfo">The culture info.</param>
|
||||
|
private static decimal ParsePercent(string text, IFormatProvider cultureInfo) |
||||
|
{ |
||||
|
var info = NumberFormatInfo.GetInstance(cultureInfo); |
||||
|
text = text.Replace(info.PercentSymbol, null); |
||||
|
var result = decimal.Parse(text, NumberStyles.Any, info); |
||||
|
result = result / 100; |
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
private bool IsPercent(string stringToTest) |
||||
|
{ |
||||
|
var PIndex = stringToTest.IndexOf("P", StringComparison.Ordinal); |
||||
|
if (PIndex >= 0) |
||||
|
{ |
||||
|
//stringToTest contains a "P" between 2 "'", it's considered as text, not percent
|
||||
|
var isText = stringToTest.Substring(0, PIndex).Contains("'") |
||||
|
&& stringToTest.Substring(PIndex, FormatString.Length - PIndex).Contains("'"); |
||||
|
|
||||
|
return !isText; |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,16 @@ |
|||||
|
using Avalonia.Interactivity; |
||||
|
|
||||
|
namespace Avalonia.Controls |
||||
|
{ |
||||
|
public class NumericUpDownValueChangedEventArgs : RoutedEventArgs |
||||
|
{ |
||||
|
public NumericUpDownValueChangedEventArgs(RoutedEvent routedEvent, double oldValue, double newValue) : base(routedEvent) |
||||
|
{ |
||||
|
OldValue = oldValue; |
||||
|
NewValue = newValue; |
||||
|
} |
||||
|
|
||||
|
public double OldValue { get; } |
||||
|
public double NewValue { get; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,210 @@ |
|||||
|
using System; |
||||
|
using System.Linq; |
||||
|
using System.Reactive.Linq; |
||||
|
using System.Reactive.Subjects; |
||||
|
using System.Threading.Tasks; |
||||
|
using Avalonia.Controls; |
||||
|
using Avalonia.Input; |
||||
|
using Avalonia.Input.Platform; |
||||
|
using Avalonia.Input.Raw; |
||||
|
using Avalonia.Threading; |
||||
|
using Avalonia.VisualTree; |
||||
|
|
||||
|
namespace Avalonia.Platform |
||||
|
{ |
||||
|
class InProcessDragSource : IPlatformDragSource |
||||
|
{ |
||||
|
private const InputModifiers MOUSE_INPUTMODIFIERS = InputModifiers.LeftMouseButton|InputModifiers.MiddleMouseButton|InputModifiers.RightMouseButton; |
||||
|
private readonly IDragDropDevice _dragDrop; |
||||
|
private readonly IInputManager _inputManager; |
||||
|
private readonly Subject<DragDropEffects> _result = new Subject<DragDropEffects>(); |
||||
|
|
||||
|
private DragDropEffects _allowedEffects; |
||||
|
private IDataObject _draggedData; |
||||
|
private IInputElement _lastRoot; |
||||
|
private Point _lastPosition; |
||||
|
private StandardCursorType _lastCursorType; |
||||
|
private object _originalCursor; |
||||
|
private InputModifiers? _initialInputModifiers; |
||||
|
|
||||
|
public InProcessDragSource() |
||||
|
{ |
||||
|
_inputManager = AvaloniaLocator.Current.GetService<IInputManager>(); |
||||
|
_dragDrop = AvaloniaLocator.Current.GetService<IDragDropDevice>(); |
||||
|
} |
||||
|
|
||||
|
public async Task<DragDropEffects> DoDragDrop(IDataObject data, DragDropEffects allowedEffects) |
||||
|
{ |
||||
|
Dispatcher.UIThread.VerifyAccess(); |
||||
|
if (_draggedData == null) |
||||
|
{ |
||||
|
_draggedData = data; |
||||
|
_lastRoot = null; |
||||
|
_lastPosition = default(Point); |
||||
|
_allowedEffects = allowedEffects; |
||||
|
|
||||
|
using (_inputManager.PreProcess.OfType<RawMouseEventArgs>().Subscribe(ProcessMouseEvents)) |
||||
|
{ |
||||
|
using (_inputManager.PreProcess.OfType<RawKeyEventArgs>().Subscribe(ProcessKeyEvents)) |
||||
|
{ |
||||
|
var effect = await _result.FirstAsync(); |
||||
|
return effect; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
return DragDropEffects.None; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
private DragDropEffects RaiseEventAndUpdateCursor(RawDragEventType type, IInputElement root, Point pt, InputModifiers modifiers) |
||||
|
{ |
||||
|
_lastPosition = pt; |
||||
|
|
||||
|
RawDragEvent rawEvent = new RawDragEvent(_dragDrop, type, root, pt, _draggedData, _allowedEffects); |
||||
|
var tl = root.GetSelfAndVisualAncestors().OfType<TopLevel>().FirstOrDefault(); |
||||
|
tl.PlatformImpl.Input(rawEvent); |
||||
|
|
||||
|
var effect = GetPreferredEffect(rawEvent.Effects & _allowedEffects, modifiers); |
||||
|
UpdateCursor(root, effect); |
||||
|
return effect; |
||||
|
} |
||||
|
|
||||
|
private DragDropEffects GetPreferredEffect(DragDropEffects effect, InputModifiers modifiers) |
||||
|
{ |
||||
|
if (effect == DragDropEffects.Copy || effect == DragDropEffects.Move || effect == DragDropEffects.Link || effect == DragDropEffects.None) |
||||
|
return effect; // No need to check for the modifiers.
|
||||
|
if (effect.HasFlag(DragDropEffects.Link) && modifiers.HasFlag(InputModifiers.Alt)) |
||||
|
return DragDropEffects.Link; |
||||
|
if (effect.HasFlag(DragDropEffects.Copy) && modifiers.HasFlag(InputModifiers.Control)) |
||||
|
return DragDropEffects.Copy; |
||||
|
return DragDropEffects.Move; |
||||
|
} |
||||
|
|
||||
|
private StandardCursorType GetCursorForDropEffect(DragDropEffects effects) |
||||
|
{ |
||||
|
if (effects.HasFlag(DragDropEffects.Copy)) |
||||
|
return StandardCursorType.DragCopy; |
||||
|
if (effects.HasFlag(DragDropEffects.Move)) |
||||
|
return StandardCursorType.DragMove; |
||||
|
if (effects.HasFlag(DragDropEffects.Link)) |
||||
|
return StandardCursorType.DragLink; |
||||
|
return StandardCursorType.No; |
||||
|
} |
||||
|
|
||||
|
private void UpdateCursor(IInputElement root, DragDropEffects effect) |
||||
|
{ |
||||
|
if (_lastRoot != root) |
||||
|
{ |
||||
|
if (_lastRoot is InputElement ieLast) |
||||
|
{ |
||||
|
if (_originalCursor == AvaloniaProperty.UnsetValue) |
||||
|
ieLast.ClearValue(InputElement.CursorProperty); |
||||
|
else |
||||
|
ieLast.Cursor = _originalCursor as Cursor; |
||||
|
} |
||||
|
|
||||
|
if (root is InputElement ieNew) |
||||
|
{ |
||||
|
if (!ieNew.IsSet(InputElement.CursorProperty)) |
||||
|
_originalCursor = AvaloniaProperty.UnsetValue; |
||||
|
else |
||||
|
_originalCursor = root.Cursor; |
||||
|
} |
||||
|
else |
||||
|
_originalCursor = null; |
||||
|
|
||||
|
_lastCursorType = StandardCursorType.Arrow; |
||||
|
_lastRoot = root; |
||||
|
} |
||||
|
|
||||
|
if (root is InputElement ie) |
||||
|
{ |
||||
|
var ct = GetCursorForDropEffect(effect); |
||||
|
if (ct != _lastCursorType) |
||||
|
{ |
||||
|
_lastCursorType = ct; |
||||
|
ie.Cursor = new Cursor(ct); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void CancelDragging() |
||||
|
{ |
||||
|
if (_lastRoot != null) |
||||
|
RaiseEventAndUpdateCursor(RawDragEventType.DragLeave, _lastRoot, _lastPosition, InputModifiers.None); |
||||
|
UpdateCursor(null, DragDropEffects.None); |
||||
|
_result.OnNext(DragDropEffects.None); |
||||
|
} |
||||
|
|
||||
|
private void ProcessKeyEvents(RawKeyEventArgs e) |
||||
|
{ |
||||
|
if (e.Type == RawKeyEventType.KeyDown && e.Key == Key.Escape) |
||||
|
{ |
||||
|
if (_lastRoot != null) |
||||
|
RaiseEventAndUpdateCursor(RawDragEventType.DragLeave, _lastRoot, _lastPosition, e.Modifiers); |
||||
|
UpdateCursor(null, DragDropEffects.None); |
||||
|
_result.OnNext(DragDropEffects.None); |
||||
|
e.Handled = true; |
||||
|
} |
||||
|
else if (e.Key == Key.LeftCtrl || e.Key == Key.RightCtrl || e.Key == Key.LeftAlt || e.Key == Key.RightAlt) |
||||
|
RaiseEventAndUpdateCursor(RawDragEventType.DragOver, _lastRoot, _lastPosition, e.Modifiers); |
||||
|
} |
||||
|
|
||||
|
private void ProcessMouseEvents(RawMouseEventArgs e) |
||||
|
{ |
||||
|
if (!_initialInputModifiers.HasValue) |
||||
|
_initialInputModifiers = e.InputModifiers & MOUSE_INPUTMODIFIERS; |
||||
|
|
||||
|
|
||||
|
void CheckDraggingAccepted(InputModifiers changedMouseButton) |
||||
|
{ |
||||
|
if (_initialInputModifiers.Value.HasFlag(changedMouseButton)) |
||||
|
{ |
||||
|
var result = RaiseEventAndUpdateCursor(RawDragEventType.Drop, e.Root, e.Position, e.InputModifiers); |
||||
|
UpdateCursor(null, DragDropEffects.None); |
||||
|
_result.OnNext(result); |
||||
|
} |
||||
|
else |
||||
|
CancelDragging(); |
||||
|
e.Handled = true; |
||||
|
} |
||||
|
|
||||
|
switch (e.Type) |
||||
|
{ |
||||
|
case RawMouseEventType.LeftButtonDown: |
||||
|
case RawMouseEventType.RightButtonDown: |
||||
|
case RawMouseEventType.MiddleButtonDown: |
||||
|
case RawMouseEventType.NonClientLeftButtonDown: |
||||
|
CancelDragging(); |
||||
|
e.Handled = true; |
||||
|
return; |
||||
|
case RawMouseEventType.LeaveWindow: |
||||
|
RaiseEventAndUpdateCursor(RawDragEventType.DragLeave, e.Root, e.Position, e.InputModifiers); break; |
||||
|
case RawMouseEventType.LeftButtonUp: |
||||
|
CheckDraggingAccepted(InputModifiers.LeftMouseButton); break; |
||||
|
case RawMouseEventType.MiddleButtonUp: |
||||
|
CheckDraggingAccepted(InputModifiers.MiddleMouseButton); break; |
||||
|
case RawMouseEventType.RightButtonUp: |
||||
|
CheckDraggingAccepted(InputModifiers.RightMouseButton); break; |
||||
|
case RawMouseEventType.Move: |
||||
|
var mods = e.InputModifiers & MOUSE_INPUTMODIFIERS; |
||||
|
if (_initialInputModifiers.Value != mods) |
||||
|
{ |
||||
|
CancelDragging(); |
||||
|
e.Handled = true; |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
if (e.Root != _lastRoot) |
||||
|
{ |
||||
|
if (_lastRoot != null) |
||||
|
RaiseEventAndUpdateCursor(RawDragEventType.DragLeave, _lastRoot, _lastRoot.PointToClient(e.Root.PointToScreen(e.Position)), e.InputModifiers); |
||||
|
RaiseEventAndUpdateCursor(RawDragEventType.DragEnter, e.Root, e.Position, e.InputModifiers); |
||||
|
} |
||||
|
else |
||||
|
RaiseEventAndUpdateCursor(RawDragEventType.DragOver, e.Root, e.Position, e.InputModifiers); |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,174 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Text; |
||||
|
using Avalonia.Interactivity; |
||||
|
|
||||
|
namespace Avalonia.Controls |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Represents spin directions that are valid.
|
||||
|
/// </summary>
|
||||
|
[Flags] |
||||
|
public enum ValidSpinDirections |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Can not increase nor decrease.
|
||||
|
/// </summary>
|
||||
|
None = 0, |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Can increase.
|
||||
|
/// </summary>
|
||||
|
Increase = 1, |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Can decrease.
|
||||
|
/// </summary>
|
||||
|
Decrease = 2 |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Represents spin directions that could be initiated by the end-user.
|
||||
|
/// </summary>
|
||||
|
public enum SpinDirection |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Represents a spin initiated by the end-user in order to Increase a value.
|
||||
|
/// </summary>
|
||||
|
Increase = 0, |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Represents a spin initiated by the end-user in order to Decrease a value.
|
||||
|
/// </summary>
|
||||
|
Decrease = 1 |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Provides data for the Spinner.Spin event.
|
||||
|
/// </summary>
|
||||
|
public class SpinEventArgs : RoutedEventArgs |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Gets the SpinDirection for the spin that has been initiated by the end-user.
|
||||
|
/// </summary>
|
||||
|
public SpinDirection Direction { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Get or set whheter the spin event originated from a mouse wheel event.
|
||||
|
/// </summary>
|
||||
|
public bool UsingMouseWheel{ get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the SpinEventArgs class.
|
||||
|
/// </summary>
|
||||
|
/// <param name="direction">Spin direction.</param>
|
||||
|
public SpinEventArgs(SpinDirection direction) |
||||
|
{ |
||||
|
Direction = direction; |
||||
|
} |
||||
|
|
||||
|
public SpinEventArgs(RoutedEvent routedEvent, SpinDirection direction) |
||||
|
: base(routedEvent) |
||||
|
{ |
||||
|
Direction = direction; |
||||
|
} |
||||
|
|
||||
|
public SpinEventArgs(SpinDirection direction, bool usingMouseWheel) |
||||
|
{ |
||||
|
Direction = direction; |
||||
|
UsingMouseWheel = usingMouseWheel; |
||||
|
} |
||||
|
|
||||
|
public SpinEventArgs(RoutedEvent routedEvent, SpinDirection direction, bool usingMouseWheel) |
||||
|
: base(routedEvent) |
||||
|
{ |
||||
|
Direction = direction; |
||||
|
UsingMouseWheel = usingMouseWheel; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Base class for controls that represents controls that can spin.
|
||||
|
/// </summary>
|
||||
|
public abstract class Spinner : ContentControl |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Defines the <see cref="ValidSpinDirection"/> property.
|
||||
|
/// </summary>
|
||||
|
public static readonly StyledProperty<ValidSpinDirections> ValidSpinDirectionProperty = |
||||
|
AvaloniaProperty.Register<Spinner, ValidSpinDirections>(nameof(ValidSpinDirection), |
||||
|
ValidSpinDirections.Increase | ValidSpinDirections.Decrease); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Defines the <see cref="Spin"/> event.
|
||||
|
/// </summary>
|
||||
|
public static readonly RoutedEvent<SpinEventArgs> SpinEvent = |
||||
|
RoutedEvent.Register<Spinner, SpinEventArgs>(nameof(Spin), RoutingStrategies.Bubble); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes static members of the <see cref="Spinner"/> class.
|
||||
|
/// </summary>
|
||||
|
static Spinner() |
||||
|
{ |
||||
|
ValidSpinDirectionProperty.Changed.Subscribe(OnValidSpinDirectionPropertyChanged); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Occurs when spinning is initiated by the end-user.
|
||||
|
/// </summary>
|
||||
|
public event EventHandler<SpinEventArgs> Spin |
||||
|
{ |
||||
|
add { AddHandler(SpinEvent, value); } |
||||
|
remove { RemoveHandler(SpinEvent, value); } |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets or sets <see cref="ValidSpinDirections"/> allowed for this control.
|
||||
|
/// </summary>
|
||||
|
public ValidSpinDirections ValidSpinDirection |
||||
|
{ |
||||
|
get { return GetValue(ValidSpinDirectionProperty); } |
||||
|
set { SetValue(ValidSpinDirectionProperty, value); } |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Called when valid spin direction changed.
|
||||
|
/// </summary>
|
||||
|
/// <param name="oldValue">The old value.</param>
|
||||
|
/// <param name="newValue">The new value.</param>
|
||||
|
protected virtual void OnValidSpinDirectionChanged(ValidSpinDirections oldValue, ValidSpinDirections newValue) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Raises the OnSpin event when spinning is initiated by the end-user.
|
||||
|
/// </summary>
|
||||
|
/// <param name="e">Spin event args.</param>
|
||||
|
protected virtual void OnSpin(SpinEventArgs e) |
||||
|
{ |
||||
|
var valid = e.Direction == SpinDirection.Increase |
||||
|
? ValidSpinDirections.Increase |
||||
|
: ValidSpinDirections.Decrease; |
||||
|
|
||||
|
//Only raise the event if spin is allowed.
|
||||
|
if ((ValidSpinDirection & valid) == valid) |
||||
|
{ |
||||
|
RaiseEvent(e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Called when the <see cref="ValidSpinDirection"/> property value changed.
|
||||
|
/// </summary>
|
||||
|
/// <param name="e">The event args.</param>
|
||||
|
private static void OnValidSpinDirectionPropertyChanged(AvaloniaPropertyChangedEventArgs e) |
||||
|
{ |
||||
|
if (e.Sender is Spinner spinner) |
||||
|
{ |
||||
|
var oldValue = (ValidSpinDirections)e.OldValue; |
||||
|
var newValue = (ValidSpinDirections)e.NewValue; |
||||
|
spinner.OnValidSpinDirectionChanged(oldValue, newValue); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,279 @@ |
|||||
|
// Copyright (c) The Avalonia Project. All rights reserved.
|
||||
|
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
||||
|
|
||||
|
using System; |
||||
|
using Avalonia.Media; |
||||
|
|
||||
|
namespace Avalonia.Controls.Utils |
||||
|
{ |
||||
|
internal class BorderRenderHelper |
||||
|
{ |
||||
|
private bool _useComplexRendering; |
||||
|
private StreamGeometry _backgroundGeometryCache; |
||||
|
private StreamGeometry _borderGeometryCache; |
||||
|
|
||||
|
public void Update(Size finalSize, Thickness borderThickness, CornerRadius cornerRadius) |
||||
|
{ |
||||
|
if (borderThickness.IsUniform && cornerRadius.IsUniform) |
||||
|
{ |
||||
|
_backgroundGeometryCache = null; |
||||
|
_borderGeometryCache = null; |
||||
|
_useComplexRendering = false; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
_useComplexRendering = true; |
||||
|
|
||||
|
var boundRect = new Rect(finalSize); |
||||
|
var innerRect = boundRect.Deflate(borderThickness); |
||||
|
var innerCoordinates = new BorderCoordinates(cornerRadius, borderThickness, false); |
||||
|
|
||||
|
StreamGeometry backgroundGeometry = null; |
||||
|
|
||||
|
if (innerRect.Width != 0 && innerRect.Height != 0) |
||||
|
{ |
||||
|
backgroundGeometry = new StreamGeometry(); |
||||
|
|
||||
|
using (var ctx = backgroundGeometry.Open()) |
||||
|
{ |
||||
|
CreateGeometry(ctx, innerRect, innerCoordinates); |
||||
|
} |
||||
|
|
||||
|
_backgroundGeometryCache = backgroundGeometry; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
_backgroundGeometryCache = null; |
||||
|
} |
||||
|
|
||||
|
if (boundRect.Width != 0 && innerRect.Height != 0) |
||||
|
{ |
||||
|
var outerCoordinates = new BorderCoordinates(cornerRadius, borderThickness, true); |
||||
|
var borderGeometry = new StreamGeometry(); |
||||
|
|
||||
|
using (var ctx = borderGeometry.Open()) |
||||
|
{ |
||||
|
CreateGeometry(ctx, boundRect, outerCoordinates); |
||||
|
|
||||
|
if (backgroundGeometry != null) |
||||
|
{ |
||||
|
CreateGeometry(ctx, innerRect, innerCoordinates); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
_borderGeometryCache = borderGeometry; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
_borderGeometryCache = null; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public void Render(DrawingContext context, Size size, Thickness borders, CornerRadius radii, IBrush background, IBrush borderBrush) |
||||
|
{ |
||||
|
if (_useComplexRendering) |
||||
|
{ |
||||
|
var backgroundGeometry = _backgroundGeometryCache; |
||||
|
if (backgroundGeometry != null) |
||||
|
{ |
||||
|
context.DrawGeometry(background, null, backgroundGeometry); |
||||
|
} |
||||
|
|
||||
|
var borderGeometry = _borderGeometryCache; |
||||
|
if (borderGeometry != null) |
||||
|
{ |
||||
|
context.DrawGeometry(borderBrush, null, borderGeometry); |
||||
|
} |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
var borderThickness = borders.Left; |
||||
|
var cornerRadius = (float)radii.TopLeft; |
||||
|
var rect = new Rect(size); |
||||
|
|
||||
|
if (background != null) |
||||
|
{ |
||||
|
context.FillRectangle(background, rect.Deflate(borders), cornerRadius); |
||||
|
} |
||||
|
|
||||
|
if (borderBrush != null && borderThickness > 0) |
||||
|
{ |
||||
|
context.DrawRectangle(new Pen(borderBrush, borderThickness), rect.Deflate(borderThickness), cornerRadius); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private static void CreateGeometry(StreamGeometryContext context, Rect boundRect, BorderCoordinates borderCoordinates) |
||||
|
{ |
||||
|
var topLeft = new Point(borderCoordinates.LeftTop, 0); |
||||
|
var topRight = new Point(boundRect.Width - borderCoordinates.RightTop, 0); |
||||
|
var rightTop = new Point(boundRect.Width, borderCoordinates.TopRight); |
||||
|
var rightBottom = new Point(boundRect.Width, boundRect.Height - borderCoordinates.BottomRight); |
||||
|
var bottomRight = new Point(boundRect.Width - borderCoordinates.RightBottom, boundRect.Height); |
||||
|
var bottomLeft = new Point(borderCoordinates.LeftBottom, boundRect.Height); |
||||
|
var leftBottom = new Point(0, boundRect.Height - borderCoordinates.BottomLeft); |
||||
|
var leftTop = new Point(0, borderCoordinates.TopLeft); |
||||
|
|
||||
|
|
||||
|
if (topLeft.X > topRight.X) |
||||
|
{ |
||||
|
var scaledX = borderCoordinates.LeftTop / (borderCoordinates.LeftTop + borderCoordinates.RightTop) * boundRect.Width; |
||||
|
topLeft = new Point(scaledX, topLeft.Y); |
||||
|
topRight = new Point(scaledX, topRight.Y); |
||||
|
} |
||||
|
|
||||
|
if (rightTop.Y > rightBottom.Y) |
||||
|
{ |
||||
|
var scaledY = borderCoordinates.TopRight / (borderCoordinates.TopRight + borderCoordinates.BottomRight) * boundRect.Height; |
||||
|
rightTop = new Point(rightTop.X, scaledY); |
||||
|
rightBottom = new Point(rightBottom.X, scaledY); |
||||
|
} |
||||
|
|
||||
|
if (bottomRight.X < bottomLeft.X) |
||||
|
{ |
||||
|
var scaledX = borderCoordinates.LeftBottom / (borderCoordinates.LeftBottom + borderCoordinates.RightBottom) * boundRect.Width; |
||||
|
bottomRight = new Point(scaledX, bottomRight.Y); |
||||
|
bottomLeft = new Point(scaledX, bottomLeft.Y); |
||||
|
} |
||||
|
|
||||
|
if (leftBottom.Y < leftTop.Y) |
||||
|
{ |
||||
|
var scaledY = borderCoordinates.TopLeft / (borderCoordinates.TopLeft + borderCoordinates.BottomLeft) * boundRect.Height; |
||||
|
leftBottom = new Point(leftBottom.X, scaledY); |
||||
|
leftTop = new Point(leftTop.X, scaledY); |
||||
|
} |
||||
|
|
||||
|
var offset = new Vector(boundRect.TopLeft.X, boundRect.TopLeft.Y); |
||||
|
topLeft += offset; |
||||
|
topRight += offset; |
||||
|
rightTop += offset; |
||||
|
rightBottom += offset; |
||||
|
bottomRight += offset; |
||||
|
bottomLeft += offset; |
||||
|
leftBottom += offset; |
||||
|
leftTop += offset; |
||||
|
|
||||
|
context.BeginFigure(topLeft, true); |
||||
|
|
||||
|
//Top
|
||||
|
context.LineTo(topRight); |
||||
|
|
||||
|
//TopRight corner
|
||||
|
var radiusX = boundRect.TopRight.X - topRight.X; |
||||
|
var radiusY = rightTop.Y - boundRect.TopRight.Y; |
||||
|
if (radiusX != 0 || radiusY != 0) |
||||
|
{ |
||||
|
context.ArcTo(rightTop, new Size(radiusY, radiusY), 0, false, SweepDirection.Clockwise); |
||||
|
} |
||||
|
|
||||
|
//Right
|
||||
|
context.LineTo(rightBottom); |
||||
|
|
||||
|
//BottomRight corner
|
||||
|
radiusX = boundRect.BottomRight.X - bottomRight.X; |
||||
|
radiusY = boundRect.BottomRight.Y - rightBottom.Y; |
||||
|
if (radiusX != 0 || radiusY != 0) |
||||
|
{ |
||||
|
context.ArcTo(bottomRight, new Size(radiusX, radiusY), 0, false, SweepDirection.Clockwise); |
||||
|
} |
||||
|
|
||||
|
//Bottom
|
||||
|
context.LineTo(bottomLeft); |
||||
|
|
||||
|
//BottomLeft corner
|
||||
|
radiusX = bottomLeft.X - boundRect.BottomLeft.X; |
||||
|
radiusY = boundRect.BottomLeft.Y - leftBottom.Y; |
||||
|
if (radiusX != 0 || radiusY != 0) |
||||
|
{ |
||||
|
context.ArcTo(leftBottom, new Size(radiusX, radiusY), 0, false, SweepDirection.Clockwise); |
||||
|
} |
||||
|
|
||||
|
//Left
|
||||
|
context.LineTo(leftTop); |
||||
|
|
||||
|
//TopLeft corner
|
||||
|
radiusX = topLeft.X - boundRect.TopLeft.X; |
||||
|
radiusY = leftTop.Y - boundRect.TopLeft.Y; |
||||
|
|
||||
|
if (radiusX != 0 || radiusY != 0) |
||||
|
{ |
||||
|
context.ArcTo(topLeft, new Size(radiusX, radiusY), 0, false, SweepDirection.Clockwise); |
||||
|
} |
||||
|
|
||||
|
context.EndFigure(true); |
||||
|
} |
||||
|
|
||||
|
private struct BorderCoordinates |
||||
|
{ |
||||
|
internal BorderCoordinates(CornerRadius cornerRadius, Thickness borderThickness, bool isOuter) |
||||
|
{ |
||||
|
var left = 0.5 * borderThickness.Left; |
||||
|
var top = 0.5 * borderThickness.Top; |
||||
|
var right = 0.5 * borderThickness.Right; |
||||
|
var bottom = 0.5 * borderThickness.Bottom; |
||||
|
|
||||
|
if (isOuter) |
||||
|
{ |
||||
|
if (cornerRadius.TopLeft == 0) |
||||
|
{ |
||||
|
LeftTop = TopLeft = 0.0; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
LeftTop = cornerRadius.TopLeft + left; |
||||
|
TopLeft = cornerRadius.TopLeft + top; |
||||
|
} |
||||
|
if (cornerRadius.TopRight == 0) |
||||
|
{ |
||||
|
TopRight = RightTop = 0; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
TopRight = cornerRadius.TopRight + top; |
||||
|
RightTop = cornerRadius.TopRight + right; |
||||
|
} |
||||
|
if (cornerRadius.BottomRight == 0) |
||||
|
{ |
||||
|
RightBottom = BottomRight = 0; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
RightBottom = cornerRadius.BottomRight + right; |
||||
|
BottomRight = cornerRadius.BottomRight + bottom; |
||||
|
} |
||||
|
if (cornerRadius.BottomLeft == 0) |
||||
|
{ |
||||
|
BottomLeft = LeftBottom = 0; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
BottomLeft = cornerRadius.BottomLeft + bottom; |
||||
|
LeftBottom = cornerRadius.BottomLeft + left; |
||||
|
} |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
LeftTop = Math.Max(0, cornerRadius.TopLeft - left); |
||||
|
TopLeft = Math.Max(0, cornerRadius.TopLeft - top); |
||||
|
TopRight = Math.Max(0, cornerRadius.TopRight - top); |
||||
|
RightTop = Math.Max(0, cornerRadius.TopRight - right); |
||||
|
RightBottom = Math.Max(0, cornerRadius.BottomRight - right); |
||||
|
BottomRight = Math.Max(0, cornerRadius.BottomRight - bottom); |
||||
|
BottomLeft = Math.Max(0, cornerRadius.BottomLeft - bottom); |
||||
|
LeftBottom = Math.Max(0, cornerRadius.BottomLeft - left); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
internal readonly double LeftTop; |
||||
|
internal readonly double TopLeft; |
||||
|
internal readonly double TopRight; |
||||
|
internal readonly double RightTop; |
||||
|
internal readonly double RightBottom; |
||||
|
internal readonly double BottomRight; |
||||
|
internal readonly double BottomLeft; |
||||
|
internal readonly double LeftBottom; |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,700 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Diagnostics; |
||||
|
using System.Globalization; |
||||
|
using System.Linq; |
||||
|
using System.Runtime.CompilerServices; |
||||
|
using Avalonia.Layout; |
||||
|
using JetBrains.Annotations; |
||||
|
|
||||
|
namespace Avalonia.Controls.Utils |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Contains algorithms that can help to measure and arrange a Grid.
|
||||
|
/// </summary>
|
||||
|
internal class GridLayout |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Initialize a new <see cref="GridLayout"/> instance from the column definitions.
|
||||
|
/// The instance doesn't care about whether the definitions are rows or columns.
|
||||
|
/// It will not calculate the column or row differently.
|
||||
|
/// </summary>
|
||||
|
internal GridLayout([NotNull] ColumnDefinitions columns) |
||||
|
{ |
||||
|
if (columns == null) throw new ArgumentNullException(nameof(columns)); |
||||
|
_conventions = columns.Count == 0 |
||||
|
? new List<LengthConvention> { new LengthConvention() } |
||||
|
: columns.Select(x => new LengthConvention(x.Width, x.MinWidth, x.MaxWidth)).ToList(); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initialize a new <see cref="GridLayout"/> instance from the row definitions.
|
||||
|
/// The instance doesn't care about whether the definitions are rows or columns.
|
||||
|
/// It will not calculate the column or row differently.
|
||||
|
/// </summary>
|
||||
|
internal GridLayout([NotNull] RowDefinitions rows) |
||||
|
{ |
||||
|
if (rows == null) throw new ArgumentNullException(nameof(rows)); |
||||
|
_conventions = rows.Count == 0 |
||||
|
? new List<LengthConvention> { new LengthConvention() } |
||||
|
: rows.Select(x => new LengthConvention(x.Height, x.MinHeight, x.MaxHeight)).ToList(); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the layout tolerance. If any length offset is less than this value, we will treat them the same.
|
||||
|
/// </summary>
|
||||
|
private const double LayoutTolerance = 1.0 / 256.0; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets all the length conventions that come from column/row definitions.
|
||||
|
/// These conventions provide cell limitations, such as the expected pixel length, the min/max pixel length and the * count.
|
||||
|
/// </summary>
|
||||
|
[NotNull] |
||||
|
private readonly List<LengthConvention> _conventions; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets all the length conventions that come from the grid children.
|
||||
|
/// </summary>
|
||||
|
[NotNull] |
||||
|
private readonly List<AdditionalLengthConvention> _additionalConventions = |
||||
|
new List<AdditionalLengthConvention>(); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Appending these elements into the convention list helps lay them out according to their desired sizes.
|
||||
|
/// <para/>
|
||||
|
/// Some elements are not only in a single grid cell, they have one or more column/row spans,
|
||||
|
/// and these elements may affect the grid layout especially the measuring procedure.<para/>
|
||||
|
/// Append these elements into the convention list can help to layout them correctly through
|
||||
|
/// their desired size. Only a small subset of children need to be measured before layout starts
|
||||
|
/// and they will be called via the<paramref name="getDesiredLength"/> callback.
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="T">The grid children type.</typeparam>
|
||||
|
/// <param name="source">
|
||||
|
/// Contains the safe column/row index and its span.
|
||||
|
/// Notice that we will not verify whether the range is in the column/row count,
|
||||
|
/// so you should get the safe column/row info first.
|
||||
|
/// </param>
|
||||
|
/// <param name="getDesiredLength">
|
||||
|
/// This callback will be called if the <see cref="GridLayout"/> thinks that a child should be
|
||||
|
/// measured first. Usually, these are the children that have the * or Auto length.
|
||||
|
/// </param>
|
||||
|
internal void AppendMeasureConventions<T>([NotNull] IDictionary<T, (int index, int span)> source, |
||||
|
[NotNull] Func<T, double> getDesiredLength) |
||||
|
{ |
||||
|
if (source == null) throw new ArgumentNullException(nameof(source)); |
||||
|
if (getDesiredLength == null) throw new ArgumentNullException(nameof(getDesiredLength)); |
||||
|
|
||||
|
// M1/7. Find all the Auto and * length columns/rows. (M1/7 means the 1st procedure of measurement.)
|
||||
|
// Only these columns/rows' layout can be affected by the child desired size.
|
||||
|
//
|
||||
|
// Find all columns/rows that have Auto or * length. We'll measure the children in advance.
|
||||
|
// Only these kind of columns/rows will affect the Grid layout.
|
||||
|
// Please note:
|
||||
|
// - If the column / row has Auto length, the Grid.DesiredSize and the column width
|
||||
|
// will be affected by the child's desired size.
|
||||
|
// - If the column / row has* length, the Grid.DesiredSize will be affected by the
|
||||
|
// child's desired size but the column width not.
|
||||
|
|
||||
|
// +-----------------------------------------------------------+
|
||||
|
// | * | A | * | P | A | * | P | * | * |
|
||||
|
// +-----------------------------------------------------------+
|
||||
|
// _conventions: | min | max | | | min | | min max | max |
|
||||
|
// _additionalC: |<- desired ->| |< desired >|
|
||||
|
// _additionalC: |< desired >| |<- desired ->|
|
||||
|
|
||||
|
// 寻找所有行列范围中包含 Auto 和 * 的元素,使用全部可用尺寸提前测量。
|
||||
|
// 因为只有这部分元素的布局才会被 Grid 的子元素尺寸影响。
|
||||
|
// 请注意:
|
||||
|
// - Auto 长度的行列必定会受到子元素布局影响,会影响到行列的布局长度和 Grid 本身的 DesiredSize;
|
||||
|
// - 而对于 * 长度,只有 Grid.DesiredSize 会受到子元素布局影响,而行列长度不会受影响。
|
||||
|
|
||||
|
// Find all the Auto and * length columns/rows.
|
||||
|
var found = new Dictionary<T, (int index, int span)>(); |
||||
|
for (var i = 0; i < _conventions.Count; i++) |
||||
|
{ |
||||
|
var index = i; |
||||
|
var convention = _conventions[index]; |
||||
|
if (convention.Length.IsAuto || convention.Length.IsStar) |
||||
|
{ |
||||
|
foreach (var pair in source.Where(x => |
||||
|
x.Value.index <= index && index < x.Value.index + x.Value.span)) |
||||
|
{ |
||||
|
found[pair.Key] = pair.Value; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Append these layout into the additional convention list.
|
||||
|
foreach (var pair in found) |
||||
|
{ |
||||
|
var t = pair.Key; |
||||
|
var (index, span) = pair.Value; |
||||
|
var desiredLength = getDesiredLength(t); |
||||
|
if (Math.Abs(desiredLength) > LayoutTolerance) |
||||
|
{ |
||||
|
_additionalConventions.Add(new AdditionalLengthConvention(index, span, desiredLength)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Run measure procedure according to the <paramref name="containerLength"/> and gets the <see cref="MeasureResult"/>.
|
||||
|
/// </summary>
|
||||
|
/// <param name="containerLength">
|
||||
|
/// The container length. Usually, it is the constraint of the <see cref="Layoutable.MeasureOverride"/> method.
|
||||
|
/// </param>
|
||||
|
/// <returns>
|
||||
|
/// The measured result that containing the desired size and all the column/row lengths.
|
||||
|
/// </returns>
|
||||
|
[NotNull, Pure] |
||||
|
internal MeasureResult Measure(double containerLength) |
||||
|
{ |
||||
|
// Prepare all the variables that this method needs to use.
|
||||
|
var conventions = _conventions.Select(x => x.Clone()).ToList(); |
||||
|
var starCount = conventions.Where(x => x.Length.IsStar).Sum(x => x.Length.Value); |
||||
|
var aggregatedLength = 0.0; |
||||
|
double starUnitLength; |
||||
|
|
||||
|
// M2/7. Aggregate all the pixel lengths. Then we can get the remaining length by `containerLength - aggregatedLength`.
|
||||
|
// We mark the aggregated length as "fix" because we can completely determine their values. Same as below.
|
||||
|
//
|
||||
|
// +-----------------------------------------------------------+
|
||||
|
// | * | A | * | P | A | * | P | * | * |
|
||||
|
// +-----------------------------------------------------------+
|
||||
|
// |#fix#| |#fix#|
|
||||
|
//
|
||||
|
// 将全部的固定像素长度的行列长度累加。这样,containerLength - aggregatedLength 便能得到剩余长度。
|
||||
|
// 我们会将所有能够确定下长度的行列标记为 fix。下同。
|
||||
|
// 请注意:
|
||||
|
// - 我们并没有直接从 containerLength 一直减下去,而是使用 aggregatedLength 进行累加,是因为无穷大相减得到的是 NaN,不利于后续计算。
|
||||
|
|
||||
|
aggregatedLength += conventions.Where(x => x.Length.IsAbsolute).Sum(x => x.Length.Value); |
||||
|
|
||||
|
// M3/7. Fix all the * lengths that have reached the minimum.
|
||||
|
//
|
||||
|
// +-----------------------------------------------------------+
|
||||
|
// | * | A | * | P | A | * | P | * | * |
|
||||
|
// +-----------------------------------------------------------+
|
||||
|
// | min | max | | | min | | min max | max |
|
||||
|
// | fix | |#fix#| fix |
|
||||
|
|
||||
|
var shouldTestStarMin = true; |
||||
|
while (shouldTestStarMin) |
||||
|
{ |
||||
|
// Calculate the unit * length to estimate the length of each column/row that has * length.
|
||||
|
// Under this estimated length, check if there is a minimum value that has a length less than its constraint.
|
||||
|
// If there is such a *, then fix the size of this cell, and then loop it again until there is no * that can be constrained by the minimum value.
|
||||
|
//
|
||||
|
// 计算单位 * 的长度,以便预估出每一个 * 行列的长度。
|
||||
|
// 在此预估的长度下,从前往后寻找是否存在某个 * 长度已经小于其约束的最小值。
|
||||
|
// 如果发现存在这样的 *,那么将此单元格的尺寸固定下来(Fix),然后循环重来,直至再也没有能被最小值约束的 *。
|
||||
|
var @fixed = false; |
||||
|
starUnitLength = (containerLength - aggregatedLength) / starCount; |
||||
|
foreach (var convention in conventions.Where(x => x.Length.IsStar)) |
||||
|
{ |
||||
|
var (star, min) = (convention.Length.Value, convention.MinLength); |
||||
|
var starLength = star * starUnitLength; |
||||
|
if (starLength < min) |
||||
|
{ |
||||
|
convention.Fix(min); |
||||
|
starLength = min; |
||||
|
aggregatedLength += starLength; |
||||
|
starCount -= star; |
||||
|
@fixed = true; |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
shouldTestStarMin = @fixed; |
||||
|
} |
||||
|
|
||||
|
// M4/7. Determine the absolute pixel size of all columns/rows that have an Auto length.
|
||||
|
//
|
||||
|
// +-----------------------------------------------------------+
|
||||
|
// | * | A | * | P | A | * | P | * | * |
|
||||
|
// +-----------------------------------------------------------+
|
||||
|
// | min | max | | | min | | min max | max |
|
||||
|
// |#fix#| | fix |#fix#| fix | fix |
|
||||
|
|
||||
|
var shouldTestAuto = true; |
||||
|
while (shouldTestAuto) |
||||
|
{ |
||||
|
var @fixed = false; |
||||
|
starUnitLength = (containerLength - aggregatedLength) / starCount; |
||||
|
for (var i = 0; i < conventions.Count; i++) |
||||
|
{ |
||||
|
var convention = conventions[i]; |
||||
|
if (!convention.Length.IsAuto) |
||||
|
{ |
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
var more = ApplyAdditionalConventionsForAuto(conventions, i, starUnitLength); |
||||
|
convention.Fix(more); |
||||
|
aggregatedLength += more; |
||||
|
@fixed = true; |
||||
|
break; |
||||
|
} |
||||
|
|
||||
|
shouldTestAuto = @fixed; |
||||
|
} |
||||
|
|
||||
|
// M5/7. Expand the stars according to the additional conventions (usually the child desired length).
|
||||
|
// We can't fix this kind of length, so we just mark them as desired (des).
|
||||
|
//
|
||||
|
// +-----------------------------------------------------------+
|
||||
|
// | * | A | * | P | A | * | P | * | * |
|
||||
|
// +-----------------------------------------------------------+
|
||||
|
// | min | max | | | min | | min max | max |
|
||||
|
// |#des#| fix |#des#| fix | fix | fix | fix | #des# |#des#|
|
||||
|
|
||||
|
var desiredStarMin = AggregateAdditionalConventionsForStars(conventions); |
||||
|
aggregatedLength += desiredStarMin; |
||||
|
|
||||
|
// M6/7. Determine the desired length of the grid for current container length. Its value is stored in desiredLength.
|
||||
|
// Assume if the container has infinite length, the grid desired length is stored in greedyDesiredLength.
|
||||
|
//
|
||||
|
// +-----------------------------------------------------------+
|
||||
|
// | * | A | * | P | A | * | P | * | * |
|
||||
|
// +-----------------------------------------------------------+
|
||||
|
// | min | max | | | min | | min max | max |
|
||||
|
// |#des#| fix |#des#| fix | fix | fix | fix | #des# |#des#|
|
||||
|
// Note: This table will be stored as the intermediate result into the MeasureResult and it will be reused by Arrange procedure.
|
||||
|
//
|
||||
|
// desiredLength = Math.Max(0.0, des + fix + des + fix + fix + fix + fix + des + des)
|
||||
|
// greedyDesiredLength = des + fix + des + fix + fix + fix + fix + des + des
|
||||
|
|
||||
|
var desiredLength = containerLength - aggregatedLength >= 0.0 ? aggregatedLength : containerLength; |
||||
|
var greedyDesiredLength = aggregatedLength; |
||||
|
|
||||
|
// M7/7. Expand all the rest stars. These stars have no conventions or only have
|
||||
|
// max value they can be expanded from zero to constraint.
|
||||
|
//
|
||||
|
// +-----------------------------------------------------------+
|
||||
|
// | * | A | * | P | A | * | P | * | * |
|
||||
|
// +-----------------------------------------------------------+
|
||||
|
// | min | max | | | min | | min max | max |
|
||||
|
// |#fix#| fix |#fix#| fix | fix | fix | fix | #fix# |#fix#|
|
||||
|
// Note: This table will be stored as the final result into the MeasureResult.
|
||||
|
|
||||
|
var dynamicConvention = ExpandStars(conventions, containerLength); |
||||
|
Clip(dynamicConvention, containerLength); |
||||
|
|
||||
|
// Returns the measuring result.
|
||||
|
return new MeasureResult(containerLength, desiredLength, greedyDesiredLength, |
||||
|
conventions, dynamicConvention); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Run arrange procedure according to the <paramref name="measure"/> and gets the <see cref="ArrangeResult"/>.
|
||||
|
/// </summary>
|
||||
|
/// <param name="finalLength">
|
||||
|
/// The container length. Usually, it is the finalSize of the <see cref="Layoutable.ArrangeOverride"/> method.
|
||||
|
/// </param>
|
||||
|
/// <param name="measure">
|
||||
|
/// The result that the measuring procedure returns. If it is null, a new measure procedure will run.
|
||||
|
/// </param>
|
||||
|
/// <returns>
|
||||
|
/// The measured result that containing the desired size and all the column/row length.
|
||||
|
/// </returns>
|
||||
|
[NotNull, Pure] |
||||
|
public ArrangeResult Arrange(double finalLength, [CanBeNull] MeasureResult measure) |
||||
|
{ |
||||
|
measure = measure ?? Measure(finalLength); |
||||
|
|
||||
|
// If the arrange final length does not equal to the measure length, we should measure again.
|
||||
|
if (finalLength - measure.ContainerLength > LayoutTolerance) |
||||
|
{ |
||||
|
// If the final length is larger, we will rerun the whole measure.
|
||||
|
measure = Measure(finalLength); |
||||
|
} |
||||
|
else if (finalLength - measure.ContainerLength < -LayoutTolerance) |
||||
|
{ |
||||
|
// If the final length is smaller, we measure the M6/6 procedure only.
|
||||
|
var dynamicConvention = ExpandStars(measure.LeanLengthList, finalLength); |
||||
|
measure = new MeasureResult(finalLength, measure.DesiredLength, measure.GreedyDesiredLength, |
||||
|
measure.LeanLengthList, dynamicConvention); |
||||
|
} |
||||
|
|
||||
|
return new ArrangeResult(measure.LengthList); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Use the <see cref="_additionalConventions"/> to calculate the fixed length of the Auto column/row.
|
||||
|
/// </summary>
|
||||
|
/// <param name="conventions">The convention list that all the * with minimum length are fixed.</param>
|
||||
|
/// <param name="index">The column/row index that should be fixed.</param>
|
||||
|
/// <param name="starUnitLength">The unit * length for the current rest length.</param>
|
||||
|
/// <returns>The final length of the Auto length column/row.</returns>
|
||||
|
[Pure] |
||||
|
private double ApplyAdditionalConventionsForAuto(IReadOnlyList<LengthConvention> conventions, |
||||
|
int index, double starUnitLength) |
||||
|
{ |
||||
|
// 1. Calculate all the * length with starUnitLength.
|
||||
|
// 2. Exclude all the fixed length and all the * length.
|
||||
|
// 3. Compare the rest of the desired length and the convention.
|
||||
|
// +-----------------+
|
||||
|
// | * | A | * |
|
||||
|
// +-----------------+
|
||||
|
// | exl | | exl |
|
||||
|
// |< desired >|
|
||||
|
// |< desired >|
|
||||
|
|
||||
|
var more = 0.0; |
||||
|
foreach (var additional in _additionalConventions) |
||||
|
{ |
||||
|
// If the additional convention's last column/row contains the Auto column/row, try to determine the Auto column/row length.
|
||||
|
if (index == additional.Index + additional.Span - 1) |
||||
|
{ |
||||
|
var min = Enumerable.Range(additional.Index, additional.Span) |
||||
|
.Select(x => |
||||
|
{ |
||||
|
var c = conventions[x]; |
||||
|
if (c.Length.IsAbsolute) return c.Length.Value; |
||||
|
if (c.Length.IsStar) return c.Length.Value * starUnitLength; |
||||
|
return 0.0; |
||||
|
}).Sum(); |
||||
|
more = Math.Max(additional.Min - min, more); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return Math.Min(conventions[index].MaxLength, more); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Calculate the total desired length of all the * length.
|
||||
|
/// Bug Warning:
|
||||
|
/// - The behavior of this method is undefined! Different UI Frameworks have different behaviors.
|
||||
|
/// - We ignore all the span columns/rows and just take single cells into consideration.
|
||||
|
/// </summary>
|
||||
|
/// <param name="conventions">All the conventions that have almost been fixed except the rest *.</param>
|
||||
|
/// <returns>The total desired length of all the * length.</returns>
|
||||
|
[Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
private double AggregateAdditionalConventionsForStars( |
||||
|
IReadOnlyList<LengthConvention> conventions) |
||||
|
{ |
||||
|
// 1. Determine all one-span column's desired widths or row's desired heights.
|
||||
|
// 2. Order the multi-span conventions by its last index
|
||||
|
// (Notice that the sorted data is much smaller than the source.)
|
||||
|
// 3. Determine each multi-span last index by calculating the maximun desired size.
|
||||
|
|
||||
|
// Before we determine the behavior of this method, we just aggregate the one-span * columns.
|
||||
|
|
||||
|
var fixedLength = conventions.Where(x => x.Length.IsAbsolute).Sum(x => x.Length.Value); |
||||
|
|
||||
|
// Prepare a lengthList variable indicating the fixed length of each column/row.
|
||||
|
var lengthList = conventions.Select(x => x.Length.IsAbsolute ? x.Length.Value : 0.0).ToList(); |
||||
|
foreach (var group in _additionalConventions |
||||
|
.Where(x => x.Span == 1 && conventions[x.Index].Length.IsStar) |
||||
|
.ToLookup(x => x.Index)) |
||||
|
{ |
||||
|
lengthList[group.Key] = Math.Max(lengthList[group.Key], group.Max(x => x.Min)); |
||||
|
} |
||||
|
|
||||
|
// Now the lengthList is fixed by every one-span columns/rows.
|
||||
|
// Then we should determine the multi-span column's/row's length.
|
||||
|
foreach (var group in _additionalConventions |
||||
|
.Where(x => x.Span > 1) |
||||
|
.ToLookup(x => x.Index + x.Span - 1) |
||||
|
// Order the multi-span columns/rows by last index.
|
||||
|
.OrderBy(x => x.Key)) |
||||
|
{ |
||||
|
var length = group.Max(x => x.Min - Enumerable.Range(x.Index, x.Span - 1).Sum(r => lengthList[r])); |
||||
|
lengthList[group.Key] = Math.Max(lengthList[group.Key], length > 0 ? length : 0); |
||||
|
} |
||||
|
|
||||
|
return lengthList.Sum() - fixedLength; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// This method implements the last procedure (M7/7) of measure.
|
||||
|
/// It expands all the * length to the fixed length according to the <paramref name="constraint"/>.
|
||||
|
/// </summary>
|
||||
|
/// <param name="conventions">All the conventions that have almost been fixed except the remaining *.</param>
|
||||
|
/// <param name="constraint">The container length.</param>
|
||||
|
/// <returns>The final pixel length list.</returns>
|
||||
|
[Pure] |
||||
|
private static List<double> ExpandStars(IEnumerable<LengthConvention> conventions, double constraint) |
||||
|
{ |
||||
|
// Initial.
|
||||
|
var dynamicConvention = conventions.Select(x => x.Clone()).ToList(); |
||||
|
constraint -= dynamicConvention.Where(x => x.Length.IsAbsolute).Sum(x => x.Length.Value); |
||||
|
var starUnitLength = 0.0; |
||||
|
|
||||
|
// M6/6.
|
||||
|
if (constraint >= 0) |
||||
|
{ |
||||
|
var starCount = dynamicConvention.Where(x => x.Length.IsStar).Sum(x => x.Length.Value); |
||||
|
|
||||
|
var shouldTestStarMax = true; |
||||
|
while (shouldTestStarMax) |
||||
|
{ |
||||
|
var @fixed = false; |
||||
|
starUnitLength = constraint / starCount; |
||||
|
foreach (var convention in dynamicConvention.Where(x => |
||||
|
x.Length.IsStar && !double.IsPositiveInfinity(x.MaxLength))) |
||||
|
{ |
||||
|
var (star, max) = (convention.Length.Value, convention.MaxLength); |
||||
|
var starLength = star * starUnitLength; |
||||
|
if (starLength > max) |
||||
|
{ |
||||
|
convention.Fix(max); |
||||
|
starLength = max; |
||||
|
constraint -= starLength; |
||||
|
starCount -= star; |
||||
|
@fixed = true; |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
shouldTestStarMax = @fixed; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
Debug.Assert(dynamicConvention.All(x => !x.Length.IsAuto)); |
||||
|
|
||||
|
var starUnit = starUnitLength; |
||||
|
var result = dynamicConvention.Select(x => |
||||
|
{ |
||||
|
if (x.Length.IsStar) |
||||
|
{ |
||||
|
return double.IsInfinity(starUnit) ? double.PositiveInfinity : starUnit * x.Length.Value; |
||||
|
} |
||||
|
|
||||
|
return x.Length.Value; |
||||
|
}).ToList(); |
||||
|
|
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// If the container length is not infinity. It may be not enough to contain all the columns/rows.
|
||||
|
/// We should clip the columns/rows that have been out of the container bounds.
|
||||
|
/// Note: This method may change the items value of <paramref name="lengthList"/>.
|
||||
|
/// </summary>
|
||||
|
/// <param name="lengthList">A list of all the column widths and row heights with a fixed pixel length</param>
|
||||
|
/// <param name="constraint">the container length. It can be positive infinity.</param>
|
||||
|
private static void Clip([NotNull] IList<double> lengthList, double constraint) |
||||
|
{ |
||||
|
if (double.IsInfinity(constraint)) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
var measureLength = 0.0; |
||||
|
for (var i = 0; i < lengthList.Count; i++) |
||||
|
{ |
||||
|
var length = lengthList[i]; |
||||
|
if (constraint - measureLength > length) |
||||
|
{ |
||||
|
measureLength += length; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
lengthList[i] = constraint - measureLength; |
||||
|
measureLength = constraint; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Contains the convention of each column/row.
|
||||
|
/// This is mostly the same as <see cref="RowDefinition"/> or <see cref="ColumnDefinition"/>.
|
||||
|
/// We use this because we can treat the column and the row the same.
|
||||
|
/// </summary>
|
||||
|
[DebuggerDisplay("{" + nameof(DebuggerDisplay) + ",nq}")] |
||||
|
internal class LengthConvention : ICloneable |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Initialize a new instance of <see cref="LengthConvention"/>.
|
||||
|
/// </summary>
|
||||
|
public LengthConvention() |
||||
|
{ |
||||
|
Length = new GridLength(1.0, GridUnitType.Star); |
||||
|
MinLength = 0.0; |
||||
|
MaxLength = double.PositiveInfinity; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initialize a new instance of <see cref="LengthConvention"/>.
|
||||
|
/// </summary>
|
||||
|
public LengthConvention(GridLength length, double minLength, double maxLength) |
||||
|
{ |
||||
|
Length = length; |
||||
|
MinLength = minLength; |
||||
|
MaxLength = maxLength; |
||||
|
if (length.IsAbsolute) |
||||
|
{ |
||||
|
_isFixed = true; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the <see cref="GridLength"/> of a column or a row.
|
||||
|
/// </summary>
|
||||
|
internal GridLength Length { get; private set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the minimum convention for a column or a row.
|
||||
|
/// </summary>
|
||||
|
internal double MinLength { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the maximum convention for a column or a row.
|
||||
|
/// </summary>
|
||||
|
internal double MaxLength { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Fix the <see cref="LengthConvention"/>.
|
||||
|
/// If all columns/rows are fixed, we can get the size of all columns/rows in pixels.
|
||||
|
/// </summary>
|
||||
|
/// <param name="pixel">
|
||||
|
/// The pixel length that should be used to fix the convention.
|
||||
|
/// </param>
|
||||
|
/// <exception cref="InvalidOperationException">
|
||||
|
/// If the convention is pixel length, this exception will throw.
|
||||
|
/// </exception>
|
||||
|
public void Fix(double pixel) |
||||
|
{ |
||||
|
if (_isFixed) |
||||
|
{ |
||||
|
throw new InvalidOperationException("Cannot fix the length convention if it is fixed."); |
||||
|
} |
||||
|
|
||||
|
Length = new GridLength(pixel); |
||||
|
_isFixed = true; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets a value that indicates whether this convention is fixed.
|
||||
|
/// </summary>
|
||||
|
private bool _isFixed; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Helps the debugger to display the intermediate column/row calculation result.
|
||||
|
/// </summary>
|
||||
|
private string DebuggerDisplay => |
||||
|
$"{(_isFixed ? Length.Value.ToString(CultureInfo.InvariantCulture) : (Length.GridUnitType == GridUnitType.Auto ? "Auto" : $"{Length.Value}*"))}, ∈[{MinLength}, {MaxLength}]"; |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
object ICloneable.Clone() => Clone(); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Get a deep copy of this convention list.
|
||||
|
/// We need this because we want to store some intermediate states.
|
||||
|
/// </summary>
|
||||
|
internal LengthConvention Clone() => new LengthConvention(Length, MinLength, MaxLength); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Contains the convention that comes from the grid children.
|
||||
|
/// Some children span multiple columns or rows, so even a simple column/row can have multiple conventions.
|
||||
|
/// </summary>
|
||||
|
[DebuggerDisplay("{" + nameof(DebuggerDisplay) + ",nq}")] |
||||
|
internal struct AdditionalLengthConvention |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Initialize a new instance of <see cref="AdditionalLengthConvention"/>.
|
||||
|
/// </summary>
|
||||
|
public AdditionalLengthConvention(int index, int span, double min) |
||||
|
{ |
||||
|
Index = index; |
||||
|
Span = span; |
||||
|
Min = min; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the start index of this additional convention.
|
||||
|
/// </summary>
|
||||
|
public int Index { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the span of this additional convention.
|
||||
|
/// </summary>
|
||||
|
public int Span { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the minimum length of this additional convention.
|
||||
|
/// This value is usually provided by the child's desired length.
|
||||
|
/// </summary>
|
||||
|
public double Min { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Helps the debugger to display the intermediate column/row calculation result.
|
||||
|
/// </summary>
|
||||
|
private string DebuggerDisplay => |
||||
|
$"{{{string.Join(",", Enumerable.Range(Index, Span))}}}, ∈[{Min},∞)"; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Stores the result of the measuring procedure.
|
||||
|
/// This result can be used to measure children and assign the desired size.
|
||||
|
/// Passing this result to <see cref="Arrange"/> can reduce calculation.
|
||||
|
/// </summary>
|
||||
|
[DebuggerDisplay("{" + nameof(LengthList) + ",nq}")] |
||||
|
internal class MeasureResult |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Initialize a new instance of <see cref="MeasureResult"/>.
|
||||
|
/// </summary>
|
||||
|
internal MeasureResult(double containerLength, double desiredLength, double greedyDesiredLength, |
||||
|
IReadOnlyList<LengthConvention> leanConventions, IReadOnlyList<double> expandedConventions) |
||||
|
{ |
||||
|
ContainerLength = containerLength; |
||||
|
DesiredLength = desiredLength; |
||||
|
GreedyDesiredLength = greedyDesiredLength; |
||||
|
LeanLengthList = leanConventions; |
||||
|
LengthList = expandedConventions; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the container length for this result.
|
||||
|
/// This property will be used by <see cref="Arrange"/> to determine whether to measure again or not.
|
||||
|
/// </summary>
|
||||
|
public double ContainerLength { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the desired length of this result.
|
||||
|
/// Just return this value as the desired size in <see cref="Layoutable.MeasureOverride"/>.
|
||||
|
/// </summary>
|
||||
|
public double DesiredLength { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the desired length if the container has infinite length.
|
||||
|
/// </summary>
|
||||
|
public double GreedyDesiredLength { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Contains the column/row calculation intermediate result.
|
||||
|
/// This value is used by <see cref="Arrange"/> for reducing repeat calculation.
|
||||
|
/// </summary>
|
||||
|
public IReadOnlyList<LengthConvention> LeanLengthList { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the length list for each column/row.
|
||||
|
/// </summary>
|
||||
|
public IReadOnlyList<double> LengthList { get; } |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Stores the result of the measuring procedure.
|
||||
|
/// This result can be used to arrange children and assign the render size.
|
||||
|
/// </summary>
|
||||
|
[DebuggerDisplay("{" + nameof(LengthList) + ",nq}")] |
||||
|
internal class ArrangeResult |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Initialize a new instance of <see cref="ArrangeResult"/>.
|
||||
|
/// </summary>
|
||||
|
internal ArrangeResult(IReadOnlyList<double> lengthList) |
||||
|
{ |
||||
|
LengthList = lengthList; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the length list for each column/row.
|
||||
|
/// </summary>
|
||||
|
public IReadOnlyList<double> LengthList { get; } |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,64 @@ |
|||||
|
// (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; |
||||
|
using Avalonia.Interactivity; |
||||
|
using Avalonia.Input; |
||||
|
|
||||
|
namespace Avalonia.Controls.Utils |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Defines an item collection, selection members, and key handling for the
|
||||
|
/// selection adapter contained in the drop-down portion of an
|
||||
|
/// <see cref="T:Avalonia.Controls.AutoCompleteBox" /> control.
|
||||
|
/// </summary>
|
||||
|
public interface ISelectionAdapter |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Gets or sets the selected item.
|
||||
|
/// </summary>
|
||||
|
/// <value>The currently selected item.</value>
|
||||
|
object SelectedItem { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Occurs when the
|
||||
|
/// <see cref="P:Avalonia.Controls.Utils.ISelectionAdapter.SelectedItem" />
|
||||
|
/// property value changes.
|
||||
|
/// </summary>
|
||||
|
event EventHandler<SelectionChangedEventArgs> SelectionChanged; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets or sets a collection that is used to generate content for the
|
||||
|
/// selection adapter.
|
||||
|
/// </summary>
|
||||
|
/// <value>The collection that is used to generate content for the
|
||||
|
/// selection adapter.</value>
|
||||
|
IEnumerable Items { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Occurs when a selected item is not cancelled and is committed as the
|
||||
|
/// selected item.
|
||||
|
/// </summary>
|
||||
|
event EventHandler<RoutedEventArgs> Commit; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Occurs when a selection has been canceled.
|
||||
|
/// </summary>
|
||||
|
event EventHandler<RoutedEventArgs> Cancel; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Provides handling for the
|
||||
|
/// <see cref="E:Avalonia.Input.InputElement.KeyDown" /> event that occurs
|
||||
|
/// when a key is pressed while the drop-down portion of the
|
||||
|
/// <see cref="T:Avalonia.Controls.AutoCompleteBox" /> has focus.
|
||||
|
/// </summary>
|
||||
|
/// <param name="e">A <see cref="T:Avalonia.Input.KeyEventArgs" />
|
||||
|
/// that contains data about the
|
||||
|
/// <see cref="E:Avalonia.Input.InputElement.KeyDown" /> event.</param>
|
||||
|
void HandleKeyDown(KeyEventArgs e); |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,342 @@ |
|||||
|
// (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.Linq; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Text; |
||||
|
using Avalonia.Controls.Primitives; |
||||
|
using Avalonia.Interactivity; |
||||
|
using Avalonia.Input; |
||||
|
using Avalonia.LogicalTree; |
||||
|
using System.Collections; |
||||
|
using System.Diagnostics; |
||||
|
|
||||
|
namespace Avalonia.Controls.Utils |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Represents the selection adapter contained in the drop-down portion of
|
||||
|
/// an <see cref="T:Avalonia.Controls.AutoCompleteBox" /> control.
|
||||
|
/// </summary>
|
||||
|
public class SelectingItemsControlSelectionAdapter : ISelectionAdapter |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// The SelectingItemsControl instance.
|
||||
|
/// </summary>
|
||||
|
private SelectingItemsControl _selector; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets or sets a value indicating whether the selection change event
|
||||
|
/// should not be fired.
|
||||
|
/// </summary>
|
||||
|
private bool IgnoringSelectionChanged { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets or sets the underlying
|
||||
|
/// <see cref="T:Avalonia.Controls.Primitives.SelectingItemsControl" />
|
||||
|
/// control.
|
||||
|
/// </summary>
|
||||
|
/// <value>The underlying
|
||||
|
/// <see cref="T:Avalonia.Controls.Primitives.SelectingItemsControl" />
|
||||
|
/// control.</value>
|
||||
|
public SelectingItemsControl SelectorControl |
||||
|
{ |
||||
|
get { return _selector; } |
||||
|
|
||||
|
set |
||||
|
{ |
||||
|
if (_selector != null) |
||||
|
{ |
||||
|
_selector.SelectionChanged -= OnSelectionChanged; |
||||
|
_selector.PointerReleased -= OnSelectorPointerReleased; |
||||
|
} |
||||
|
|
||||
|
_selector = value; |
||||
|
|
||||
|
if (_selector != null) |
||||
|
{ |
||||
|
_selector.SelectionChanged += OnSelectionChanged; |
||||
|
_selector.PointerReleased += OnSelectorPointerReleased; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Occurs when the
|
||||
|
/// <see cref="P:Avalonia.Controls.Utils.SelectingItemsControlSelectionAdapter.SelectedItem" />
|
||||
|
/// property value changes.
|
||||
|
/// </summary>
|
||||
|
public event EventHandler<SelectionChangedEventArgs> SelectionChanged; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Occurs when an item is selected and is committed to the underlying
|
||||
|
/// <see cref="T:Avalonia.Controls.Primitives.SelectingItemsControl" />
|
||||
|
/// control.
|
||||
|
/// </summary>
|
||||
|
public event EventHandler<RoutedEventArgs> Commit; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Occurs when a selection is canceled before it is committed.
|
||||
|
/// </summary>
|
||||
|
public event EventHandler<RoutedEventArgs> Cancel; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the
|
||||
|
/// <see cref="T:Avalonia.Controls.Utils.SelectingItemsControlSelectionAdapter" />
|
||||
|
/// class.
|
||||
|
/// </summary>
|
||||
|
public SelectingItemsControlSelectionAdapter() |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the
|
||||
|
/// <see cref="T:Avalonia.Controls.Utils.SelectingItemsControlSelectionAdapterr" />
|
||||
|
/// class with the specified
|
||||
|
/// <see cref="T:Avalonia.Controls.Primitives.SelectingItemsControl" />
|
||||
|
/// control.
|
||||
|
/// </summary>
|
||||
|
/// <param name="selector">The
|
||||
|
/// <see cref="T:Avalonia.Controls.Primitives.SelectingItemsControl" /> control
|
||||
|
/// to wrap as a
|
||||
|
/// <see cref="T:Avalonia.Controls.Utils.SelectingItemsControlSelectionAdapter" />.</param>
|
||||
|
public SelectingItemsControlSelectionAdapter(SelectingItemsControl selector) |
||||
|
{ |
||||
|
SelectorControl = selector; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets or sets the selected item of the selection adapter.
|
||||
|
/// </summary>
|
||||
|
/// <value>The selected item of the underlying selection adapter.</value>
|
||||
|
public object SelectedItem |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return SelectorControl?.SelectedItem; |
||||
|
} |
||||
|
|
||||
|
set |
||||
|
{ |
||||
|
IgnoringSelectionChanged = true; |
||||
|
if (SelectorControl != null) |
||||
|
{ |
||||
|
SelectorControl.SelectedItem = value; |
||||
|
} |
||||
|
|
||||
|
// Attempt to reset the scroll viewer's position
|
||||
|
if (value == null) |
||||
|
{ |
||||
|
ResetScrollViewer(); |
||||
|
} |
||||
|
|
||||
|
IgnoringSelectionChanged = false; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets or sets a collection that is used to generate the content of
|
||||
|
/// the selection adapter.
|
||||
|
/// </summary>
|
||||
|
/// <value>The collection used to generate content for the selection
|
||||
|
/// adapter.</value>
|
||||
|
public IEnumerable Items |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return SelectorControl?.Items; |
||||
|
} |
||||
|
set |
||||
|
{ |
||||
|
if (SelectorControl != null) |
||||
|
{ |
||||
|
SelectorControl.Items = value; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// If the control contains a ScrollViewer, this will reset the viewer
|
||||
|
/// to be scrolled to the top.
|
||||
|
/// </summary>
|
||||
|
private void ResetScrollViewer() |
||||
|
{ |
||||
|
if (SelectorControl != null) |
||||
|
{ |
||||
|
ScrollViewer sv = SelectorControl.GetLogicalDescendants().OfType<ScrollViewer>().FirstOrDefault(); |
||||
|
if (sv != null) |
||||
|
{ |
||||
|
sv.Offset = new Vector(0, 0); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Handles the mouse left button up event on the selector control.
|
||||
|
/// </summary>
|
||||
|
/// <param name="sender">The source object.</param>
|
||||
|
/// <param name="e">The event data.</param>
|
||||
|
private void OnSelectorPointerReleased(object sender, PointerReleasedEventArgs e) |
||||
|
{ |
||||
|
if (e.MouseButton == MouseButton.Left) |
||||
|
{ |
||||
|
OnCommit(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Handles the SelectionChanged event on the SelectingItemsControl control.
|
||||
|
/// </summary>
|
||||
|
/// <param name="sender">The source object.</param>
|
||||
|
/// <param name="e">The selection changed event data.</param>
|
||||
|
private void OnSelectionChanged(object sender, SelectionChangedEventArgs e) |
||||
|
{ |
||||
|
if (IgnoringSelectionChanged) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
SelectionChanged?.Invoke(sender, e); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Increments the
|
||||
|
/// <see cref="P:Avalonia.Controls.Primitives.SelectingItemsControl.SelectedIndex" />
|
||||
|
/// property of the underlying
|
||||
|
/// <see cref="T:Avalonia.Controls.Primitives.SelectingItemsControl" />
|
||||
|
/// control.
|
||||
|
/// </summary>
|
||||
|
protected void SelectedIndexIncrement() |
||||
|
{ |
||||
|
if (SelectorControl != null) |
||||
|
{ |
||||
|
SelectorControl.SelectedIndex = SelectorControl.SelectedIndex + 1 >= SelectorControl.ItemCount ? -1 : SelectorControl.SelectedIndex + 1; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Decrements the
|
||||
|
/// <see cref="P:Avalonia.Controls.Primitives.SelectingItemsControl.SelectedIndex" />
|
||||
|
/// property of the underlying
|
||||
|
/// <see cref="T:Avalonia.Controls.Primitives.SelectingItemsControl" />
|
||||
|
/// control.
|
||||
|
/// </summary>
|
||||
|
protected void SelectedIndexDecrement() |
||||
|
{ |
||||
|
if (SelectorControl != null) |
||||
|
{ |
||||
|
int index = SelectorControl.SelectedIndex; |
||||
|
if (index >= 0) |
||||
|
{ |
||||
|
SelectorControl.SelectedIndex--; |
||||
|
} |
||||
|
else if (index == -1) |
||||
|
{ |
||||
|
SelectorControl.SelectedIndex = SelectorControl.ItemCount - 1; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Provides handling for the
|
||||
|
/// <see cref="E:Avalonia.Input.InputElement.KeyDown" /> event that occurs
|
||||
|
/// when a key is pressed while the drop-down portion of the
|
||||
|
/// <see cref="T:Avalonia.Controls.AutoCompleteBox" /> has focus.
|
||||
|
/// </summary>
|
||||
|
/// <param name="e">A <see cref="T:Avalonia.Input.KeyEventArgs" />
|
||||
|
/// that contains data about the
|
||||
|
/// <see cref="E:Avalonia.Input.InputElement.KeyDown" /> event.</param>
|
||||
|
public void HandleKeyDown(KeyEventArgs e) |
||||
|
{ |
||||
|
switch (e.Key) |
||||
|
{ |
||||
|
case Key.Enter: |
||||
|
OnCommit(); |
||||
|
e.Handled = true; |
||||
|
break; |
||||
|
|
||||
|
case Key.Up: |
||||
|
SelectedIndexDecrement(); |
||||
|
e.Handled = true; |
||||
|
break; |
||||
|
|
||||
|
case Key.Down: |
||||
|
if ((e.Modifiers & InputModifiers.Alt) == InputModifiers.None) |
||||
|
{ |
||||
|
SelectedIndexIncrement(); |
||||
|
e.Handled = true; |
||||
|
} |
||||
|
break; |
||||
|
|
||||
|
case Key.Escape: |
||||
|
OnCancel(); |
||||
|
e.Handled = true; |
||||
|
break; |
||||
|
|
||||
|
default: |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Raises the
|
||||
|
/// <see cref="E:Avalonia.Controls.Utils.SelectingItemsControlSelectionAdapter.Commit" />
|
||||
|
/// event.
|
||||
|
/// </summary>
|
||||
|
protected virtual void OnCommit() |
||||
|
{ |
||||
|
OnCommit(this, new RoutedEventArgs()); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Fires the Commit event.
|
||||
|
/// </summary>
|
||||
|
/// <param name="sender">The source object.</param>
|
||||
|
/// <param name="e">The event data.</param>
|
||||
|
private void OnCommit(object sender, RoutedEventArgs e) |
||||
|
{ |
||||
|
Commit?.Invoke(sender, e); |
||||
|
|
||||
|
AfterAdapterAction(); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Raises the
|
||||
|
/// <see cref="E:Avalonia.Controls.Utils.SelectingItemsControlSelectionAdapter.Cancel" />
|
||||
|
/// event.
|
||||
|
/// </summary>
|
||||
|
protected virtual void OnCancel() |
||||
|
{ |
||||
|
OnCancel(this, new RoutedEventArgs()); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Fires the Cancel event.
|
||||
|
/// </summary>
|
||||
|
/// <param name="sender">The source object.</param>
|
||||
|
/// <param name="e">The event data.</param>
|
||||
|
private void OnCancel(object sender, RoutedEventArgs e) |
||||
|
{ |
||||
|
Cancel?.Invoke(sender, e); |
||||
|
|
||||
|
AfterAdapterAction(); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Change the selection after the actions are complete.
|
||||
|
/// </summary>
|
||||
|
private void AfterAdapterAction() |
||||
|
{ |
||||
|
IgnoringSelectionChanged = true; |
||||
|
if (SelectorControl != null) |
||||
|
{ |
||||
|
SelectorControl.SelectedItem = null; |
||||
|
SelectorControl.SelectedIndex = -1; |
||||
|
} |
||||
|
IgnoringSelectionChanged = false; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,23 @@ |
|||||
|
namespace Avalonia.Controls |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Determines the startup location of the window.
|
||||
|
/// </summary>
|
||||
|
public enum WindowStartupLocation |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// The startup location is defined by the Position property.
|
||||
|
/// </summary>
|
||||
|
Manual, |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The startup location is the center of the screen.
|
||||
|
/// </summary>
|
||||
|
CenterScreen, |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The startup location is the center of the owner window. If the owner window is not specified, the startup location will be <see cref="Manual"/>.
|
||||
|
/// </summary>
|
||||
|
CenterOwner |
||||
|
} |
||||
|
} |
||||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue