commit
491ac53e79
59 changed files with 33637 additions and 0 deletions
@ -0,0 +1,2 @@ |
|||
# Auto detect text files and perform LF normalization |
|||
* text=auto |
|||
@ -0,0 +1,330 @@ |
|||
## Ignore Visual Studio temporary files, build results, and |
|||
## files generated by popular Visual Studio add-ons. |
|||
## |
|||
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore |
|||
|
|||
# User-specific files |
|||
*.suo |
|||
*.user |
|||
*.userosscache |
|||
*.sln.docstates |
|||
|
|||
# User-specific files (MonoDevelop/Xamarin Studio) |
|||
*.userprefs |
|||
|
|||
# Build results |
|||
[Dd]ebug/ |
|||
[Dd]ebugPublic/ |
|||
[Rr]elease/ |
|||
[Rr]eleases/ |
|||
x64/ |
|||
x86/ |
|||
bld/ |
|||
[Bb]in/ |
|||
[Oo]bj/ |
|||
[Ll]og/ |
|||
|
|||
# Visual Studio 2015/2017 cache/options directory |
|||
.vs/ |
|||
# Uncomment if you have tasks that create the project's static files in wwwroot |
|||
#wwwroot/ |
|||
|
|||
# Visual Studio 2017 auto generated files |
|||
Generated\ Files/ |
|||
|
|||
# MSTest test Results |
|||
[Tt]est[Rr]esult*/ |
|||
[Bb]uild[Ll]og.* |
|||
|
|||
# NUNIT |
|||
*.VisualState.xml |
|||
TestResult.xml |
|||
|
|||
# Build Results of an ATL Project |
|||
[Dd]ebugPS/ |
|||
[Rr]eleasePS/ |
|||
dlldata.c |
|||
|
|||
# Benchmark Results |
|||
BenchmarkDotNet.Artifacts/ |
|||
|
|||
# .NET Core |
|||
project.lock.json |
|||
project.fragment.lock.json |
|||
artifacts/ |
|||
**/Properties/launchSettings.json |
|||
|
|||
# StyleCop |
|||
StyleCopReport.xml |
|||
|
|||
# Files built by Visual Studio |
|||
*_i.c |
|||
*_p.c |
|||
*_i.h |
|||
*.ilk |
|||
*.meta |
|||
*.obj |
|||
*.iobj |
|||
*.pch |
|||
*.pdb |
|||
*.ipdb |
|||
*.pgc |
|||
*.pgd |
|||
*.rsp |
|||
*.sbr |
|||
*.tlb |
|||
*.tli |
|||
*.tlh |
|||
*.tmp |
|||
*.tmp_proj |
|||
*.log |
|||
*.vspscc |
|||
*.vssscc |
|||
.builds |
|||
*.pidb |
|||
*.svclog |
|||
*.scc |
|||
|
|||
# Chutzpah Test files |
|||
_Chutzpah* |
|||
|
|||
# Visual C++ cache files |
|||
ipch/ |
|||
*.aps |
|||
*.ncb |
|||
*.opendb |
|||
*.opensdf |
|||
*.sdf |
|||
*.cachefile |
|||
*.VC.db |
|||
*.VC.VC.opendb |
|||
|
|||
# Visual Studio profiler |
|||
*.psess |
|||
*.vsp |
|||
*.vspx |
|||
*.sap |
|||
|
|||
# Visual Studio Trace Files |
|||
*.e2e |
|||
|
|||
# TFS 2012 Local Workspace |
|||
$tf/ |
|||
|
|||
# Guidance Automation Toolkit |
|||
*.gpState |
|||
|
|||
# ReSharper is a .NET coding add-in |
|||
_ReSharper*/ |
|||
*.[Rr]e[Ss]harper |
|||
*.DotSettings.user |
|||
|
|||
# JustCode is a .NET coding add-in |
|||
.JustCode |
|||
|
|||
# TeamCity is a build add-in |
|||
_TeamCity* |
|||
|
|||
# DotCover is a Code Coverage Tool |
|||
*.dotCover |
|||
|
|||
# AxoCover is a Code Coverage Tool |
|||
.axoCover/* |
|||
!.axoCover/settings.json |
|||
|
|||
# Visual Studio code coverage results |
|||
*.coverage |
|||
*.coveragexml |
|||
|
|||
# NCrunch |
|||
_NCrunch_* |
|||
.*crunch*.local.xml |
|||
nCrunchTemp_* |
|||
|
|||
# MightyMoose |
|||
*.mm.* |
|||
AutoTest.Net/ |
|||
|
|||
# Web workbench (sass) |
|||
.sass-cache/ |
|||
|
|||
# Installshield output folder |
|||
[Ee]xpress/ |
|||
|
|||
# DocProject is a documentation generator add-in |
|||
DocProject/buildhelp/ |
|||
DocProject/Help/*.HxT |
|||
DocProject/Help/*.HxC |
|||
DocProject/Help/*.hhc |
|||
DocProject/Help/*.hhk |
|||
DocProject/Help/*.hhp |
|||
DocProject/Help/Html2 |
|||
DocProject/Help/html |
|||
|
|||
# Click-Once directory |
|||
publish/ |
|||
|
|||
# Publish Web Output |
|||
*.[Pp]ublish.xml |
|||
*.azurePubxml |
|||
# Note: Comment the next line if you want to checkin your web deploy settings, |
|||
# but database connection strings (with potential passwords) will be unencrypted |
|||
*.pubxml |
|||
*.publishproj |
|||
|
|||
# Microsoft Azure Web App publish settings. Comment the next line if you want to |
|||
# checkin your Azure Web App publish settings, but sensitive information contained |
|||
# in these scripts will be unencrypted |
|||
PublishScripts/ |
|||
|
|||
# NuGet Packages |
|||
*.nupkg |
|||
# The packages folder can be ignored because of Package Restore |
|||
**/[Pp]ackages/* |
|||
# except build/, which is used as an MSBuild target. |
|||
!**/[Pp]ackages/build/ |
|||
# Uncomment if necessary however generally it will be regenerated when needed |
|||
#!**/[Pp]ackages/repositories.config |
|||
# NuGet v3's project.json files produces more ignorable files |
|||
*.nuget.props |
|||
*.nuget.targets |
|||
|
|||
# Microsoft Azure Build Output |
|||
csx/ |
|||
*.build.csdef |
|||
|
|||
# Microsoft Azure Emulator |
|||
ecf/ |
|||
rcf/ |
|||
|
|||
# Windows Store app package directories and files |
|||
AppPackages/ |
|||
BundleArtifacts/ |
|||
Package.StoreAssociation.xml |
|||
_pkginfo.txt |
|||
*.appx |
|||
|
|||
# Visual Studio cache files |
|||
# files ending in .cache can be ignored |
|||
*.[Cc]ache |
|||
# but keep track of directories ending in .cache |
|||
!*.[Cc]ache/ |
|||
|
|||
# Others |
|||
ClientBin/ |
|||
~$* |
|||
*~ |
|||
*.dbmdl |
|||
*.dbproj.schemaview |
|||
*.jfm |
|||
*.pfx |
|||
*.publishsettings |
|||
orleans.codegen.cs |
|||
|
|||
# Including strong name files can present a security risk |
|||
# (https://github.com/github/gitignore/pull/2483#issue-259490424) |
|||
#*.snk |
|||
|
|||
# Since there are multiple workflows, uncomment next line to ignore bower_components |
|||
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) |
|||
#bower_components/ |
|||
|
|||
# RIA/Silverlight projects |
|||
Generated_Code/ |
|||
|
|||
# Backup & report files from converting an old project file |
|||
# to a newer Visual Studio version. Backup files are not needed, |
|||
# because we have git ;-) |
|||
_UpgradeReport_Files/ |
|||
Backup*/ |
|||
UpgradeLog*.XML |
|||
UpgradeLog*.htm |
|||
ServiceFabricBackup/ |
|||
*.rptproj.bak |
|||
|
|||
# SQL Server files |
|||
*.mdf |
|||
*.ldf |
|||
*.ndf |
|||
|
|||
# Business Intelligence projects |
|||
*.rdl.data |
|||
*.bim.layout |
|||
*.bim_*.settings |
|||
*.rptproj.rsuser |
|||
|
|||
# Microsoft Fakes |
|||
FakesAssemblies/ |
|||
|
|||
# GhostDoc plugin setting file |
|||
*.GhostDoc.xml |
|||
|
|||
# Node.js Tools for Visual Studio |
|||
.ntvs_analysis.dat |
|||
node_modules/ |
|||
|
|||
# Visual Studio 6 build log |
|||
*.plg |
|||
|
|||
# Visual Studio 6 workspace options file |
|||
*.opt |
|||
|
|||
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) |
|||
*.vbw |
|||
|
|||
# Visual Studio LightSwitch build output |
|||
**/*.HTMLClient/GeneratedArtifacts |
|||
**/*.DesktopClient/GeneratedArtifacts |
|||
**/*.DesktopClient/ModelManifest.xml |
|||
**/*.Server/GeneratedArtifacts |
|||
**/*.Server/ModelManifest.xml |
|||
_Pvt_Extensions |
|||
|
|||
# Paket dependency manager |
|||
.paket/paket.exe |
|||
paket-files/ |
|||
|
|||
# FAKE - F# Make |
|||
.fake/ |
|||
|
|||
# JetBrains Rider |
|||
.idea/ |
|||
*.sln.iml |
|||
|
|||
# CodeRush |
|||
.cr/ |
|||
|
|||
# Python Tools for Visual Studio (PTVS) |
|||
__pycache__/ |
|||
*.pyc |
|||
|
|||
# Cake - Uncomment if you are using it |
|||
# tools/** |
|||
# !tools/packages.config |
|||
|
|||
# Tabs Studio |
|||
*.tss |
|||
|
|||
# Telerik's JustMock configuration file |
|||
*.jmconfig |
|||
|
|||
# BizTalk build output |
|||
*.btp.cs |
|||
*.btm.cs |
|||
*.odx.cs |
|||
*.xsd.cs |
|||
|
|||
# OpenCover UI analysis results |
|||
OpenCover/ |
|||
|
|||
# Azure Stream Analytics local run output |
|||
ASALocalRun/ |
|||
|
|||
# MSBuild Binary and Structured Log |
|||
*.binlog |
|||
|
|||
# NVidia Nsight GPU debugger configuration file |
|||
*.nvuser |
|||
|
|||
# MFractors (Xamarin productivity tool) working folder |
|||
.mfractor/ |
|||
@ -0,0 +1,42 @@ |
|||
|
|||
Microsoft Visual Studio Solution File, Format Version 12.00 |
|||
# Visual Studio 15 |
|||
VisualStudioVersion = 15.0.27130.2024 |
|||
MinimumVisualStudioVersion = 10.0.40219.1 |
|||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.DataGrid", "src\Avalonia.DataGrid\Avalonia.DataGrid.csproj", "{D38EB864-DE82-4037-9F60-4562E80D8A9C}" |
|||
EndProject |
|||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{3CCEE0A9-1B8E-4A16-9EFB-72B5CA7CA0D8}" |
|||
EndProject |
|||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DataGridSample", "samples\DataGridSample\DataGridSample.csproj", "{906E00A7-28E7-427F-8EAB-4F86E2BE112D}" |
|||
EndProject |
|||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.DataGrid.Themes.Default", "src\Avalonia.DataGrid.Themes.Default\Avalonia.DataGrid.Themes.Default.csproj", "{E5FB0738-B47F-4CBE-995B-B8EB5694E392}" |
|||
EndProject |
|||
Global |
|||
GlobalSection(SolutionConfigurationPlatforms) = preSolution |
|||
Debug|Any CPU = Debug|Any CPU |
|||
Release|Any CPU = Release|Any CPU |
|||
EndGlobalSection |
|||
GlobalSection(ProjectConfigurationPlatforms) = postSolution |
|||
{D38EB864-DE82-4037-9F60-4562E80D8A9C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU |
|||
{D38EB864-DE82-4037-9F60-4562E80D8A9C}.Debug|Any CPU.Build.0 = Debug|Any CPU |
|||
{D38EB864-DE82-4037-9F60-4562E80D8A9C}.Release|Any CPU.ActiveCfg = Release|Any CPU |
|||
{D38EB864-DE82-4037-9F60-4562E80D8A9C}.Release|Any CPU.Build.0 = Release|Any CPU |
|||
{906E00A7-28E7-427F-8EAB-4F86E2BE112D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU |
|||
{906E00A7-28E7-427F-8EAB-4F86E2BE112D}.Debug|Any CPU.Build.0 = Debug|Any CPU |
|||
{906E00A7-28E7-427F-8EAB-4F86E2BE112D}.Release|Any CPU.ActiveCfg = Release|Any CPU |
|||
{906E00A7-28E7-427F-8EAB-4F86E2BE112D}.Release|Any CPU.Build.0 = Release|Any CPU |
|||
{E5FB0738-B47F-4CBE-995B-B8EB5694E392}.Debug|Any CPU.ActiveCfg = Debug|Any CPU |
|||
{E5FB0738-B47F-4CBE-995B-B8EB5694E392}.Debug|Any CPU.Build.0 = Debug|Any CPU |
|||
{E5FB0738-B47F-4CBE-995B-B8EB5694E392}.Release|Any CPU.ActiveCfg = Release|Any CPU |
|||
{E5FB0738-B47F-4CBE-995B-B8EB5694E392}.Release|Any CPU.Build.0 = Release|Any CPU |
|||
EndGlobalSection |
|||
GlobalSection(SolutionProperties) = preSolution |
|||
HideSolutionNode = FALSE |
|||
EndGlobalSection |
|||
GlobalSection(NestedProjects) = preSolution |
|||
{906E00A7-28E7-427F-8EAB-4F86E2BE112D} = {3CCEE0A9-1B8E-4A16-9EFB-72B5CA7CA0D8} |
|||
EndGlobalSection |
|||
GlobalSection(ExtensibilityGlobals) = postSolution |
|||
SolutionGuid = {6CCBA620-07B7-4E10-B82C-97D4715387BB} |
|||
EndGlobalSection |
|||
EndGlobal |
|||
@ -0,0 +1,21 @@ |
|||
MIT License |
|||
|
|||
Copyright (c) 2018 sdoroff |
|||
|
|||
Permission is hereby granted, free of charge, to any person obtaining a copy |
|||
of this software and associated documentation files (the "Software"), to deal |
|||
in the Software without restriction, including without limitation the rights |
|||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|||
copies of the Software, and to permit persons to whom the Software is |
|||
furnished to do so, subject to the following conditions: |
|||
|
|||
The above copyright notice and this permission notice shall be included in all |
|||
copies or substantial portions of the Software. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
|||
SOFTWARE. |
|||
@ -0,0 +1 @@ |
|||
# DataGrid |
|||
@ -0,0 +1,8 @@ |
|||
<Application xmlns="https://github.com/avaloniaui" |
|||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> |
|||
<Application.Styles> |
|||
<StyleInclude Source="resm:Avalonia.Themes.Default.DefaultTheme.xaml?assembly=Avalonia.Themes.Default"/> |
|||
<StyleInclude Source="resm:Avalonia.Themes.Default.Accents.BaseLight.xaml?assembly=Avalonia.Themes.Default"/> |
|||
<StyleInclude Source="resm:Avalonia.DataGrid.Themes.Default.DefaultTheme.xaml?assembly=Avalonia.DataGrid.Themes.Default"/> |
|||
</Application.Styles> |
|||
</Application> |
|||
@ -0,0 +1,13 @@ |
|||
using Avalonia; |
|||
using Avalonia.Markup.Xaml; |
|||
|
|||
namespace DataGridSample |
|||
{ |
|||
public class App : Application |
|||
{ |
|||
public override void Initialize() |
|||
{ |
|||
AvaloniaXamlLoader.Load(this); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,533 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
using System.Linq; |
|||
|
|||
namespace DataGridSample |
|||
{ |
|||
public class Country |
|||
{ |
|||
public string Name { get; private set; } |
|||
public string Region { get; private set; } |
|||
public int Population { get; private set; } |
|||
//Square Miles
|
|||
public int Area { get; private set; } |
|||
//Per Square Mile
|
|||
public double PopulationDensity { get; private set; } |
|||
//Coast / Area
|
|||
public double CoastLine { get; private set; } |
|||
public double? NetMigration { get; private set; } |
|||
//per 1000 births
|
|||
public double? InfantMortality { get; private set; } |
|||
public int GDP { get; private set; } |
|||
public double? LiteracyPercent { get; private set; } |
|||
//per 1000
|
|||
public double? Phones { get; private set; } |
|||
public double? BirthRate { get; private set; } |
|||
public double? DeathRate { get; private set; } |
|||
|
|||
public Country(string name, string region, int population, int area, double density, double coast, double? migration, |
|||
double? infantMorality, int gdp, double? literacy, double? phones, double? birth, double? death) |
|||
{ |
|||
Name = name; |
|||
Region = region; |
|||
Population = population; |
|||
Area = area; |
|||
PopulationDensity = density; |
|||
CoastLine = coast; |
|||
NetMigration = migration; |
|||
InfantMortality = infantMorality; |
|||
GDP = gdp; |
|||
LiteracyPercent = literacy; |
|||
BirthRate = birth; |
|||
DeathRate = death; |
|||
} |
|||
} |
|||
|
|||
public static class Countries |
|||
{ |
|||
static IEnumerable<Country> GetCountries() |
|||
{ |
|||
yield return new Country("Afghanistan", "ASIA (EX. NEAR EAST)", 31056997, 647500, 48, 0, 23.06, 163.07, 700, 36, 3.2, 46.6, 20.34); |
|||
yield return new Country("Albania", "EASTERN EUROPE", 3581655, 28748, 124.6, 1.26, -4.93, 21.52, 4500, 86.5, 71.2, 15.11, 5.22); |
|||
yield return new Country("Algeria", "NORTHERN AFRICA", 32930091, 2381740, 13.8, 0.04, -0.39, 31, 6000, 70, 78.1, 17.14, 4.61); |
|||
yield return new Country("American Samoa", "OCEANIA", 57794, 199, 290.4, 58.29, -20.71, 9.27, 8000, 97, 259.5, 22.46, 3.27); |
|||
yield return new Country("Andorra", "WESTERN EUROPE", 71201, 468, 152.1, 0, 6.6, 4.05, 19000, 100, 497.2, 8.71, 6.25); |
|||
yield return new Country("Angola", "SUB-SAHARAN AFRICA", 12127071, 1246700, 9.7, 0.13, 0, 191.19, 1900, 42, 7.8, 45.11, 24.2); |
|||
yield return new Country("Anguilla", "LATIN AMER. & CARIB", 13477, 102, 132.1, 59.8, 10.76, 21.03, 8600, 95, 460, 14.17, 5.34); |
|||
yield return new Country("Antigua & Barbuda", "LATIN AMER. & CARIB", 69108, 443, 156, 34.54, -6.15, 19.46, 11000, 89, 549.9, 16.93, 5.37); |
|||
yield return new Country("Argentina", "LATIN AMER. & CARIB", 39921833, 2766890, 14.4, 0.18, 0.61, 15.18, 11200, 97.1, 220.4, 16.73, 7.55); |
|||
yield return new Country("Armenia", "C.W. OF IND. STATES", 2976372, 29800, 99.9, 0, -6.47, 23.28, 3500, 98.6, 195.7, 12.07, 8.23); |
|||
yield return new Country("Aruba", "LATIN AMER. & CARIB", 71891, 193, 372.5, 35.49, 0, 5.89, 28000, 97, 516.1, 11.03, 6.68); |
|||
yield return new Country("Australia", "OCEANIA", 20264082, 7686850, 2.6, 0.34, 3.98, 4.69, 29000, 100, 565.5, 12.14, 7.51); |
|||
yield return new Country("Austria", "WESTERN EUROPE", 8192880, 83870, 97.7, 0, 2, 4.66, 30000, 98, 452.2, 8.74, 9.76); |
|||
yield return new Country("Azerbaijan", "C.W. OF IND. STATES", 7961619, 86600, 91.9, 0, -4.9, 81.74, 3400, 97, 137.1, 20.74, 9.75); |
|||
yield return new Country("The Bahamas", "LATIN AMER. & CARIB", 303770, 13940, 21.8, 25.41, -2.2, 25.21, 16700, 95.6, 460.6, 17.57, 9.05); |
|||
yield return new Country("Bahrain", "NEAR EAST", 698585, 665, 1050.5, 24.21, 1.05, 17.27, 16900, 89.1, 281.3, 17.8, 4.14); |
|||
yield return new Country("Bangladesh", "ASIA (EX. NEAR EAST)", 147365352, 144000, 1023.4, 0.4, -0.71, 62.6, 1900, 43.1, 7.3, 29.8, 8.27); |
|||
yield return new Country("Barbados", "LATIN AMER. & CARIB", 279912, 431, 649.5, 22.51, -0.31, 12.5, 15700, 97.4, 481.9, 12.71, 8.67); |
|||
yield return new Country("Belarus", "C.W. OF IND. STATES", 10293011, 207600, 49.6, 0, 2.54, 13.37, 6100, 99.6, 319.1, 11.16, 14.02); |
|||
yield return new Country("Belgium", "WESTERN EUROPE", 10379067, 30528, 340, 0.22, 1.23, 4.68, 29100, 98, 462.6, 10.38, 10.27); |
|||
yield return new Country("Belize", "LATIN AMER. & CARIB", 287730, 22966, 12.5, 1.68, 0, 25.69, 4900, 94.1, 115.7, 28.84, 5.72); |
|||
yield return new Country("Benin", "SUB-SAHARAN AFRICA", 7862944, 112620, 69.8, 0.11, 0, 85, 1100, 40.9, 9.7, 38.85, 12.22); |
|||
yield return new Country("Bermuda", "NORTHERN AMERICA", 65773, 53, 1241, 194.34, 2.49, 8.53, 36000, 98, 851.4, 11.4, 7.74); |
|||
yield return new Country("Bhutan", "ASIA (EX. NEAR EAST)", 2279723, 47000, 48.5, 0, 0, 100.44, 1300, 42.2, 14.3, 33.65, 12.7); |
|||
yield return new Country("Bolivia", "LATIN AMER. & CARIB", 8989046, 1098580, 8.2, 0, -1.32, 53.11, 2400, 87.2, 71.9, 23.3, 7.53); |
|||
yield return new Country("Bosnia & Herzegovina", "EASTERN EUROPE", 4498976, 51129, 88, 0.04, 0.31, 21.05, 6100,null, 215.4, 8.77, 8.27); |
|||
yield return new Country("Botswana", "SUB-SAHARAN AFRICA", 1639833, 600370, 2.7, 0, 0, 54.58, 9000, 79.8, 80.5, 23.08, 29.5); |
|||
yield return new Country("Brazil", "LATIN AMER. & CARIB", 188078227, 8511965, 22.1, 0.09, -0.03, 29.61, 7600, 86.4, 225.3, 16.56, 6.17); |
|||
yield return new Country("British Virgin Is.", "LATIN AMER. & CARIB", 23098, 153, 151, 52.29, 10.01, 18.05, 16000, 97.8, 506.5, 14.89, 4.42); |
|||
yield return new Country("Brunei", "ASIA (EX. NEAR EAST)", 379444, 5770, 65.8, 2.79, 3.59, 12.61, 18600, 93.9, 237.2, 18.79, 3.45); |
|||
yield return new Country("Bulgaria", "EASTERN EUROPE", 7385367, 110910, 66.6, 0.32, -4.58, 20.55, 7600, 98.6, 336.3, 9.65, 14.27); |
|||
yield return new Country("Burkina Faso", "SUB-SAHARAN AFRICA", 13902972, 274200, 50.7, 0, 0, 97.57, 1100, 26.6, 7, 45.62, 15.6); |
|||
yield return new Country("Burma", "ASIA (EX. NEAR EAST)", 47382633, 678500, 69.8, 0.28, -1.8, 67.24, 1800, 85.3, 10.1, 17.91, 9.83); |
|||
yield return new Country("Burundi", "SUB-SAHARAN AFRICA", 8090068, 27830, 290.7, 0, -0.06, 69.29, 600, 51.6, 3.4, 42.22, 13.46); |
|||
yield return new Country("Cambodia", "ASIA (EX. NEAR EAST)", 13881427, 181040, 76.7, 0.24, 0, 71.48, 1900, 69.4, 2.6, 26.9, 9.06); |
|||
yield return new Country("Cameroon", "SUB-SAHARAN AFRICA", 17340702, 475440, 36.5, 0.08, 0, 68.26, 1800, 79, 5.7, 33.89, 13.47); |
|||
yield return new Country("Canada", "NORTHERN AMERICA", 33098932, 9984670, 3.3, 2.02, 5.96, 4.75, 29800, 97, 552.2, 10.78, 7.8); |
|||
yield return new Country("Cape Verde", "SUB-SAHARAN AFRICA", 420979, 4033, 104.4, 23.93, -12.07, 47.77, 1400, 76.6, 169.6, 24.87, 6.55); |
|||
yield return new Country("Cayman Islands", "LATIN AMER. & CARIB", 45436, 262, 173.4, 61.07, 18.75, 8.19, 35000, 98, 836.3, 12.74, 4.89); |
|||
yield return new Country("Central African Rep.", "SUB-SAHARAN AFRICA", 4303356, 622984, 6.9, 0, 0, 91, 1100, 51, 2.3, 33.91, 18.65); |
|||
yield return new Country("Chad", "SUB-SAHARAN AFRICA", 9944201, 1284000, 7.7, 0, -0.11, 93.82, 1200, 47.5, 1.3, 45.73, 16.38); |
|||
yield return new Country("Chile", "LATIN AMER. & CARIB", 16134219, 756950, 21.3, 0.85, 0, 8.8, 9900, 96.2, 213, 15.23, 5.81); |
|||
yield return new Country("China", "ASIA (EX. NEAR EAST)", 1313973713, 9596960, 136.9, 0.15, -0.4, 24.18, 5000, 90.9, 266.7, 13.25, 6.97); |
|||
yield return new Country("Colombia", "LATIN AMER. & CARIB", 43593035, 1138910, 38.3, 0.28, -0.31, 20.97, 6300, 92.5, 176.2, 20.48, 5.58); |
|||
yield return new Country("Comoros", "SUB-SAHARAN AFRICA", 690948, 2170, 318.4, 15.67, 0, 74.93, 700, 56.5, 24.5, 36.93, 8.2); |
|||
yield return new Country("Congo, Dem.Rep.", "SUB - SAHARAN AFRICA", 62660551, 2345410, 26.7, 0, 0, 94.69, 700, 65.5, 0.2, 43.69, 13.27); |
|||
yield return new Country("Congo, Repub.of the", "SUB - SAHARAN AFRICA", 3702314, 342000, 10.8, 0.05, -0.17, 93.86, 700, 83.8, 3.7, 42.57, 12.93); |
|||
yield return new Country("Cook Islands", "OCEANIA", 21388, 240, 89.1, 50,null,null, 5000, 95, 289.9, 21,null); |
|||
yield return new Country("Costa Rica", "LATIN AMER. & CARIB", 4075261, 51100, 79.8, 2.52, 0.51, 9.95, 9100, 96, 340.7, 18.32, 4.36); |
|||
yield return new Country("Cote d'Ivoire", "SUB-SAHARAN AFRICA", 17654843, 322460, 54.8, 0.16, -0.07, 90.83, 1400, 50.9, 14.6, 35.11, 14.84); |
|||
yield return new Country("Croatia", "EASTERN EUROPE", 4494749, 56542, 79.5, 10.32, 1.58, 6.84, 10600, 98.5, 420.4, 9.61, 11.48); |
|||
yield return new Country("Cuba", "LATIN AMER. & CARIB", 11382820, 110860, 102.7, 3.37, -1.58, 6.33, 2900, 97, 74.7, 11.89, 7.22); |
|||
yield return new Country("Cyprus", "NEAR EAST", 784301, 9250, 84.8, 7.01, 0.43, 7.18, 19200, 97.6,null, 12.56, 7.68); |
|||
yield return new Country("Czech Republic", "EASTERN EUROPE", 10235455, 78866, 129.8, 0, 0.97, 3.93, 15700, 99.9, 314.3, 9.02, 10.59); |
|||
yield return new Country("Denmark", "WESTERN EUROPE", 5450661, 43094, 126.5, 16.97, 2.48, 4.56, 31100, 100, 614.6, 11.13, 10.36); |
|||
yield return new Country("Djibouti", "SUB-SAHARAN AFRICA", 486530, 23000, 21.2, 1.37, 0, 104.13, 1300, 67.9, 22.8, 39.53, 19.31); |
|||
yield return new Country("Dominica", "LATIN AMER. & CARIB", 68910, 754, 91.4, 19.63, -13.87, 14.15, 5400, 94, 304.8, 15.27, 6.73); |
|||
yield return new Country("Dominican Republic", "LATIN AMER. & CARIB", 9183984, 48730, 188.5, 2.64, -3.22, 32.38, 6000, 84.7, 97.4, 23.22, 5.73); |
|||
yield return new Country("East Timor", "ASIA (EX. NEAR EAST)", 1062777, 15007, 70.8, 4.7, 0, 47.41, 500, 58.6,null, 26.99, 6.24); |
|||
yield return new Country("Ecuador", "LATIN AMER. & CARIB", 13547510, 283560, 47.8, 0.79, -8.58, 23.66, 3300, 92.5, 125.6, 22.29, 4.23); |
|||
yield return new Country("Egypt", "NORTHERN AFRICA", 78887007, 1001450, 78.8, 0.24, -0.22, 32.59, 4000, 57.7, 131.8, 22.94, 5.23); |
|||
yield return new Country("El Salvador", "LATIN AMER. & CARIB", 6822378, 21040, 324.3, 1.46, -3.74, 25.1, 4800, 80.2, 142.4, 26.61, 5.78); |
|||
yield return new Country("Equatorial Guinea", "SUB-SAHARAN AFRICA", 540109, 28051, 19.3, 1.06, 0, 85.13, 2700, 85.7, 18.5, 35.59, 15.06); |
|||
yield return new Country("Eritrea", "SUB-SAHARAN AFRICA", 4786994, 121320, 39.5, 1.84, 0, 74.87, 700, 58.6, 7.9, 34.33, 9.6); |
|||
yield return new Country("Estonia", "BALTICS", 1324333, 45226, 29.3, 8.39, -3.16, 7.87, 12300, 99.8, 333.8, 10.04, 13.25); |
|||
yield return new Country("Ethiopia", "SUB-SAHARAN AFRICA", 74777981, 1127127, 66.3, 0, 0, 95.32, 700, 42.7, 8.2, 37.98, 14.86); |
|||
yield return new Country("Faroe Islands", "WESTERN EUROPE", 47246, 1399, 33.8, 79.84, 1.41, 6.24, 22000,null, 503.8, 14.05, 8.7); |
|||
yield return new Country("Fiji", "OCEANIA", 905949, 18270, 49.6, 6.18, -3.14, 12.62, 5800, 93.7, 112.6, 22.55, 5.65); |
|||
yield return new Country("Finland", "WESTERN EUROPE", 5231372, 338145, 15.5, 0.37, 0.95, 3.57, 27400, 100, 405.3, 10.45, 9.86); |
|||
yield return new Country("France", "WESTERN EUROPE", 60876136, 547030, 111.3, 0.63, 0.66, 4.26, 27600, 99, 586.4, 11.99, 9.14); |
|||
yield return new Country("French Guiana", "LATIN AMER. & CARIB", 199509, 91000, 2.2, 0.42, 6.27, 12.07, 8300, 83, 255.6, 20.46, 4.88); |
|||
yield return new Country("French Polynesia", "OCEANIA", 274578, 4167, 65.9, 60.6, 2.94, 8.44, 17500, 98, 194.5, 16.68, 4.69); |
|||
yield return new Country("Gabon", "SUB-SAHARAN AFRICA", 1424906, 267667, 5.3, 0.33, 0, 53.64, 5500, 63.2, 27.4, 36.16, 12.25); |
|||
yield return new Country("Gambia, The", "SUB - SAHARAN AFRICA", 1641564, 11300, 145.3, 0.71, 1.57, 72.02, 1700, 40.1, 26.8, 39.37, 12.25); |
|||
yield return new Country("Gaza Strip", "NEAR EAST", 1428757, 360, 3968.8, 11.11, 1.6, 22.93, 600,null, 244.3, 39.45, 3.8); |
|||
yield return new Country("Georgia", "C.W. OF IND. STATES", 4661473, 69700, 66.9, 0.44, -4.7, 18.59, 2500, 99, 146.6, 10.41, 9.23); |
|||
yield return new Country("Germany", "WESTERN EUROPE", 82422299, 357021, 230.9, 0.67, 2.18, 4.16, 27600, 99, 667.9, 8.25, 10.62); |
|||
yield return new Country("Ghana", "SUB-SAHARAN AFRICA", 22409572, 239460, 93.6, 0.23, -0.64, 51.43, 2200, 74.8, 14.4, 30.52, 9.72); |
|||
yield return new Country("Gibraltar", "WESTERN EUROPE", 27928, 7, 3989.7, 171.43, 0, 5.13, 17500,null, 877.7, 10.74, 9.31); |
|||
yield return new Country("Greece", "WESTERN EUROPE", 10688058, 131940, 81, 10.37, 2.35, 5.53, 20000, 97.5, 589.7, 9.68, 10.24); |
|||
yield return new Country("Greenland", "NORTHERN AMERICA", 56361, 2166086, 0, 2.04, -8.37, 15.82, 20000,null, 448.9, 15.93, 7.84); |
|||
yield return new Country("Grenada", "LATIN AMER. & CARIB", 89703, 344, 260.8, 35.17, -13.92, 14.62, 5000, 98, 364.5, 22.08, 6.88); |
|||
yield return new Country("Guadeloupe", "LATIN AMER. & CARIB", 452776, 1780, 254.4, 17.19, -0.15, 8.6, 8000, 90, 463.8, 15.05, 6.09); |
|||
yield return new Country("Guam", "OCEANIA", 171019, 541, 316.1, 23.2, 0, 6.94, 21000, 99, 492, 18.79, 4.48); |
|||
yield return new Country("Guatemala", "LATIN AMER. & CARIB", 12293545, 108890, 112.9, 0.37, -1.67, 35.93, 4100, 70.6, 92.1, 29.88, 5.2); |
|||
yield return new Country("Guernsey", "WESTERN EUROPE", 65409, 78, 838.6, 64.1, 3.84, 4.71, 20000,null, 842.4, 8.81, 10.01); |
|||
yield return new Country("Guinea", "SUB-SAHARAN AFRICA", 9690222, 245857, 39.4, 0.13, -3.06, 90.37, 2100, 35.9, 2.7, 41.76, 15.48); |
|||
yield return new Country("Guinea-Bissau", "SUB-SAHARAN AFRICA", 1442029, 36120, 39.9, 0.97, -1.57, 107.17, 800, 42.4, 7.4, 37.22, 16.53); |
|||
yield return new Country("Guyana", "LATIN AMER. & CARIB", 767245, 214970, 3.6, 0.21, -2.07, 33.26, 4000, 98.8, 143.5, 18.28, 8.28); |
|||
yield return new Country("Haiti", "LATIN AMER. & CARIB", 8308504, 27750, 299.4, 6.38, -3.4, 73.45, 1600, 52.9, 16.9, 36.44, 12.17); |
|||
yield return new Country("Honduras", "LATIN AMER. & CARIB", 7326496, 112090, 65.4, 0.73, -1.99, 29.32, 2600, 76.2, 67.5, 28.24, 5.28); |
|||
yield return new Country("Hong Kong", "ASIA (EX. NEAR EAST)", 6940432, 1092, 6355.7, 67.12, 5.24, 2.97, 28800, 93.5, 546.7, 7.29, 6.29); |
|||
yield return new Country("Hungary", "EASTERN EUROPE", 9981334, 93030, 107.3, 0, 0.86, 8.57, 13900, 99.4, 336.2, 9.72, 13.11); |
|||
yield return new Country("Iceland", "WESTERN EUROPE", 299388, 103000, 2.9, 4.83, 2.38, 3.31, 30900, 99.9, 647.7, 13.64, 6.72); |
|||
yield return new Country("India", "ASIA (EX. NEAR EAST)", 1095351995, 3287590, 333.2, 0.21, -0.07, 56.29, 2900, 59.5, 45.4, 22.01, 8.18); |
|||
yield return new Country("Indonesia", "ASIA (EX. NEAR EAST)", 245452739, 1919440, 127.9, 2.85, 0, 35.6, 3200, 87.9, 52, 20.34, 6.25); |
|||
yield return new Country("Iran", "ASIA (EX. NEAR EAST)", 68688433, 1648000, 41.7, 0.15, -0.84, 41.58, 7000, 79.4, 276.4, 17, 5.55); |
|||
yield return new Country("Iraq", "NEAR EAST", 26783383, 437072, 61.3, 0.01, 0, 50.25, 1500, 40.4, 38.6, 31.98, 5.37); |
|||
yield return new Country("Ireland", "WESTERN EUROPE", 4062235, 70280, 57.8, 2.06, 4.99, 5.39, 29600, 98, 500.5, 14.45, 7.82); |
|||
yield return new Country("Isle of Man", "WESTERN EUROPE", 75441, 572, 131.9, 27.97, 5.36, 5.93, 21000,null, 676, 11.05, 11.19); |
|||
yield return new Country("Israel", "NEAR EAST", 6352117, 20770, 305.8, 1.31, 0.68, 7.03, 19800, 95.4, 462.3, 17.97, 6.18); |
|||
yield return new Country("Italy", "WESTERN EUROPE", 58133509, 301230, 193, 2.52, 2.07, 5.94, 26700, 98.6, 430.9, 8.72, 10.4); |
|||
yield return new Country("Jamaica", "LATIN AMER. & CARIB", 2758124, 10991, 250.9, 9.3, -4.92, 12.36, 3900, 87.9, 124, 20.82, 6.52); |
|||
yield return new Country("Japan", "ASIA (EX. NEAR EAST)", 127463611, 377835, 337.4, 7.87, 0, 3.26, 28200, 99, 461.2, 9.37, 9.16); |
|||
yield return new Country("Jersey", "WESTERN EUROPE", 91084, 116, 785.2, 60.34, 2.76, 5.24, 24800,null, 811.3, 9.3, 9.28); |
|||
yield return new Country("Jordan", "NEAR EAST", 5906760, 92300, 64, 0.03, 6.59, 17.35, 4300, 91.3, 104.5, 21.25, 2.65); |
|||
yield return new Country("Kazakhstan", "C.W. OF IND. STATES", 15233244, 2717300, 5.6, 0, -3.35, 29.21, 6300, 98.4, 164.1, 16, 9.42); |
|||
yield return new Country("Kenya", "SUB-SAHARAN AFRICA", 34707817, 582650, 59.6, 0.09, -0.1, 61.47, 1000, 85.1, 8.1, 39.72, 14.02); |
|||
yield return new Country("Kiribati", "OCEANIA", 105432, 811, 130, 140.94, 0, 48.52, 800,null, 42.7, 30.65, 8.26); |
|||
yield return new Country("North Korea", "ASIA(EX.NEAR EAST)", 23113019, 120540, 191.8, 2.07, 0, 24.04, 1300, 99, 42.4, 15.54, 7.13); |
|||
yield return new Country("South Korea", "ASIA(EX.NEAR EAST)", 48846823, 98480, 496, 2.45, 0, 7.05, 17800, 97.9, 486.1, 10, 5.85); |
|||
yield return new Country("Kuwait", "NEAR EAST", 2418393, 17820, 135.7, 2.8, 14.18, 9.95, 19000, 83.5, 211, 21.94, 2.41); |
|||
yield return new Country("Kyrgyzstan", "C.W. OF IND. STATES", 5213898, 198500, 26.3, 0, -2.45, 35.64, 1600, 97, 84, 22.8, 7.08); |
|||
yield return new Country("Laos", "ASIA (EX. NEAR EAST)", 6368481, 236800, 26.9, 0, 0, 85.22, 1700, 66.4, 14.1, 35.49, 11.55); |
|||
yield return new Country("Latvia", "BALTICS", 2274735, 64589, 35.2, 0.82, -2.23, 9.55, 10200, 99.8, 321.4, 9.24, 13.66); |
|||
yield return new Country("Lebanon", "NEAR EAST", 3874050, 10400, 372.5, 2.16, 0, 24.52, 4800, 87.4, 255.6, 18.52, 6.21); |
|||
yield return new Country("Lesotho", "SUB-SAHARAN AFRICA", 2022331, 30355, 66.6, 0, -0.74, 84.23, 3000, 84.8, 23.7, 24.75, 28.71); |
|||
yield return new Country("Liberia", "SUB-SAHARAN AFRICA", 3042004, 111370, 27.3, 0.52, 0, 128.87, 1000, 57.5, 2.3, 44.77, 23.1); |
|||
yield return new Country("Libya", "NORTHERN AFRICA", 5900754, 1759540, 3.4, 0.1, 0, 24.6, 6400, 82.6, 127.1, 26.49, 3.48); |
|||
yield return new Country("Liechtenstein", "WESTERN EUROPE", 33987, 160, 212.4, 0, 4.85, 4.7, 25000, 100, 585.5, 10.21, 7.18); |
|||
yield return new Country("Lithuania", "BALTICS", 3585906, 65200, 55, 0.14, -0.71, 6.89, 11400, 99.6, 223.4, 8.75, 10.98); |
|||
yield return new Country("Luxembourg", "WESTERN EUROPE", 474413, 2586, 183.5, 0, 8.97, 4.81, 55100, 100, 515.4, 11.94, 8.41); |
|||
yield return new Country("Macau", "ASIA (EX. NEAR EAST)", 453125, 28, 16183, 146.43, 4.86, 4.39, 19400, 94.5, 384.9, 8.48, 4.47); |
|||
yield return new Country("Macedonia", "EASTERN EUROPE", 2050554, 25333, 80.9, 0, -1.45, 10.09, 6700,null, 260, 12.02, 8.77); |
|||
yield return new Country("Madagascar", "SUB-SAHARAN AFRICA", 18595469, 587040, 31.7, 0.82, 0, 76.83, 800, 68.9, 3.6, 41.41, 11.11); |
|||
yield return new Country("Malawi", "SUB-SAHARAN AFRICA", 13013926, 118480, 109.8, 0, 0, 103.32, 600, 62.7, 7.9, 43.13, 19.33); |
|||
yield return new Country("Malaysia", "ASIA (EX. NEAR EAST)", 24385858, 329750, 74, 1.42, 0, 17.7, 9000, 88.7, 179, 22.86, 5.05); |
|||
yield return new Country("Maldives", "ASIA (EX. NEAR EAST)", 359008, 300, 1196.7, 214.67, 0, 56.52, 3900, 97.2, 90, 34.81, 7.06); |
|||
yield return new Country("Mali", "SUB-SAHARAN AFRICA", 11716829, 1240000, 9.5, 0, -0.33, 116.79, 900, 46.4, 6.4, 49.82, 16.89); |
|||
yield return new Country("Malta", "WESTERN EUROPE", 400214, 316, 1266.5, 62.28, 2.07, 3.89, 17700, 92.8, 505, 10.22, 8.1); |
|||
yield return new Country("Marshall Islands", "OCEANIA", 60422, 11854, 5.1, 3.12, -6.04, 29.45, 1600, 93.7, 91.2, 33.05, 4.78); |
|||
yield return new Country("Martinique", "LATIN AMER. & CARIB", 436131, 1100, 396.5, 31.82, -0.05, 7.09, 14400, 97.7, 394.4, 13.74, 6.48); |
|||
yield return new Country("Mauritania", "SUB-SAHARAN AFRICA", 3177388, 1030700, 3.1, 0.07, 0, 70.89, 1800, 41.7, 12.9, 40.99, 12.16); |
|||
yield return new Country("Mauritius", "SUB-SAHARAN AFRICA", 1240827, 2040, 608.3, 8.68, -0.9, 15.03, 11400, 85.6, 289.3, 15.43, 6.86); |
|||
yield return new Country("Mayotte", "SUB-SAHARAN AFRICA", 201234, 374, 538.1, 49.52, 6.78, 62.4, 2600,null, 49.7, 40.95, 7.7); |
|||
yield return new Country("Mexico", "LATIN AMER. & CARIB", 107449525, 1972550, 54.5, 0.47, -4.87, 20.91, 9000, 92.2, 181.6, 20.69, 4.74); |
|||
yield return new Country("Micronesia, Fed.St.", "OCEANIA", 108004, 702, 153.9, 870.66, -20.99, 30.21, 2000, 89, 114.8, 24.68, 4.75); |
|||
yield return new Country("Moldova", "C.W. OF IND. STATES", 4466706, 33843, 132, 0, -0.26, 40.42, 1800, 99.1, 208.1, 15.7, 12.64); |
|||
yield return new Country("Monaco", "WESTERN EUROPE", 32543, 2, 16271.5, 205, 7.75, 5.43, 27000, 99, 1035.6, 9.19, 12.91); |
|||
yield return new Country("Mongolia", "ASIA (EX. NEAR EAST)", 2832224, 1564116, 1.8, 0, 0, 53.79, 1800, 97.8, 55.1, 21.59, 6.95); |
|||
yield return new Country("Montserrat", "LATIN AMER. & CARIB", 9439, 102, 92.5, 39.22, 0, 7.35, 3400, 97,null, 17.59, 7.1); |
|||
yield return new Country("Morocco", "NORTHERN AFRICA", 33241259, 446550, 74.4, 0.41, -0.98, 41.62, 4000, 51.7, 40.4, 21.98, 5.58); |
|||
yield return new Country("Mozambique", "SUB-SAHARAN AFRICA", 19686505, 801590, 24.6, 0.31, 0, 130.79, 1200, 47.8, 3.5, 35.18, 21.35); |
|||
yield return new Country("Namibia", "SUB-SAHARAN AFRICA", 2044147, 825418, 2.5, 0.19, 0, 48.98, 7200, 84, 62.6, 24.32, 18.86); |
|||
yield return new Country("Nauru", "OCEANIA", 13287, 21, 632.7, 142.86, 0, 9.95, 5000,null, 143, 24.76, 6.7); |
|||
yield return new Country("Nepal", "ASIA (EX. NEAR EAST)", 28287147, 147181, 192.2, 0, 0, 66.98, 1400, 45.2, 15.9, 30.98, 9.31); |
|||
yield return new Country("Netherlands", "WESTERN EUROPE", 16491461, 41526, 397.1, 1.09, 2.91, 5.04, 28600, 99, 460.8, 10.9, 8.68); |
|||
yield return new Country("Netherlands Antilles", "LATIN AMER. & CARIB", 221736, 960, 231, 37.92, -0.41, 10.03, 11400, 96.7, 365.3, 14.78, 6.45); |
|||
yield return new Country("New Caledonia", "OCEANIA", 219246, 19060, 11.5, 11.83, 0, 7.72, 15000, 91, 252.2, 18.11, 5.69); |
|||
yield return new Country("New Zealand", "OCEANIA", 4076140, 268680, 15.2, 5.63, 4.05, 5.85, 21600, 99, 441.7, 13.76, 7.53); |
|||
yield return new Country("Nicaragua", "LATIN AMER. & CARIB", 5570129, 129494, 43, 0.7, -1.22, 29.11, 2300, 67.5, 39.7, 24.51, 4.45); |
|||
yield return new Country("Niger", "SUB-SAHARAN AFRICA", 12525094, 1267000, 9.9, 0, -0.67, 121.69, 800, 17.6, 1.9, 50.73, 20.91); |
|||
yield return new Country("Nigeria", "SUB-SAHARAN AFRICA", 131859731, 923768, 142.7, 0.09, 0.26, 98.8, 900, 68, 9.3, 40.43, 16.94); |
|||
yield return new Country("N. Mariana Islands", "OCEANIA", 82459, 477, 172.9, 310.69, 9.61, 7.11, 12500, 97, 254.7, 19.43, 2.29); |
|||
yield return new Country("Norway", "WESTERN EUROPE", 4610820, 323802, 14.2, 7.77, 1.74, 3.7, 37800, 100, 461.7, 11.46, 9.4); |
|||
yield return new Country("Oman", "NEAR EAST", 3102229, 212460, 14.6, 0.98, 0.28, 19.51, 13100, 75.8, 85.5, 36.24, 3.81); |
|||
yield return new Country("Pakistan", "ASIA (EX. NEAR EAST)", 165803560, 803940, 206.2, 0.13, -2.77, 72.44, 2100, 45.7, 31.8, 29.74, 8.23); |
|||
yield return new Country("Palau", "OCEANIA", 20579, 458, 44.9, 331.66, 2.85, 14.84, 9000, 92, 325.6, 18.03, 6.8); |
|||
yield return new Country("Panama", "LATIN AMER. & CARIB", 3191319, 78200, 40.8, 3.18, -0.91, 20.47, 6300, 92.6, 137.9, 21.74, 5.36); |
|||
yield return new Country("Papua New Guinea", "OCEANIA", 5670544, 462840, 12.3, 1.11, 0, 51.45, 2200, 64.6, 10.9, 29.36, 7.25); |
|||
yield return new Country("Paraguay", "LATIN AMER. & CARIB", 6506464, 406750, 16, 0, -0.08, 25.63, 4700, 94, 49.2, 29.1, 4.49); |
|||
yield return new Country("Peru", "LATIN AMER. & CARIB", 28302603, 1285220, 22, 0.19, -1.05, 31.94, 5100, 90.9, 79.5, 20.48, 6.23); |
|||
yield return new Country("Philippines", "ASIA (EX. NEAR EAST)", 89468677, 300000, 298.2, 12.1, -1.5, 23.51, 4600, 92.6, 38.4, 24.89, 5.41); |
|||
yield return new Country("Poland", "EASTERN EUROPE", 38536869, 312685, 123.3, 0.16, -0.49, 8.51, 11100, 99.8, 306.3, 9.85, 9.89); |
|||
yield return new Country("Portugal", "WESTERN EUROPE", 10605870, 92391, 114.8, 1.94, 3.57, 5.05, 18000, 93.3, 399.2, 10.72, 10.5); |
|||
yield return new Country("Puerto Rico", "LATIN AMER. & CARIB", 3927188, 13790, 284.8, 3.63, -1.46, 8.24, 16800, 94.1, 283.1, 12.77, 7.65); |
|||
yield return new Country("Qatar", "NEAR EAST", 885359, 11437, 77.4, 4.92, 16.29, 18.61, 21500, 82.5, 232, 15.56, 4.72); |
|||
yield return new Country("Reunion", "SUB-SAHARAN AFRICA", 787584, 2517, 312.9, 8.22, 0, 7.78, 5800, 88.9, 380.9, 18.9, 5.49); |
|||
yield return new Country("Romania", "EASTERN EUROPE", 22303552, 237500, 93.9, 0.09, -0.13, 26.43, 7000, 98.4, 196.9, 10.7, 11.77); |
|||
yield return new Country("Russia", "C.W. OF IND. STATES", 142893540, 17075200, 8.4, 0.22, 1.02, 15.39, 8900, 99.6, 280.6, 9.95, 14.65); |
|||
yield return new Country("Rwanda", "SUB-SAHARAN AFRICA", 8648248, 26338, 328.4, 0, 0, 91.23, 1300, 70.4, 2.7, 40.37, 16.09); |
|||
yield return new Country("Saint Helena", "SUB-SAHARAN AFRICA", 7502, 413, 18.2, 14.53, 0, 19, 2500, 97, 293.3, 12.13, 6.53); |
|||
yield return new Country("Saint Kitts & Nevis", "LATIN AMER. & CARIB", 39129, 261, 149.9, 51.72, -7.11, 14.49, 8800, 97, 638.9, 18.02, 8.33); |
|||
yield return new Country("Saint Lucia", "LATIN AMER. & CARIB", 168458, 616, 273.5, 25.65, -2.67, 13.53, 5400, 67, 303.3, 19.68, 5.08); |
|||
yield return new Country("St Pierre & Miquelon", "NORTHERN AMERICA", 7026, 242, 29, 49.59, -4.86, 7.54, 6900, 99, 683.2, 13.52, 6.83); |
|||
yield return new Country("Saint Vincent and the Grenadines", "LATIN AMER. & CARIB", 117848, 389, 303, 21.59, -7.64, 14.78, 2900, 96, 190.9, 16.18, 5.98); |
|||
yield return new Country("Samoa", "OCEANIA", 176908, 2944, 60.1, 13.69, -11.7, 27.71, 5600, 99.7, 75.2, 16.43, 6.62); |
|||
yield return new Country("San Marino", "WESTERN EUROPE", 29251, 61, 479.5, 0, 10.98, 5.73, 34600, 96, 704.3, 10.02, 8.17); |
|||
yield return new Country("Sao Tome & Principe", "SUB-SAHARAN AFRICA", 193413, 1001, 193.2, 20.88, -2.72, 43.11, 1200, 79.3, 36.2, 40.25, 6.47); |
|||
yield return new Country("Saudi Arabia", "NEAR EAST", 27019731, 1960582, 13.8, 0.13, -2.71, 13.24, 11800, 78.8, 140.6, 29.34, 2.58); |
|||
yield return new Country("Senegal", "SUB-SAHARAN AFRICA", 11987121, 196190, 61.1, 0.27, 0.2, 55.51, 1600, 40.2, 22.2, 32.78, 9.42); |
|||
yield return new Country("Serbia", "EASTERN EUROPE", 9396411, 88361, 106.3, 0, -1.33, 12.89, 2200, 93, 285.8,null,null); |
|||
yield return new Country("Seychelles", "SUB-SAHARAN AFRICA", 81541, 455, 179.2, 107.91, -5.69, 15.53, 7800, 58, 262.4, 16.03, 6.29); |
|||
yield return new Country("Sierra Leone", "SUB-SAHARAN AFRICA", 6005250, 71740, 83.7, 0.56, 0, 143.64, 500, 31.4, 4, 45.76, 23.03); |
|||
yield return new Country("Singapore", "ASIA (EX. NEAR EAST)", 4492150, 693, 6482.2, 27.85, 11.53, 2.29, 23700, 92.5, 411.4, 9.34, 4.28); |
|||
yield return new Country("Slovakia", "EASTERN EUROPE", 5439448, 48845, 111.4, 0, 0.3, 7.41, 13300,null, 220.1, 10.65, 9.45); |
|||
yield return new Country("Slovenia", "EASTERN EUROPE", 2010347, 20273, 99.2, 0.23, 1.12, 4.45, 19000, 99.7, 406.1, 8.98, 10.31); |
|||
yield return new Country("Solomon Islands", "OCEANIA", 552438, 28450, 19.4, 18.67, 0, 21.29, 1700,null, 13.4, 30.01, 3.92); |
|||
yield return new Country("Somalia", "SUB-SAHARAN AFRICA", 8863338, 637657, 13.9, 0.47, 5.37, 116.7, 500, 37.8, 11.3, 45.13, 16.63); |
|||
yield return new Country("South Africa", "SUB-SAHARAN AFRICA", 44187637, 1219912, 36.2, 0.23, -0.29, 61.81, 10700, 86.4, 107, 18.2, 22); |
|||
yield return new Country("Spain", "WESTERN EUROPE", 40397842, 504782, 80, 0.98, 0.99, 4.42, 22000, 97.9, 453.5, 10.06, 9.72); |
|||
yield return new Country("Sri Lanka", "ASIA (EX. NEAR EAST)", 20222240, 65610, 308.2, 2.04, -1.31, 14.35, 3700, 92.3, 61.5, 15.51, 6.52); |
|||
yield return new Country("Sudan", "SUB-SAHARAN AFRICA", 41236378, 2505810, 16.5, 0.03, -0.02, 62.5, 1900, 61.1, 16.3, 34.53, 8.97); |
|||
yield return new Country("Suriname", "LATIN AMER. & CARIB", 439117, 163270, 2.7, 0.24, -8.81, 23.57, 4000, 93, 184.7, 18.02, 7.27); |
|||
yield return new Country("Swaziland", "SUB-SAHARAN AFRICA", 1136334, 17363, 65.5, 0, 0, 69.27, 4900, 81.6, 30.8, 27.41, 29.74); |
|||
yield return new Country("Sweden", "WESTERN EUROPE", 9016596, 449964, 20, 0.72, 1.67, 2.77, 26800, 99, 715, 10.27, 10.31); |
|||
yield return new Country("Switzerland", "WESTERN EUROPE", 7523934, 41290, 182.2, 0, 4.05, 4.39, 32700, 99, 680.9, 9.71, 8.49); |
|||
yield return new Country("Syria", "NEAR EAST", 18881361, 185180, 102, 0.1, 0, 29.53, 3300, 76.9, 153.8, 27.76, 4.81); |
|||
yield return new Country("Taiwan", "ASIA (EX. NEAR EAST)", 23036087, 35980, 640.3, 4.35, 0, 6.4, 23400, 96.1, 591, 12.56, 6.48); |
|||
yield return new Country("Tajikistan", "C.W. OF IND. STATES", 7320815, 143100, 51.2, 0, -2.86, 110.76, 1000, 99.4, 33.5, 32.65, 8.25); |
|||
yield return new Country("Tanzania", "SUB-SAHARAN AFRICA", 37445392, 945087, 39.6, 0.15, -2.06, 98.54, 600, 78.2, 4, 37.71, 16.39); |
|||
yield return new Country("Thailand", "ASIA (EX. NEAR EAST)", 64631595, 514000, 125.7, 0.63, 0, 20.48, 7400, 92.6, 108.9, 13.87, 7.04); |
|||
yield return new Country("Togo", "SUB-SAHARAN AFRICA", 5548702, 56785, 97.7, 0.1, 0, 66.61, 1500, 60.9, 10.6, 37.01, 9.83); |
|||
yield return new Country("Tonga", "OCEANIA", 114689, 748, 153.3, 56.02, 0, 12.62, 2200, 98.5, 97.7, 25.37, 5.28); |
|||
yield return new Country("Trinidad & Tobago", "LATIN AMER. & CARIB", 1065842, 5128, 207.9, 7.06, -10.83, 24.31, 9500, 98.6, 303.5, 12.9, 10.57); |
|||
yield return new Country("Tunisia", "NORTHERN AFRICA", 10175014, 163610, 62.2, 0.7, -0.57, 24.77, 6900, 74.2, 123.6, 15.52, 5.13); |
|||
yield return new Country("Turkey", "NEAR EAST", 70413958, 780580, 90.2, 0.92, 0, 41.04, 6700, 86.5, 269.5, 16.62, 5.97); |
|||
yield return new Country("Turkmenistan", "C.W. OF IND. STATES", 5042920, 488100, 10.3, 0, -0.86, 73.08, 5800, 98, 74.6, 27.61, 8.6); |
|||
yield return new Country("Turks & Caicos Is", "LATIN AMER. & CARIB", 21152, 430, 49.2, 90.47, 11.68, 15.67, 9600, 98, 269.5, 21.84, 4.21); |
|||
yield return new Country("Tuvalu", "OCEANIA", 11810, 26, 454.2, 92.31, 0, 20.03, 1100,null, 59.3, 22.18, 7.11); |
|||
yield return new Country("Uganda", "SUB-SAHARAN AFRICA", 28195754, 236040, 119.5, 0, 0, 67.83, 1400, 69.9, 3.6, 47.35, 12.24); |
|||
yield return new Country("Ukraine", "C.W. OF IND. STATES", 46710816, 603700, 77.4, 0.46, -0.39, 20.34, 5400, 99.7, 259.9, 8.82, 14.39); |
|||
yield return new Country("United Arab Emirates", "NEAR EAST", 2602713, 82880, 31.4, 1.59, 1.03, 14.51, 23200, 77.9, 475.3, 18.96, 4.4); |
|||
yield return new Country("United Kingdom", "WESTERN EUROPE", 60609153, 244820, 247.6, 5.08, 2.19, 5.16, 27700, 99, 543.5, 10.71, 10.13); |
|||
yield return new Country("United States", "NORTHERN AMERICA", 298444215, 9631420, 31, 0.21, 3.41, 6.5, 37800, 97, 898, 14.14, 8.26); |
|||
yield return new Country("Uruguay", "LATIN AMER. & CARIB", 3431932, 176220, 19.5, 0.37, -0.32, 11.95, 12800, 98, 291.4, 13.91, 9.05); |
|||
yield return new Country("Uzbekistan", "C.W. OF IND. STATES", 27307134, 447400, 61, 0, -1.72, 71.1, 1700, 99.3, 62.9, 26.36, 7.84); |
|||
yield return new Country("Vanuatu", "OCEANIA", 208869, 12200, 17.1, 20.72, 0, 55.16, 2900, 53, 32.6, 22.72, 7.82); |
|||
yield return new Country("Venezuela", "LATIN AMER. & CARIB", 25730435, 912050, 28.2, 0.31, -0.04, 22.2, 4800, 93.4, 140.1, 18.71, 4.92); |
|||
yield return new Country("Vietnam", "ASIA (EX. NEAR EAST)", 84402966, 329560, 256.1, 1.05, -0.45, 25.95, 2500, 90.3, 187.7, 16.86, 6.22); |
|||
yield return new Country("Virgin Islands", "LATIN AMER. & CARIB", 108605, 1910, 56.9, 9.84, -8.94, 8.03, 17200,null, 652.8, 13.96, 6.43); |
|||
yield return new Country("Wallis and Futuna", "OCEANIA", 16025, 274, 58.5, 47.08,null,null, 3700, 50, 118.6,null,null); |
|||
yield return new Country("West Bank", "NEAR EAST", 2460492, 5860, 419.9, 0, 2.98, 19.62, 800,null, 145.2, 31.67, 3.92); |
|||
//yield return new Country("Western Sahara", "NORTHERN AFRICA", 273008, 266000, 1, 0.42,,,,,,,);
|
|||
yield return new Country("Yemen", "NEAR EAST", 21456188, 527970, 40.6, 0.36, 0, 61.5, 800, 50.2, 37.2, 42.89, 8.3); |
|||
yield return new Country("Zambia", "SUB-SAHARAN AFRICA", 11502010, 752614, 15.3, 0, 0, 88.29, 800, 80.6, 8.2, 41, 19.93); |
|||
yield return new Country("Zimbabwe", "SUB-SAHARAN AFRICA", 12236805, 390580, 31.3, 0, 0, 67.69, 1900, 90.7, 26.8, 28.01, 21.84); |
|||
|
|||
|
|||
} |
|||
|
|||
static IReadOnlyList<Country> _all; |
|||
|
|||
public static IReadOnlyList<Country> All |
|||
{ |
|||
get |
|||
{ |
|||
if(_all == null) |
|||
{ |
|||
_all = GetCountries().ToList().AsReadOnly(); |
|||
} |
|||
|
|||
return _all; |
|||
} |
|||
|
|||
} |
|||
|
|||
/* |
|||
|
|||
|
|||
yield return new Country("Afghanistan","ASIA (EX. NEAR EAST)",31056997,647500,48,0,23.06,163.07,700,36,3.2,46.6,20.34); |
|||
yield return new Country("Albania","EASTERN EUROPE",3581655,28748,124.6,1.26,-4.93,21.52,4500,86.5,71.2,15.11,5.22); |
|||
yield return new Country("Algeria","NORTHERN AFRICA",32930091,2381740,13.8,0.04,-0.39,31,6000,70,78.1,17.14,4.61); |
|||
yield return new Country("American Samoa","OCEANIA",57794,199,290.4,58.29,-20.71,9.27,8000,97,259.5,22.46,3.27); |
|||
yield return new Country("Andorra","WESTERN EUROPE",71201,468,152.1,0,6.6,4.05,19000,100,497.2,8.71,6.25); |
|||
yield return new Country("Angola","SUB-SAHARAN AFRICA",12127071,1246700,9.7,0.13,0,191.19,1900,42,7.8,45.11,24.2); |
|||
yield return new Country("Anguilla","LATIN AMER. & CARIB",13477,102,132.1,59.8,10.76,21.03,8600,95,460,14.17,5.34); |
|||
yield return new Country("Antigua & Barbuda","LATIN AMER. & CARIB",69108,443,156,34.54,-6.15,19.46,11000,89,549.9,16.93,5.37); |
|||
yield return new Country("Argentina","LATIN AMER. & CARIB",39921833,2766890,14.4,0.18,0.61,15.18,11200,97.1,220.4,16.73,7.55); |
|||
yield return new Country("Armenia","C.W. OF IND. STATES",2976372,29800,99.9,0,-6.47,23.28,3500,98.6,195.7,12.07,8.23); |
|||
yield return new Country("Aruba","LATIN AMER. & CARIB",71891,193,372.5,35.49,0,5.89,28000,97,516.1,11.03,6.68); |
|||
yield return new Country("Australia","OCEANIA",20264082,7686850,2.6,0.34,3.98,4.69,29000,100,565.5,12.14,7.51); |
|||
yield return new Country("Austria","WESTERN EUROPE",8192880,83870,97.7,0,2,4.66,30000,98,452.2,8.74,9.76); |
|||
yield return new Country("Azerbaijan","C.W. OF IND. STATES",7961619,86600,91.9,0,-4.9,81.74,3400,97,137.1,20.74,9.75); |
|||
yield return new Country(""Bahamas"," The"",LATIN AMER. & CARIB,303770,13940,21.8,25.41,-2.2,25.21,16700,95.6,460.6,17.57,9.05); |
|||
yield return new Country("Bahrain","NEAR EAST",698585,665,1050.5,24.21,1.05,17.27,16900,89.1,281.3,17.8,4.14); |
|||
yield return new Country("Bangladesh","ASIA (EX. NEAR EAST)",147365352,144000,1023.4,0.4,-0.71,62.6,1900,43.1,7.3,29.8,8.27); |
|||
yield return new Country("Barbados","LATIN AMER. & CARIB",279912,431,649.5,22.51,-0.31,12.5,15700,97.4,481.9,12.71,8.67); |
|||
yield return new Country("Belarus","C.W. OF IND. STATES",10293011,207600,49.6,0,2.54,13.37,6100,99.6,319.1,11.16,14.02); |
|||
yield return new Country("Belgium","WESTERN EUROPE",10379067,30528,340,0.22,1.23,4.68,29100,98,462.6,10.38,10.27); |
|||
yield return new Country("Belize","LATIN AMER. & CARIB",287730,22966,12.5,1.68,0,25.69,4900,94.1,115.7,28.84,5.72); |
|||
yield return new Country("Benin","SUB-SAHARAN AFRICA",7862944,112620,69.8,0.11,0,85,1100,40.9,9.7,38.85,12.22); |
|||
yield return new Country("Bermuda","NORTHERN AMERICA",65773,53,1241,194.34,2.49,8.53,36000,98,851.4,11.4,7.74); |
|||
yield return new Country("Bhutan","ASIA (EX. NEAR EAST)",2279723,47000,48.5,0,0,100.44,1300,42.2,14.3,33.65,12.7); |
|||
yield return new Country("Bolivia","LATIN AMER. & CARIB",8989046,1098580,8.2,0,-1.32,53.11,2400,87.2,71.9,23.3,7.53); |
|||
yield return new Country("Bosnia & Herzegovina","EASTERN EUROPE",4498976,51129,88,0.04,0.31,21.05,6100,,215.4,8.77,8.27); |
|||
yield return new Country("Botswana","SUB-SAHARAN AFRICA",1639833,600370,2.7,0,0,54.58,9000,79.8,80.5,23.08,29.5); |
|||
yield return new Country("Brazil","LATIN AMER. & CARIB",188078227,8511965,22.1,0.09,-0.03,29.61,7600,86.4,225.3,16.56,6.17); |
|||
yield return new Country("British Virgin Is.","LATIN AMER. & CARIB",23098,153,151,52.29,10.01,18.05,16000,97.8,506.5,14.89,4.42); |
|||
yield return new Country("Brunei","ASIA (EX. NEAR EAST)",379444,5770,65.8,2.79,3.59,12.61,18600,93.9,237.2,18.79,3.45); |
|||
yield return new Country("Bulgaria","EASTERN EUROPE",7385367,110910,66.6,0.32,-4.58,20.55,7600,98.6,336.3,9.65,14.27); |
|||
yield return new Country("Burkina Faso","SUB-SAHARAN AFRICA",13902972,274200,50.7,0,0,97.57,1100,26.6,7,45.62,15.6); |
|||
yield return new Country("Burma","ASIA (EX. NEAR EAST)",47382633,678500,69.8,0.28,-1.8,67.24,1800,85.3,10.1,17.91,9.83); |
|||
yield return new Country("Burundi","SUB-SAHARAN AFRICA",8090068,27830,290.7,0,-0.06,69.29,600,51.6,3.4,42.22,13.46); |
|||
yield return new Country("Cambodia","ASIA (EX. NEAR EAST)",13881427,181040,76.7,0.24,0,71.48,1900,69.4,2.6,26.9,9.06); |
|||
yield return new Country("Cameroon","SUB-SAHARAN AFRICA",17340702,475440,36.5,0.08,0,68.26,1800,79,5.7,33.89,13.47); |
|||
yield return new Country("Canada","NORTHERN AMERICA",33098932,9984670,3.3,2.02,5.96,4.75,29800,97,552.2,10.78,7.8); |
|||
yield return new Country("Cape Verde","SUB-SAHARAN AFRICA",420979,4033,104.4,23.93,-12.07,47.77,1400,76.6,169.6,24.87,6.55); |
|||
yield return new Country("Cayman Islands","LATIN AMER. & CARIB",45436,262,173.4,61.07,18.75,8.19,35000,98,836.3,12.74,4.89); |
|||
yield return new Country("Central African Rep.","SUB-SAHARAN AFRICA",4303356,622984,6.9,0,0,91,1100,51,2.3,33.91,18.65); |
|||
yield return new Country("Chad","SUB-SAHARAN AFRICA",9944201,1284000,7.7,0,-0.11,93.82,1200,47.5,1.3,45.73,16.38); |
|||
yield return new Country("Chile","LATIN AMER. & CARIB",16134219,756950,21.3,0.85,0,8.8,9900,96.2,213,15.23,5.81); |
|||
yield return new Country("China","ASIA (EX. NEAR EAST)",1313973713,9596960,136.9,0.15,-0.4,24.18,5000,90.9,266.7,13.25,6.97); |
|||
yield return new Country("Colombia","LATIN AMER. & CARIB",43593035,1138910,38.3,0.28,-0.31,20.97,6300,92.5,176.2,20.48,5.58); |
|||
yield return new Country("Comoros","SUB-SAHARAN AFRICA",690948,2170,318.4,15.67,0,74.93,700,56.5,24.5,36.93,8.2); |
|||
yield return new Country(""Congo"," Dem. Rep."",SUB-SAHARAN AFRICA,62660551,2345410,26.7,0,0,94.69,700,65.5,0.2,43.69,13.27); |
|||
yield return new Country(""Congo"," Repub. of the"",SUB-SAHARAN AFRICA,3702314,342000,10.8,0.05,-0.17,93.86,700,83.8,3.7,42.57,12.93); |
|||
yield return new Country("Cook Islands","OCEANIA",21388,240,89.1,50,,,5000,95,289.9,21,); |
|||
yield return new Country("Costa Rica","LATIN AMER. & CARIB",4075261,51100,79.8,2.52,0.51,9.95,9100,96,340.7,18.32,4.36); |
|||
yield return new Country("Cote d'Ivoire","SUB-SAHARAN AFRICA",17654843,322460,54.8,0.16,-0.07,90.83,1400,50.9,14.6,35.11,14.84); |
|||
yield return new Country("Croatia","EASTERN EUROPE",4494749,56542,79.5,10.32,1.58,6.84,10600,98.5,420.4,9.61,11.48); |
|||
yield return new Country("Cuba","LATIN AMER. & CARIB",11382820,110860,102.7,3.37,-1.58,6.33,2900,97,74.7,11.89,7.22); |
|||
yield return new Country("Cyprus","NEAR EAST",784301,9250,84.8,7.01,0.43,7.18,19200,97.6,,12.56,7.68); |
|||
yield return new Country("Czech Republic","EASTERN EUROPE",10235455,78866,129.8,0,0.97,3.93,15700,99.9,314.3,9.02,10.59); |
|||
yield return new Country("Denmark","WESTERN EUROPE",5450661,43094,126.5,16.97,2.48,4.56,31100,100,614.6,11.13,10.36); |
|||
yield return new Country("Djibouti","SUB-SAHARAN AFRICA",486530,23000,21.2,1.37,0,104.13,1300,67.9,22.8,39.53,19.31); |
|||
yield return new Country("Dominica","LATIN AMER. & CARIB",68910,754,91.4,19.63,-13.87,14.15,5400,94,304.8,15.27,6.73); |
|||
yield return new Country("Dominican Republic","LATIN AMER. & CARIB",9183984,48730,188.5,2.64,-3.22,32.38,6000,84.7,97.4,23.22,5.73); |
|||
yield return new Country("East Timor","ASIA (EX. NEAR EAST)",1062777,15007,70.8,4.7,0,47.41,500,58.6,,26.99,6.24); |
|||
yield return new Country("Ecuador","LATIN AMER. & CARIB",13547510,283560,47.8,0.79,-8.58,23.66,3300,92.5,125.6,22.29,4.23); |
|||
yield return new Country("Egypt","NORTHERN AFRICA",78887007,1001450,78.8,0.24,-0.22,32.59,4000,57.7,131.8,22.94,5.23); |
|||
yield return new Country("El Salvador","LATIN AMER. & CARIB",6822378,21040,324.3,1.46,-3.74,25.1,4800,80.2,142.4,26.61,5.78); |
|||
yield return new Country("Equatorial Guinea","SUB-SAHARAN AFRICA",540109,28051,19.3,1.06,0,85.13,2700,85.7,18.5,35.59,15.06); |
|||
yield return new Country("Eritrea","SUB-SAHARAN AFRICA",4786994,121320,39.5,1.84,0,74.87,700,58.6,7.9,34.33,9.6); |
|||
yield return new Country("Estonia","BALTICS",1324333,45226,29.3,8.39,-3.16,7.87,12300,99.8,333.8,10.04,13.25); |
|||
yield return new Country("Ethiopia","SUB-SAHARAN AFRICA",74777981,1127127,66.3,0,0,95.32,700,42.7,8.2,37.98,14.86); |
|||
yield return new Country("Faroe Islands","WESTERN EUROPE",47246,1399,33.8,79.84,1.41,6.24,22000,,503.8,14.05,8.7); |
|||
yield return new Country("Fiji","OCEANIA",905949,18270,49.6,6.18,-3.14,12.62,5800,93.7,112.6,22.55,5.65); |
|||
yield return new Country("Finland","WESTERN EUROPE",5231372,338145,15.5,0.37,0.95,3.57,27400,100,405.3,10.45,9.86); |
|||
yield return new Country("France","WESTERN EUROPE",60876136,547030,111.3,0.63,0.66,4.26,27600,99,586.4,11.99,9.14); |
|||
yield return new Country("French Guiana","LATIN AMER. & CARIB",199509,91000,2.2,0.42,6.27,12.07,8300,83,255.6,20.46,4.88); |
|||
yield return new Country("French Polynesia","OCEANIA",274578,4167,65.9,60.6,2.94,8.44,17500,98,194.5,16.68,4.69); |
|||
yield return new Country("Gabon","SUB-SAHARAN AFRICA",1424906,267667,5.3,0.33,0,53.64,5500,63.2,27.4,36.16,12.25); |
|||
yield return new Country(""Gambia"," The"",SUB-SAHARAN AFRICA,1641564,11300,145.3,0.71,1.57,72.02,1700,40.1,26.8,39.37,12.25); |
|||
yield return new Country("Gaza Strip","NEAR EAST",1428757,360,3968.8,11.11,1.6,22.93,600,,244.3,39.45,3.8); |
|||
yield return new Country("Georgia","C.W. OF IND. STATES",4661473,69700,66.9,0.44,-4.7,18.59,2500,99,146.6,10.41,9.23); |
|||
yield return new Country("Germany","WESTERN EUROPE",82422299,357021,230.9,0.67,2.18,4.16,27600,99,667.9,8.25,10.62); |
|||
yield return new Country("Ghana","SUB-SAHARAN AFRICA",22409572,239460,93.6,0.23,-0.64,51.43,2200,74.8,14.4,30.52,9.72); |
|||
yield return new Country("Gibraltar","WESTERN EUROPE",27928,7,3989.7,171.43,0,5.13,17500,,877.7,10.74,9.31); |
|||
yield return new Country("Greece","WESTERN EUROPE",10688058,131940,81,10.37,2.35,5.53,20000,97.5,589.7,9.68,10.24); |
|||
yield return new Country("Greenland","NORTHERN AMERICA",56361,2166086,0,2.04,-8.37,15.82,20000,,448.9,15.93,7.84); |
|||
yield return new Country("Grenada","LATIN AMER. & CARIB",89703,344,260.8,35.17,-13.92,14.62,5000,98,364.5,22.08,6.88); |
|||
yield return new Country("Guadeloupe","LATIN AMER. & CARIB",452776,1780,254.4,17.19,-0.15,8.6,8000,90,463.8,15.05,6.09); |
|||
yield return new Country("Guam","OCEANIA",171019,541,316.1,23.2,0,6.94,21000,99,492,18.79,4.48); |
|||
yield return new Country("Guatemala","LATIN AMER. & CARIB",12293545,108890,112.9,0.37,-1.67,35.93,4100,70.6,92.1,29.88,5.2); |
|||
yield return new Country("Guernsey","WESTERN EUROPE",65409,78,838.6,64.1,3.84,4.71,20000,,842.4,8.81,10.01); |
|||
yield return new Country("Guinea","SUB-SAHARAN AFRICA",9690222,245857,39.4,0.13,-3.06,90.37,2100,35.9,2.7,41.76,15.48); |
|||
yield return new Country("Guinea-Bissau","SUB-SAHARAN AFRICA",1442029,36120,39.9,0.97,-1.57,107.17,800,42.4,7.4,37.22,16.53); |
|||
yield return new Country("Guyana","LATIN AMER. & CARIB",767245,214970,3.6,0.21,-2.07,33.26,4000,98.8,143.5,18.28,8.28); |
|||
yield return new Country("Haiti","LATIN AMER. & CARIB",8308504,27750,299.4,6.38,-3.4,73.45,1600,52.9,16.9,36.44,12.17); |
|||
yield return new Country("Honduras","LATIN AMER. & CARIB",7326496,112090,65.4,0.73,-1.99,29.32,2600,76.2,67.5,28.24,5.28); |
|||
yield return new Country("Hong Kong","ASIA (EX. NEAR EAST)",6940432,1092,6355.7,67.12,5.24,2.97,28800,93.5,546.7,7.29,6.29); |
|||
yield return new Country("Hungary","EASTERN EUROPE",9981334,93030,107.3,0,0.86,8.57,13900,99.4,336.2,9.72,13.11); |
|||
yield return new Country("Iceland","WESTERN EUROPE",299388,103000,2.9,4.83,2.38,3.31,30900,99.9,647.7,13.64,6.72); |
|||
yield return new Country("India","ASIA (EX. NEAR EAST)",1095351995,3287590,333.2,0.21,-0.07,56.29,2900,59.5,45.4,22.01,8.18); |
|||
yield return new Country("Indonesia","ASIA (EX. NEAR EAST)",245452739,1919440,127.9,2.85,0,35.6,3200,87.9,52,20.34,6.25); |
|||
yield return new Country("Iran","ASIA (EX. NEAR EAST)",68688433,1648000,41.7,0.15,-0.84,41.58,7000,79.4,276.4,17,5.55); |
|||
yield return new Country("Iraq","NEAR EAST",26783383,437072,61.3,0.01,0,50.25,1500,40.4,38.6,31.98,5.37); |
|||
yield return new Country("Ireland","WESTERN EUROPE",4062235,70280,57.8,2.06,4.99,5.39,29600,98,500.5,14.45,7.82); |
|||
yield return new Country("Isle of Man","WESTERN EUROPE",75441,572,131.9,27.97,5.36,5.93,21000,,676,11.05,11.19); |
|||
yield return new Country("Israel","NEAR EAST",6352117,20770,305.8,1.31,0.68,7.03,19800,95.4,462.3,17.97,6.18); |
|||
yield return new Country("Italy","WESTERN EUROPE",58133509,301230,193,2.52,2.07,5.94,26700,98.6,430.9,8.72,10.4); |
|||
yield return new Country("Jamaica","LATIN AMER. & CARIB",2758124,10991,250.9,9.3,-4.92,12.36,3900,87.9,124,20.82,6.52); |
|||
yield return new Country("Japan","ASIA (EX. NEAR EAST)",127463611,377835,337.4,7.87,0,3.26,28200,99,461.2,9.37,9.16); |
|||
yield return new Country("Jersey","WESTERN EUROPE",91084,116,785.2,60.34,2.76,5.24,24800,,811.3,9.3,9.28); |
|||
yield return new Country("Jordan","NEAR EAST",5906760,92300,64,0.03,6.59,17.35,4300,91.3,104.5,21.25,2.65); |
|||
yield return new Country("Kazakhstan","C.W. OF IND. STATES",15233244,2717300,5.6,0,-3.35,29.21,6300,98.4,164.1,16,9.42); |
|||
yield return new Country("Kenya","SUB-SAHARAN AFRICA",34707817,582650,59.6,0.09,-0.1,61.47,1000,85.1,8.1,39.72,14.02); |
|||
yield return new Country("Kiribati","OCEANIA",105432,811,130,140.94,0,48.52,800,,42.7,30.65,8.26); |
|||
yield return new Country(""Korea"," North"",ASIA (EX. NEAR EAST),23113019,120540,191.8,2.07,0,24.04,1300,99,42.4,15.54,7.13); |
|||
yield return new Country(""Korea"," South"",ASIA (EX. NEAR EAST),48846823,98480,496,2.45,0,7.05,17800,97.9,486.1,10,5.85); |
|||
yield return new Country("Kuwait","NEAR EAST",2418393,17820,135.7,2.8,14.18,9.95,19000,83.5,211,21.94,2.41); |
|||
yield return new Country("Kyrgyzstan","C.W. OF IND. STATES",5213898,198500,26.3,0,-2.45,35.64,1600,97,84,22.8,7.08); |
|||
yield return new Country("Laos","ASIA (EX. NEAR EAST)",6368481,236800,26.9,0,0,85.22,1700,66.4,14.1,35.49,11.55); |
|||
yield return new Country("Latvia","BALTICS",2274735,64589,35.2,0.82,-2.23,9.55,10200,99.8,321.4,9.24,13.66); |
|||
yield return new Country("Lebanon","NEAR EAST",3874050,10400,372.5,2.16,0,24.52,4800,87.4,255.6,18.52,6.21); |
|||
yield return new Country("Lesotho","SUB-SAHARAN AFRICA",2022331,30355,66.6,0,-0.74,84.23,3000,84.8,23.7,24.75,28.71); |
|||
yield return new Country("Liberia","SUB-SAHARAN AFRICA",3042004,111370,27.3,0.52,0,128.87,1000,57.5,2.3,44.77,23.1); |
|||
yield return new Country("Libya","NORTHERN AFRICA",5900754,1759540,3.4,0.1,0,24.6,6400,82.6,127.1,26.49,3.48); |
|||
yield return new Country("Liechtenstein","WESTERN EUROPE",33987,160,212.4,0,4.85,4.7,25000,100,585.5,10.21,7.18); |
|||
yield return new Country("Lithuania","BALTICS",3585906,65200,55,0.14,-0.71,6.89,11400,99.6,223.4,8.75,10.98); |
|||
yield return new Country("Luxembourg","WESTERN EUROPE",474413,2586,183.5,0,8.97,4.81,55100,100,515.4,11.94,8.41); |
|||
yield return new Country("Macau","ASIA (EX. NEAR EAST)",453125,28,16183,146.43,4.86,4.39,19400,94.5,384.9,8.48,4.47); |
|||
yield return new Country("Macedonia","EASTERN EUROPE",2050554,25333,80.9,0,-1.45,10.09,6700,,260,12.02,8.77); |
|||
yield return new Country("Madagascar","SUB-SAHARAN AFRICA",18595469,587040,31.7,0.82,0,76.83,800,68.9,3.6,41.41,11.11); |
|||
yield return new Country("Malawi","SUB-SAHARAN AFRICA",13013926,118480,109.8,0,0,103.32,600,62.7,7.9,43.13,19.33); |
|||
yield return new Country("Malaysia","ASIA (EX. NEAR EAST)",24385858,329750,74,1.42,0,17.7,9000,88.7,179,22.86,5.05); |
|||
yield return new Country("Maldives","ASIA (EX. NEAR EAST)",359008,300,1196.7,214.67,0,56.52,3900,97.2,90,34.81,7.06); |
|||
yield return new Country("Mali","SUB-SAHARAN AFRICA",11716829,1240000,9.5,0,-0.33,116.79,900,46.4,6.4,49.82,16.89); |
|||
yield return new Country("Malta","WESTERN EUROPE",400214,316,1266.5,62.28,2.07,3.89,17700,92.8,505,10.22,8.1); |
|||
yield return new Country("Marshall Islands","OCEANIA",60422,11854,5.1,3.12,-6.04,29.45,1600,93.7,91.2,33.05,4.78); |
|||
yield return new Country("Martinique","LATIN AMER. & CARIB",436131,1100,396.5,31.82,-0.05,7.09,14400,97.7,394.4,13.74,6.48); |
|||
yield return new Country("Mauritania","SUB-SAHARAN AFRICA",3177388,1030700,3.1,0.07,0,70.89,1800,41.7,12.9,40.99,12.16); |
|||
yield return new Country("Mauritius","SUB-SAHARAN AFRICA",1240827,2040,608.3,8.68,-0.9,15.03,11400,85.6,289.3,15.43,6.86); |
|||
yield return new Country("Mayotte","SUB-SAHARAN AFRICA",201234,374,538.1,49.52,6.78,62.4,2600,,49.7,40.95,7.7); |
|||
yield return new Country("Mexico","LATIN AMER. & CARIB",107449525,1972550,54.5,0.47,-4.87,20.91,9000,92.2,181.6,20.69,4.74); |
|||
yield return new Country(""Micronesia"," Fed. St."",OCEANIA,108004,702,153.9,870.66,-20.99,30.21,2000,89,114.8,24.68,4.75); |
|||
yield return new Country("Moldova","C.W. OF IND. STATES",4466706,33843,132,0,-0.26,40.42,1800,99.1,208.1,15.7,12.64); |
|||
yield return new Country("Monaco","WESTERN EUROPE",32543,2,16271.5,205,7.75,5.43,27000,99,1035.6,9.19,12.91); |
|||
yield return new Country("Mongolia","ASIA (EX. NEAR EAST)",2832224,1564116,1.8,0,0,53.79,1800,97.8,55.1,21.59,6.95); |
|||
yield return new Country("Montserrat","LATIN AMER. & CARIB",9439,102,92.5,39.22,0,7.35,3400,97,,17.59,7.1); |
|||
yield return new Country("Morocco","NORTHERN AFRICA",33241259,446550,74.4,0.41,-0.98,41.62,4000,51.7,40.4,21.98,5.58); |
|||
yield return new Country("Mozambique","SUB-SAHARAN AFRICA",19686505,801590,24.6,0.31,0,130.79,1200,47.8,3.5,35.18,21.35); |
|||
yield return new Country("Namibia","SUB-SAHARAN AFRICA",2044147,825418,2.5,0.19,0,48.98,7200,84,62.6,24.32,18.86); |
|||
yield return new Country("Nauru","OCEANIA",13287,21,632.7,142.86,0,9.95,5000,,143,24.76,6.7); |
|||
yield return new Country("Nepal","ASIA (EX. NEAR EAST)",28287147,147181,192.2,0,0,66.98,1400,45.2,15.9,30.98,9.31); |
|||
yield return new Country("Netherlands","WESTERN EUROPE",16491461,41526,397.1,1.09,2.91,5.04,28600,99,460.8,10.9,8.68); |
|||
yield return new Country("Netherlands Antilles","LATIN AMER. & CARIB",221736,960,231,37.92,-0.41,10.03,11400,96.7,365.3,14.78,6.45); |
|||
yield return new Country("New Caledonia","OCEANIA",219246,19060,11.5,11.83,0,7.72,15000,91,252.2,18.11,5.69); |
|||
yield return new Country("New Zealand","OCEANIA",4076140,268680,15.2,5.63,4.05,5.85,21600,99,441.7,13.76,7.53); |
|||
yield return new Country("Nicaragua","LATIN AMER. & CARIB",5570129,129494,43,0.7,-1.22,29.11,2300,67.5,39.7,24.51,4.45); |
|||
yield return new Country("Niger","SUB-SAHARAN AFRICA",12525094,1267000,9.9,0,-0.67,121.69,800,17.6,1.9,50.73,20.91); |
|||
yield return new Country("Nigeria","SUB-SAHARAN AFRICA",131859731,923768,142.7,0.09,0.26,98.8,900,68,9.3,40.43,16.94); |
|||
yield return new Country("N. Mariana Islands","OCEANIA",82459,477,172.9,310.69,9.61,7.11,12500,97,254.7,19.43,2.29); |
|||
yield return new Country("Norway","WESTERN EUROPE",4610820,323802,14.2,7.77,1.74,3.7,37800,100,461.7,11.46,9.4); |
|||
yield return new Country("Oman","NEAR EAST",3102229,212460,14.6,0.98,0.28,19.51,13100,75.8,85.5,36.24,3.81); |
|||
yield return new Country("Pakistan","ASIA (EX. NEAR EAST)",165803560,803940,206.2,0.13,-2.77,72.44,2100,45.7,31.8,29.74,8.23); |
|||
yield return new Country("Palau","OCEANIA",20579,458,44.9,331.66,2.85,14.84,9000,92,325.6,18.03,6.8); |
|||
yield return new Country("Panama","LATIN AMER. & CARIB",3191319,78200,40.8,3.18,-0.91,20.47,6300,92.6,137.9,21.74,5.36); |
|||
yield return new Country("Papua New Guinea","OCEANIA",5670544,462840,12.3,1.11,0,51.45,2200,64.6,10.9,29.36,7.25); |
|||
yield return new Country("Paraguay","LATIN AMER. & CARIB",6506464,406750,16,0,-0.08,25.63,4700,94,49.2,29.1,4.49); |
|||
yield return new Country("Peru","LATIN AMER. & CARIB",28302603,1285220,22,0.19,-1.05,31.94,5100,90.9,79.5,20.48,6.23); |
|||
yield return new Country("Philippines","ASIA (EX. NEAR EAST)",89468677,300000,298.2,12.1,-1.5,23.51,4600,92.6,38.4,24.89,5.41); |
|||
yield return new Country("Poland","EASTERN EUROPE",38536869,312685,123.3,0.16,-0.49,8.51,11100,99.8,306.3,9.85,9.89); |
|||
yield return new Country("Portugal","WESTERN EUROPE",10605870,92391,114.8,1.94,3.57,5.05,18000,93.3,399.2,10.72,10.5); |
|||
yield return new Country("Puerto Rico","LATIN AMER. & CARIB",3927188,13790,284.8,3.63,-1.46,8.24,16800,94.1,283.1,12.77,7.65); |
|||
yield return new Country("Qatar","NEAR EAST",885359,11437,77.4,4.92,16.29,18.61,21500,82.5,232,15.56,4.72); |
|||
yield return new Country("Reunion","SUB-SAHARAN AFRICA",787584,2517,312.9,8.22,0,7.78,5800,88.9,380.9,18.9,5.49); |
|||
yield return new Country("Romania","EASTERN EUROPE",22303552,237500,93.9,0.09,-0.13,26.43,7000,98.4,196.9,10.7,11.77); |
|||
yield return new Country("Russia","C.W. OF IND. STATES",142893540,17075200,8.4,0.22,1.02,15.39,8900,99.6,280.6,9.95,14.65); |
|||
yield return new Country("Rwanda","SUB-SAHARAN AFRICA",8648248,26338,328.4,0,0,91.23,1300,70.4,2.7,40.37,16.09); |
|||
yield return new Country("Saint Helena","SUB-SAHARAN AFRICA",7502,413,18.2,14.53,0,19,2500,97,293.3,12.13,6.53); |
|||
yield return new Country("Saint Kitts & Nevis","LATIN AMER. & CARIB",39129,261,149.9,51.72,-7.11,14.49,8800,97,638.9,18.02,8.33); |
|||
yield return new Country("Saint Lucia","LATIN AMER. & CARIB",168458,616,273.5,25.65,-2.67,13.53,5400,67,303.3,19.68,5.08); |
|||
yield return new Country("St Pierre & Miquelon","NORTHERN AMERICA",7026,242,29,49.59,-4.86,7.54,6900,99,683.2,13.52,6.83); |
|||
yield return new Country("Saint Vincent and the Grenadines","LATIN AMER. & CARIB",117848,389,303,21.59,-7.64,14.78,2900,96,190.9,16.18,5.98); |
|||
yield return new Country("Samoa","OCEANIA",176908,2944,60.1,13.69,-11.7,27.71,5600,99.7,75.2,16.43,6.62); |
|||
yield return new Country("San Marino","WESTERN EUROPE",29251,61,479.5,0,10.98,5.73,34600,96,704.3,10.02,8.17); |
|||
yield return new Country("Sao Tome & Principe","SUB-SAHARAN AFRICA",193413,1001,193.2,20.88,-2.72,43.11,1200,79.3,36.2,40.25,6.47); |
|||
yield return new Country("Saudi Arabia","NEAR EAST",27019731,1960582,13.8,0.13,-2.71,13.24,11800,78.8,140.6,29.34,2.58); |
|||
yield return new Country("Senegal","SUB-SAHARAN AFRICA",11987121,196190,61.1,0.27,0.2,55.51,1600,40.2,22.2,32.78,9.42); |
|||
yield return new Country("Serbia","EASTERN EUROPE",9396411,88361,106.3,0,-1.33,12.89,2200,93,285.8,,); |
|||
yield return new Country("Seychelles","SUB-SAHARAN AFRICA",81541,455,179.2,107.91,-5.69,15.53,7800,58,262.4,16.03,6.29); |
|||
yield return new Country("Sierra Leone","SUB-SAHARAN AFRICA",6005250,71740,83.7,0.56,0,143.64,500,31.4,4,45.76,23.03); |
|||
yield return new Country("Singapore","ASIA (EX. NEAR EAST)",4492150,693,6482.2,27.85,11.53,2.29,23700,92.5,411.4,9.34,4.28); |
|||
yield return new Country("Slovakia","EASTERN EUROPE",5439448,48845,111.4,0,0.3,7.41,13300,,220.1,10.65,9.45); |
|||
yield return new Country("Slovenia","EASTERN EUROPE",2010347,20273,99.2,0.23,1.12,4.45,19000,99.7,406.1,8.98,10.31); |
|||
yield return new Country("Solomon Islands","OCEANIA",552438,28450,19.4,18.67,0,21.29,1700,,13.4,30.01,3.92); |
|||
yield return new Country("Somalia","SUB-SAHARAN AFRICA",8863338,637657,13.9,0.47,5.37,116.7,500,37.8,11.3,45.13,16.63); |
|||
yield return new Country("South Africa","SUB-SAHARAN AFRICA",44187637,1219912,36.2,0.23,-0.29,61.81,10700,86.4,107,18.2,22); |
|||
yield return new Country("Spain","WESTERN EUROPE",40397842,504782,80,0.98,0.99,4.42,22000,97.9,453.5,10.06,9.72); |
|||
yield return new Country("Sri Lanka","ASIA (EX. NEAR EAST)",20222240,65610,308.2,2.04,-1.31,14.35,3700,92.3,61.5,15.51,6.52); |
|||
yield return new Country("Sudan","SUB-SAHARAN AFRICA",41236378,2505810,16.5,0.03,-0.02,62.5,1900,61.1,16.3,34.53,8.97); |
|||
yield return new Country("Suriname","LATIN AMER. & CARIB",439117,163270,2.7,0.24,-8.81,23.57,4000,93,184.7,18.02,7.27); |
|||
yield return new Country("Swaziland","SUB-SAHARAN AFRICA",1136334,17363,65.5,0,0,69.27,4900,81.6,30.8,27.41,29.74); |
|||
yield return new Country("Sweden","WESTERN EUROPE",9016596,449964,20,0.72,1.67,2.77,26800,99,715,10.27,10.31); |
|||
yield return new Country("Switzerland","WESTERN EUROPE",7523934,41290,182.2,0,4.05,4.39,32700,99,680.9,9.71,8.49); |
|||
yield return new Country("Syria","NEAR EAST",18881361,185180,102,0.1,0,29.53,3300,76.9,153.8,27.76,4.81); |
|||
yield return new Country("Taiwan","ASIA (EX. NEAR EAST)",23036087,35980,640.3,4.35,0,6.4,23400,96.1,591,12.56,6.48); |
|||
yield return new Country("Tajikistan","C.W. OF IND. STATES",7320815,143100,51.2,0,-2.86,110.76,1000,99.4,33.5,32.65,8.25); |
|||
yield return new Country("Tanzania","SUB-SAHARAN AFRICA",37445392,945087,39.6,0.15,-2.06,98.54,600,78.2,4,37.71,16.39); |
|||
yield return new Country("Thailand","ASIA (EX. NEAR EAST)",64631595,514000,125.7,0.63,0,20.48,7400,92.6,108.9,13.87,7.04); |
|||
yield return new Country("Togo","SUB-SAHARAN AFRICA",5548702,56785,97.7,0.1,0,66.61,1500,60.9,10.6,37.01,9.83); |
|||
yield return new Country("Tonga","OCEANIA",114689,748,153.3,56.02,0,12.62,2200,98.5,97.7,25.37,5.28); |
|||
yield return new Country("Trinidad & Tobago","LATIN AMER. & CARIB",1065842,5128,207.9,7.06,-10.83,24.31,9500,98.6,303.5,12.9,10.57); |
|||
yield return new Country("Tunisia","NORTHERN AFRICA",10175014,163610,62.2,0.7,-0.57,24.77,6900,74.2,123.6,15.52,5.13); |
|||
yield return new Country("Turkey","NEAR EAST",70413958,780580,90.2,0.92,0,41.04,6700,86.5,269.5,16.62,5.97); |
|||
yield return new Country("Turkmenistan","C.W. OF IND. STATES",5042920,488100,10.3,0,-0.86,73.08,5800,98,74.6,27.61,8.6); |
|||
yield return new Country("Turks & Caicos Is","LATIN AMER. & CARIB",21152,430,49.2,90.47,11.68,15.67,9600,98,269.5,21.84,4.21); |
|||
yield return new Country("Tuvalu","OCEANIA",11810,26,454.2,92.31,0,20.03,1100,,59.3,22.18,7.11); |
|||
yield return new Country("Uganda","SUB-SAHARAN AFRICA",28195754,236040,119.5,0,0,67.83,1400,69.9,3.6,47.35,12.24); |
|||
yield return new Country("Ukraine","C.W. OF IND. STATES",46710816,603700,77.4,0.46,-0.39,20.34,5400,99.7,259.9,8.82,14.39); |
|||
yield return new Country("United Arab Emirates","NEAR EAST",2602713,82880,31.4,1.59,1.03,14.51,23200,77.9,475.3,18.96,4.4); |
|||
yield return new Country("United Kingdom","WESTERN EUROPE",60609153,244820,247.6,5.08,2.19,5.16,27700,99,543.5,10.71,10.13); |
|||
yield return new Country("United States","NORTHERN AMERICA",298444215,9631420,31,0.21,3.41,6.5,37800,97,898,14.14,8.26); |
|||
yield return new Country("Uruguay","LATIN AMER. & CARIB",3431932,176220,19.5,0.37,-0.32,11.95,12800,98,291.4,13.91,9.05); |
|||
yield return new Country("Uzbekistan","C.W. OF IND. STATES",27307134,447400,61,0,-1.72,71.1,1700,99.3,62.9,26.36,7.84); |
|||
yield return new Country("Vanuatu","OCEANIA",208869,12200,17.1,20.72,0,55.16,2900,53,32.6,22.72,7.82); |
|||
yield return new Country("Venezuela","LATIN AMER. & CARIB",25730435,912050,28.2,0.31,-0.04,22.2,4800,93.4,140.1,18.71,4.92); |
|||
yield return new Country("Vietnam","ASIA (EX. NEAR EAST)",84402966,329560,256.1,1.05,-0.45,25.95,2500,90.3,187.7,16.86,6.22); |
|||
yield return new Country("Virgin Islands","LATIN AMER. & CARIB",108605,1910,56.9,9.84,-8.94,8.03,17200,,652.8,13.96,6.43); |
|||
yield return new Country("Wallis and Futuna","OCEANIA",16025,274,58.5,47.08,,,3700,50,118.6,,); |
|||
yield return new Country("West Bank","NEAR EAST",2460492,5860,419.9,0,2.98,19.62,800,,145.2,31.67,3.92); |
|||
yield return new Country("Western Sahara","NORTHERN AFRICA",273008,266000,1,0.42,,,,,,,); |
|||
yield return new Country("Yemen","NEAR EAST",21456188,527970,40.6,0.36,0,61.5,800,50.2,37.2,42.89,8.3); |
|||
yield return new Country("Zambia","SUB-SAHARAN AFRICA",11502010,752614,15.3,0,0,88.29,800,80.6,8.2,41,19.93); |
|||
yield return new Country("Zimbabwe","SUB-SAHARAN AFRICA",12236805,390580,31.3,0,0,67.69,1900,90.7,26.8,28.01,21.84); |
|||
|
|||
|
|||
*/ |
|||
|
|||
} |
|||
} |
|||
@ -0,0 +1,22 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
<PropertyGroup> |
|||
<OutputType>Exe</OutputType> |
|||
<TargetFrameworks>netcoreapp2.0;net461</TargetFrameworks> |
|||
</PropertyGroup> |
|||
<ItemGroup> |
|||
<Compile Update="**\*.xaml.cs"> |
|||
<DependentUpon>%(Filename)</DependentUpon> |
|||
</Compile> |
|||
<EmbeddedResource Include="**\*.xaml"> |
|||
<SubType>Designer</SubType> |
|||
</EmbeddedResource> |
|||
</ItemGroup> |
|||
<ItemGroup> |
|||
<PackageReference Include="Avalonia" Version="0.6.2-build5521-beta" /> |
|||
<PackageReference Include="Avalonia.Desktop" Version="0.6.2-build5521-beta" /> |
|||
</ItemGroup> |
|||
<ItemGroup> |
|||
<ProjectReference Include="..\..\src\Avalonia.DataGrid.Themes.Default\Avalonia.DataGrid.Themes.Default.csproj" /> |
|||
<ProjectReference Include="..\..\src\Avalonia.DataGrid\Avalonia.DataGrid.csproj" /> |
|||
</ItemGroup> |
|||
</Project> |
|||
@ -0,0 +1,187 @@ |
|||
<Window xmlns="https://github.com/avaloniaui" |
|||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" |
|||
xmlns:dg="clr-namespace:Avalonia.Controls;assembly=Avalonia.DataGrid" |
|||
xmlns:local="clr-namespace:DataGridSample;assembly=DataGridSample" |
|||
Title="DataGridSample" |
|||
Width="800" |
|||
Height="600"> |
|||
|
|||
<Window.Resources> |
|||
<local:GDPValueConverter |
|||
x:Key="GDPConverter"/> |
|||
</Window.Resources> |
|||
|
|||
<Window.Styles> |
|||
<Style Selector="dg|DataGridCell.gdp"> |
|||
<Setter |
|||
Property="FontWeight" |
|||
Value="Bold"/> |
|||
<Setter |
|||
Property="Background" |
|||
Value="{Binding Path=GDP, Mode=OneWay, Converter={StaticResource GDPConverter}}"/> |
|||
</Style> |
|||
</Window.Styles> |
|||
|
|||
<TabControl> |
|||
<TabItem Header="Page 1"> |
|||
<dg:DataGrid |
|||
Name="dataGrid1" |
|||
Margin="12" |
|||
CanUserResizeColumns="True" |
|||
CanUserReorderColumns="True"> |
|||
|
|||
<dg:DataGrid.Columns> |
|||
|
|||
<dg:DataGridTextColumn |
|||
Header="Country" |
|||
Binding="{Binding Name}" |
|||
Width="6*" /> |
|||
<dg:DataGridTextColumn |
|||
Header="Region" |
|||
Binding="{Binding Region}" |
|||
Width="4*"/> |
|||
<dg:DataGridTextColumn |
|||
Header="Population" |
|||
Binding="{Binding Population}" |
|||
Width="3*"/> |
|||
<dg:DataGridTextColumn |
|||
Header="Area" |
|||
Binding="{Binding Area}" |
|||
Width="3*"/> |
|||
<dg:DataGridTextColumn |
|||
Header="GDP" |
|||
Binding="{Binding GDP}" |
|||
Width="3*" |
|||
CellStyleClasses="gdp"/> |
|||
|
|||
</dg:DataGrid.Columns> |
|||
|
|||
</dg:DataGrid> |
|||
</TabItem> |
|||
|
|||
<TabItem Header="Page 2"> |
|||
<dg:DataGrid |
|||
Name="dataGridGrouping" |
|||
Margin="12"> |
|||
|
|||
<dg:DataGrid.Columns> |
|||
|
|||
<dg:DataGridTextColumn |
|||
Header="Country" |
|||
Binding="{Binding Name}" |
|||
Width="6*" /> |
|||
<dg:DataGridTextColumn |
|||
Header="Region" |
|||
Binding="{Binding Region}" |
|||
Width="4*"/> |
|||
<dg:DataGridTextColumn |
|||
Header="Population" |
|||
Binding="{Binding Population}" |
|||
Width="3*"/> |
|||
<dg:DataGridTextColumn |
|||
Header="Area" |
|||
Binding="{Binding Area}" |
|||
Width="3*"/> |
|||
<dg:DataGridTextColumn |
|||
Header="GDP" |
|||
Binding="{Binding GDP}" |
|||
Width="3*"/> |
|||
|
|||
</dg:DataGrid.Columns> |
|||
|
|||
</dg:DataGrid> |
|||
</TabItem> |
|||
|
|||
<TabItem Header="Page 3"> |
|||
<Grid |
|||
RowDefinitions="*,Auto"> |
|||
<dg:DataGrid |
|||
Name="dataGridEdit" |
|||
Margin="12" |
|||
Grid.Row="0"> |
|||
|
|||
<dg:DataGrid.Columns> |
|||
<dg:DataGridTextColumn |
|||
Header="First Name" |
|||
Binding="{Binding FirstName}" |
|||
Width="2*" /> |
|||
<dg:DataGridTextColumn |
|||
Header="Last" |
|||
Binding="{Binding LastName}" |
|||
Width="*"/> |
|||
</dg:DataGrid.Columns> |
|||
</dg:DataGrid> |
|||
<Button |
|||
Grid.Row="1" |
|||
Name="btnAdd" |
|||
Margin="12,0,12,12" |
|||
Content="Add" |
|||
HorizontalAlignment="Right"/> |
|||
</Grid> |
|||
</TabItem> |
|||
|
|||
<TabItem Header="Grid Test"> |
|||
|
|||
<Grid |
|||
ColumnDefinitions="*,Auto,*" |
|||
RowDefinitions="Auto,Auto" |
|||
HorizontalAlignment="Stretch" |
|||
VerticalAlignment="Top"> |
|||
|
|||
<Border |
|||
BorderBrush="Black" |
|||
BorderThickness="1" |
|||
Grid.Column="0" |
|||
Grid.Row="0"> |
|||
|
|||
<TextBlock |
|||
TextWrapping="Wrap" |
|||
Text="Hello World!"/> |
|||
|
|||
</Border> |
|||
|
|||
<Border |
|||
BorderBrush="Black" |
|||
BorderThickness="1" |
|||
Grid.Column="1" |
|||
Grid.Row="0"> |
|||
|
|||
<TextBlock |
|||
TextWrapping="NoWrap" |
|||
Text="TEST"/> |
|||
|
|||
</Border> |
|||
|
|||
|
|||
<Border |
|||
BorderBrush="Black" |
|||
BorderThickness="1" |
|||
Grid.Column="2" |
|||
Grid.Row="0"> |
|||
|
|||
<TextBlock |
|||
TextWrapping="Wrap" |
|||
Text="Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello "/> |
|||
|
|||
</Border> |
|||
|
|||
<Border |
|||
BorderBrush="Black" |
|||
BorderThickness="1" |
|||
Grid.Column="0" |
|||
Grid.Row="2"> |
|||
|
|||
<TextBlock |
|||
TextWrapping="NoWrap" |
|||
Text="TEST2"/> |
|||
|
|||
</Border> |
|||
|
|||
</Grid> |
|||
|
|||
</TabItem> |
|||
|
|||
|
|||
</TabControl> |
|||
|
|||
</Window> |
|||
@ -0,0 +1,168 @@ |
|||
using Avalonia; |
|||
using Avalonia.Controls; |
|||
using Avalonia.Markup.Xaml; |
|||
using System; |
|||
using System.Collections; |
|||
using System.Collections.Generic; |
|||
using System.ComponentModel; |
|||
using System.Globalization; |
|||
using System.Linq; |
|||
using Avalonia.Media; |
|||
|
|||
namespace DataGridSample |
|||
{ |
|||
public class Person : System.ComponentModel.INotifyDataErrorInfo, INotifyPropertyChanged |
|||
{ |
|||
string _firstName; |
|||
string _lastName; |
|||
|
|||
public string FirstName |
|||
{ |
|||
get => _firstName; |
|||
set |
|||
{ |
|||
if (string.IsNullOrWhiteSpace(value)) |
|||
SetError(nameof(FirstName), "First Name Required"); |
|||
else |
|||
SetError(nameof(FirstName), null); |
|||
|
|||
_firstName = value; |
|||
OnPropertyChanged(nameof(FirstName)); |
|||
} |
|||
|
|||
} |
|||
|
|||
public string LastName |
|||
{ |
|||
get => _lastName; |
|||
set |
|||
{ |
|||
if (string.IsNullOrWhiteSpace(value)) |
|||
SetError(nameof(LastName), "Last Name Required"); |
|||
else |
|||
SetError(nameof(LastName), null); |
|||
|
|||
_lastName = value; |
|||
OnPropertyChanged(nameof(LastName)); |
|||
} |
|||
} |
|||
|
|||
Dictionary<string, List<string>> _errorLookup = new Dictionary<string, List<string>>(); |
|||
|
|||
void SetError(string propertyName, string error) |
|||
{ |
|||
if (string.IsNullOrEmpty(error)) |
|||
{ |
|||
if (_errorLookup.Remove(propertyName)) |
|||
OnErrorsChanged(propertyName); |
|||
} |
|||
else |
|||
{ |
|||
if (_errorLookup.TryGetValue(propertyName, out List<string> errorList)) |
|||
{ |
|||
errorList.Clear(); |
|||
errorList.Add(error); |
|||
} |
|||
else |
|||
{ |
|||
var errors = new List<string> { error }; |
|||
_errorLookup.Add(propertyName, errors); |
|||
} |
|||
|
|||
OnErrorsChanged(propertyName); |
|||
} |
|||
} |
|||
|
|||
public bool HasErrors => _errorLookup.Count > 0; |
|||
|
|||
public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged; |
|||
public event PropertyChangedEventHandler PropertyChanged; |
|||
|
|||
void OnErrorsChanged(string propertyName) |
|||
{ |
|||
ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(propertyName)); |
|||
} |
|||
void OnPropertyChanged(string propertyName) |
|||
{ |
|||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); |
|||
} |
|||
|
|||
public IEnumerable GetErrors(string propertyName) |
|||
{ |
|||
if (_errorLookup.TryGetValue(propertyName, out List<string> errorList)) |
|||
return errorList; |
|||
else |
|||
return null; |
|||
} |
|||
} |
|||
|
|||
public class GDPValueConverter : Avalonia.Data.Converters.IValueConverter |
|||
{ |
|||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) |
|||
{ |
|||
if(value is int gdp) |
|||
{ |
|||
if (gdp <= 5000) |
|||
return Brushes.Orange; |
|||
else if (gdp <= 10000) |
|||
return Brushes.Yellow; |
|||
else |
|||
return Brushes.LightGreen; |
|||
} |
|||
|
|||
return value; |
|||
} |
|||
|
|||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) |
|||
{ |
|||
throw new NotImplementedException(); |
|||
} |
|||
} |
|||
|
|||
public class MainWindow : Window |
|||
{ |
|||
public MainWindow() |
|||
{ |
|||
InitializeComponent(); |
|||
#if DEBUG
|
|||
this.AttachDevTools(); |
|||
#endif
|
|||
var dg1 = this.FindControl<DataGrid>("dataGrid1"); |
|||
dg1.IsReadOnly = true; |
|||
|
|||
var collectionView1 = new Avalonia.Collections.CollectionViewBase(Countries.All); |
|||
//collectionView.GroupDescriptions.Add(new Avalonia.Collections.PathGroupDescription("Region"));
|
|||
|
|||
dg1.Items = collectionView1; |
|||
|
|||
var dg2 = this.FindControl<DataGrid>("dataGridGrouping"); |
|||
dg2.IsReadOnly = true; |
|||
|
|||
var collectionView2 = new Avalonia.Collections.CollectionViewBase(Countries.All); |
|||
collectionView2.GroupDescriptions.Add(new Avalonia.Collections.PathGroupDescription("Region")); |
|||
|
|||
dg2.Items = collectionView2; |
|||
|
|||
var dg3 = this.FindControl<DataGrid>("dataGridEdit"); |
|||
dg3.IsReadOnly = false; |
|||
|
|||
var items = new List<Person> |
|||
{ |
|||
new Person { FirstName = "John", LastName = "Doe" }, |
|||
new Person { FirstName = "Elizabeth", LastName = "Thomas" }, |
|||
new Person { FirstName = "Zack", LastName = "Ward" } |
|||
}; |
|||
var collectionView3 = new Avalonia.Collections.CollectionViewBase(items); |
|||
|
|||
dg3.Items = collectionView3; |
|||
|
|||
var addButton = this.FindControl<Button>("btnAdd"); |
|||
addButton.Click += (a, b) => collectionView3.AddNew(); |
|||
} |
|||
|
|||
private void InitializeComponent() |
|||
{ |
|||
AvaloniaXamlLoader.Load(this); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,19 @@ |
|||
using System; |
|||
using Avalonia; |
|||
using Avalonia.Logging.Serilog; |
|||
|
|||
namespace DataGridSample |
|||
{ |
|||
class Program |
|||
{ |
|||
static void Main(string[] args) |
|||
{ |
|||
BuildAvaloniaApp().Start<MainWindow>(); |
|||
} |
|||
|
|||
public static AppBuilder BuildAvaloniaApp() |
|||
=> AppBuilder.Configure<App>() |
|||
.UsePlatformDetect() |
|||
.LogToDebug(); |
|||
} |
|||
} |
|||
@ -0,0 +1,29 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netstandard2.0</TargetFramework> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Avalonia" Version="0.6.2-build5521-beta" /> |
|||
</ItemGroup> |
|||
<ItemGroup> |
|||
<Compile Update="**\*.xaml.cs"> |
|||
<DependentUpon>%(Filename)</DependentUpon> |
|||
</Compile> |
|||
<EmbeddedResource Include="**\*.xaml"> |
|||
<SubType>Designer</SubType> |
|||
</EmbeddedResource> |
|||
</ItemGroup> |
|||
<ItemGroup> |
|||
<None Remove="DefaultTheme.xaml" /> |
|||
</ItemGroup> |
|||
<ItemGroup> |
|||
<ProjectReference Include="..\Avalonia.DataGrid\Avalonia.DataGrid.csproj" /> |
|||
</ItemGroup> |
|||
<ItemGroup> |
|||
<EmbeddedResource Update="DefaultTheme.xaml"> |
|||
<Generator>MSBuild:Compile</Generator> |
|||
</EmbeddedResource> |
|||
</ItemGroup> |
|||
</Project> |
|||
@ -0,0 +1,865 @@ |
|||
<Styles |
|||
xmlns="https://github.com/avaloniaui" |
|||
xmlns:dg="clr-namespace:Avalonia.Controls;assembly=Avalonia.DataGrid" |
|||
xmlns:prim="clr-namespace:Avalonia.Controls.Primitives;assembly=Avalonia.DataGrid"> |
|||
|
|||
<Style Selector="dg|DataGridCell"> |
|||
<Setter Property="Background" Value="Transparent"/> |
|||
<Setter Property="HorizontalContentAlignment" Value="Stretch" /> |
|||
<Setter Property="VerticalContentAlignment" Value="Stretch" /> |
|||
<Setter Property="Template"> |
|||
<ControlTemplate> |
|||
<Grid ColumnDefinitions="*,Auto" |
|||
Background="{TemplateBinding Background}"> |
|||
<ContentPresenter ContentTemplate="{TemplateBinding ContentTemplate}" |
|||
Content="{TemplateBinding Content}" |
|||
Margin="{TemplateBinding Padding}" |
|||
TextBlock.Foreground="{TemplateBinding Foreground}" |
|||
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" |
|||
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/> |
|||
|
|||
<Rectangle Name="PART_RightGridLine" |
|||
Grid.Column="1" |
|||
VerticalAlignment="Stretch" |
|||
Width="1" /> |
|||
</Grid> |
|||
</ControlTemplate> |
|||
</Setter> |
|||
</Style> |
|||
<!-- |
|||
<Style TargetType="local:DataGridCell"> |
|||
<Setter Property="IsTabStop" Value="False" /> |
|||
<Setter Property="Template"> |
|||
<Setter.Value> |
|||
<ControlTemplate TargetType="local:DataGridCell"> |
|||
<Grid Name="Root" Background="{TemplateBinding Background}"> |
|||
|
|||
|
|||
<Grid.ColumnDefinitions> |
|||
<ColumnDefinition /> |
|||
<ColumnDefinition Width="Auto" /> |
|||
</Grid.ColumnDefinitions> |
|||
|
|||
<Rectangle Name="FocusVisual" Stroke="#FF6DBDD1" StrokeThickness="1" Fill="#66FFFFFF" HorizontalAlignment="Stretch" |
|||
VerticalAlignment="Stretch" IsHitTestVisible="false" Opacity="0" /> |
|||
|
|||
<ContentPresenter |
|||
Content="{TemplateBinding Content}" |
|||
ContentTemplate="{TemplateBinding ContentTemplate}" |
|||
Margin="{TemplateBinding Padding}" /> |
|||
|
|||
<Rectangle x:Name="InvalidVisualElement" IsHitTestVisible="False" StrokeThickness="1" Stroke="#FFDC000C" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Opacity="0"/> |
|||
|
|||
<Rectangle Name="RightGridLine" Grid.Column="1" VerticalAlignment="Stretch" Width="1" /> |
|||
</Grid> |
|||
</ControlTemplate> |
|||
</Setter.Value> |
|||
</Setter> |
|||
</Style> |
|||
|
|||
--> |
|||
<!-- |
|||
<vsm:VisualStateManager.VisualStateGroups> |
|||
<vsm:VisualStateGroup x:Name="CurrentStates"> |
|||
<vsm:VisualState x:Name="Regular" /> |
|||
<vsm:VisualState x:Name="Current"> |
|||
<Storyboard> |
|||
<DoubleAnimation Storyboard.TargetName="FocusVisual" Storyboard.TargetProperty="Opacity" To="1" Duration="0" /> |
|||
</Storyboard> |
|||
</vsm:VisualState> |
|||
</vsm:VisualStateGroup> |
|||
<vsm:VisualStateGroup x:Name="ValidationStates"> |
|||
<vsm:VisualState x:Name="Valid"/> |
|||
<vsm:VisualState x:Name="Invalid"> |
|||
<Storyboard> |
|||
<DoubleAnimation Storyboard.TargetName="InvalidVisualElement" Storyboard.TargetProperty="Opacity" Duration="0" To="1"/> |
|||
<ColorAnimation Storyboard.TargetName="FocusVisual" Storyboard.TargetProperty="(Fill).Color" Duration="0" To="#FFFFFFFF"/> |
|||
</Storyboard> |
|||
</vsm:VisualState> |
|||
</vsm:VisualStateGroup> |
|||
</vsm:VisualStateManager.VisualStateGroups> |
|||
--> |
|||
|
|||
|
|||
<!--FIX--> |
|||
<Style Selector="dg|DataGridColumnHeader"> |
|||
<Setter Property="Foreground" Value="{DynamicResource ThemeForegroundBrush}" /> |
|||
<Setter Property="HorizontalContentAlignment" Value="Left" /> |
|||
<Setter Property="VerticalContentAlignment" Value="Center" /> |
|||
<Setter Property="SeparatorBrush" Value="{DynamicResource ThemeControlDarkBrush}" /> |
|||
<Setter Property="Padding" Value="4" /> |
|||
<Setter Property="Background" Value="{DynamicResource ThemeControlMidBrush}"/> |
|||
|
|||
<Setter Property="Template"> |
|||
<ControlTemplate> |
|||
<Grid ColumnDefinitions="*,Auto" |
|||
Background="{TemplateBinding Background}"> |
|||
|
|||
<!--<Rectangle Name="BackgroundRectangle" Stretch="Fill" Fill="#FF1F3B53" Grid.ColumnSpan="2" />--> |
|||
<Grid ColumnDefinitions="*,Auto" |
|||
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" |
|||
VerticalAlignment="{TemplateBinding VerticalContentAlignment}" |
|||
Margin="{TemplateBinding Padding}"> |
|||
<ContentPresenter Content="{TemplateBinding Content}"/> |
|||
</Grid> |
|||
|
|||
<Rectangle Name="VerticalSeparator" |
|||
Grid.Column="1" Width="1" |
|||
VerticalAlignment="Stretch" |
|||
Fill="{TemplateBinding SeparatorBrush}" |
|||
IsVisible="{TemplateBinding AreSeparatorsVisible}" /> |
|||
|
|||
</Grid> |
|||
</ControlTemplate> |
|||
</Setter> |
|||
|
|||
</Style> |
|||
<!-- |
|||
<Style TargetType="localprimitives:DataGridColumnHeader"> |
|||
<Setter Property="IsTabStop" Value="False" /> |
|||
<Setter Property="Template"> |
|||
<Setter.Value> |
|||
<ControlTemplate TargetType="localprimitives:DataGridColumnHeader"> |
|||
<Grid Name="Root"> |
|||
|
|||
|
|||
<Grid.ColumnDefinitions> |
|||
<ColumnDefinition/> |
|||
<ColumnDefinition Width="Auto" /> |
|||
</Grid.ColumnDefinitions> |
|||
|
|||
<Rectangle x:Name="BackgroundGradient" Stretch="Fill" Grid.ColumnSpan="2" > |
|||
<Rectangle.Fill> |
|||
<LinearGradientBrush StartPoint=".7,0" EndPoint=".7,1"> |
|||
<GradientStop Color="#FCFFFFFF" Offset="0.015" /> |
|||
<GradientStop Color="#F7FFFFFF" Offset="0.375" /> |
|||
<GradientStop Color="#E5FFFFFF" Offset="0.6" /> |
|||
<GradientStop Color="#D1FFFFFF" Offset="1" /> |
|||
</LinearGradientBrush> |
|||
</Rectangle.Fill> |
|||
</Rectangle> |
|||
|
|||
<Grid HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" Margin="{TemplateBinding Padding}"> |
|||
<Grid.ColumnDefinitions> |
|||
<ColumnDefinition/> |
|||
<ColumnDefinition Width="Auto" /> |
|||
</Grid.ColumnDefinitions> |
|||
|
|||
<ContentPresenter Content="{TemplateBinding Content}"/> |
|||
|
|||
<Path Grid.Column="1" Name="SortIcon" Fill="#FF444444" RenderTransformOrigin=".5,.5" HorizontalAlignment="Left" VerticalAlignment="Center" Opacity="0" Stretch="Uniform" Width="8" Margin="4,0,0,0" Data="F1 M -5.215,6.099L 5.215,6.099L 0,0L -5.215,6.099 Z "> |
|||
<Path.RenderTransform> |
|||
<ScaleTransform ScaleX=".9" ScaleY=".9" /> |
|||
</Path.RenderTransform> |
|||
</Path> |
|||
</Grid> |
|||
|
|||
<Rectangle Name="VerticalSeparator" Grid.Column="1" Width="1" VerticalAlignment="Stretch" Fill="{TemplateBinding SeparatorBrush}" Visibility="{TemplateBinding SeparatorVisibility}" /> |
|||
|
|||
</Grid> |
|||
</ControlTemplate> |
|||
</Setter.Value> |
|||
</Setter> |
|||
</Style> |
|||
|
|||
--> |
|||
<!-- |
|||
<vsm:VisualStateManager.VisualStateGroups> |
|||
<vsm:VisualStateGroup x:Name="CommonStates"> |
|||
<vsm:VisualState x:Name="Normal" /> |
|||
<vsm:VisualState x:Name="MouseOver"> |
|||
<Storyboard> |
|||
<ColorAnimation Duration="0" Storyboard.TargetName="BackgroundRectangle" Storyboard.TargetProperty="(Fill).Color" To="#FF448DCA"/> |
|||
<ColorAnimation Duration="0" Storyboard.TargetName="BackgroundGradient" Storyboard.TargetProperty="(Fill).(GradientStops)[3].Color" To="#7FFFFFFF"/> |
|||
<ColorAnimation Duration="0" Storyboard.TargetName="BackgroundGradient" Storyboard.TargetProperty="(Fill).(GradientStops)[2].Color" To="#CCFFFFFF"/> |
|||
<ColorAnimation Duration="0" Storyboard.TargetName="BackgroundGradient" Storyboard.TargetProperty="(Fill).(GradientStops)[1].Color" To="#F2FFFFFF"/> |
|||
</Storyboard> |
|||
</vsm:VisualState> |
|||
<vsm:VisualState x:Name="Pressed"> |
|||
<Storyboard> |
|||
<ColorAnimation Duration="0" Storyboard.TargetName="BackgroundRectangle" Storyboard.TargetProperty="(Fill).Color" To="#FF448DCA"/> |
|||
<ColorAnimation Duration="0" Storyboard.TargetName="BackgroundGradient" Storyboard.TargetProperty="(Fill).(GradientStops)[0].Color" To="#D8FFFFFF"/> |
|||
<ColorAnimation Duration="0" Storyboard.TargetName="BackgroundGradient" Storyboard.TargetProperty="(Fill).(GradientStops)[1].Color" To="#C6FFFFFF"/> |
|||
<ColorAnimation Duration="0" Storyboard.TargetName="BackgroundGradient" Storyboard.TargetProperty="(Fill).(GradientStops)[2].Color" To="#8CFFFFFF"/> |
|||
<ColorAnimation Duration="0" Storyboard.TargetName="BackgroundGradient" Storyboard.TargetProperty="(Fill).(GradientStops)[3].Color" To="#3FFFFFFF"/> |
|||
</Storyboard> |
|||
</vsm:VisualState> |
|||
</vsm:VisualStateGroup> |
|||
<vsm:VisualStateGroup x:Name="SortStates"> |
|||
<vsm:VisualState x:Name="Unsorted" /> |
|||
<vsm:VisualState x:Name="SortAscending"> |
|||
<Storyboard> |
|||
<DoubleAnimation Storyboard.TargetName="SortIcon" Storyboard.TargetProperty="Opacity" Duration="0" To="1.0"/> |
|||
</Storyboard> |
|||
</vsm:VisualState> |
|||
<vsm:VisualState x:Name="SortDescending"> |
|||
<Storyboard> |
|||
<DoubleAnimation Storyboard.TargetName="SortIcon" Storyboard.TargetProperty="Opacity" Duration="0" To="1.0"/> |
|||
<DoubleAnimation Storyboard.TargetName="SortIcon" Storyboard.TargetProperty="(RenderTransform).ScaleY" Duration="0" To="-.9"/> |
|||
</Storyboard> |
|||
</vsm:VisualState> |
|||
</vsm:VisualStateGroup> |
|||
</vsm:VisualStateManager.VisualStateGroups> |
|||
--> |
|||
|
|||
<!--FIX--> |
|||
<Style Selector="dg|DataGridRow"> |
|||
<Setter Property="Template"> |
|||
<ControlTemplate> |
|||
<prim:DataGridFrozenGrid Name="PART_Root" |
|||
RowDefinitions="*,Auto,Auto" |
|||
ColumnDefinitions="Auto,*"> |
|||
|
|||
<Rectangle Name="BackgroundRectangle" Grid.RowSpan="2" Grid.ColumnSpan="2" Fill="#FFBADDE9"/> |
|||
|
|||
<prim:DataGridRowHeader Grid.RowSpan="3" Name="PART_RowHeader" prim:DataGridFrozenGrid.IsFrozen="True" /> |
|||
<prim:DataGridCellsPresenter Grid.Column="1" Name="PART_CellsPresenter" prim:DataGridFrozenGrid.IsFrozen="True" /> |
|||
<Rectangle Grid.Row="2" Grid.Column="1" Name="PART_BottomGridLine" HorizontalAlignment="Stretch" Height="1" /> |
|||
|
|||
</prim:DataGridFrozenGrid> |
|||
</ControlTemplate> |
|||
</Setter> |
|||
</Style> |
|||
<!-- |
|||
<Style TargetType="local:DataGridRow"> |
|||
<Setter Property="IsTabStop" Value="False" /> |
|||
<Setter Property="Template"> |
|||
<Setter.Value> |
|||
<ControlTemplate TargetType="local:DataGridRow"> |
|||
<localprimitives:DataGridFrozenGrid Name="Root"> |
|||
|
|||
<Grid.RowDefinitions> |
|||
<RowDefinition/> |
|||
<RowDefinition Height="Auto"/> |
|||
<RowDefinition Height="Auto"/> |
|||
</Grid.RowDefinitions> |
|||
<Grid.ColumnDefinitions> |
|||
<ColumnDefinition Width="Auto" /> |
|||
<ColumnDefinition/> |
|||
</Grid.ColumnDefinitions> |
|||
|
|||
<Grid.Resources> |
|||
<Storyboard x:Key="DetailsVisibleTransition"> |
|||
<DoubleAnimation Storyboard.TargetName="DetailsPresenter" Storyboard.TargetProperty="ContentHeight" Duration="00:00:0.1" /> |
|||
</Storyboard> |
|||
</Grid.Resources> |
|||
|
|||
<Rectangle Name="BackgroundRectangle" Grid.RowSpan="2" Grid.ColumnSpan="2" Opacity="0" Fill="#FFBADDE9"/> |
|||
<Rectangle x:Name="InvalidVisualElement" Grid.RowSpan="2" Grid.ColumnSpan="2" Opacity="0" Fill="#FFF7D8DB"/> |
|||
|
|||
<localprimitives:DataGridRowHeader Grid.RowSpan="3" Name="RowHeader" localprimitives:DataGridFrozenGrid.IsFrozen="True" /> |
|||
<localprimitives:DataGridCellsPresenter Grid.Column="1" Name="CellsPresenter" localprimitives:DataGridFrozenGrid.IsFrozen="True" /> |
|||
<localprimitives:DataGridDetailsPresenter Grid.Row="1" Grid.Column="1" Name="DetailsPresenter" /> |
|||
<Rectangle Grid.Row="2" Grid.Column="1" Name="BottomGridLine" HorizontalAlignment="Stretch" Height="1" /> |
|||
</localprimitives:DataGridFrozenGrid> |
|||
</ControlTemplate> |
|||
</Setter.Value> |
|||
</Setter> |
|||
</Style> |
|||
--> |
|||
<!-- |
|||
<vsm:VisualStateManager.VisualStateGroups> |
|||
<vsm:VisualStateGroup x:Name="CommonStates"> |
|||
<vsm:VisualState x:Name="Normal"/> |
|||
<vsm:VisualState x:Name="NormalAlternatingRow"> |
|||
<Storyboard> |
|||
<DoubleAnimation Storyboard.TargetName="BackgroundRectangle" Storyboard.TargetProperty="Opacity" Duration="0" To="0"/> |
|||
</Storyboard> |
|||
</vsm:VisualState> |
|||
<vsm:VisualState x:Name="MouseOver"> |
|||
<Storyboard> |
|||
<DoubleAnimation Storyboard.TargetName="BackgroundRectangle" Storyboard.TargetProperty="Opacity" Duration="0" To=".5"/> |
|||
</Storyboard> |
|||
</vsm:VisualState> |
|||
<vsm:VisualState x:Name="NormalSelected"> |
|||
<Storyboard> |
|||
<DoubleAnimation Storyboard.TargetName="BackgroundRectangle" Storyboard.TargetProperty="Opacity" Duration="0" To="1"/> |
|||
</Storyboard> |
|||
</vsm:VisualState> |
|||
<vsm:VisualState x:Name="MouseOverSelected"> |
|||
<Storyboard> |
|||
<DoubleAnimation Storyboard.TargetName="BackgroundRectangle" Storyboard.TargetProperty="Opacity" Duration="0" To="1"/> |
|||
</Storyboard> |
|||
</vsm:VisualState> |
|||
<vsm:VisualState x:Name="UnfocusedSelected"> |
|||
<Storyboard> |
|||
<DoubleAnimation Storyboard.TargetName="BackgroundRectangle" Storyboard.TargetProperty="Opacity" Duration="0" To="1"/> |
|||
<ColorAnimation Duration="0" Storyboard.TargetName="BackgroundRectangle" Storyboard.TargetProperty="(Fill).Color" To="#FFE1E7EC"/> |
|||
</Storyboard> |
|||
</vsm:VisualState> |
|||
</vsm:VisualStateGroup> |
|||
<vsm:VisualStateGroup x:Name="ValidationStates"> |
|||
<vsm:VisualState x:Name="Valid"/> |
|||
<vsm:VisualState x:Name="Invalid"> |
|||
<Storyboard> |
|||
<ObjectAnimationUsingKeyFrames Duration="0" Storyboard.TargetName="BackgroundRectangle" Storyboard.TargetProperty="Visibility"> |
|||
<DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/> |
|||
</ObjectAnimationUsingKeyFrames> |
|||
<DoubleAnimation Storyboard.TargetName="InvalidVisualElement" Storyboard.TargetProperty="Opacity" Duration="0" To="1"/> |
|||
</Storyboard> |
|||
</vsm:VisualState> |
|||
</vsm:VisualStateGroup> |
|||
</vsm:VisualStateManager.VisualStateGroups> |
|||
--> |
|||
|
|||
<!--FIX--> |
|||
<Style Selector="dg|DataGridRow /template/ Rectangle#BackgroundRectangle"> |
|||
<Setter Property="IsVisible" Value="False"/> |
|||
</Style> |
|||
<Style Selector="dg|DataGridRow:pointerover /template/ Rectangle#BackgroundRectangle"> |
|||
<Setter Property="IsVisible" Value="True"/> |
|||
<Setter Property="Opacity" Value="0.5"/> |
|||
</Style> |
|||
<Style Selector="dg|DataGridRow:selected /template/ Rectangle#BackgroundRectangle"> |
|||
<Setter Property="IsVisible" Value="True"/> |
|||
<Setter Property="Opacity" Value="1"/> |
|||
</Style> |
|||
|
|||
<!-- |
|||
|
|||
<Style Selector="Button:pointerover /template/ ContentPresenter"> |
|||
<Setter Property="BorderBrush" Value="{DynamicResource ThemeBorderMidBrush}"/> |
|||
</Style> |
|||
<Style Selector="Button:pressed /template/ ContentPresenter"> |
|||
<Setter Property="Background" Value="{DynamicResource ThemeControlDarkBrush}"/> |
|||
</Style> |
|||
--> |
|||
|
|||
<!--FIX--> |
|||
<Style Selector="prim|DataGridRowHeader"> |
|||
<Setter Property="Template"> |
|||
<ControlTemplate> |
|||
<Grid |
|||
RowDefinitions="*,*,Auto" |
|||
ColumnDefinitions="Auto,*"> |
|||
|
|||
</Grid> |
|||
</ControlTemplate> |
|||
</Setter> |
|||
</Style> |
|||
|
|||
<!-- |
|||
<Style TargetType="localprimitives:DataGridRowHeader"> |
|||
<Setter Property="Background" Value="#99E9EEF4" /> |
|||
<Setter Property="IsTabStop" Value="False" /> |
|||
<Setter Property="SeparatorBrush" Value="#FFFFFFFF" /> |
|||
<Setter Property="SeparatorVisibility" Value="Collapsed"/> |
|||
<Setter Property="Template"> |
|||
<Setter.Value> |
|||
<ControlTemplate TargetType="localprimitives:DataGridRowHeader"> |
|||
<Grid Name="Root"> |
|||
<vsm:VisualStateManager.VisualStateGroups> |
|||
<vsm:VisualStateGroup x:Name="CommonStates"> |
|||
<vsm:VisualState x:Name="Normal" /> |
|||
<vsm:VisualState x:Name="MouseOver"> |
|||
<Storyboard> |
|||
<ColorAnimation Storyboard.TargetName="BackgroundRectangle" Storyboard.TargetProperty="(Fill).Color" Duration="0" To="#99D5EAF1"/> |
|||
<DoubleAnimation Storyboard.TargetName="CurrentRowGlyph" Storyboard.TargetProperty="Opacity" Duration="0" To="0"/> |
|||
<DoubleAnimation Storyboard.TargetName="EditingRowGlyph" Storyboard.TargetProperty="Opacity" Duration="0" To="0"/> |
|||
</Storyboard> |
|||
</vsm:VisualState> |
|||
<vsm:VisualState x:Name="MouseOverCurrentRowSelected"> |
|||
<Storyboard> |
|||
<ColorAnimation Storyboard.TargetName="BackgroundRectangle" Storyboard.TargetProperty="(Fill).Color" Duration="0" To="#FFD5EAF1"/> |
|||
<DoubleAnimation Storyboard.TargetName="CurrentRowGlyph" Storyboard.TargetProperty="Opacity" Duration="0" To="1"/> |
|||
<DoubleAnimation Storyboard.TargetName="EditingRowGlyph" Storyboard.TargetProperty="Opacity" Duration="0" To="0"/> |
|||
</Storyboard> |
|||
</vsm:VisualState> |
|||
<vsm:VisualState x:Name="NormalCurrentRow"> |
|||
<Storyboard> |
|||
<DoubleAnimation Storyboard.TargetName="CurrentRowGlyph" Storyboard.TargetProperty="Opacity" Duration="0" To="1"/> |
|||
<DoubleAnimation Storyboard.TargetName="EditingRowGlyph" Storyboard.TargetProperty="Opacity" Duration="0" To="0"/> |
|||
</Storyboard> |
|||
</vsm:VisualState> |
|||
<vsm:VisualState x:Name="NormalSelected"> |
|||
<Storyboard> |
|||
<ColorAnimation Storyboard.TargetName="BackgroundRectangle" Storyboard.TargetProperty="(Fill).Color" Duration="0" To="#FFD5EAF1"/> |
|||
<DoubleAnimation Storyboard.TargetName="CurrentRowGlyph" Storyboard.TargetProperty="Opacity" Duration="0" To="0"/> |
|||
<DoubleAnimation Storyboard.TargetName="EditingRowGlyph" Storyboard.TargetProperty="Opacity" Duration="0" To="0"/> |
|||
</Storyboard> |
|||
</vsm:VisualState> |
|||
<vsm:VisualState x:Name="NormalEditingRow"> |
|||
<Storyboard> |
|||
<ColorAnimation Storyboard.TargetName="BackgroundRectangle" Storyboard.TargetProperty="(Fill).Color" Duration="0" To="#FFD5EAF1"/> |
|||
<DoubleAnimation Storyboard.TargetName="CurrentRowGlyph" Storyboard.TargetProperty="Opacity" Duration="0" To="1"/> |
|||
<DoubleAnimation Storyboard.TargetName="EditingRowGlyph" Storyboard.TargetProperty="Opacity" Duration="0" To="0"/> |
|||
</Storyboard> |
|||
</vsm:VisualState> |
|||
</vsm:VisualStateGroup> |
|||
<vsm:VisualStateGroup x:Name="ValidationStates"> |
|||
<vsm:VisualState x:Name="RowValid"/> |
|||
<vsm:VisualState x:Name="RowInvalid"> |
|||
<Storyboard> |
|||
<DoubleAnimation Storyboard.TargetName="BackgroundRectangle" Storyboard.TargetProperty="Opacity" Duration="0" To="0"/> |
|||
<DoubleAnimation Storyboard.TargetName="RowInvalidVisualElement" Storyboard.TargetProperty="Opacity" Duration="0" To="1"/> |
|||
<ColorAnimation Storyboard.TargetName="CurrentRowGlyph" Storyboard.TargetProperty="(Fill).(GradientStops)[0].Color" Duration="0" To="#FFDC000C"/> |
|||
<ColorAnimation Storyboard.TargetName="CurrentRowGlyph" Storyboard.TargetProperty="(Fill).(GradientStops)[1].Color" Duration="0" To="#FFDC000C"/> |
|||
<ColorAnimation Storyboard.TargetName="CurrentRowGlyph" Storyboard.TargetProperty="(Fill).(GradientStops)[2].Color" Duration="0" To="#FFDC000C"/> |
|||
<ColorAnimation Storyboard.TargetName="EditingRowGlyph" Storyboard.TargetProperty="(Fill).(GradientStops)[0].Color" Duration="0" To="#FFDC000C"/> |
|||
<ColorAnimation Storyboard.TargetName="EditingRowGlyph" Storyboard.TargetProperty="(Fill).(GradientStops)[1].Color" Duration="0" To="#FFDC000C"/> |
|||
<ColorAnimation Storyboard.TargetName="EditingRowGlyph" Storyboard.TargetProperty="(Fill).(GradientStops)[2].Color" Duration="0" To="#FFDC000C"/> |
|||
</Storyboard> |
|||
</vsm:VisualState> |
|||
</vsm:VisualStateGroup> |
|||
</vsm:VisualStateManager.VisualStateGroups> |
|||
|
|||
<Grid.RowDefinitions> |
|||
<RowDefinition/> |
|||
<RowDefinition/> |
|||
<RowDefinition Height="Auto" /> |
|||
</Grid.RowDefinitions> |
|||
|
|||
<Grid.ColumnDefinitions> |
|||
<ColumnDefinition Width="Auto" /> |
|||
<ColumnDefinition/> |
|||
</Grid.ColumnDefinitions> |
|||
|
|||
<Border BorderBrush="#FFFFFFFF" BorderThickness="1,0,1,0" Grid.RowSpan="3" Grid.ColumnSpan="2"> |
|||
<Grid Background="{TemplateBinding Background}"> |
|||
<Rectangle x:Name="RowInvalidVisualElement" Stretch="Fill" Grid.RowSpan="3" Grid.ColumnSpan="2" Fill="#FFF7D8DB" Opacity="0" /> |
|||
<Rectangle x:Name="BackgroundRectangle" Stretch="Fill" Grid.RowSpan="3" Grid.ColumnSpan="2" Fill="Transparent" /> |
|||
</Grid> |
|||
</Border> |
|||
<Rectangle Name="HorizontalSeparator" Grid.ColumnSpan="2" Grid.Row="2" Height="1" Margin="1,0,1,0" HorizontalAlignment="Stretch" Fill="{TemplateBinding SeparatorBrush}" Visibility="{TemplateBinding SeparatorVisibility}" /> |
|||
|
|||
<ContentPresenter Content="{TemplateBinding Content}" VerticalAlignment="Center" HorizontalAlignment="Center" Grid.Column="1" Grid.RowSpan="2" /> |
|||
|
|||
<Path Name="CurrentRowGlyph" VerticalAlignment="Center" HorizontalAlignment="Center" Width="6" Height="10" Margin="8,0,8,0" Grid.RowSpan="2" Stretch="Fill" Opacity="0" Data="F1 M 511.047,352.682L 511.047,342.252L 517.145,347.467L 511.047,352.682 Z "> |
|||
<Path.Fill> |
|||
<LinearGradientBrush StartPoint="0,-.15" EndPoint="0,1.75"> |
|||
<GradientStop Color="#FF84E3FF" Offset="0"/> |
|||
<GradientStop Color="#FF6ABFD8" Offset="0.5"/> |
|||
<GradientStop Color="#FF5297AB" Offset="1"/> |
|||
</LinearGradientBrush> |
|||
</Path.Fill> |
|||
</Path> |
|||
|
|||
<Path Name="EditingRowGlyph" VerticalAlignment="Center" HorizontalAlignment="Center" Width="6" Height="10" Margin="8,0,8,0" Grid.RowSpan="2" Stretch="Fill" Opacity="0" Data="F1 M 511.047,352.682L 511.047,342.252L 517.145,347.467L 511.047,352.682 Z "> |
|||
<Path.Fill> |
|||
<LinearGradientBrush StartPoint="0,-.15" EndPoint="0,1.75"> |
|||
<GradientStop Color="#FF84E3FF" Offset="0"/> |
|||
<GradientStop Color="#FF6ABFD8" Offset="0.5"/> |
|||
<GradientStop Color="#FF5297AB" Offset="1"/> |
|||
</LinearGradientBrush> |
|||
</Path.Fill> |
|||
</Path> |
|||
|
|||
</Grid> |
|||
</ControlTemplate> |
|||
</Setter.Value> |
|||
</Setter> |
|||
</Style> |
|||
--> |
|||
|
|||
<Style Selector="dg|DataGridRowGroupHeader"> |
|||
<Setter Property="Background" Value="#FFE4E8EA" /> |
|||
<Setter Property="Height" Value="20"/> |
|||
<Setter Property="Template"> |
|||
<ControlTemplate> |
|||
<prim:DataGridFrozenGrid Name="Root" |
|||
Background="{TemplateBinding Background}" |
|||
ColumnDefinitions="Auto,Auto,Auto,Auto" |
|||
RowDefinitions="Auto,*,Auto"> |
|||
|
|||
<Rectangle Grid.Column="1" Grid.ColumnSpan="5" Fill="#FFFFFFFF" Height="1"/> |
|||
<Rectangle Grid.Column="1" Grid.Row="1" Name="IndentSpacer" /> |
|||
<ToggleButton Grid.Column="2" Grid.Row="1" Name="ExpanderButton" Height="15" Width="15" Margin="2,0,0,0"/> |
|||
|
|||
<StackPanel Grid.Column="3" Grid.Row="1" Orientation="Horizontal" VerticalAlignment="Center" Margin="0,1,0,1"> |
|||
<TextBlock Name="PropertyNameElement" Margin="4,0,0,0" IsVisible="{TemplateBinding IsPropertyNameVisible}"/> |
|||
<TextBlock Margin="4,0,0,0" Text="{Binding Key}" /> |
|||
<TextBlock Name="ItemCountElement" Margin="4,0,0,0" IsVisible="{TemplateBinding IsItemCountVisible}"/> |
|||
</StackPanel> |
|||
|
|||
<prim:DataGridRowHeader Name="RowHeader" Grid.RowSpan="3" prim:DataGridFrozenGrid.IsFrozen="True"/> |
|||
|
|||
</prim:DataGridFrozenGrid> |
|||
</ControlTemplate> |
|||
</Setter> |
|||
</Style> |
|||
|
|||
<!-- |
|||
|
|||
<ControlTemplate TargetType="local:DataGridRowGroupHeader"> |
|||
<localprimitives:DataGridFrozenGrid Name="Root" Background="{TemplateBinding Background}"> |
|||
<vsm:VisualStateManager.VisualStateGroups> |
|||
<vsm:VisualStateGroup x:Name="CurrentStates"> |
|||
<vsm:VisualState x:Name="Regular"/> |
|||
<vsm:VisualState x:Name="Current"> |
|||
<Storyboard> |
|||
<DoubleAnimation Storyboard.TargetName="FocusVisual" Storyboard.TargetProperty="Opacity" To="1" Duration="0" /> |
|||
</Storyboard> |
|||
</vsm:VisualState> |
|||
</vsm:VisualStateGroup> |
|||
</vsm:VisualStateManager.VisualStateGroups> |
|||
<localprimitives:DataGridFrozenGrid.Resources> |
|||
<ControlTemplate x:Key="ToggleButtonTemplate" TargetType="ToggleButton"> |
|||
<Grid Background="Transparent"> |
|||
<vsm:VisualStateManager.VisualStateGroups> |
|||
<vsm:VisualStateGroup x:Name="CommonStates"> |
|||
<vsm:VisualState x:Name="Normal"/> |
|||
<vsm:VisualState x:Name="MouseOver"> |
|||
<Storyboard> |
|||
<ColorAnimation Storyboard.TargetName="CollapsedArrow" Storyboard.TargetProperty="(Stroke).Color" Duration="0" To="#FF6DBDD1"/> |
|||
<ColorAnimation Storyboard.TargetName="ExpandedArrow" Storyboard.TargetProperty="(Fill).Color" Duration="0" To="#FF6DBDD1"/> |
|||
</Storyboard> |
|||
</vsm:VisualState> |
|||
<vsm:VisualState x:Name="Pressed"> |
|||
<Storyboard> |
|||
<ColorAnimation Storyboard.TargetName="CollapsedArrow" Storyboard.TargetProperty="(Stroke).Color" Duration="0" To="#FF6DBDD1"/> |
|||
<ColorAnimation Storyboard.TargetName="ExpandedArrow" Storyboard.TargetProperty="(Fill).Color" Duration="0" To="#FF6DBDD1"/> |
|||
</Storyboard> |
|||
</vsm:VisualState> |
|||
<vsm:VisualState x:Name="Disabled"> |
|||
<Storyboard> |
|||
<DoubleAnimation Duration="0" Storyboard.TargetName="CollapsedArrow" Storyboard.TargetProperty="Opacity" To=".5"/> |
|||
<DoubleAnimation Duration="0" Storyboard.TargetName="ExpandedArrow" Storyboard.TargetProperty="Opacity" To=".5"/> |
|||
</Storyboard> |
|||
</vsm:VisualState> |
|||
</vsm:VisualStateGroup> |
|||
<vsm:VisualStateGroup x:Name="CheckStates"> |
|||
<vsm:VisualState x:Name="Checked" /> |
|||
<vsm:VisualState x:Name="Unchecked"> |
|||
<Storyboard> |
|||
<ObjectAnimationUsingKeyFrames Duration="0" Storyboard.TargetName="CollapsedArrow" Storyboard.TargetProperty="Visibility"> |
|||
<DiscreteObjectKeyFrame KeyTime="0" Value="Visible"/> |
|||
</ObjectAnimationUsingKeyFrames> |
|||
<ObjectAnimationUsingKeyFrames Duration="0" Storyboard.TargetName="ExpandedArrow" Storyboard.TargetProperty="Visibility"> |
|||
<DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/> |
|||
</ObjectAnimationUsingKeyFrames> |
|||
</Storyboard> |
|||
</vsm:VisualState> |
|||
</vsm:VisualStateGroup> |
|||
</vsm:VisualStateManager.VisualStateGroups> |
|||
|
|||
<Path Stretch="Uniform" Data="F1 M 0,0 L 0,1 L .6,.5 L 0,0 Z" Width="5" HorizontalAlignment="Center" VerticalAlignment="Center" x:Name="CollapsedArrow" Visibility="Collapsed" Stroke="#FF414345"/> |
|||
<Path Stretch="Uniform" Data="F1 M 0,1 L 1,1 L 1,0 L 0,1 Z" Width="6" HorizontalAlignment="Center" VerticalAlignment="Center" x:Name="ExpandedArrow" Fill="#FF414345"/> |
|||
</Grid> |
|||
</ControlTemplate> |
|||
</localprimitives:DataGridFrozenGrid.Resources> |
|||
|
|||
<Grid.ColumnDefinitions> |
|||
<ColumnDefinition Width="Auto" /> |
|||
<ColumnDefinition Width="Auto" /> |
|||
<ColumnDefinition Width="Auto" /> |
|||
<ColumnDefinition Width="Auto" /> |
|||
<ColumnDefinition/> |
|||
</Grid.ColumnDefinitions> |
|||
<Grid.RowDefinitions> |
|||
<RowDefinition Height="Auto"/> |
|||
<RowDefinition/> |
|||
<RowDefinition Height="Auto"/> |
|||
</Grid.RowDefinitions> |
|||
|
|||
<Rectangle Grid.Column="1" Grid.ColumnSpan="5" Fill="#FFFFFFFF" Height="1"/> |
|||
<Rectangle Grid.Column="1" Grid.Row="1" Name="IndentSpacer" /> |
|||
<ToggleButton Grid.Column="2" Grid.Row="1" Name="ExpanderButton" Height="15" Width="15" IsTabStop="False" Template="{StaticResource ToggleButtonTemplate}" Margin="2,0,0,0"/> |
|||
|
|||
<StackPanel Grid.Column="3" Grid.Row="1" Orientation="Horizontal" VerticalAlignment="Center" Margin="0,1,0,1"> |
|||
<TextBlock x:Name="PropertyNameElement" Margin="4,0,0,0" Visibility="{TemplateBinding PropertyNameVisibility}"/> |
|||
<TextBlock Margin="4,0,0,0" Text="{Binding Name}" /> |
|||
<TextBlock x:Name="ItemCountElement" Margin="4,0,0,0" Visibility="{TemplateBinding ItemCountVisibility}"/> |
|||
</StackPanel> |
|||
|
|||
<Rectangle Grid.Column="1" Grid.ColumnSpan="5" Fill="#FFD3D3D3" Height="1" Grid.Row="2"/> |
|||
<Rectangle Name="FocusVisual" Grid.Column="1" Grid.ColumnSpan="4" Grid.RowSpan="3" Stroke="#FF6DBDD1" StrokeThickness="1" HorizontalAlignment="Stretch" |
|||
VerticalAlignment="Stretch" IsHitTestVisible="false" Opacity="0" /> |
|||
<localprimitives:DataGridRowHeader Name="RowHeader" Grid.RowSpan="3" localprimitives:DataGridFrozenGrid.IsFrozen="True"/> |
|||
|
|||
</localprimitives:DataGridFrozenGrid> |
|||
</ControlTemplate> |
|||
|
|||
--> |
|||
|
|||
<!--FIX--> |
|||
<Style Selector="dg|DataGrid"> |
|||
<Setter Property="RowBackground" Value="{DynamicResource ThemeAccentBrush4}" /> |
|||
<!--<Setter Property="RowBackground" Value="#AAEAEFF4" />--> |
|||
<Setter Property="AlternatingRowBackground" Value="#00FFFFFF" /> |
|||
<Setter Property="Background" Value="{DynamicResource ThemeBackgroundBrush}" /> |
|||
<Setter Property="HeadersVisibility" Value="Column" /> |
|||
<Setter Property="HorizontalScrollBarVisibility" Value="Auto" /> |
|||
<Setter Property="VerticalScrollBarVisibility" Value="Auto" /> |
|||
<Setter Property="SelectionMode" Value="Extended" /> |
|||
<Setter Property="GridLinesVisibility" Value="Vertical" /> |
|||
<Setter Property="HorizontalGridLinesBrush" Value="{DynamicResource ThemeBorderLightBrush}" /> |
|||
<Setter Property="VerticalGridLinesBrush" Value="{DynamicResource ThemeBorderLightBrush}" /> |
|||
<Setter Property="HeadersVisibility" Value="Column" /> |
|||
<Setter Property="BorderBrush" Value="{DynamicResource ThemeBorderDarkBrush}"/> |
|||
<Setter Property="BorderThickness" Value="{DynamicResource ThemeBorderThickness}" /> |
|||
<Setter Property="Template"> |
|||
<ControlTemplate> |
|||
<Border BorderThickness="{TemplateBinding BorderThickness}" BorderBrush="{TemplateBinding BorderBrush}"> |
|||
<Grid |
|||
RowDefinitions="Auto,*,Auto,Auto" |
|||
ColumnDefinitions="Auto,*,Auto"> |
|||
|
|||
<dg:DataGridColumnHeader Name="PART_TopLeftCornerHeader" Width="22" /> |
|||
<prim:DataGridColumnHeadersPresenter Name="PART_ColumnHeadersPresenter" Grid.Column="1"/> |
|||
<dg:DataGridColumnHeader Name="PART_TopRightCornerHeader" Grid.Column="2"/> |
|||
<Rectangle Name="PART_ColumnHeadersAndRowsSeparator" Grid.ColumnSpan="3" VerticalAlignment="Bottom" StrokeThickness="1" Height="1" Fill="#FFC9CACA"/> |
|||
|
|||
<prim:DataGridRowsPresenter Name="PART_RowsPresenter" Grid.ColumnSpan="2" Grid.Row="1" /> |
|||
<Rectangle Name="BottomRightCorner" Fill="#FFE9EEF4" Grid.Column="2" Grid.Row="2" /> |
|||
<Rectangle Name="BottomLeftCorner" Fill="#FFE9EEF4" Grid.Row="2" Grid.ColumnSpan="2" /> |
|||
<ScrollBar Name="PART_VerticalScrollbar" Orientation="Vertical" Grid.Column="2" Grid.Row="1" Width="18" Margin="0,-1,-1,-1"/> |
|||
|
|||
<Grid Grid.Column="1" Grid.Row="2" |
|||
ColumnDefinitions="Auto,*"> |
|||
<Rectangle Name="PART_FrozenColumnScrollBarSpacer" /> |
|||
<ScrollBar Name="PART_HorizontalScrollbar" Grid.Column="1" Orientation="Horizontal" Height="18" Margin="-1,0,-1,-1"/> |
|||
</Grid> |
|||
</Grid> |
|||
</Border> |
|||
</ControlTemplate> |
|||
</Setter> |
|||
</Style> |
|||
<!-- |
|||
<Grid> |
|||
|
|||
<Border BorderThickness="{TemplateBinding BorderThickness}" BorderBrush="{TemplateBinding BorderBrush}" CornerRadius="2"> |
|||
<Grid Name="Root" Background="{TemplateBinding Background}"> |
|||
|
|||
<localprimitives:DataGridColumnHeader Name="TopLeftCornerHeader" Template="{StaticResource TopLeftHeaderTemplate}" Width="22" /> |
|||
<localprimitives:DataGridColumnHeadersPresenter Name="ColumnHeadersPresenter" Grid.Column="1"/> |
|||
<localprimitives:DataGridColumnHeader Name="TopRightCornerHeader" Grid.Column="2" Template="{StaticResource TopRightHeaderTemplate}" /> |
|||
<Rectangle Name="ColumnHeadersAndRowsSeparator" Grid.ColumnSpan="3" VerticalAlignment="Bottom" Width="Auto" StrokeThickness="1" Height="1" Fill="#FFC9CACA"/> |
|||
|
|||
<localprimitives:DataGridRowsPresenter Name="RowsPresenter" Grid.ColumnSpan="2" Grid.Row="1" /> |
|||
<Rectangle Name="BottomRightCorner" Fill="#FFE9EEF4" Grid.Column="2" Grid.Row="2" /> |
|||
<Rectangle Name="BottomLeftCorner" Fill="#FFE9EEF4" Grid.Row="2" Grid.ColumnSpan="2" /> |
|||
<ScrollBar Name="VerticalScrollbar" Orientation="Vertical" Grid.Column="2" Grid.Row="1" Width="18" Margin="0,-1,-1,-1"/> |
|||
|
|||
|
|||
<dataInput:ValidationSummary Name="ValidationSummary" Grid.Row="3" Grid.ColumnSpan="3" MaxHeight="90"/> |
|||
</Grid> |
|||
</Border> |
|||
<Border x:Name="DisabledVisualElement" IsHitTestVisible="False" Height="Auto" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Width="Auto" CornerRadius="2" Background="#8CFFFFFF" Opacity="0"/> |
|||
</Grid> |
|||
--> |
|||
|
|||
<!-- |
|||
<Style TargetType="local:DataGrid"> |
|||
<Setter Property="CanUserReorderColumns" Value="True" /> |
|||
<Setter Property="CanUserResizeColumns" Value="True" /> |
|||
<Setter Property="CanUserSortColumns" Value="True" /> |
|||
<Setter Property="AutoGenerateColumns" Value="True" /> |
|||
<Setter Property="RowDetailsVisibilityMode" Value="VisibleWhenSelected" /> |
|||
<Setter Property="DragIndicatorStyle"> |
|||
<Setter.Value> |
|||
<Style TargetType="ContentControl"> |
|||
<Setter Property="Foreground" Value="#7FFFFFFF" /> |
|||
<Setter Property="Template"> |
|||
<Setter.Value> |
|||
<ControlTemplate TargetType="ContentControl"> |
|||
<Grid> |
|||
<vsm:VisualStateManager.VisualStateGroups> |
|||
<vsm:VisualStateGroup x:Name="SortStates"> |
|||
<vsm:VisualState x:Name="Unsorted" /> |
|||
<vsm:VisualState x:Name="SortAscending"> |
|||
<Storyboard> |
|||
<DoubleAnimation Storyboard.TargetName="SortIcon" Storyboard.TargetProperty="Opacity" Duration="0" To="1.0"/> |
|||
</Storyboard> |
|||
</vsm:VisualState> |
|||
<vsm:VisualState x:Name="SortDescending"> |
|||
<Storyboard> |
|||
<DoubleAnimation Storyboard.TargetName="SortIcon" Storyboard.TargetProperty="Opacity" Duration="0" To="1.0"/> |
|||
<DoubleAnimation Storyboard.TargetName="SortIcon" Storyboard.TargetProperty="(RenderTransform).ScaleY" Duration="0" To="-.9"/> |
|||
</Storyboard> |
|||
</vsm:VisualState> |
|||
</vsm:VisualStateGroup> |
|||
</vsm:VisualStateManager.VisualStateGroups> |
|||
|
|||
<Rectangle x:Name="BackgroundRectangle" Stretch="Fill" Fill="#66808080" Grid.ColumnSpan="2" /> |
|||
|
|||
<Rectangle x:Name="BackgroundGradient" Stretch="Fill" Grid.ColumnSpan="2" Opacity="0" > |
|||
<Rectangle.Fill> |
|||
<LinearGradientBrush StartPoint=".7,0" EndPoint=".7,1"> |
|||
<GradientStop Color="#FFFFFFFF" Offset="0.015" /> |
|||
<GradientStop Color="#F9FFFFFF" Offset="0.375" /> |
|||
<GradientStop Color="#E5FFFFFF" Offset="0.6" /> |
|||
<GradientStop Color="#C6FFFFFF" Offset="1" /> |
|||
</LinearGradientBrush> |
|||
</Rectangle.Fill> |
|||
</Rectangle> |
|||
|
|||
<Grid HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" Margin="{TemplateBinding Padding}"> |
|||
<Grid.ColumnDefinitions> |
|||
<ColumnDefinition/> |
|||
<ColumnDefinition Width="Auto" /> |
|||
</Grid.ColumnDefinitions> |
|||
|
|||
<ContentPresenter Content="{TemplateBinding Content}"/> |
|||
|
|||
<Path Grid.Column="1" Name="SortIcon" Fill="#7FFFFFFF" RenderTransformOrigin=".5,.5" HorizontalAlignment="Left" VerticalAlignment="Center" Opacity="0" Stretch="Uniform" Width="8" Margin="4,0,0,0" Data="F1 M -5.215,6.099L 5.215,6.099L 0,0L -5.215,6.099 Z "> |
|||
<Path.RenderTransform> |
|||
<ScaleTransform ScaleX=".9" ScaleY=".9" /> |
|||
</Path.RenderTransform> |
|||
</Path> |
|||
</Grid> |
|||
</Grid> |
|||
</ControlTemplate> |
|||
</Setter.Value> |
|||
</Setter> |
|||
</Style> |
|||
</Setter.Value> |
|||
</Setter> |
|||
<Setter Property="DropLocationIndicatorStyle"> |
|||
<Setter.Value> |
|||
<Style TargetType="ContentControl"> |
|||
<Setter Property="Background" Value="#FF3F4346"/> |
|||
<Setter Property="Width" Value="2"/> |
|||
<Setter Property="Template"> |
|||
<Setter.Value> |
|||
<ControlTemplate TargetType="ContentControl"> |
|||
<Rectangle Fill="{TemplateBinding Background}" Width="{TemplateBinding Width}" Height="{TemplateBinding Height}"/> |
|||
</ControlTemplate> |
|||
</Setter.Value> |
|||
</Setter> |
|||
</Style> |
|||
</Setter.Value> |
|||
</Setter> |
|||
<Setter Property="IsTabStop" Value="True" /> |
|||
<Setter Property="Template"> |
|||
<Setter.Value> |
|||
<ControlTemplate TargetType="local:DataGrid"> |
|||
<Grid> |
|||
<vsm:VisualStateManager.VisualStateGroups> |
|||
<vsm:VisualStateGroup x:Name="CommonStates"> |
|||
<vsm:VisualState x:Name="Normal"/> |
|||
<vsm:VisualState x:Name="Disabled"> |
|||
<Storyboard> |
|||
<DoubleAnimation Storyboard.TargetName="DisabledVisualElement" Storyboard.TargetProperty="Opacity" Duration="0" To="1"/> |
|||
</Storyboard> |
|||
</vsm:VisualState> |
|||
</vsm:VisualStateGroup> |
|||
</vsm:VisualStateManager.VisualStateGroups> |
|||
<Border BorderThickness="{TemplateBinding BorderThickness}" BorderBrush="{TemplateBinding BorderBrush}" CornerRadius="2"> |
|||
<Grid Name="Root" Background="{TemplateBinding Background}"> |
|||
<Grid.Resources> |
|||
<ControlTemplate x:Key="TopLeftHeaderTemplate" TargetType="localprimitives:DataGridColumnHeader"> |
|||
<Grid Name="Root"> |
|||
<Grid.RowDefinitions> |
|||
<RowDefinition/> |
|||
<RowDefinition/> |
|||
<RowDefinition Height="Auto" /> |
|||
</Grid.RowDefinitions> |
|||
<Border BorderThickness="0,0,1,0" BorderBrush="#FFC9CACA" Background="#FF1F3B53" Grid.RowSpan="2"> |
|||
<Rectangle Stretch="Fill" StrokeThickness="1"> |
|||
<Rectangle.Fill> |
|||
<LinearGradientBrush StartPoint=".7,0" EndPoint=".7,1"> |
|||
<GradientStop Color="#FCFFFFFF" Offset="0.015" /> |
|||
<GradientStop Color="#F7FFFFFF" Offset="0.375" /> |
|||
<GradientStop Color="#E5FFFFFF" Offset="0.6" /> |
|||
<GradientStop Color="#D1FFFFFF" Offset="1" /> |
|||
</LinearGradientBrush> |
|||
</Rectangle.Fill> |
|||
</Rectangle> |
|||
</Border> |
|||
<Rectangle VerticalAlignment="Bottom" Width="Auto" StrokeThickness="1" Height="1" Fill="#FFDBDCDC" Grid.RowSpan="2"/> |
|||
</Grid> |
|||
</ControlTemplate> |
|||
|
|||
<ControlTemplate x:Key="TopRightHeaderTemplate" TargetType="localprimitives:DataGridColumnHeader"> |
|||
<Grid Name="RootElement"> |
|||
<Grid.RowDefinitions> |
|||
<RowDefinition/> |
|||
<RowDefinition/> |
|||
<RowDefinition Height="Auto" /> |
|||
</Grid.RowDefinitions> |
|||
<Border BorderThickness="1,0,0,0" BorderBrush="#FFC9CACA" Background="#FF1F3B53" Grid.RowSpan="2"> |
|||
<Rectangle Stretch="Fill"> |
|||
<Rectangle.Fill> |
|||
<LinearGradientBrush StartPoint=".7,0" EndPoint=".7,1"> |
|||
<GradientStop Color="#FCFFFFFF" Offset="0.015" /> |
|||
<GradientStop Color="#F7FFFFFF" Offset="0.375" /> |
|||
<GradientStop Color="#E5FFFFFF" Offset="0.6" /> |
|||
<GradientStop Color="#D1FFFFFF" Offset="1" /> |
|||
</LinearGradientBrush> |
|||
</Rectangle.Fill> |
|||
</Rectangle> |
|||
</Border> |
|||
</Grid> |
|||
</ControlTemplate> |
|||
</Grid.Resources> |
|||
|
|||
<Grid.RowDefinitions> |
|||
<RowDefinition Height="Auto" /> |
|||
<RowDefinition/> |
|||
<RowDefinition Height="Auto" /> |
|||
<RowDefinition Height="Auto" /> |
|||
</Grid.RowDefinitions> |
|||
<Grid.ColumnDefinitions> |
|||
<ColumnDefinition Width="Auto" /> |
|||
<ColumnDefinition/> |
|||
<ColumnDefinition Width="Auto" /> |
|||
</Grid.ColumnDefinitions> |
|||
|
|||
<localprimitives:DataGridColumnHeader Name="TopLeftCornerHeader" Template="{StaticResource TopLeftHeaderTemplate}" Width="22" /> |
|||
<localprimitives:DataGridColumnHeadersPresenter Name="ColumnHeadersPresenter" Grid.Column="1"/> |
|||
<localprimitives:DataGridColumnHeader Name="TopRightCornerHeader" Grid.Column="2" Template="{StaticResource TopRightHeaderTemplate}" /> |
|||
<Rectangle Name="ColumnHeadersAndRowsSeparator" Grid.ColumnSpan="3" VerticalAlignment="Bottom" Width="Auto" StrokeThickness="1" Height="1" Fill="#FFC9CACA"/> |
|||
|
|||
<localprimitives:DataGridRowsPresenter Name="RowsPresenter" Grid.ColumnSpan="2" Grid.Row="1" /> |
|||
<Rectangle Name="BottomRightCorner" Fill="#FFE9EEF4" Grid.Column="2" Grid.Row="2" /> |
|||
<Rectangle Name="BottomLeftCorner" Fill="#FFE9EEF4" Grid.Row="2" Grid.ColumnSpan="2" /> |
|||
<ScrollBar Name="VerticalScrollbar" Orientation="Vertical" Grid.Column="2" Grid.Row="1" Width="18" Margin="0,-1,-1,-1"/> |
|||
|
|||
<Grid Grid.Column="1" Grid.Row="2"> |
|||
<Grid.ColumnDefinitions> |
|||
<ColumnDefinition Width="Auto" /> |
|||
<ColumnDefinition/> |
|||
</Grid.ColumnDefinitions> |
|||
<Rectangle Name="FrozenColumnScrollBarSpacer" /> |
|||
<ScrollBar Name="HorizontalScrollbar" Grid.Column="1" Orientation="Horizontal" Height="18" Margin="-1,0,-1,-1"/> |
|||
</Grid> |
|||
|
|||
<dataInput:ValidationSummary Name="ValidationSummary" Grid.Row="3" Grid.ColumnSpan="3" MaxHeight="90"/> |
|||
</Grid> |
|||
</Border> |
|||
<Border x:Name="DisabledVisualElement" IsHitTestVisible="False" Height="Auto" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Width="Auto" CornerRadius="2" Background="#8CFFFFFF" Opacity="0"/> |
|||
</Grid> |
|||
</ControlTemplate> |
|||
</Setter.Value> |
|||
</Setter> |
|||
</Style> |
|||
|
|||
--> |
|||
|
|||
<!-- |
|||
<Style Selector="Button"> |
|||
<Setter Property="Background" Value="{DynamicResource ThemeControlMidBrush}"/> |
|||
<Setter Property="BorderBrush" Value="{DynamicResource ThemeBorderLightBrush}"/> |
|||
<Setter Property="BorderThickness" Value="{DynamicResource ThemeBorderThickness}"/> |
|||
<Setter Property="Foreground" Value="{DynamicResource ThemeForegroundBrush}"/> |
|||
<Setter Property="HorizontalContentAlignment" Value="Center"/> |
|||
<Setter Property="VerticalContentAlignment" Value="Center"/> |
|||
<Setter Property="Padding" Value="4"/> |
|||
<Setter Property="Template"> |
|||
<ControlTemplate> |
|||
<ContentPresenter Name="PART_ContentPresenter" |
|||
Background="{TemplateBinding Background}" |
|||
BorderBrush="{TemplateBinding BorderBrush}" |
|||
BorderThickness="{TemplateBinding BorderThickness}" |
|||
ContentTemplate="{TemplateBinding ContentTemplate}" |
|||
Content="{TemplateBinding Content}" |
|||
Padding="{TemplateBinding Padding}" |
|||
TextBlock.Foreground="{TemplateBinding Foreground}" |
|||
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" |
|||
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"/> |
|||
</ControlTemplate> |
|||
</Setter> |
|||
</Style> |
|||
<Style Selector="Button:pointerover /template/ ContentPresenter"> |
|||
<Setter Property="BorderBrush" Value="{DynamicResource ThemeBorderMidBrush}"/> |
|||
</Style> |
|||
<Style Selector="Button:pressed /template/ ContentPresenter"> |
|||
<Setter Property="Background" Value="{DynamicResource ThemeControlDarkBrush}"/> |
|||
</Style> |
|||
<Style Selector="Button:disabled"> |
|||
<Setter Property="Opacity" Value="{DynamicResource ThemeDisabledOpacity}"/> |
|||
</Style> |
|||
--> |
|||
|
|||
|
|||
</Styles> |
|||
@ -0,0 +1,20 @@ |
|||
using Avalonia; |
|||
using Avalonia.Controls; |
|||
using Avalonia.Markup.Xaml; |
|||
using Avalonia.Styling; |
|||
|
|||
namespace Avalonia.DataGrid.Themes.Default |
|||
{ |
|||
public class DefaultTheme : Styles |
|||
{ |
|||
public DefaultTheme() |
|||
{ |
|||
this.InitializeComponent(); |
|||
} |
|||
|
|||
private void InitializeComponent() |
|||
{ |
|||
AvaloniaXamlLoader.Load(this); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,11 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netstandard2.0</TargetFramework> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Avalonia" Version="0.6.2-build5521-beta" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
File diff suppressed because it is too large
@ -0,0 +1,261 @@ |
|||
// 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.Globalization; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using Avalonia.Controls; |
|||
using Avalonia.Utilities; |
|||
|
|||
namespace Avalonia.Collections |
|||
{ |
|||
public abstract class AvaloniaSortDescription |
|||
{ |
|||
public virtual string PropertyPath => null; |
|||
public virtual bool Descending => false; |
|||
public bool HasPropertyPath => !String.IsNullOrEmpty(PropertyPath); |
|||
public abstract IComparer<object> Comparer { get; } |
|||
|
|||
public virtual IOrderedEnumerable<object> OrderBy(IEnumerable<object> seq) |
|||
{ |
|||
return seq.OrderBy(o => o, Comparer); |
|||
} |
|||
public virtual IOrderedEnumerable<object> ThenBy(IOrderedEnumerable<object> seq) |
|||
{ |
|||
return seq.ThenBy(o => o, Comparer); |
|||
} |
|||
|
|||
internal virtual AvaloniaSortDescription SwitchSortDirection() |
|||
{ |
|||
return this; |
|||
} |
|||
|
|||
internal virtual void Initialize(Type itemType) |
|||
{ } |
|||
|
|||
private static object InvokePath(object item, string propertyPath, Type propertyType) |
|||
{ |
|||
object propertyValue = TypeHelper.GetNestedPropertyValue(item, propertyPath, propertyType, out Exception exception); |
|||
if (exception != null) |
|||
{ |
|||
throw exception; |
|||
} |
|||
return propertyValue; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Creates a comparer class that takes in a CultureInfo as a parameter,
|
|||
/// which it will use when comparing strings.
|
|||
/// </summary>
|
|||
private class CultureSensitiveComparer : Comparer<object> |
|||
{ |
|||
/// <summary>
|
|||
/// Private accessor for the CultureInfo of our comparer
|
|||
/// </summary>
|
|||
private CultureInfo _culture; |
|||
|
|||
/// <summary>
|
|||
/// Creates a comparer which will respect the CultureInfo
|
|||
/// that is passed in when comparing strings.
|
|||
/// </summary>
|
|||
/// <param name="culture">The CultureInfo to use in string comparisons</param>
|
|||
public CultureSensitiveComparer(CultureInfo culture) |
|||
: base() |
|||
{ |
|||
_culture = culture ?? CultureInfo.InvariantCulture; |
|||
} |
|||
|
|||
#region IComparer<object> Members
|
|||
|
|||
/// <summary>
|
|||
/// Compares two objects and returns a value indicating whether one is less than, equal to or greater than the other.
|
|||
/// </summary>
|
|||
/// <param name="x">first item to compare</param>
|
|||
/// <param name="y">second item to compare</param>
|
|||
/// <returns>Negative number if x is less than y, zero if equal, and a positive number if x is greater than y</returns>
|
|||
/// <remarks>
|
|||
/// Compares the 2 items using the specified CultureInfo for string and using the default object comparer for all other objects.
|
|||
/// </remarks>
|
|||
public override int Compare(object x, object y) |
|||
{ |
|||
if (x == null) |
|||
{ |
|||
if (y != null) |
|||
{ |
|||
return -1; |
|||
} |
|||
return 0; |
|||
} |
|||
if (y == null) |
|||
{ |
|||
return 1; |
|||
} |
|||
|
|||
// at this point x and y are not null
|
|||
if (x.GetType() == typeof(string) && y.GetType() == typeof(string)) |
|||
{ |
|||
return _culture.CompareInfo.Compare((string)x, (string)y); |
|||
} |
|||
else |
|||
{ |
|||
return Comparer<object>.Default.Compare(x, y); |
|||
} |
|||
} |
|||
|
|||
#endregion
|
|||
} |
|||
|
|||
private class PathSortDescription : AvaloniaSortDescription |
|||
{ |
|||
private readonly bool _descending; |
|||
private readonly string _propertyPath; |
|||
private readonly Lazy<CultureSensitiveComparer> _cultureSensitiveComparer; |
|||
private readonly Lazy<IComparer<object>> _comparer; |
|||
private Type _propertyType; |
|||
private IComparer _internalComparer; |
|||
private IComparer<object> _internalComparerTyped; |
|||
private IComparer<object> InternalComparer |
|||
{ |
|||
get |
|||
{ |
|||
if (_internalComparerTyped == null && _internalComparer != null) |
|||
{ |
|||
if (_internalComparerTyped is IComparer<object> c) |
|||
_internalComparerTyped = c; |
|||
else |
|||
_internalComparerTyped = Comparer<object>.Create((x, y) => _internalComparer.Compare(x, y)); |
|||
} |
|||
|
|||
return _internalComparerTyped; |
|||
} |
|||
} |
|||
|
|||
public override string PropertyPath => _propertyPath; |
|||
public override IComparer<object> Comparer => _comparer.Value; |
|||
public override bool Descending => _descending; |
|||
|
|||
public PathSortDescription(string propertyPath, bool descending, CultureInfo culture) |
|||
{ |
|||
_propertyPath = propertyPath; |
|||
_descending = descending; |
|||
_cultureSensitiveComparer = new Lazy<CultureSensitiveComparer>(() => new CultureSensitiveComparer(culture ?? CultureInfo.CurrentCulture)); |
|||
_comparer = new Lazy<IComparer<object>>(() => Comparer<object>.Create((x, y) => Compare(x, y))); |
|||
} |
|||
private PathSortDescription(PathSortDescription inner, bool descending) |
|||
{ |
|||
_propertyPath = inner._propertyPath; |
|||
_descending = descending; |
|||
_propertyType = inner._propertyType; |
|||
_cultureSensitiveComparer = inner._cultureSensitiveComparer; |
|||
_internalComparer = inner._internalComparer; |
|||
_internalComparerTyped = inner._internalComparerTyped; |
|||
|
|||
_comparer = new Lazy<IComparer<object>>(() => Comparer<object>.Create((x, y) => Compare(x, y))); |
|||
} |
|||
|
|||
private object GetValue(object o) |
|||
{ |
|||
if (o == null) |
|||
return null; |
|||
|
|||
if (HasPropertyPath) |
|||
return InvokePath(o, _propertyPath, _propertyType); |
|||
|
|||
if (_propertyType == o.GetType()) |
|||
return o; |
|||
else |
|||
return null; |
|||
} |
|||
|
|||
private IComparer GetComparerForType(Type type) |
|||
{ |
|||
if (type == typeof(string)) |
|||
return _cultureSensitiveComparer.Value; |
|||
else |
|||
return (typeof(Comparer<>).MakeGenericType(type).GetProperty("Default")).GetValue(null, null) as IComparer; |
|||
} |
|||
private Type GetPropertyType(object o) |
|||
{ |
|||
return o.GetType().GetNestedPropertyType(_propertyPath); |
|||
} |
|||
|
|||
private int Compare(object x, object y) |
|||
{ |
|||
int result = 0; |
|||
|
|||
if(_propertyType == null) |
|||
{ |
|||
if(x != null) |
|||
{ |
|||
_propertyType = GetPropertyType(x); |
|||
} |
|||
if(_propertyType == null && y != null) |
|||
{ |
|||
_propertyType = GetPropertyType(y); |
|||
} |
|||
} |
|||
|
|||
object v1 = GetValue(x); |
|||
object v2 = GetValue(y); |
|||
|
|||
if (_propertyType != null && _internalComparer == null) |
|||
_internalComparer = GetComparerForType(_propertyType); |
|||
|
|||
result = _internalComparer?.Compare(v1, v2) ?? 0; |
|||
|
|||
if (_descending) |
|||
return -result; |
|||
else |
|||
return result; |
|||
} |
|||
|
|||
internal override void Initialize(Type itemType) |
|||
{ |
|||
base.Initialize(itemType); |
|||
|
|||
if(_propertyType == null) |
|||
_propertyType = itemType.GetNestedPropertyType(_propertyPath); |
|||
if (_internalComparer == null && _propertyType != null) |
|||
_internalComparer = GetComparerForType(_propertyType); |
|||
} |
|||
public override IOrderedEnumerable<object> OrderBy(IEnumerable<object> seq) |
|||
{ |
|||
if(_descending) |
|||
{ |
|||
return seq.OrderByDescending(o => GetValue(o), InternalComparer); |
|||
} |
|||
else |
|||
{ |
|||
return seq.OrderBy(o => GetValue(o), InternalComparer); |
|||
} |
|||
} |
|||
public override IOrderedEnumerable<object> ThenBy(IOrderedEnumerable<object> seq) |
|||
{ |
|||
if (_descending) |
|||
{ |
|||
return seq.ThenByDescending(o => GetValue(o), InternalComparer); |
|||
} |
|||
else |
|||
{ |
|||
return seq.ThenByDescending(o => GetValue(o), InternalComparer); |
|||
} |
|||
} |
|||
|
|||
internal override AvaloniaSortDescription SwitchSortDirection() |
|||
{ |
|||
return new PathSortDescription(this, !_descending); |
|||
} |
|||
} |
|||
|
|||
public static AvaloniaSortDescription FromPath(string propertyPath, bool descending = false, CultureInfo culture = null) |
|||
{ |
|||
return new PathSortDescription(propertyPath, descending, culture); |
|||
} |
|||
} |
|||
|
|||
public class AvaloniaSortDescriptionCollection : AvaloniaList<AvaloniaSortDescription> |
|||
{ } |
|||
} |
|||
File diff suppressed because it is too large
@ -0,0 +1,233 @@ |
|||
// (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 System.Collections.Generic; |
|||
using System.Collections.Specialized; |
|||
using System.Globalization; |
|||
using System.Text; |
|||
|
|||
namespace Avalonia.Collections |
|||
{ |
|||
/// <summary>Provides data for the <see cref="E:Avalonia.Collections.ICollectionView.CurrentChanging" /> event.</summary>
|
|||
public class CurrentChangingEventArgs : EventArgs |
|||
{ |
|||
private bool _cancel; |
|||
private bool _isCancelable; |
|||
|
|||
/// <summary>Initializes a new instance of the <see cref="T:System.ComponentModel.CurrentChangingEventArgs" /> class and sets the <see cref="P:System.ComponentModel.CurrentChangingEventArgs.IsCancelable" /> property to true.</summary>
|
|||
public CurrentChangingEventArgs() |
|||
{ |
|||
Initialize(true); |
|||
} |
|||
|
|||
/// <summary>Initializes a new instance of the <see cref="T:System.ComponentModel.CurrentChangingEventArgs" /> class and sets the <see cref="P:System.ComponentModel.CurrentChangingEventArgs.IsCancelable" /> property to the specified value.</summary>
|
|||
/// <param name="isCancelable">true to disable the ability to cancel a <see cref="P:System.ComponentModel.ICollectionView.CurrentItem" /> change; false to enable cancellation.</param>
|
|||
public CurrentChangingEventArgs(bool isCancelable) |
|||
{ |
|||
Initialize(isCancelable); |
|||
} |
|||
|
|||
private void Initialize(bool isCancelable) |
|||
{ |
|||
_isCancelable = isCancelable; |
|||
} |
|||
|
|||
/// <summary>Gets a value that indicates whether the <see cref="P:System.ComponentModel.ICollectionView.CurrentItem" /> change can be canceled. </summary>
|
|||
/// <returns>true if the event can be canceled; false if the event cannot be canceled.</returns>
|
|||
public bool IsCancelable |
|||
{ |
|||
get |
|||
{ |
|||
return _isCancelable; |
|||
} |
|||
} |
|||
|
|||
/// <summary>Gets or sets a value that indicates whether the <see cref="P:System.ComponentModel.ICollectionView.CurrentItem" /> change should be canceled. </summary>
|
|||
/// <returns>true if the event should be canceled; otherwise, false. The default is false.</returns>
|
|||
/// <exception cref="T:System.InvalidOperationException">The <see cref="P:System.ComponentModel.CurrentChangingEventArgs.IsCancelable" /> property value is false.</exception>
|
|||
public bool Cancel |
|||
{ |
|||
get |
|||
{ |
|||
return _cancel; |
|||
} |
|||
set |
|||
{ |
|||
if (IsCancelable) |
|||
_cancel = value; |
|||
else if (value) |
|||
throw new InvalidOperationException("CurrentChanging Cannot Be Canceled"); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>Enables collections to have the functionalities of current record management, custom sorting, filtering, and grouping.</summary>
|
|||
public interface ICollectionView : IEnumerable, INotifyCollectionChanged |
|||
{ |
|||
/// <summary>Gets or sets the cultural information for any operations of the view that may differ by culture, such as sorting.</summary>
|
|||
/// <returns>The culture information to use during culture-sensitive operations. </returns>
|
|||
CultureInfo Culture { get; set; } |
|||
|
|||
/// <summary>Indicates whether the specified item belongs to this collection view. </summary>
|
|||
/// <returns>true if the item belongs to this collection view; otherwise, false.</returns>
|
|||
/// <param name="item">The object to check. </param>
|
|||
bool Contains(object item); |
|||
|
|||
/// <summary>Gets the underlying collection.</summary>
|
|||
/// <returns>The underlying collection.</returns>
|
|||
IEnumerable SourceCollection { get; } |
|||
|
|||
/// <summary>Gets or sets a callback that is used to determine whether an item is appropriate for inclusion in the view. </summary>
|
|||
/// <returns>A method that is used to determine whether an item is appropriate for inclusion in the view.</returns>
|
|||
Func<object, bool> Filter { get; set; } |
|||
|
|||
/// <summary>Gets a value that indicates whether this view supports filtering by way of the <see cref="P:System.ComponentModel.ICollectionView.Filter" /> property.</summary>
|
|||
/// <returns>true if this view supports filtering; otherwise, false.</returns>
|
|||
bool CanFilter { get; } |
|||
|
|||
/// <summary>Gets a collection of <see cref="T:System.ComponentModel.SortDescription" /> instances that describe how the items in the collection are sorted in the view.</summary>
|
|||
/// <returns>A collection of values that describe how the items in the collection are sorted in the view.</returns>
|
|||
AvaloniaSortDescriptionCollection SortDescriptions { get; } |
|||
|
|||
/// <summary>Gets a value that indicates whether this view supports sorting by way of the <see cref="P:System.ComponentModel.ICollectionView.SortDescriptions" /> property.</summary>
|
|||
/// <returns>true if this view supports sorting; otherwise, false.</returns>
|
|||
bool CanSort { get; } |
|||
|
|||
/// <summary>Gets a value that indicates whether this view supports grouping by way of the <see cref="P:System.ComponentModel.ICollectionView.GroupDescriptions" /> property.</summary>
|
|||
/// <returns>true if this view supports grouping; otherwise, false.</returns>
|
|||
bool CanGroup { get; } |
|||
|
|||
/// <summary>Gets a collection of <see cref="T:System.ComponentModel.GroupDescription" /> objects that describe how the items in the collection are grouped in the view. </summary>
|
|||
/// <returns>A collection of objects that describe how the items in the collection are grouped in the view. </returns>
|
|||
//ObservableCollection<GroupDescription> GroupDescriptions { get; }
|
|||
|
|||
bool IsGrouping { get; } |
|||
int GroupingDepth { get; } |
|||
string GetGroupingPropertyNameAtDepth(int level); |
|||
|
|||
/// <summary>Gets the top-level groups.</summary>
|
|||
/// <returns>A read-only collection of the top-level groups or null if there are no groups.</returns>
|
|||
IAvaloniaReadOnlyList<object> Groups { get; } |
|||
|
|||
/// <summary>Gets a value that indicates whether the view is empty.</summary>
|
|||
/// <returns>true if the view is empty; otherwise, false.</returns>
|
|||
bool IsEmpty { get; } |
|||
|
|||
/// <summary>Recreates the view.</summary>
|
|||
void Refresh(); |
|||
|
|||
/// <summary>Enters a defer cycle that you can use to merge changes to the view and delay automatic refresh. </summary>
|
|||
/// <returns>The typical usage is to create a using scope with an implementation of this method and then include multiple view-changing calls within the scope. The implementation should delay automatic refresh until after the using scope exits. </returns>
|
|||
IDisposable DeferRefresh(); |
|||
|
|||
/// <summary>Gets the current item in the view.</summary>
|
|||
/// <returns>The current item in the view or null if there is no current item.</returns>
|
|||
object CurrentItem { get; } |
|||
|
|||
/// <summary>Gets the ordinal position of the <see cref="P:System.ComponentModel.ICollectionView.CurrentItem" /> in the view.</summary>
|
|||
/// <returns>The ordinal position of the <see cref="P:System.ComponentModel.ICollectionView.CurrentItem" /> in the view.</returns>
|
|||
int CurrentPosition { get; } |
|||
|
|||
/// <summary>Gets a value that indicates whether the <see cref="P:System.ComponentModel.ICollectionView.CurrentItem" /> of the view is beyond the end of the collection.</summary>
|
|||
/// <returns>true if the <see cref="P:System.ComponentModel.ICollectionView.CurrentItem" /> of the view is beyond the end of the collection; otherwise, false.</returns>
|
|||
bool IsCurrentAfterLast { get; } |
|||
|
|||
/// <summary>Gets a value that indicates whether the <see cref="P:System.ComponentModel.ICollectionView.CurrentItem" /> of the view is beyond the start of the collection.</summary>
|
|||
/// <returns>true if the <see cref="P:System.ComponentModel.ICollectionView.CurrentItem" /> of the view is beyond the start of the collection; otherwise, false.</returns>
|
|||
bool IsCurrentBeforeFirst { get; } |
|||
|
|||
/// <summary>Sets the first item in the view as the <see cref="P:System.ComponentModel.ICollectionView.CurrentItem" />.</summary>
|
|||
/// <returns>true if the resulting <see cref="P:System.ComponentModel.ICollectionView.CurrentItem" /> is an item in the view; otherwise, false.</returns>
|
|||
bool MoveCurrentToFirst(); |
|||
|
|||
/// <summary>Sets the last item in the view as the <see cref="P:System.ComponentModel.ICollectionView.CurrentItem" />.</summary>
|
|||
/// <returns>true if the resulting <see cref="P:System.ComponentModel.ICollectionView.CurrentItem" /> is an item in the view; otherwise, false.</returns>
|
|||
bool MoveCurrentToLast(); |
|||
|
|||
/// <summary>Sets the item after the <see cref="P:System.ComponentModel.ICollectionView.CurrentItem" /> in the view as the <see cref="P:System.ComponentModel.ICollectionView.CurrentItem" />.</summary>
|
|||
/// <returns>true if the resulting <see cref="P:System.ComponentModel.ICollectionView.CurrentItem" /> is an item in the view; otherwise, false.</returns>
|
|||
bool MoveCurrentToNext(); |
|||
|
|||
/// <summary>Sets the item before the <see cref="P:System.ComponentModel.ICollectionView.CurrentItem" /> in the view to the <see cref="P:System.ComponentModel.ICollectionView.CurrentItem" />.</summary>
|
|||
/// <returns>true if the resulting <see cref="P:System.ComponentModel.ICollectionView.CurrentItem" /> is an item in the view; otherwise, false.</returns>
|
|||
bool MoveCurrentToPrevious(); |
|||
|
|||
/// <summary>Sets the specified item in the view as the <see cref="P:System.ComponentModel.ICollectionView.CurrentItem" />.</summary>
|
|||
/// <returns>true if the resulting <see cref="P:System.ComponentModel.ICollectionView.CurrentItem" /> is an item in the view; otherwise, false.</returns>
|
|||
/// <param name="item">The item to set as the current item.</param>
|
|||
bool MoveCurrentTo(object item); |
|||
|
|||
/// <summary>Sets the item at the specified index to be the <see cref="P:System.ComponentModel.ICollectionView.CurrentItem" /> in the view.</summary>
|
|||
/// <returns>true if the resulting <see cref="P:System.ComponentModel.ICollectionView.CurrentItem" /> is an item in the view; otherwise, false.</returns>
|
|||
/// <param name="position">The index to set the <see cref="P:System.ComponentModel.ICollectionView.CurrentItem" /> to.</param>
|
|||
bool MoveCurrentToPosition(int position); |
|||
|
|||
/// <summary>Occurs before the current item changes.</summary>
|
|||
event EventHandler<CurrentChangingEventArgs> CurrentChanging; |
|||
|
|||
/// <summary>Occurs after the current item has been changed.</summary>
|
|||
event EventHandler CurrentChanged; |
|||
} |
|||
public interface IEditableCollectionView |
|||
{ |
|||
/// <summary>Gets a value that indicates whether a new item can be added to the collection.</summary>
|
|||
/// <returns>true if a new item can be added to the collection; otherwise, false.</returns>
|
|||
bool CanAddNew { get; } |
|||
|
|||
/// <summary>Adds a new item to the underlying collection.</summary>
|
|||
/// <returns>The new item that is added to the collection.</returns>
|
|||
object AddNew(); |
|||
|
|||
/// <summary>Ends the add transaction and saves the pending new item.</summary>
|
|||
void CommitNew(); |
|||
|
|||
/// <summary>Ends the add transaction and discards the pending new item.</summary>
|
|||
void CancelNew(); |
|||
|
|||
/// <summary>Gets a value that indicates whether an add transaction is in progress.</summary>
|
|||
/// <returns>true if an add transaction is in progress; otherwise, false.</returns>
|
|||
bool IsAddingNew { get; } |
|||
|
|||
/// <summary>Gets the item that is being added during the current add transaction.</summary>
|
|||
/// <returns>The item that is being added if <see cref="P:System.ComponentModel.IEditableCollectionView.IsAddingNew" /> is true; otherwise, null.</returns>
|
|||
object CurrentAddItem { get; } |
|||
|
|||
/// <summary>Gets a value that indicates whether an item can be removed from the collection.</summary>
|
|||
/// <returns>true if an item can be removed from the collection; otherwise, false.</returns>
|
|||
bool CanRemove { get; } |
|||
|
|||
/// <summary>Removes the item at the specified position from the collection.</summary>
|
|||
/// <param name="index">Index of item to remove.</param>
|
|||
void RemoveAt(int index); |
|||
|
|||
/// <summary>Removes the specified item from the collection.</summary>
|
|||
/// <param name="item">The item to remove.</param>
|
|||
void Remove(object item); |
|||
|
|||
/// <summary>Begins an edit transaction on the specified item.</summary>
|
|||
/// <param name="item">The item to edit.</param>
|
|||
void EditItem(object item); |
|||
|
|||
/// <summary>Ends the edit transaction and saves the pending changes.</summary>
|
|||
void CommitEdit(); |
|||
|
|||
/// <summary>Ends the edit transaction and, if possible, restores the original value of the item.</summary>
|
|||
void CancelEdit(); |
|||
|
|||
/// <summary>Gets a value that indicates whether the collection view can discard pending changes and restore the original values of an edited object.</summary>
|
|||
/// <returns>true if the collection view can discard pending changes and restore the original values of an edited object; otherwise, false.</returns>
|
|||
bool CanCancelEdit { get; } |
|||
|
|||
/// <summary>Gets a value that indicates whether an edit transaction is in progress.</summary>
|
|||
/// <returns>true if an edit transaction is in progress; otherwise, false.</returns>
|
|||
bool IsEditingItem { get; } |
|||
|
|||
/// <summary>Gets the item in the collection that is being edited.</summary>
|
|||
/// <returns>The item that is being edited if <see cref="P:System.ComponentModel.IEditableCollectionView.IsEditingItem" /> is true; otherwise, null.</returns>
|
|||
object CurrentEditItem { get; } |
|||
} |
|||
} |
|||
File diff suppressed because it is too large
@ -0,0 +1,464 @@ |
|||
// (c) Copyright Microsoft Corporation.
|
|||
// This source is subject to the Microsoft Public License (Ms-PL).
|
|||
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
|
|||
// All other rights reserved.
|
|||
|
|||
using Avalonia.Data; |
|||
using Avalonia.Utilities; |
|||
using System; |
|||
using System.Reactive.Disposables; |
|||
using System.Reactive.Subjects; |
|||
using Avalonia.Reactive; |
|||
using System.Diagnostics; |
|||
|
|||
namespace Avalonia.Controls |
|||
{ |
|||
/// <summary>
|
|||
/// Represents a <see cref="T:Avalonia.Controls.DataGrid" /> column that can
|
|||
/// bind to a property in the grid's data source.
|
|||
/// </summary>
|
|||
public abstract class DataGridBoundColumn : DataGridColumn |
|||
{ |
|||
private IBinding _binding; |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the binding that associates the column with a property in the data source.
|
|||
/// </summary>
|
|||
//TODO Binding
|
|||
public virtual IBinding Binding |
|||
{ |
|||
get |
|||
{ |
|||
return _binding; |
|||
} |
|||
set |
|||
{ |
|||
if (_binding != value) |
|||
{ |
|||
if (OwningGrid != null && !OwningGrid.CommitEdit(DataGridEditingUnit.Row, exitEditingMode: true)) |
|||
{ |
|||
// Edited value couldn't be committed, so we force a CancelEdit
|
|||
OwningGrid.CancelEdit(DataGridEditingUnit.Row, raiseEvents: false); |
|||
} |
|||
|
|||
_binding = value; |
|||
|
|||
|
|||
if (_binding != null) |
|||
{ |
|||
if(_binding is Avalonia.Data.Binding binding) |
|||
{ |
|||
// Force the TwoWay binding mode if there is a Path present. TwoWay binding requires a Path.
|
|||
if (!String.IsNullOrEmpty(binding.Path)) |
|||
{ |
|||
binding.Mode = BindingMode.TwoWay; |
|||
} |
|||
|
|||
if (binding.Converter == null) |
|||
{ |
|||
binding.Converter = DataGridValueConverter.Instance; |
|||
} |
|||
} |
|||
|
|||
//// Setup the binding for validation
|
|||
//_binding.ValidatesOnDataErrors = true;
|
|||
//_binding.ValidatesOnExceptions = true;
|
|||
//_binding.NotifyOnValidationError = true;
|
|||
//_binding.UpdateSourceTrigger = UpdateSourceTrigger.Explicit;
|
|||
|
|||
// Apply the new Binding to existing rows in the DataGrid
|
|||
if (OwningGrid != null) |
|||
{ |
|||
OwningGrid.OnColumnBindingChanged(this); |
|||
} |
|||
} |
|||
|
|||
RemoveEditingElement(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// The binding that will be used to get or set cell content for the clipboard.
|
|||
/// If the base ClipboardContentBinding is not explicitly set, this will return the value of Binding.
|
|||
/// </summary>
|
|||
public override IBinding ClipboardContentBinding |
|||
{ |
|||
get |
|||
{ |
|||
return base.ClipboardContentBinding ?? Binding; |
|||
} |
|||
set |
|||
{ |
|||
base.ClipboardContentBinding = value; |
|||
} |
|||
} |
|||
|
|||
//TODO Rename
|
|||
protected sealed override Control GenerateEditingElement(DataGridCell cell, object dataItem) |
|||
{ |
|||
Control element = GenerateEditingElementDirect(cell, dataItem); |
|||
|
|||
if (Binding != null) |
|||
//if (Binding != null || !DesignerProperties.IsInDesignTool)
|
|||
{ |
|||
//var t1 = Binding.Initiate(textBox, BindingTarget, anchor: null, enableDataValidation: true);
|
|||
//BindingOperations.Apply(textBox, BindingTarget, t1, null);
|
|||
|
|||
BindEditingElement(element, BindingTarget, Binding); |
|||
//element.Bind(BindingTarget, Binding);
|
|||
//textBox.SetBinding(BindingTarget, Binding);
|
|||
} |
|||
|
|||
return element; |
|||
} |
|||
|
|||
private static IDisposable BindEditingElement(IAvaloniaObject target, AvaloniaProperty property, IBinding binding) |
|||
{ |
|||
var result = binding.Initiate(target, property, enableDataValidation: true); |
|||
|
|||
if (result != null) |
|||
{ |
|||
//if(result.Subject != null)
|
|||
//{
|
|||
// var watcher = new BindingWatcher(result.Subject, result);
|
|||
// result = watcher.InstancedBinding;
|
|||
//}
|
|||
|
|||
return BindingOperations.Apply(target, property, result, null); |
|||
} |
|||
else |
|||
{ |
|||
return Disposable.Empty; |
|||
} |
|||
} |
|||
|
|||
public class LightweightSubject<T> : LightweightObservableBase<T>, ISubject<T> |
|||
{ |
|||
public void OnCompleted() |
|||
{ |
|||
PublishCompleted(); |
|||
} |
|||
public void OnError(Exception error) |
|||
{ |
|||
PublishError(error); |
|||
} |
|||
public void OnNext(T value) |
|||
{ |
|||
PublishNext(value); |
|||
} |
|||
|
|||
protected override void Deinitialize() |
|||
{ } |
|||
protected override void Initialize() |
|||
{ } |
|||
|
|||
protected override void Subscribed(IObserver<T> observer, bool first) |
|||
{ |
|||
base.Subscribed(observer, first); |
|||
} |
|||
} |
|||
|
|||
class BindingWatcher |
|||
{ |
|||
ISubject<object> _innerSubject; |
|||
ISubject<object> _wrappedSubject; |
|||
InstancedBinding _instancedBinding; |
|||
bool _isWriting = false; |
|||
|
|||
public BindingWatcher(ISubject<object> innerSubject, InstancedBinding innerBinding) |
|||
{ |
|||
_innerSubject = innerSubject; |
|||
_wrappedSubject = new LightweightSubject<object>(); |
|||
_instancedBinding = new InstancedBinding(_wrappedSubject, innerBinding.Mode, innerBinding.Priority); |
|||
|
|||
_innerSubject.Subscribe(InnerSubjectOnNext); |
|||
_wrappedSubject.Subscribe(WrappedSubjectOnNext); |
|||
} |
|||
|
|||
private void InnerSubjectOnNext(object value) |
|||
{ |
|||
Debug.WriteLine($"InnerSubject: {value} ({value?.GetType().Name})"); |
|||
if (!_isWriting) |
|||
{ |
|||
_isWriting = true; |
|||
_wrappedSubject.OnNext(value); |
|||
_isWriting = false; |
|||
} |
|||
} |
|||
private void WrappedSubjectOnNext(object value) |
|||
{ |
|||
Debug.WriteLine($"WrappedSubject: {value} ({value?.GetType().Name})"); |
|||
if (!_isWriting) |
|||
{ |
|||
_isWriting = true; |
|||
_innerSubject.OnNext(value); |
|||
_isWriting = false; |
|||
} |
|||
} |
|||
|
|||
public InstancedBinding InstancedBinding => _instancedBinding; |
|||
} |
|||
|
|||
/* |
|||
public static IDisposable Apply( |
|||
IAvaloniaObject target, |
|||
AvaloniaProperty property, |
|||
InstancedBinding binding, |
|||
object anchor) |
|||
{ |
|||
Contract.Requires<ArgumentNullException>(target != null); |
|||
Contract.Requires<ArgumentNullException>(property != null); |
|||
Contract.Requires<ArgumentNullException>(binding != null); |
|||
|
|||
var mode = binding.Mode; |
|||
|
|||
if (mode == BindingMode.Default) |
|||
{ |
|||
mode = property.GetMetadata(target.GetType()).DefaultBindingMode; |
|||
} |
|||
|
|||
switch (mode) |
|||
{ |
|||
case BindingMode.Default: |
|||
case BindingMode.OneWay: |
|||
return target.Bind(property, binding.Observable ?? binding.Subject, binding.Priority); |
|||
case BindingMode.TwoWay: |
|||
return new CompositeDisposable( |
|||
target.Bind(property, binding.Subject, binding.Priority), |
|||
target.GetObservable(property).Subscribe(binding.Subject)); |
|||
case BindingMode.OneTime: |
|||
var source = binding.Subject ?? binding.Observable; |
|||
|
|||
if (source != null) |
|||
{ |
|||
return source |
|||
.Where(x => BindingNotification.ExtractValue(x) != AvaloniaProperty.UnsetValue) |
|||
.Take(1) |
|||
.Subscribe(x => target.SetValue(property, x, binding.Priority)); |
|||
} |
|||
else |
|||
{ |
|||
target.SetValue(property, binding.Value, binding.Priority); |
|||
return Disposable.Empty; |
|||
} |
|||
case BindingMode.OneWayToSource: |
|||
return target.GetObservable(property).Subscribe(binding.Subject); |
|||
default: |
|||
throw new ArgumentException("Invalid binding mode."); |
|||
} |
|||
*/ |
|||
|
|||
|
|||
protected abstract Control GenerateEditingElementDirect(DataGridCell cell, object dataItem); |
|||
|
|||
|
|||
internal AvaloniaProperty BindingTarget { get; set; } |
|||
|
|||
internal void SetHeaderFromBinding() |
|||
{ |
|||
if (OwningGrid != null && OwningGrid.DataConnection.DataType != null |
|||
&& Header == null && Binding != null && Binding is Binding binding |
|||
&& !String.IsNullOrWhiteSpace(binding.Path)) |
|||
{ |
|||
string header = OwningGrid.DataConnection.DataType.GetDisplayName(binding.Path); |
|||
if (header != null) |
|||
{ |
|||
Header = header; |
|||
} |
|||
} |
|||
} |
|||
|
|||
private class AdvancedBinding |
|||
{ |
|||
|
|||
class SubjectWrapper : LightweightObservableBase<object>, ISubject<object>, IDisposable |
|||
{ |
|||
private readonly ISubject<object> _sourceSubject; |
|||
private IDisposable _subscription; |
|||
private object _controlValue; |
|||
private bool _isControlValueSet = false; |
|||
|
|||
public SubjectWrapper(ISubject<object> bindingSourceSubject) |
|||
{ |
|||
_sourceSubject = bindingSourceSubject; |
|||
} |
|||
|
|||
private void SetSourceValue(object value) |
|||
{ |
|||
_sourceSubject.OnNext(value); |
|||
} |
|||
private void SetControlValue(object value) |
|||
{ |
|||
PublishNext(value); |
|||
} |
|||
|
|||
private void OnValidationError(BindingNotification notification) |
|||
{ |
|||
|
|||
} |
|||
private void OnControlValueUpdated(object value) |
|||
{ |
|||
_controlValue = value; |
|||
_isControlValueSet = true; |
|||
} |
|||
private void OnSourceValueUpdated(object value) |
|||
{ |
|||
if(value is BindingNotification notification) |
|||
{ |
|||
if (notification.ErrorType != BindingErrorType.None) |
|||
OnValidationError(notification); |
|||
else |
|||
SetControlValue(value); |
|||
} |
|||
else |
|||
{ |
|||
SetControlValue(value); |
|||
} |
|||
} |
|||
|
|||
protected override void Deinitialize() |
|||
{ |
|||
_subscription?.Dispose(); |
|||
_subscription = null; |
|||
} |
|||
protected override void Initialize() |
|||
{ |
|||
_subscription = _sourceSubject.Subscribe(OnSourceValueUpdated); |
|||
} |
|||
|
|||
void IObserver<object>.OnCompleted() |
|||
{ |
|||
throw new NotImplementedException(); |
|||
} |
|||
void IObserver<object>.OnError(Exception error) |
|||
{ |
|||
throw new NotImplementedException(); |
|||
} |
|||
void IObserver<object>.OnNext(object value) |
|||
{ |
|||
OnControlValueUpdated(value); |
|||
} |
|||
|
|||
public void Dispose() |
|||
{ |
|||
_subscription?.Dispose(); |
|||
_subscription = null; |
|||
} |
|||
} |
|||
} |
|||
|
|||
} |
|||
|
|||
/* |
|||
|
|||
[StyleTypedProperty(Property = "ElementStyle", StyleTargetType = typeof(FrameworkElement))] |
|||
[StyleTypedProperty(Property = "EditingElementStyle", StyleTargetType = typeof(FrameworkElement))] |
|||
*/ |
|||
|
|||
#region Binding
|
|||
|
|||
|
|||
/*internal override List<string> CreateBindingPaths() |
|||
{ |
|||
if (Binding != null && Binding.Path != null) |
|||
{ |
|||
return new List<string>() { Binding.Path.Path }; |
|||
} |
|||
return base.CreateBindingPaths(); |
|||
}*/ |
|||
|
|||
/*internal override List<BindingInfo> CreateBindings(FrameworkElement element, object dataItem, bool twoWay) |
|||
{ |
|||
BindingInfo bindingData = new BindingInfo(); |
|||
if (twoWay && BindingTarget != null) |
|||
{ |
|||
bindingData.BindingExpression = element.GetBindingExpression(BindingTarget); |
|||
if (bindingData.BindingExpression != null) |
|||
{ |
|||
bindingData.BindingTarget = BindingTarget; |
|||
bindingData.Element = element; |
|||
return new List<BindingInfo> { bindingData }; |
|||
} |
|||
} |
|||
foreach (DependencyProperty bindingTarget in element.GetDependencyProperties(false)) |
|||
{ |
|||
bindingData.BindingExpression = element.GetBindingExpression(bindingTarget); |
|||
if (bindingData.BindingExpression != null |
|||
&& bindingData.BindingExpression.ParentBinding == Binding) |
|||
{ |
|||
BindingTarget = bindingTarget; |
|||
bindingData.BindingTarget = BindingTarget; |
|||
bindingData.Element = element; |
|||
return new List<BindingInfo> { bindingData }; |
|||
} |
|||
} |
|||
return base.CreateBindings(element, dataItem, twoWay); |
|||
}*/ |
|||
|
|||
|
|||
#endregion
|
|||
|
|||
#region ClipBoard
|
|||
|
|||
|
|||
|
|||
#endregion
|
|||
|
|||
|
|||
#region Styles
|
|||
|
|||
//TODO Styles
|
|||
|
|||
/* |
|||
private Style _elementStyle; |
|||
private Style _editingElementStyle; |
|||
*/ |
|||
|
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the style that is used when rendering the element that the column displays for a cell in editing mode.
|
|||
/// </summary>
|
|||
/*public Style EditingElementStyle |
|||
{ |
|||
get |
|||
{ |
|||
return _editingElementStyle; |
|||
} |
|||
set |
|||
{ |
|||
if (_editingElementStyle != value) |
|||
{ |
|||
_editingElementStyle = value; |
|||
// We choose not to update the elements already editing in the Grid here. They
|
|||
// will get the EditingElementStyle next time they go into edit mode
|
|||
} |
|||
} |
|||
}*/ |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the style that is used when rendering the element that the column displays for a cell
|
|||
/// that is not in editing mode.
|
|||
/// </summary>
|
|||
/*public Style ElementStyle |
|||
{ |
|||
get |
|||
{ |
|||
return _elementStyle; |
|||
} |
|||
set |
|||
{ |
|||
if (_elementStyle != value) |
|||
{ |
|||
_elementStyle = value; |
|||
if (OwningGrid != null) |
|||
{ |
|||
OwningGrid.OnColumnElementStyleChanged(this); |
|||
} |
|||
} |
|||
} |
|||
}*/ |
|||
|
|||
|
|||
#endregion
|
|||
} |
|||
@ -0,0 +1,336 @@ |
|||
// (c) Copyright Microsoft Corporation.
|
|||
// This source is subject to the Microsoft Public License (Ms-PL).
|
|||
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
|
|||
// All other rights reserved.
|
|||
|
|||
using Avalonia.Controls.Primitives; |
|||
using Avalonia.Controls.Shapes; |
|||
using Avalonia.Input; |
|||
|
|||
namespace Avalonia.Controls |
|||
{ |
|||
/// <summary>
|
|||
/// Represents an individual <see cref="T:Avalonia.Controls.DataGrid" /> cell.
|
|||
/// </summary>
|
|||
public class DataGridCell : ContentControl |
|||
{ |
|||
private const string DATAGRIDCELL_elementRightGridLine = "PART_RightGridLine"; |
|||
|
|||
private Rectangle _rightGridLine; |
|||
private DataGridColumn _owningColumn; |
|||
|
|||
bool _isValid; |
|||
|
|||
public static readonly DirectProperty<DataGridCell, bool> IsValidProperty = |
|||
AvaloniaProperty.RegisterDirect<DataGridCell, bool>( |
|||
nameof(IsValid), |
|||
o => o.IsValid); |
|||
|
|||
static DataGridCell() |
|||
{ |
|||
PointerPressedEvent.AddClassHandler<DataGridCell>( |
|||
x => x.DataGridCell_PointerPressed, handledEventsToo: true); |
|||
} |
|||
public DataGridCell() |
|||
{ } |
|||
|
|||
public bool IsValid |
|||
{ |
|||
get { return _isValid; } |
|||
internal set { SetAndRaise(IsValidProperty, ref _isValid, value); } |
|||
} |
|||
|
|||
internal DataGridColumn OwningColumn |
|||
{ |
|||
get => _owningColumn; |
|||
set |
|||
{ |
|||
if (_owningColumn != value) |
|||
{ |
|||
_owningColumn = value; |
|||
OnOwningColumnSet(value); |
|||
} |
|||
} |
|||
} |
|||
internal DataGridRow OwningRow |
|||
{ |
|||
get; |
|||
set; |
|||
} |
|||
|
|||
internal DataGrid OwningGrid |
|||
{ |
|||
get { return OwningRow?.OwningGrid ?? OwningColumn?.OwningGrid; } |
|||
} |
|||
|
|||
internal double ActualRightGridLineWidth |
|||
{ |
|||
get { return _rightGridLine?.Bounds.Width ?? 0; } |
|||
} |
|||
|
|||
internal int ColumnIndex |
|||
{ |
|||
get { return OwningColumn?.Index ?? -1; } |
|||
} |
|||
|
|||
internal int RowIndex |
|||
{ |
|||
get { return OwningRow?.Index ?? -1; } |
|||
} |
|||
|
|||
internal bool IsCurrent |
|||
{ |
|||
get |
|||
{ |
|||
return OwningGrid.CurrentColumnIndex == OwningColumn.Index && |
|||
OwningGrid.CurrentSlot == OwningRow.Slot; |
|||
} |
|||
} |
|||
|
|||
private bool IsEdited |
|||
{ |
|||
get |
|||
{ |
|||
return OwningGrid.EditingRow == OwningRow && |
|||
OwningGrid.EditingColumnIndex == ColumnIndex; |
|||
} |
|||
} |
|||
|
|||
private bool IsMouseOver |
|||
{ |
|||
get |
|||
{ |
|||
return OwningRow != null && OwningRow.MouseOverColumnIndex == ColumnIndex; |
|||
} |
|||
set |
|||
{ |
|||
if (value != IsMouseOver) |
|||
{ |
|||
if (value) |
|||
{ |
|||
OwningRow.MouseOverColumnIndex = ColumnIndex; |
|||
} |
|||
else |
|||
{ |
|||
OwningRow.MouseOverColumnIndex = null; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Builds the visual tree for the cell control when a new template is applied.
|
|||
/// </summary>
|
|||
protected override void OnTemplateApplied(TemplateAppliedEventArgs e) |
|||
{ |
|||
base.OnTemplateApplied(e); |
|||
|
|||
UpdatePseudoClasses(); |
|||
_rightGridLine = e.NameScope.Find<Rectangle>(DATAGRIDCELL_elementRightGridLine); |
|||
if(_rightGridLine != null && OwningColumn == null) |
|||
{ |
|||
// Turn off the right GridLine for filler cells
|
|||
_rightGridLine.IsVisible = false; |
|||
} |
|||
else |
|||
{ |
|||
EnsureGridLine(null); |
|||
} |
|||
|
|||
} |
|||
protected override void OnPointerEnter(PointerEventArgs e) |
|||
{ |
|||
base.OnPointerEnter(e); |
|||
|
|||
if (OwningRow != null) |
|||
{ |
|||
IsMouseOver = true; |
|||
} |
|||
} |
|||
protected override void OnPointerLeave(PointerEventArgs e) |
|||
{ |
|||
base.OnPointerLeave(e); |
|||
|
|||
if (OwningRow != null) |
|||
{ |
|||
IsMouseOver = false; |
|||
} |
|||
} |
|||
|
|||
//TODO TabStop
|
|||
private void DataGridCell_PointerPressed(PointerPressedEventArgs e) |
|||
{ |
|||
if(e.MouseButton != MouseButton.Left) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
// OwningGrid is null for TopLeftHeaderCell and TopRightHeaderCell because they have no OwningRow
|
|||
if (OwningGrid != null) |
|||
{ |
|||
if (!e.Handled) |
|||
//if (!e.Handled && OwningGrid.IsTabStop)
|
|||
{ |
|||
OwningGrid.Focus(); |
|||
} |
|||
if (OwningRow != null) |
|||
{ |
|||
e.Handled = OwningGrid.UpdateStateOnMouseLeftButtonDown(e, ColumnIndex, OwningRow.Slot, !e.Handled); |
|||
OwningGrid.UpdatedStateOnMouseLeftButtonDown = true; |
|||
} |
|||
} |
|||
} |
|||
|
|||
internal void UpdatePseudoClasses() |
|||
{ |
|||
/* |
|||
if (OwningGrid == null || OwningColumn == null || OwningRow == null || OwningRow.Visibility == Visibility.Collapsed || OwningRow.Slot == -1) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
// CommonStates
|
|||
if (IsMouseOver) |
|||
{ |
|||
VisualStates.GoToState(this, animate, VisualStates.StateMouseOver, VisualStates.StateNormal); |
|||
} |
|||
else |
|||
{ |
|||
VisualStates.GoToState(this, animate, VisualStates.StateNormal); |
|||
} |
|||
|
|||
// SelectionStates
|
|||
if (OwningRow.IsSelected) |
|||
{ |
|||
VisualStates.GoToState(this, animate, VisualStates.StateSelected, VisualStates.StateUnselected); |
|||
} |
|||
else |
|||
{ |
|||
VisualStates.GoToState(this, animate, VisualStates.StateUnselected); |
|||
} |
|||
|
|||
// FocusStates
|
|||
if (OwningGrid.ContainsFocus) |
|||
{ |
|||
VisualStates.GoToState(this, animate, VisualStates.StateFocused, VisualStates.StateUnfocused); |
|||
} |
|||
else |
|||
{ |
|||
VisualStates.GoToState(this, animate, VisualStates.StateUnfocused); |
|||
} |
|||
|
|||
// CurrentStates
|
|||
if (IsCurrent) |
|||
{ |
|||
VisualStates.GoToState(this, animate, VisualStates.StateCurrent, VisualStates.StateRegular); |
|||
} |
|||
else |
|||
{ |
|||
VisualStates.GoToState(this, animate, VisualStates.StateRegular); |
|||
} |
|||
|
|||
// Interaction states
|
|||
if (IsEdited) |
|||
{ |
|||
VisualStates.GoToState(this, animate, VisualStates.StateEditing, VisualStates.StateDisplay); |
|||
} |
|||
else |
|||
{ |
|||
VisualStates.GoToState(this, animate, VisualStates.StateDisplay); |
|||
} |
|||
|
|||
// Validation states
|
|||
if (IsValid) |
|||
{ |
|||
VisualStates.GoToState(this, animate, VisualStates.StateValid); |
|||
} |
|||
else |
|||
{ |
|||
VisualStates.GoToState(this, animate, VisualStates.StateInvalid, VisualStates.StateValid); |
|||
} |
|||
*/ |
|||
} |
|||
|
|||
// Makes sure the right gridline has the proper stroke and visibility. If lastVisibleColumn is specified, the
|
|||
// right gridline will be collapsed if this cell belongs to the lastVisibileColumn and there is no filler column
|
|||
internal void EnsureGridLine(DataGridColumn lastVisibleColumn) |
|||
{ |
|||
if (OwningGrid != null && _rightGridLine != null) |
|||
{ |
|||
if (OwningGrid.VerticalGridLinesBrush != null && OwningGrid.VerticalGridLinesBrush != _rightGridLine.Fill) |
|||
{ |
|||
_rightGridLine.Fill = OwningGrid.VerticalGridLinesBrush; |
|||
} |
|||
|
|||
bool newVisibility = |
|||
(OwningGrid.GridLinesVisibility == DataGridGridLinesVisibility.Vertical || OwningGrid.GridLinesVisibility == DataGridGridLinesVisibility.All) |
|||
&& (OwningGrid.ColumnsInternal.FillerColumn.IsActive || OwningColumn != lastVisibleColumn); |
|||
|
|||
if (newVisibility != _rightGridLine.IsVisible) |
|||
{ |
|||
_rightGridLine.IsVisible = newVisibility; |
|||
} |
|||
} |
|||
} |
|||
|
|||
private void OnOwningColumnSet(DataGridColumn column) |
|||
{ |
|||
if(column == null) |
|||
{ |
|||
Classes.Clear(); |
|||
} |
|||
else |
|||
{ |
|||
Classes.Replace(column.CellStyleClasses); |
|||
} |
|||
} |
|||
|
|||
} |
|||
|
|||
/* |
|||
[TemplatePart(Name = DATAGRIDCELL_elementRightGridLine, Type = typeof(Rectangle))] |
|||
[TemplateVisualState(Name = VisualStates.StateNormal, GroupName = VisualStates.GroupCommon)] |
|||
[TemplateVisualState(Name = VisualStates.StateMouseOver, GroupName = VisualStates.GroupCommon)] |
|||
[TemplateVisualState(Name = VisualStates.StateUnselected, GroupName = VisualStates.GroupSelection)] |
|||
[TemplateVisualState(Name = VisualStates.StateSelected, GroupName = VisualStates.GroupSelection)] |
|||
[TemplateVisualState(Name = VisualStates.StateUnfocused, GroupName = VisualStates.GroupFocus)] |
|||
[TemplateVisualState(Name = VisualStates.StateFocused, GroupName = VisualStates.GroupFocus)] |
|||
[TemplateVisualState(Name = VisualStates.StateRegular, GroupName = VisualStates.GroupCurrent)] |
|||
[TemplateVisualState(Name = VisualStates.StateCurrent, GroupName = VisualStates.GroupCurrent)] |
|||
[TemplateVisualState(Name = VisualStates.StateDisplay, GroupName = VisualStates.GroupInteraction)] |
|||
[TemplateVisualState(Name = VisualStates.StateEditing, GroupName = VisualStates.GroupInteraction)] |
|||
[TemplateVisualState(Name = VisualStates.StateInvalid, GroupName = VisualStates.GroupValidation)] |
|||
[TemplateVisualState(Name = VisualStates.StateValid, GroupName = VisualStates.GroupValidation)] |
|||
public sealed partial class DataGridCell : ContentControl |
|||
*/ |
|||
|
|||
|
|||
//TODO Styles
|
|||
/// <summary>
|
|||
/// Ensures that the correct Style is applied to this object.
|
|||
/// </summary>
|
|||
/// <param name="previousStyle">Caller's previous associated Style</param>
|
|||
/*internal void EnsureStyle(Style previousStyle) |
|||
{ |
|||
if (Style != null |
|||
&& (OwningColumn == null || Style != OwningColumn.CellStyle) |
|||
&& (OwningGrid == null || Style != OwningGrid.CellStyle) |
|||
&& (Style != previousStyle)) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
Style style = null; |
|||
if (OwningColumn != null) |
|||
{ |
|||
style = OwningColumn.CellStyle; |
|||
} |
|||
if (style == null && OwningGrid != null) |
|||
{ |
|||
style = OwningGrid.CellStyle; |
|||
} |
|||
SetStyleWithType(style); |
|||
} */ |
|||
|
|||
|
|||
} |
|||
@ -0,0 +1,71 @@ |
|||
// (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 System.Collections.Generic; |
|||
using System.Diagnostics; |
|||
|
|||
namespace Avalonia.Controls |
|||
{ |
|||
internal class DataGridCellCollection |
|||
{ |
|||
private List<DataGridCell> _cells; |
|||
private DataGridRow _owningRow; |
|||
|
|||
internal event EventHandler<DataGridCellEventArgs> CellAdded; |
|||
internal event EventHandler<DataGridCellEventArgs> CellRemoved; |
|||
|
|||
public DataGridCellCollection(DataGridRow owningRow) |
|||
{ |
|||
_owningRow = owningRow; |
|||
_cells = new List<DataGridCell>(); |
|||
} |
|||
|
|||
public int Count |
|||
{ |
|||
get |
|||
{ |
|||
return _cells.Count; |
|||
} |
|||
} |
|||
|
|||
public IEnumerator GetEnumerator() |
|||
{ |
|||
return _cells.GetEnumerator(); |
|||
} |
|||
|
|||
public void Insert(int cellIndex, DataGridCell cell) |
|||
{ |
|||
Debug.Assert(cellIndex >= 0 && cellIndex <= _cells.Count); |
|||
Debug.Assert(cell != null); |
|||
|
|||
cell.OwningRow = _owningRow; |
|||
_cells.Insert(cellIndex, cell); |
|||
|
|||
CellAdded?.Invoke(this, new DataGridCellEventArgs(cell)); |
|||
} |
|||
|
|||
public void RemoveAt(int cellIndex) |
|||
{ |
|||
DataGridCell dataGridCell = _cells[cellIndex]; |
|||
_cells.RemoveAt(cellIndex); |
|||
dataGridCell.OwningRow = null; |
|||
CellRemoved?.Invoke(this, new DataGridCellEventArgs(dataGridCell)); |
|||
} |
|||
|
|||
public DataGridCell this[int index] |
|||
{ |
|||
get |
|||
{ |
|||
if (index < 0 || index >= _cells.Count) |
|||
{ |
|||
throw DataGridError.DataGrid.ValueMustBeBetween("index", "Index", 0, true, _cells.Count, false); |
|||
} |
|||
return _cells[index]; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,57 @@ |
|||
// (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.Globalization; |
|||
|
|||
namespace Avalonia.Controls |
|||
{ |
|||
internal class DataGridCellCoordinates |
|||
{ |
|||
public DataGridCellCoordinates(int columnIndex, int slot) |
|||
{ |
|||
ColumnIndex = columnIndex; |
|||
Slot = slot; |
|||
} |
|||
|
|||
public DataGridCellCoordinates(DataGridCellCoordinates dataGridCellCoordinates) : this(dataGridCellCoordinates.ColumnIndex, dataGridCellCoordinates.Slot) |
|||
{ |
|||
} |
|||
|
|||
public int ColumnIndex |
|||
{ |
|||
get; |
|||
set; |
|||
} |
|||
|
|||
public int Slot |
|||
{ |
|||
get; |
|||
set; |
|||
} |
|||
|
|||
public override bool Equals(object o) |
|||
{ |
|||
if (o is DataGridCellCoordinates dataGridCellCoordinates) |
|||
{ |
|||
return dataGridCellCoordinates.ColumnIndex == ColumnIndex && dataGridCellCoordinates.Slot == Slot; |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
// There is build warning if this is missiing
|
|||
public override int GetHashCode() |
|||
{ |
|||
return base.GetHashCode(); |
|||
} |
|||
|
|||
#if DEBUG
|
|||
public override string ToString() |
|||
{ |
|||
return "DataGridCellCoordinates {ColumnIndex = " + ColumnIndex.ToString(CultureInfo.CurrentCulture) + |
|||
", Slot = " + Slot.ToString(CultureInfo.CurrentCulture) + "}"; |
|||
} |
|||
#endif
|
|||
} |
|||
} |
|||
@ -0,0 +1,330 @@ |
|||
// (c) Copyright Microsoft Corporation.
|
|||
// This source is subject to the Microsoft Public License (Ms-PL).
|
|||
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
|
|||
// All other rights reserved.
|
|||
|
|||
using Avalonia.Input; |
|||
using Avalonia.Interactivity; |
|||
using Avalonia.Layout; |
|||
using System; |
|||
using System.Collections.Specialized; |
|||
|
|||
namespace Avalonia.Controls |
|||
{ |
|||
/// <summary>
|
|||
/// Represents a <see cref="T:System.Windows.Controls.DataGrid" /> column that hosts
|
|||
/// <see cref="T:System.Windows.Controls.CheckBox" /> controls in its cells.
|
|||
/// </summary>
|
|||
public class DataGridCheckBoxColumn : DataGridBoundColumn |
|||
{ |
|||
#region Data
|
|||
|
|||
private bool _beganEditWithKeyboard; |
|||
private bool _isThreeState; |
|||
private CheckBox _currentCheckBox; |
|||
private DataGrid _owningGrid; |
|||
|
|||
#endregion Data
|
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="T:System.Windows.Controls.DataGridCheckBoxColumn" /> class.
|
|||
/// </summary>
|
|||
public DataGridCheckBoxColumn() |
|||
{ |
|||
BindingTarget = CheckBox.IsCheckedProperty; |
|||
} |
|||
|
|||
#region Public Properties
|
|||
|
|||
/// <summary>
|
|||
/// Gets or sets a value that indicates whether the hosted <see cref="T:System.Windows.Controls.CheckBox" /> controls allow three states or two.
|
|||
/// </summary>
|
|||
/// <returns>
|
|||
/// true if the hosted controls support three states; false if they support two states. The default is false.
|
|||
/// </returns>
|
|||
public bool IsThreeState |
|||
{ |
|||
get |
|||
{ |
|||
return _isThreeState; |
|||
} |
|||
set |
|||
{ |
|||
if (_isThreeState != value) |
|||
{ |
|||
_isThreeState = value; |
|||
NotifyPropertyChanged(nameof(IsThreeState)); |
|||
} |
|||
} |
|||
} |
|||
|
|||
#endregion Public Properties
|
|||
|
|||
#region Protected Methods
|
|||
|
|||
/// <summary>
|
|||
/// Causes the column cell being edited to revert to the specified value.
|
|||
/// </summary>
|
|||
/// <param name="editingElement">
|
|||
/// The element that the column displays for a cell in editing mode.
|
|||
/// </param>
|
|||
/// <param name="uneditedValue">
|
|||
/// The previous, unedited value in the cell being edited.
|
|||
/// </param>
|
|||
protected override void CancelCellEdit(Control editingElement, object uneditedValue) |
|||
{ |
|||
if (editingElement is CheckBox editingCheckBox) |
|||
{ |
|||
editingCheckBox.IsChecked = (bool?)uneditedValue; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets a <see cref="T:System.Windows.Controls.CheckBox" /> control that is bound to the column's <see cref="P:System.Windows.Controls.DataGridBoundColumn.Binding" /> property value.
|
|||
/// </summary>
|
|||
/// <param name="cell">
|
|||
/// The cell that will contain the generated element.
|
|||
/// </param>
|
|||
/// <param name="dataItem">
|
|||
/// The data item represented by the row that contains the intended cell.
|
|||
/// </param>
|
|||
/// <returns>
|
|||
/// A new <see cref="T:System.Windows.Controls.CheckBox" /> control that is bound to the column's <see cref="P:System.Windows.Controls.DataGridBoundColumn.Binding" /> property value.
|
|||
/// </returns>
|
|||
protected override Control GenerateEditingElementDirect(DataGridCell cell, object dataItem) |
|||
{ |
|||
var checkBox = new CheckBox |
|||
{ |
|||
Margin = new Thickness(0) |
|||
}; |
|||
ConfigureCheckBox(checkBox); |
|||
return checkBox; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets a read-only <see cref="T:System.Windows.Controls.CheckBox" /> control that is bound to the column's <see cref="P:System.Windows.Controls.DataGridBoundColumn.Binding" /> property value.
|
|||
/// </summary>
|
|||
/// <param name="cell">
|
|||
/// The cell that will contain the generated element.
|
|||
/// </param>
|
|||
/// <param name="dataItem">
|
|||
/// The data item represented by the row that contains the intended cell.
|
|||
/// </param>
|
|||
/// <returns>
|
|||
/// A new, read-only <see cref="T:System.Windows.Controls.CheckBox" /> control that is bound to the column's <see cref="P:System.Windows.Controls.DataGridBoundColumn.Binding" /> property value.
|
|||
/// </returns>
|
|||
protected override Control GenerateElement(DataGridCell cell, object dataItem) |
|||
{ |
|||
bool isEnabled = false; |
|||
CheckBox checkBoxElement = new CheckBox(); |
|||
if (EnsureOwningGrid()) |
|||
{ |
|||
if (cell.RowIndex != -1 && cell.ColumnIndex != -1 && |
|||
cell.OwningRow != null && |
|||
cell.OwningRow.Slot == this.OwningGrid.CurrentSlot && |
|||
cell.ColumnIndex == this.OwningGrid.CurrentColumnIndex) |
|||
{ |
|||
isEnabled = true; |
|||
if (_currentCheckBox != null) |
|||
{ |
|||
_currentCheckBox.IsEnabled = false; |
|||
} |
|||
_currentCheckBox = checkBoxElement; |
|||
} |
|||
} |
|||
checkBoxElement.IsEnabled = isEnabled; |
|||
checkBoxElement.IsHitTestVisible = false; |
|||
ConfigureCheckBox(checkBoxElement); |
|||
if (Binding != null) |
|||
{ |
|||
checkBoxElement.Bind(BindingTarget, Binding); |
|||
} |
|||
return checkBoxElement; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Called when a cell in the column enters editing mode.
|
|||
/// </summary>
|
|||
/// <param name="editingElement">
|
|||
/// The element that the column displays for a cell in editing mode.
|
|||
/// </param>
|
|||
/// <param name="editingEventArgs">
|
|||
/// Information about the user gesture that is causing a cell to enter editing mode.
|
|||
/// </param>
|
|||
/// <returns>
|
|||
/// The unedited value.
|
|||
/// </returns>
|
|||
protected override object PrepareCellForEdit(Control editingElement, RoutedEventArgs editingEventArgs) |
|||
{ |
|||
if (editingElement is CheckBox editingCheckBox) |
|||
{ |
|||
bool? uneditedValue = editingCheckBox.IsChecked; |
|||
bool editValue = false; |
|||
if(editingEventArgs is PointerPressedEventArgs args) |
|||
{ |
|||
// Editing was triggered by a mouse click
|
|||
Point position = args.GetPosition(editingCheckBox); |
|||
Rect rect = new Rect(0, 0, editingCheckBox.Bounds.Width, editingCheckBox.Bounds.Height); |
|||
editValue = rect.Contains(position); |
|||
} |
|||
else if (_beganEditWithKeyboard) |
|||
{ |
|||
// Editing began by a user pressing spacebar
|
|||
editValue = true; |
|||
_beganEditWithKeyboard = false; |
|||
} |
|||
|
|||
if (editValue) |
|||
{ |
|||
// User clicked the checkbox itself or pressed space, let's toggle the IsChecked value
|
|||
if (editingCheckBox.IsThreeState) |
|||
{ |
|||
switch (editingCheckBox.IsChecked) |
|||
{ |
|||
case false: |
|||
editingCheckBox.IsChecked = true; |
|||
break; |
|||
case true: |
|||
editingCheckBox.IsChecked = null; |
|||
break; |
|||
case null: |
|||
editingCheckBox.IsChecked = false; |
|||
break; |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
editingCheckBox.IsChecked = !editingCheckBox.IsChecked; |
|||
} |
|||
} |
|||
return uneditedValue; |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Called by the DataGrid control when this column asks for its elements to be
|
|||
/// updated, because its CheckBoxContent or IsThreeState property changed.
|
|||
/// </summary>
|
|||
protected internal override void RefreshCellContent(Control element, string propertyName) |
|||
{ |
|||
if (element == null) |
|||
{ |
|||
throw new ArgumentNullException("element"); |
|||
} |
|||
if(element is CheckBox checkBox) |
|||
{ |
|||
checkBox.IsThreeState = IsThreeState; |
|||
} |
|||
else |
|||
{ |
|||
throw DataGridError.DataGrid.ValueIsNotAnInstanceOf("element", typeof(CheckBox)); |
|||
} |
|||
} |
|||
|
|||
#endregion Protected Methods
|
|||
|
|||
#region Private Methods
|
|||
|
|||
private void Columns_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) |
|||
{ |
|||
if (e.Action == NotifyCollectionChangedAction.Remove && e.OldItems.Contains(this) && _owningGrid != null) |
|||
{ |
|||
_owningGrid.Columns.CollectionChanged -= Columns_CollectionChanged; |
|||
_owningGrid.CurrentCellChanged -= OwningGrid_CurrentCellChanged; |
|||
_owningGrid.KeyDown -= OwningGrid_KeyDown; |
|||
_owningGrid.LoadingRow -= OwningGrid_LoadingRow; |
|||
_owningGrid = null; |
|||
} |
|||
} |
|||
|
|||
private void ConfigureCheckBox(CheckBox checkBox) |
|||
{ |
|||
checkBox.HorizontalAlignment = HorizontalAlignment.Center; |
|||
checkBox.VerticalAlignment = VerticalAlignment.Center; |
|||
checkBox.IsThreeState = IsThreeState; |
|||
} |
|||
|
|||
private bool EnsureOwningGrid() |
|||
{ |
|||
if (OwningGrid != null) |
|||
{ |
|||
if (OwningGrid != _owningGrid) |
|||
{ |
|||
_owningGrid = OwningGrid; |
|||
_owningGrid.Columns.CollectionChanged += Columns_CollectionChanged; |
|||
_owningGrid.CurrentCellChanged += OwningGrid_CurrentCellChanged; |
|||
_owningGrid.KeyDown += OwningGrid_KeyDown; |
|||
_owningGrid.LoadingRow += OwningGrid_LoadingRow; |
|||
} |
|||
return true; |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
private void OwningGrid_CurrentCellChanged(object sender, EventArgs e) |
|||
{ |
|||
if (_currentCheckBox != null) |
|||
{ |
|||
_currentCheckBox.IsEnabled = false; |
|||
} |
|||
if (OwningGrid != null && OwningGrid.CurrentColumn == this |
|||
&& OwningGrid.IsSlotVisible(OwningGrid.CurrentSlot)) |
|||
{ |
|||
if (OwningGrid.DisplayData.GetDisplayedElement(OwningGrid.CurrentSlot) is DataGridRow row) |
|||
{ |
|||
CheckBox checkBox = GetCellContent(row) as CheckBox; |
|||
if (checkBox != null) |
|||
{ |
|||
checkBox.IsEnabled = true; |
|||
} |
|||
_currentCheckBox = checkBox; |
|||
} |
|||
} |
|||
} |
|||
|
|||
private void OwningGrid_KeyDown(object sender, KeyEventArgs e) |
|||
{ |
|||
if (e.Key == Key.Space && OwningGrid != null && |
|||
OwningGrid.CurrentColumn == this) |
|||
{ |
|||
if (OwningGrid.DisplayData.GetDisplayedElement(OwningGrid.CurrentSlot) is DataGridRow row) |
|||
{ |
|||
CheckBox checkBox = GetCellContent(row) as CheckBox; |
|||
if (checkBox == _currentCheckBox) |
|||
{ |
|||
_beganEditWithKeyboard = true; |
|||
OwningGrid.BeginEdit(); |
|||
return; |
|||
} |
|||
} |
|||
} |
|||
_beganEditWithKeyboard = false; |
|||
} |
|||
|
|||
private void OwningGrid_LoadingRow(object sender, DataGridRowEventArgs e) |
|||
{ |
|||
if (OwningGrid != null) |
|||
{ |
|||
if (GetCellContent(e.Row) is CheckBox checkBox) |
|||
{ |
|||
if (OwningGrid.CurrentColumnIndex == Index && OwningGrid.CurrentSlot == e.Row.Slot) |
|||
{ |
|||
if (_currentCheckBox != null) |
|||
{ |
|||
_currentCheckBox.IsEnabled = false; |
|||
} |
|||
checkBox.IsEnabled = true; |
|||
_currentCheckBox = checkBox; |
|||
} |
|||
else |
|||
{ |
|||
checkBox.IsEnabled = false; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
#endregion Private Methods
|
|||
} |
|||
} |
|||
@ -0,0 +1,228 @@ |
|||
// (c) Copyright Microsoft Corporation.
|
|||
// This source is subject to the Microsoft Public License (Ms-PL).
|
|||
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
|
|||
// All other rights reserved.
|
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
|
|||
namespace Avalonia.Controls |
|||
{ |
|||
/// <summary>
|
|||
/// Defines modes that indicates how DataGrid content is copied to the Clipboard.
|
|||
/// </summary>
|
|||
public enum DataGridClipboardCopyMode |
|||
{ |
|||
/// <summary>
|
|||
/// Disable the DataGrid's ability to copy selected items as text.
|
|||
/// </summary>
|
|||
None, |
|||
|
|||
/// <summary>
|
|||
/// Enable the DataGrid's ability to copy selected items as text, but do not include
|
|||
/// the column header content as the first line in the text that gets copied to the Clipboard.
|
|||
/// </summary>
|
|||
ExcludeHeader, |
|||
|
|||
/// <summary>
|
|||
/// Enable the DataGrid's ability to copy selected items as text, and include
|
|||
/// the column header content as the first line in the text that gets copied to the Clipboard.
|
|||
/// </summary>
|
|||
IncludeHeader |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// This structure encapsulate the cell information necessary when clipboard content is prepared.
|
|||
/// </summary>
|
|||
public struct DataGridClipboardCellContent |
|||
{ |
|||
#region Data
|
|||
|
|||
private DataGridColumn _column; |
|||
private object _content; |
|||
private object _item; |
|||
|
|||
#endregion Data
|
|||
|
|||
#region Constructors
|
|||
|
|||
/// <summary>
|
|||
/// Creates a new DataGridClipboardCellValue structure containing information about a DataGrid cell.
|
|||
/// </summary>
|
|||
/// <param name="item">DataGrid row item containing the cell.</param>
|
|||
/// <param name="column">DataGridColumn containing the cell.</param>
|
|||
/// <param name="content">DataGrid cell value.</param>
|
|||
public DataGridClipboardCellContent(object item, DataGridColumn column, object content) |
|||
{ |
|||
this._item = item; |
|||
this._column = column; |
|||
this._content = content; |
|||
} |
|||
|
|||
#endregion Constructors
|
|||
|
|||
#region Public Properties
|
|||
|
|||
/// <summary>
|
|||
/// DataGridColumn containing the cell.
|
|||
/// </summary>
|
|||
public DataGridColumn Column |
|||
{ |
|||
get |
|||
{ |
|||
return _column; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Cell content.
|
|||
/// </summary>
|
|||
public object Content |
|||
{ |
|||
get |
|||
{ |
|||
return _content; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// DataGrid row item containing the cell.
|
|||
/// </summary>
|
|||
public object Item |
|||
{ |
|||
get |
|||
{ |
|||
return _item; |
|||
} |
|||
} |
|||
|
|||
#endregion Public Properties
|
|||
|
|||
#region Public Methods
|
|||
|
|||
/// <summary>
|
|||
/// Field-by-field comparison to avoid reflection-based ValueType.Equals.
|
|||
/// </summary>
|
|||
/// <param name="obj">DataGridClipboardCellContent to compare.</param>
|
|||
/// <returns>True iff this and data are equal</returns>
|
|||
public override bool Equals(object obj) |
|||
{ |
|||
if(obj is DataGridClipboardCellContent content) |
|||
{ |
|||
return (((_column == content._column) && (_content == content._content)) && (_item == content._item)); |
|||
} |
|||
else |
|||
{ |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns a deterministic hash code.
|
|||
/// </summary>
|
|||
/// <returns>Hash value.</returns>
|
|||
public override int GetHashCode() |
|||
{ |
|||
return ((_column.GetHashCode() ^ _content.GetHashCode()) ^ _item.GetHashCode()); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Field-by-field comparison to avoid reflection-based ValueType.Equals.
|
|||
/// </summary>
|
|||
/// <param name="clipboardCellContent1">The first DataGridClipboardCellContent.</param>
|
|||
/// <param name="clipboardCellContent2">The second DataGridClipboardCellContent.</param>
|
|||
/// <returns>True iff clipboardCellContent1 and clipboardCellContent2 are equal.</returns>
|
|||
public static bool operator ==(DataGridClipboardCellContent clipboardCellContent1, DataGridClipboardCellContent clipboardCellContent2) |
|||
{ |
|||
return (((clipboardCellContent1._column == clipboardCellContent2._column) && (clipboardCellContent1._content == clipboardCellContent2._content)) && (clipboardCellContent1._item == clipboardCellContent2._item)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Field-by-field comparison to avoid reflection-based ValueType.Equals.
|
|||
/// </summary>
|
|||
/// <param name="clipboardCellContent1">The first DataGridClipboardCellContent.</param>
|
|||
/// <param name="clipboardCellContent2">The second DataGridClipboardCellContent.</param>
|
|||
/// <returns>True iff clipboardCellContent1 and clipboardCellContent2 are NOT equal.</returns>
|
|||
public static bool operator !=(DataGridClipboardCellContent clipboardCellContent1, DataGridClipboardCellContent clipboardCellContent2) |
|||
{ |
|||
if ((clipboardCellContent1._column == clipboardCellContent2._column) && (clipboardCellContent1._content == clipboardCellContent2._content)) |
|||
{ |
|||
return (clipboardCellContent1._item != clipboardCellContent2._item); |
|||
} |
|||
return true; |
|||
} |
|||
|
|||
#endregion Public Methods
|
|||
} |
|||
|
|||
/// <summary>
|
|||
/// This class encapsulates a selected row's information necessary for the CopyingRowClipboardContent event.
|
|||
/// </summary>
|
|||
public class DataGridRowClipboardEventArgs : EventArgs |
|||
{ |
|||
#region Data
|
|||
|
|||
private List<DataGridClipboardCellContent> _clipboardRowContent; |
|||
private bool _isColumnHeadersRow; |
|||
private object _item; |
|||
|
|||
#endregion Data
|
|||
|
|||
#region Constructor
|
|||
|
|||
/// <summary>
|
|||
/// Creates a DataGridRowClipboardEventArgs object and initializes the properties.
|
|||
/// </summary>
|
|||
/// <param name="item">The row's associated data item.</param>
|
|||
/// <param name="isColumnHeadersRow">Whether or not this EventArgs is for the column headers.</param>
|
|||
internal DataGridRowClipboardEventArgs(object item, bool isColumnHeadersRow) |
|||
{ |
|||
_isColumnHeadersRow = isColumnHeadersRow; |
|||
_item = item; |
|||
} |
|||
|
|||
#endregion Constructor
|
|||
|
|||
#region Public Properties
|
|||
|
|||
/// <summary>
|
|||
/// This list should be used to modify, add ot remove a cell content before it gets stored into the clipboard.
|
|||
/// </summary>
|
|||
public List<DataGridClipboardCellContent> ClipboardRowContent |
|||
{ |
|||
get |
|||
{ |
|||
if (_clipboardRowContent == null) |
|||
{ |
|||
_clipboardRowContent = new List<DataGridClipboardCellContent>(); |
|||
} |
|||
return _clipboardRowContent; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// This property is true when the ClipboardRowContent represents column headers, in which case the Item is null.
|
|||
/// </summary>
|
|||
public bool IsColumnHeadersRow |
|||
{ |
|||
get |
|||
{ |
|||
return _isColumnHeadersRow; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// DataGrid row item used for proparing the ClipboardRowContent.
|
|||
/// </summary>
|
|||
public object Item |
|||
{ |
|||
get |
|||
{ |
|||
return _item; |
|||
} |
|||
} |
|||
|
|||
#endregion Public Properties
|
|||
} |
|||
|
|||
} |
|||
File diff suppressed because it is too large
@ -0,0 +1,602 @@ |
|||
// (c) Copyright Microsoft Corporation.
|
|||
// This source is subject to the Microsoft Public License (Ms-PL).
|
|||
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
|
|||
// All other rights reserved.
|
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Collections.ObjectModel; |
|||
using System.Diagnostics; |
|||
|
|||
namespace Avalonia.Controls |
|||
{ |
|||
internal class DataGridColumnCollection : ObservableCollection<DataGridColumn> |
|||
{ |
|||
private DataGrid _owningGrid; |
|||
|
|||
public DataGridColumnCollection(DataGrid owningGrid) |
|||
{ |
|||
_owningGrid = owningGrid; |
|||
ItemsInternal = new List<DataGridColumn>(); |
|||
FillerColumn = new DataGridFillerColumn(owningGrid); |
|||
RowGroupSpacerColumn = new DataGridFillerColumn(owningGrid); |
|||
DisplayIndexMap = new List<int>(); |
|||
} |
|||
|
|||
#region Properties
|
|||
|
|||
internal int AutogeneratedColumnCount |
|||
{ |
|||
get; |
|||
set; |
|||
} |
|||
|
|||
internal List<int> DisplayIndexMap |
|||
{ |
|||
get; |
|||
set; |
|||
} |
|||
|
|||
internal DataGridFillerColumn FillerColumn |
|||
{ |
|||
get; |
|||
private set; |
|||
} |
|||
|
|||
internal DataGridColumn FirstColumn |
|||
{ |
|||
get |
|||
{ |
|||
return GetFirstColumn(null /*isVisible*/, null /*isFrozen*/, null /*isReadOnly*/); |
|||
} |
|||
} |
|||
|
|||
internal DataGridColumn FirstVisibleColumn |
|||
{ |
|||
get |
|||
{ |
|||
return GetFirstColumn(true /*isVisible*/, null /*isFrozen*/, null /*isReadOnly*/); |
|||
} |
|||
} |
|||
|
|||
internal DataGridColumn FirstVisibleNonFillerColumn |
|||
{ |
|||
get |
|||
{ |
|||
DataGridColumn dataGridColumn = FirstVisibleColumn; |
|||
if (dataGridColumn == RowGroupSpacerColumn) |
|||
{ |
|||
dataGridColumn = GetNextVisibleColumn(dataGridColumn); |
|||
} |
|||
return dataGridColumn; |
|||
} |
|||
} |
|||
|
|||
internal DataGridColumn FirstVisibleWritableColumn |
|||
{ |
|||
get |
|||
{ |
|||
return GetFirstColumn(true /*isVisible*/, null /*isFrozen*/, false /*isReadOnly*/); |
|||
} |
|||
} |
|||
|
|||
internal DataGridColumn FirstVisibleScrollingColumn |
|||
{ |
|||
get |
|||
{ |
|||
return GetFirstColumn(true /*isVisible*/, false /*isFrozen*/, null /*isReadOnly*/); |
|||
} |
|||
} |
|||
|
|||
internal List<DataGridColumn> ItemsInternal |
|||
{ |
|||
get; |
|||
private set; |
|||
} |
|||
|
|||
internal DataGridColumn LastVisibleColumn |
|||
{ |
|||
get |
|||
{ |
|||
return GetLastColumn(true /*isVisible*/, null /*isFrozen*/, null /*isReadOnly*/); |
|||
} |
|||
} |
|||
|
|||
internal DataGridColumn LastVisibleScrollingColumn |
|||
{ |
|||
get |
|||
{ |
|||
return GetLastColumn(true /*isVisible*/, false /*isFrozen*/, null /*isReadOnly*/); |
|||
} |
|||
} |
|||
|
|||
internal DataGridColumn LastVisibleWritableColumn |
|||
{ |
|||
get |
|||
{ |
|||
return GetLastColumn(true /*isVisible*/, null /*isFrozen*/, false /*isReadOnly*/); |
|||
} |
|||
} |
|||
|
|||
internal DataGridFillerColumn RowGroupSpacerColumn |
|||
{ |
|||
get; |
|||
private set; |
|||
} |
|||
|
|||
internal int VisibleColumnCount |
|||
{ |
|||
get |
|||
{ |
|||
int visibleColumnCount = 0; |
|||
for (int columnIndex = 0; columnIndex < ItemsInternal.Count; columnIndex++) |
|||
{ |
|||
if (ItemsInternal[columnIndex].IsVisible) |
|||
{ |
|||
visibleColumnCount++; |
|||
} |
|||
} |
|||
return visibleColumnCount; |
|||
} |
|||
} |
|||
|
|||
internal double VisibleEdgedColumnsWidth |
|||
{ |
|||
get; |
|||
private set; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// The number of star columns that are currently visible.
|
|||
/// NOTE: Requires that EnsureVisibleEdgedColumnsWidth has been called.
|
|||
/// </summary>
|
|||
internal int VisibleStarColumnCount |
|||
{ |
|||
get; |
|||
private set; |
|||
} |
|||
|
|||
#endregion Properties
|
|||
|
|||
#region Protected Methods
|
|||
|
|||
protected override void ClearItems() |
|||
{ |
|||
try |
|||
{ |
|||
_owningGrid.NoCurrentCellChangeCount++; |
|||
if (ItemsInternal.Count > 0) |
|||
{ |
|||
if (_owningGrid.InDisplayIndexAdjustments) |
|||
{ |
|||
// We are within columns display indexes adjustments. We do not allow changing the column collection while adjusting display indexes.
|
|||
throw DataGridError.DataGrid.CannotChangeColumnCollectionWhileAdjustingDisplayIndexes(); |
|||
} |
|||
|
|||
_owningGrid.OnClearingColumns(); |
|||
for (int columnIndex = 0; columnIndex < ItemsInternal.Count; columnIndex++) |
|||
{ |
|||
// Detach the column...
|
|||
ItemsInternal[columnIndex].OwningGrid = null; |
|||
} |
|||
ItemsInternal.Clear(); |
|||
DisplayIndexMap.Clear(); |
|||
AutogeneratedColumnCount = 0; |
|||
_owningGrid.OnColumnCollectionChanged_PreNotification(false /*columnsGrew*/); |
|||
base.ClearItems(); |
|||
VisibleEdgedColumnsWidth = 0; |
|||
_owningGrid.OnColumnCollectionChanged_PostNotification(false /*columnsGrew*/); |
|||
} |
|||
} |
|||
finally |
|||
{ |
|||
_owningGrid.NoCurrentCellChangeCount--; |
|||
} |
|||
} |
|||
|
|||
protected override void InsertItem(int columnIndex, DataGridColumn dataGridColumn) |
|||
{ |
|||
try |
|||
{ |
|||
_owningGrid.NoCurrentCellChangeCount++; |
|||
if (_owningGrid.InDisplayIndexAdjustments) |
|||
{ |
|||
// We are within columns display indexes adjustments. We do not allow changing the column collection while adjusting display indexes.
|
|||
throw DataGridError.DataGrid.CannotChangeColumnCollectionWhileAdjustingDisplayIndexes(); |
|||
} |
|||
if (dataGridColumn == null) |
|||
{ |
|||
throw new ArgumentNullException("dataGridColumn"); |
|||
} |
|||
|
|||
int columnIndexWithFiller = columnIndex; |
|||
if (dataGridColumn != RowGroupSpacerColumn && RowGroupSpacerColumn.IsRepresented) |
|||
{ |
|||
columnIndexWithFiller++; |
|||
} |
|||
|
|||
// get the new current cell coordinates
|
|||
DataGridCellCoordinates newCurrentCellCoordinates = _owningGrid.OnInsertingColumn(columnIndex, dataGridColumn); |
|||
|
|||
// insert the column into our internal list
|
|||
ItemsInternal.Insert(columnIndexWithFiller, dataGridColumn); |
|||
dataGridColumn.Index = columnIndexWithFiller; |
|||
dataGridColumn.OwningGrid = _owningGrid; |
|||
dataGridColumn.RemoveEditingElement(); |
|||
if (dataGridColumn.IsVisible) |
|||
{ |
|||
VisibleEdgedColumnsWidth += dataGridColumn.ActualWidth; |
|||
} |
|||
|
|||
// continue with the base insert
|
|||
_owningGrid.OnInsertedColumn_PreNotification(dataGridColumn); |
|||
_owningGrid.OnColumnCollectionChanged_PreNotification(true /*columnsGrew*/); |
|||
|
|||
if (dataGridColumn != RowGroupSpacerColumn) |
|||
{ |
|||
base.InsertItem(columnIndex, dataGridColumn); |
|||
} |
|||
_owningGrid.OnInsertedColumn_PostNotification(newCurrentCellCoordinates, dataGridColumn.DisplayIndex); |
|||
_owningGrid.OnColumnCollectionChanged_PostNotification(true /*columnsGrew*/); |
|||
} |
|||
finally |
|||
{ |
|||
_owningGrid.NoCurrentCellChangeCount--; |
|||
} |
|||
} |
|||
|
|||
protected override void RemoveItem(int columnIndex) |
|||
{ |
|||
RemoveItemPrivate(columnIndex, false /*isSpacer*/); |
|||
} |
|||
|
|||
protected override void SetItem(int columnIndex, DataGridColumn dataGridColumn) |
|||
{ |
|||
throw new NotSupportedException(); |
|||
} |
|||
|
|||
#endregion Protected Methods
|
|||
|
|||
#region Internal Methods
|
|||
|
|||
internal bool DisplayInOrder(int columnIndex1, int columnIndex2) |
|||
{ |
|||
int displayIndex1 = ItemsInternal[columnIndex1].DisplayIndexWithFiller; |
|||
int displayIndex2 = ItemsInternal[columnIndex2].DisplayIndexWithFiller; |
|||
return displayIndex1 < displayIndex2; |
|||
} |
|||
|
|||
internal bool EnsureRowGrouping(bool rowGrouping) |
|||
{ |
|||
// The insert below could cause the first column to be added. That causes a refresh
|
|||
// which re-enters method so instead of checking RowGroupSpacerColumn.IsRepresented,
|
|||
// we need to check to see if it's actually in our collection instead.
|
|||
bool spacerRepresented = (ItemsInternal.Count > 0) && (ItemsInternal[0] == RowGroupSpacerColumn); |
|||
if (rowGrouping && !spacerRepresented) |
|||
{ |
|||
Insert(0, RowGroupSpacerColumn); |
|||
RowGroupSpacerColumn.IsRepresented = true; |
|||
return true; |
|||
} |
|||
else if (!rowGrouping && spacerRepresented) |
|||
{ |
|||
// We need to set IsRepresented to false before removing the RowGroupSpacerColumn
|
|||
// otherwise, we'll remove the column after it
|
|||
RowGroupSpacerColumn.IsRepresented = false; |
|||
RemoveItemPrivate(0, true /*isSpacer*/); |
|||
return true; |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// In addition to ensuring that column widths are valid, method updates the values of
|
|||
/// VisibleEdgedColumnsWidth and VisibleStarColumnCount.
|
|||
/// </summary>
|
|||
internal void EnsureVisibleEdgedColumnsWidth() |
|||
{ |
|||
VisibleStarColumnCount = 0; |
|||
VisibleEdgedColumnsWidth = 0; |
|||
for (int columnIndex = 0; columnIndex < ItemsInternal.Count; columnIndex++) |
|||
{ |
|||
if (ItemsInternal[columnIndex].IsVisible) |
|||
{ |
|||
ItemsInternal[columnIndex].EnsureWidth(); |
|||
if (ItemsInternal[columnIndex].Width.IsStar) |
|||
{ |
|||
VisibleStarColumnCount++; |
|||
} |
|||
VisibleEdgedColumnsWidth += ItemsInternal[columnIndex].ActualWidth; |
|||
} |
|||
} |
|||
} |
|||
|
|||
internal DataGridColumn GetColumnAtDisplayIndex(int displayIndex) |
|||
{ |
|||
if (displayIndex < 0 || displayIndex >= ItemsInternal.Count || displayIndex >= DisplayIndexMap.Count) |
|||
{ |
|||
return null; |
|||
} |
|||
int columnIndex = DisplayIndexMap[displayIndex]; |
|||
return ItemsInternal[columnIndex]; |
|||
} |
|||
|
|||
internal int GetColumnCount(bool isVisible, bool isFrozen, int fromColumnIndex, int toColumnIndex) |
|||
{ |
|||
int columnCount = 0; |
|||
DataGridColumn dataGridColumn = ItemsInternal[fromColumnIndex]; |
|||
|
|||
while (dataGridColumn != ItemsInternal[toColumnIndex]) |
|||
{ |
|||
dataGridColumn = GetNextColumn(dataGridColumn, isVisible, isFrozen, null /*isReadOnly*/); |
|||
columnCount++; |
|||
} |
|||
return columnCount; |
|||
} |
|||
|
|||
internal IEnumerable<DataGridColumn> GetDisplayedColumns() |
|||
{ |
|||
foreach (int columnIndex in DisplayIndexMap) |
|||
{ |
|||
yield return ItemsInternal[columnIndex]; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns an enumeration of all columns that meet the criteria of the filter predicate.
|
|||
/// </summary>
|
|||
/// <param name="filter">Criteria for inclusion.</param>
|
|||
/// <returns>Columns that meet the criteria, in ascending DisplayIndex order.</returns>
|
|||
internal IEnumerable<DataGridColumn> GetDisplayedColumns(Predicate<DataGridColumn> filter) |
|||
{ |
|||
Debug.Assert(filter != null); |
|||
Debug.Assert(ItemsInternal.Count == DisplayIndexMap.Count); |
|||
foreach (int columnIndex in DisplayIndexMap) |
|||
{ |
|||
DataGridColumn column = ItemsInternal[columnIndex]; |
|||
if (filter(column)) |
|||
{ |
|||
yield return column; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns an enumeration of all columns that meet the criteria of the filter predicate.
|
|||
/// The columns are returned in the order specified by the reverse flag.
|
|||
/// </summary>
|
|||
/// <param name="reverse">Whether or not to return the columns in descending DisplayIndex order.</param>
|
|||
/// <param name="filter">Criteria for inclusion.</param>
|
|||
/// <returns>Columns that meet the criteria, in the order specified by the reverse flag.</returns>
|
|||
internal IEnumerable<DataGridColumn> GetDisplayedColumns(bool reverse, Predicate<DataGridColumn> filter) |
|||
{ |
|||
return reverse ? GetDisplayedColumnsReverse(filter) : GetDisplayedColumns(filter); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns an enumeration of all columns that meet the criteria of the filter predicate.
|
|||
/// The columns are returned in descending DisplayIndex order.
|
|||
/// </summary>
|
|||
/// <param name="filter">Criteria for inclusion.</param>
|
|||
/// <returns>Columns that meet the criteria, in descending DisplayIndex order.</returns>
|
|||
internal IEnumerable<DataGridColumn> GetDisplayedColumnsReverse(Predicate<DataGridColumn> filter) |
|||
{ |
|||
for (int displayIndex = DisplayIndexMap.Count - 1; displayIndex >= 0; displayIndex--) |
|||
{ |
|||
DataGridColumn column = ItemsInternal[DisplayIndexMap[displayIndex]]; |
|||
if (filter(column)) |
|||
{ |
|||
yield return column; |
|||
} |
|||
} |
|||
} |
|||
|
|||
internal DataGridColumn GetFirstColumn(bool? isVisible, bool? isFrozen, bool? isReadOnly) |
|||
{ |
|||
Debug.Assert(ItemsInternal.Count == DisplayIndexMap.Count); |
|||
int index = 0; |
|||
while (index < DisplayIndexMap.Count) |
|||
{ |
|||
DataGridColumn dataGridColumn = GetColumnAtDisplayIndex(index); |
|||
if ((isVisible == null || (dataGridColumn.IsVisible) == isVisible) && |
|||
(isFrozen == null || dataGridColumn.IsFrozen == isFrozen) && |
|||
(isReadOnly == null || dataGridColumn.IsReadOnly == isReadOnly)) |
|||
{ |
|||
return dataGridColumn; |
|||
} |
|||
index++; |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
internal DataGridColumn GetLastColumn(bool? isVisible, bool? isFrozen, bool? isReadOnly) |
|||
{ |
|||
Debug.Assert(ItemsInternal.Count == DisplayIndexMap.Count); |
|||
int index = DisplayIndexMap.Count - 1; |
|||
while (index >= 0) |
|||
{ |
|||
DataGridColumn dataGridColumn = GetColumnAtDisplayIndex(index); |
|||
if ((isVisible == null || (dataGridColumn.IsVisible) == isVisible) && |
|||
(isFrozen == null || dataGridColumn.IsFrozen == isFrozen) && |
|||
(isReadOnly == null || dataGridColumn.IsReadOnly == isReadOnly)) |
|||
{ |
|||
return dataGridColumn; |
|||
} |
|||
index--; |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
internal DataGridColumn GetNextColumn(DataGridColumn dataGridColumnStart) |
|||
{ |
|||
return GetNextColumn(dataGridColumnStart, null /*isVisible*/, null /*isFrozen*/, null /*isReadOnly*/); |
|||
} |
|||
|
|||
internal DataGridColumn GetNextColumn(DataGridColumn dataGridColumnStart, |
|||
bool? isVisible, bool? isFrozen, bool? isReadOnly) |
|||
{ |
|||
Debug.Assert(dataGridColumnStart != null); |
|||
Debug.Assert(ItemsInternal.Contains(dataGridColumnStart)); |
|||
Debug.Assert(ItemsInternal.Count == DisplayIndexMap.Count); |
|||
|
|||
int index = dataGridColumnStart.DisplayIndexWithFiller + 1; |
|||
while (index < DisplayIndexMap.Count) |
|||
{ |
|||
DataGridColumn dataGridColumn = GetColumnAtDisplayIndex(index); |
|||
|
|||
if ((isVisible == null || (dataGridColumn.IsVisible) == isVisible) && |
|||
(isFrozen == null || dataGridColumn.IsFrozen == isFrozen) && |
|||
(isReadOnly == null || dataGridColumn.IsReadOnly == isReadOnly)) |
|||
{ |
|||
return dataGridColumn; |
|||
} |
|||
index++; |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
internal DataGridColumn GetNextVisibleColumn(DataGridColumn dataGridColumnStart) |
|||
{ |
|||
return GetNextColumn(dataGridColumnStart, true /*isVisible*/, null /*isFrozen*/, null /*isReadOnly*/); |
|||
} |
|||
|
|||
internal DataGridColumn GetNextVisibleFrozenColumn(DataGridColumn dataGridColumnStart) |
|||
{ |
|||
return GetNextColumn(dataGridColumnStart, true /*isVisible*/, true /*isFrozen*/, null /*isReadOnly*/); |
|||
} |
|||
|
|||
internal DataGridColumn GetNextVisibleWritableColumn(DataGridColumn dataGridColumnStart) |
|||
{ |
|||
return GetNextColumn(dataGridColumnStart, true /*isVisible*/, null /*isFrozen*/, false /*isReadOnly*/); |
|||
} |
|||
|
|||
internal DataGridColumn GetPreviousColumn(DataGridColumn dataGridColumnStart, |
|||
bool? isVisible, bool? isFrozen, bool? isReadOnly) |
|||
{ |
|||
int index = dataGridColumnStart.DisplayIndexWithFiller - 1; |
|||
while (index >= 0) |
|||
{ |
|||
DataGridColumn dataGridColumn = GetColumnAtDisplayIndex(index); |
|||
if ((isVisible == null || (dataGridColumn.IsVisible) == isVisible) && |
|||
(isFrozen == null || dataGridColumn.IsFrozen == isFrozen) && |
|||
(isReadOnly == null || dataGridColumn.IsReadOnly == isReadOnly)) |
|||
{ |
|||
return dataGridColumn; |
|||
} |
|||
index--; |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
internal DataGridColumn GetPreviousVisibleNonFillerColumn(DataGridColumn dataGridColumnStart) |
|||
{ |
|||
DataGridColumn column = GetPreviousColumn(dataGridColumnStart, true /*isVisible*/, null /*isFrozen*/, null /*isReadOnly*/); |
|||
return (column is DataGridFillerColumn) ? null : column; |
|||
} |
|||
|
|||
internal DataGridColumn GetPreviousVisibleScrollingColumn(DataGridColumn dataGridColumnStart) |
|||
{ |
|||
return GetPreviousColumn(dataGridColumnStart, true /*isVisible*/, false /*isFrozen*/, null /*isReadOnly*/); |
|||
} |
|||
|
|||
internal DataGridColumn GetPreviousVisibleWritableColumn(DataGridColumn dataGridColumnStart) |
|||
{ |
|||
return GetPreviousColumn(dataGridColumnStart, true /*isVisible*/, null /*isFrozen*/, false /*isReadOnly*/); |
|||
} |
|||
|
|||
internal int GetVisibleColumnCount(int fromColumnIndex, int toColumnIndex) |
|||
{ |
|||
int columnCount = 0; |
|||
DataGridColumn dataGridColumn = ItemsInternal[fromColumnIndex]; |
|||
|
|||
while (dataGridColumn != ItemsInternal[toColumnIndex]) |
|||
{ |
|||
dataGridColumn = GetNextVisibleColumn(dataGridColumn); |
|||
columnCount++; |
|||
} |
|||
return columnCount; |
|||
} |
|||
|
|||
internal IEnumerable<DataGridColumn> GetVisibleColumns() |
|||
{ |
|||
Predicate<DataGridColumn> filter = column => column.IsVisible; |
|||
return GetDisplayedColumns(filter); |
|||
} |
|||
|
|||
internal IEnumerable<DataGridColumn> GetVisibleFrozenColumns() |
|||
{ |
|||
Predicate<DataGridColumn> filter = column => column.IsVisible && column.IsFrozen; |
|||
return GetDisplayedColumns(filter); |
|||
} |
|||
|
|||
internal double GetVisibleFrozenEdgedColumnsWidth() |
|||
{ |
|||
double visibleFrozenColumnsWidth = 0; |
|||
for (int columnIndex = 0; columnIndex < ItemsInternal.Count; columnIndex++) |
|||
{ |
|||
if (ItemsInternal[columnIndex].IsVisible && ItemsInternal[columnIndex].IsFrozen) |
|||
{ |
|||
visibleFrozenColumnsWidth += ItemsInternal[columnIndex].ActualWidth; |
|||
} |
|||
} |
|||
return visibleFrozenColumnsWidth; |
|||
} |
|||
|
|||
internal IEnumerable<DataGridColumn> GetVisibleScrollingColumns() |
|||
{ |
|||
Predicate<DataGridColumn> filter = column => column.IsVisible && !column.IsFrozen; |
|||
return GetDisplayedColumns(filter); |
|||
} |
|||
|
|||
#endregion Internal Methods
|
|||
|
|||
#region Private Methods
|
|||
|
|||
private void RemoveItemPrivate(int columnIndex, bool isSpacer) |
|||
{ |
|||
try |
|||
{ |
|||
_owningGrid.NoCurrentCellChangeCount++; |
|||
|
|||
if (_owningGrid.InDisplayIndexAdjustments) |
|||
{ |
|||
// We are within columns display indexes adjustments. We do not allow changing the column collection while adjusting display indexes.
|
|||
throw DataGridError.DataGrid.CannotChangeColumnCollectionWhileAdjustingDisplayIndexes(); |
|||
} |
|||
|
|||
int columnIndexWithFiller = columnIndex; |
|||
if (!isSpacer && RowGroupSpacerColumn.IsRepresented) |
|||
{ |
|||
columnIndexWithFiller++; |
|||
} |
|||
|
|||
DataGridColumn dataGridColumn = ItemsInternal[columnIndexWithFiller]; |
|||
DataGridCellCoordinates newCurrentCellCoordinates = _owningGrid.OnRemovingColumn(dataGridColumn); |
|||
ItemsInternal.RemoveAt(columnIndexWithFiller); |
|||
if (dataGridColumn.IsVisible) |
|||
{ |
|||
VisibleEdgedColumnsWidth -= dataGridColumn.ActualWidth; |
|||
} |
|||
dataGridColumn.OwningGrid = null; |
|||
dataGridColumn.RemoveEditingElement(); |
|||
|
|||
// continue with the base remove
|
|||
_owningGrid.OnRemovedColumn_PreNotification(dataGridColumn); |
|||
_owningGrid.OnColumnCollectionChanged_PreNotification(false /*columnsGrew*/); |
|||
if (!isSpacer) |
|||
{ |
|||
base.RemoveItem(columnIndex); |
|||
} |
|||
_owningGrid.OnRemovedColumn_PostNotification(newCurrentCellCoordinates); |
|||
_owningGrid.OnColumnCollectionChanged_PostNotification(false /*columnsGrew*/); |
|||
} |
|||
finally |
|||
{ |
|||
_owningGrid.NoCurrentCellChangeCount--; |
|||
} |
|||
} |
|||
|
|||
#endregion Private Methods
|
|||
|
|||
} |
|||
} |
|||
File diff suppressed because it is too large
File diff suppressed because it is too large
@ -0,0 +1,726 @@ |
|||
// (c) Copyright Microsoft Corporation.
|
|||
// This source is subject to the Microsoft Public License (Ms-PL).
|
|||
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
|
|||
// All other rights reserved.
|
|||
|
|||
using Avalonia.Collections; |
|||
using System; |
|||
using System.Linq; |
|||
using System.Collections; |
|||
using System.Collections.Generic; |
|||
using System.Collections.Specialized; |
|||
using System.ComponentModel; |
|||
using System.Diagnostics; |
|||
using System.Reflection; |
|||
using System.ComponentModel.DataAnnotations; |
|||
using Avalonia.Utilities; |
|||
|
|||
namespace Avalonia.Controls |
|||
{ |
|||
internal class DataGridDataConnection |
|||
{ |
|||
#region Data
|
|||
|
|||
private int _backupSlotForCurrentChanged; |
|||
private int _columnForCurrentChanged; |
|||
private PropertyInfo[] _dataProperties; |
|||
private IEnumerable _dataSource; |
|||
private Type _dataType; |
|||
private bool _expectingCurrentChanged; |
|||
private object _itemToSelectOnCurrentChanged; |
|||
private DataGrid _owner; |
|||
private bool _scrollForCurrentChanged; |
|||
private DataGridSelectionAction _selectionActionForCurrentChanged; |
|||
|
|||
/* |
|||
//TODO Check
|
|||
private WeakEventListener<DataGridDataConnection, object, NotifyCollectionChangedEventArgs> _weakCollectionChangedListener; |
|||
private WeakEventListener<DataGridDataConnection, object, NotifyCollectionChangedEventArgs> _weakSortDescriptionsCollectionChangedListener; |
|||
private WeakEventListener<DataGridDataConnection, object, CurrentChangingEventArgs> _weakCurrentChangingListener; |
|||
private WeakEventListener<DataGridDataConnection, object, EventArgs> _weakCurrentChangedListener; |
|||
*/ |
|||
|
|||
#endregion Data
|
|||
|
|||
public DataGridDataConnection(DataGrid owner) |
|||
{ |
|||
_owner = owner; |
|||
} |
|||
|
|||
#region Properties
|
|||
|
|||
public bool AllowEdit |
|||
{ |
|||
get |
|||
{ |
|||
if (List == null) |
|||
{ |
|||
return true; |
|||
} |
|||
else |
|||
{ |
|||
return !List.IsReadOnly; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// True if the collection view says it can sort.
|
|||
/// </summary>
|
|||
public bool AllowSort |
|||
{ |
|||
get |
|||
{ |
|||
if (CollectionView == null || |
|||
(EditableCollectionView != null && (EditableCollectionView.IsAddingNew || EditableCollectionView.IsEditingItem))) |
|||
{ |
|||
return false; |
|||
} |
|||
else |
|||
{ |
|||
return CollectionView.CanSort; |
|||
} |
|||
} |
|||
} |
|||
|
|||
public bool CommittingEdit |
|||
{ |
|||
get; |
|||
private set; |
|||
} |
|||
|
|||
public int Count |
|||
{ |
|||
get |
|||
{ |
|||
IList list = List; |
|||
if (list != null) |
|||
{ |
|||
return list.Count; |
|||
} |
|||
|
|||
if(DataSource is CollectionViewBase cv) |
|||
{ |
|||
return cv.Count; |
|||
} |
|||
|
|||
return DataSource?.Cast<object>().Count() ?? 0; |
|||
} |
|||
} |
|||
|
|||
public bool DataIsPrimitive |
|||
{ |
|||
get |
|||
{ |
|||
return DataTypeIsPrimitive(DataType); |
|||
} |
|||
} |
|||
|
|||
public PropertyInfo[] DataProperties |
|||
{ |
|||
get |
|||
{ |
|||
if (_dataProperties == null) |
|||
{ |
|||
UpdateDataProperties(); |
|||
} |
|||
return _dataProperties; |
|||
} |
|||
} |
|||
|
|||
public IEnumerable DataSource |
|||
{ |
|||
get |
|||
{ |
|||
return _dataSource; |
|||
} |
|||
set |
|||
{ |
|||
_dataSource = value; |
|||
// Because the DataSource is changing, we need to reset our cached values for DataType and DataProperties,
|
|||
// which are dependent on the current DataSource
|
|||
_dataType = null; |
|||
UpdateDataProperties(); |
|||
} |
|||
} |
|||
|
|||
public Type DataType |
|||
{ |
|||
get |
|||
{ |
|||
// We need to use the raw ItemsSource as opposed to DataSource because DataSource
|
|||
// may be the ItemsSource wrapped in a collection view, in which case we wouldn't
|
|||
// be able to take T to be the type if we're given IEnumerable<T>
|
|||
if (_dataType == null && _owner.Items != null) |
|||
{ |
|||
_dataType = _owner.Items.GetItemType(); |
|||
} |
|||
return _dataType; |
|||
} |
|||
} |
|||
|
|||
public bool EventsWired |
|||
{ |
|||
get; |
|||
private set; |
|||
} |
|||
|
|||
private bool IsGrouping |
|||
{ |
|||
get |
|||
{ |
|||
return (CollectionView != null) |
|||
&& CollectionView.CanGroup |
|||
&& CollectionView.IsGrouping |
|||
&& (CollectionView.GroupingDepth > 0); |
|||
} |
|||
} |
|||
|
|||
public IList List |
|||
{ |
|||
get |
|||
{ |
|||
return DataSource as IList; |
|||
} |
|||
} |
|||
|
|||
public bool ShouldAutoGenerateColumns |
|||
{ |
|||
get |
|||
{ |
|||
return false; |
|||
//return _owner.AutoGenerateColumns
|
|||
// && (_owner.ColumnsInternal.AutogeneratedColumnCount == 0)
|
|||
// && ((DataProperties != null && DataProperties.Length > 0) || DataIsPrimitive);
|
|||
} |
|||
} |
|||
|
|||
public ICollectionView CollectionView |
|||
{ |
|||
get |
|||
{ |
|||
return DataSource as ICollectionView; |
|||
} |
|||
} |
|||
public IEditableCollectionView EditableCollectionView |
|||
{ |
|||
get |
|||
{ |
|||
return DataSource as IEditableCollectionView; |
|||
} |
|||
} |
|||
|
|||
public AvaloniaSortDescriptionCollection SortDescriptions |
|||
{ |
|||
get |
|||
{ |
|||
if (CollectionView != null && CollectionView.CanSort) |
|||
{ |
|||
return CollectionView.SortDescriptions; |
|||
} |
|||
else |
|||
{ |
|||
return null; |
|||
} |
|||
} |
|||
} |
|||
|
|||
#endregion Properties
|
|||
|
|||
#region Public Methods
|
|||
|
|||
/// <summary>
|
|||
/// Puts the entity into editing mode if possible
|
|||
/// </summary>
|
|||
/// <param name="dataItem">The entity to edit</param>
|
|||
/// <returns>True if editing was started</returns>
|
|||
public bool BeginEdit(object dataItem) |
|||
{ |
|||
if (dataItem == null) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
IEditableCollectionView editableCollectionView = EditableCollectionView; |
|||
if (editableCollectionView != null) |
|||
{ |
|||
if (editableCollectionView.IsEditingItem && (dataItem == editableCollectionView.CurrentEditItem)) |
|||
{ |
|||
return true; |
|||
} |
|||
else |
|||
{ |
|||
editableCollectionView.EditItem(dataItem); |
|||
return editableCollectionView.IsEditingItem; |
|||
} |
|||
} |
|||
|
|||
if (dataItem is IEditableObject editableDataItem) |
|||
{ |
|||
editableDataItem.BeginEdit(); |
|||
return true; |
|||
} |
|||
|
|||
return true; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Cancels the current entity editing and exits the editing mode.
|
|||
/// </summary>
|
|||
/// <param name="dataItem">The entity being edited</param>
|
|||
/// <returns>True if a cancellation operation was invoked.</returns>
|
|||
public bool CancelEdit(object dataItem) |
|||
{ |
|||
IEditableCollectionView editableCollectionView = EditableCollectionView; |
|||
if (editableCollectionView != null) |
|||
{ |
|||
if (editableCollectionView.CanCancelEdit) |
|||
{ |
|||
editableCollectionView.CancelEdit(); |
|||
return true; |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
if (dataItem is IEditableObject editableDataItem) |
|||
{ |
|||
editableDataItem.CancelEdit(); |
|||
return true; |
|||
} |
|||
|
|||
return true; |
|||
} |
|||
|
|||
public static bool CanEdit(Type type) |
|||
{ |
|||
Debug.Assert(type != null); |
|||
|
|||
type = type.GetNonNullableType(); |
|||
|
|||
return |
|||
type.IsEnum |
|||
|| type == typeof(System.String) |
|||
|| type == typeof(System.Char) |
|||
|| type == typeof(System.DateTime) |
|||
|| type == typeof(System.Boolean) |
|||
|| type == typeof(System.Byte) |
|||
|| type == typeof(System.SByte) |
|||
|| type == typeof(System.Single) |
|||
|| type == typeof(System.Double) |
|||
|| type == typeof(System.Decimal) |
|||
|| type == typeof(System.Int16) |
|||
|| type == typeof(System.Int32) |
|||
|| type == typeof(System.Int64) |
|||
|| type == typeof(System.UInt16) |
|||
|| type == typeof(System.UInt32) |
|||
|| type == typeof(System.UInt64); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Commits the current entity editing and exits the editing mode.
|
|||
/// </summary>
|
|||
/// <param name="dataItem">The entity being edited</param>
|
|||
/// <returns>True if a commit operation was invoked.</returns>
|
|||
public bool EndEdit(object dataItem) |
|||
{ |
|||
IEditableCollectionView editableCollectionView = EditableCollectionView; |
|||
if (editableCollectionView != null) |
|||
{ |
|||
// IEditableCollectionView.CommitEdit can potentially change currency. If it does,
|
|||
// we don't want to attempt a second commit inside our CurrentChanging event handler.
|
|||
_owner.NoCurrentCellChangeCount++; |
|||
CommittingEdit = true; |
|||
try |
|||
{ |
|||
editableCollectionView.CommitEdit(); |
|||
} |
|||
finally |
|||
{ |
|||
_owner.NoCurrentCellChangeCount--; |
|||
CommittingEdit = false; |
|||
} |
|||
return true; |
|||
} |
|||
|
|||
if (dataItem is IEditableObject editableDataItem) |
|||
{ |
|||
editableDataItem.EndEdit(); |
|||
} |
|||
|
|||
return true; |
|||
} |
|||
|
|||
// Assumes index >= 0, returns null if index >= Count
|
|||
public object GetDataItem(int index) |
|||
{ |
|||
Debug.Assert(index >= 0); |
|||
|
|||
IList list = List; |
|||
if (list != null) |
|||
{ |
|||
return (index < list.Count) ? list[index] : null; |
|||
} |
|||
|
|||
if (DataSource is CollectionViewBase collectionView) |
|||
{ |
|||
return (index < collectionView.Count) ? collectionView.GetItemAt(index) : null; |
|||
} |
|||
|
|||
IEnumerable enumerable = DataSource; |
|||
if (enumerable != null) |
|||
{ |
|||
IEnumerator enumerator = enumerable.GetEnumerator(); |
|||
int i = -1; |
|||
while (enumerator.MoveNext() && i < index) |
|||
{ |
|||
i++; |
|||
if (i == index) |
|||
{ |
|||
return enumerator.Current; |
|||
} |
|||
} |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
public bool GetPropertyIsReadOnly(string propertyName) |
|||
{ |
|||
if (DataType != null) |
|||
{ |
|||
if (!String.IsNullOrEmpty(propertyName)) |
|||
{ |
|||
Type propertyType = DataType; |
|||
PropertyInfo propertyInfo = null; |
|||
List<string> propertyNames = TypeHelper.SplitPropertyPath(propertyName); |
|||
for (int i = 0; i < propertyNames.Count; i++) |
|||
{ |
|||
propertyInfo = propertyType.GetPropertyOrIndexer(propertyNames[i], out object[] index); |
|||
if (propertyInfo == null || propertyType.GetIsReadOnly() || propertyInfo.GetIsReadOnly()) |
|||
{ |
|||
// Either the data type is read-only, the property doesn't exist, or it does exist but is read-only
|
|||
return true; |
|||
} |
|||
|
|||
// Check if EditableAttribute is defined on the property and if it indicates uneditable
|
|||
object[] attributes = propertyInfo.GetCustomAttributes(typeof(EditableAttribute), true); |
|||
if (attributes != null && attributes.Length > 0) |
|||
{ |
|||
EditableAttribute editableAttribute = attributes[0] as EditableAttribute; |
|||
Debug.Assert(editableAttribute != null); |
|||
if (!editableAttribute.AllowEdit) |
|||
{ |
|||
return true; |
|||
} |
|||
} |
|||
propertyType = propertyInfo.PropertyType.GetNonNullableType(); |
|||
} |
|||
return propertyInfo == null || !propertyInfo.CanWrite || !AllowEdit || !CanEdit(propertyType); |
|||
} |
|||
else if (DataType.GetIsReadOnly()) |
|||
{ |
|||
return true; |
|||
} |
|||
} |
|||
return !AllowEdit; |
|||
} |
|||
|
|||
public int IndexOf(object dataItem) |
|||
{ |
|||
IList list = List; |
|||
if (list != null) |
|||
{ |
|||
return list.IndexOf(dataItem); |
|||
} |
|||
|
|||
if (DataSource is CollectionViewBase cv) |
|||
{ |
|||
return cv.IndexOf(dataItem); |
|||
} |
|||
|
|||
IEnumerable enumerable = DataSource; |
|||
if (enumerable != null && dataItem != null) |
|||
{ |
|||
int index = 0; |
|||
foreach (object dataItemTmp in enumerable) |
|||
{ |
|||
if ((dataItem == null && dataItemTmp == null) || |
|||
dataItem.Equals(dataItemTmp)) |
|||
{ |
|||
return index; |
|||
} |
|||
index++; |
|||
} |
|||
} |
|||
return -1; |
|||
} |
|||
|
|||
#endregion Public methods
|
|||
|
|||
#region Internal Methods
|
|||
|
|||
internal void ClearDataProperties() |
|||
{ |
|||
_dataProperties = null; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Creates a collection view around the DataGrid's source. ICollectionViewFactory is
|
|||
/// used if the source implements it. Otherwise a PagedCollectionView is returned.
|
|||
/// </summary>
|
|||
/// <param name="source">Enumerable source for which to create a view</param>
|
|||
/// <returns>ICollectionView view over the provided source</returns>
|
|||
internal static ICollectionView CreateView(IEnumerable source) |
|||
{ |
|||
Debug.Assert(source != null, "source unexpectedly null"); |
|||
Debug.Assert(!(source is ICollectionView), "source is an ICollectionView"); |
|||
|
|||
ICollectionView collectionView = null; |
|||
|
|||
if (source is ICollectionViewFactory collectionViewFactory) |
|||
{ |
|||
// If the source is a collection view factory, give it a chance to produce a custom collection view.
|
|||
collectionView = collectionViewFactory.CreateView(); |
|||
// Intentionally not catching potential exception thrown by ICollectionViewFactory.CreateView().
|
|||
} |
|||
if (collectionView == null) |
|||
{ |
|||
// If we still do not have a collection view, default to a PagedCollectionView.
|
|||
collectionView = new CollectionViewBase(source); |
|||
} |
|||
return collectionView; |
|||
} |
|||
|
|||
internal static bool DataTypeIsPrimitive(Type dataType) |
|||
{ |
|||
if (dataType != null) |
|||
{ |
|||
Type type = TypeHelper.GetNonNullableType(dataType); // no-opt if dataType isn't nullable
|
|||
return type.IsPrimitive || type == typeof(string) || type == typeof(DateTime) || type == typeof(Decimal); |
|||
} |
|||
else |
|||
{ |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
internal void MoveCurrentTo(object item, int backupSlot, int columnIndex, DataGridSelectionAction action, bool scrollIntoView) |
|||
{ |
|||
if (CollectionView != null) |
|||
{ |
|||
_expectingCurrentChanged = true; |
|||
_columnForCurrentChanged = columnIndex; |
|||
_itemToSelectOnCurrentChanged = item; |
|||
_selectionActionForCurrentChanged = action; |
|||
_scrollForCurrentChanged = scrollIntoView; |
|||
_backupSlotForCurrentChanged = backupSlot; |
|||
|
|||
CollectionView.MoveCurrentTo(item is CollectionViewGroup ? null : item); |
|||
|
|||
_expectingCurrentChanged = false; |
|||
} |
|||
} |
|||
|
|||
internal void UnWireEvents(IEnumerable value) |
|||
{ |
|||
if (value is INotifyCollectionChanged notifyingDataSource) |
|||
{ |
|||
notifyingDataSource.CollectionChanged -= NotifyingDataSource_CollectionChanged; |
|||
} |
|||
|
|||
if (SortDescriptions != null) |
|||
{ |
|||
SortDescriptions.CollectionChanged -= CollectionView_SortDescriptions_CollectionChanged; |
|||
} |
|||
|
|||
if (CollectionView != null) |
|||
{ |
|||
CollectionView.CurrentChanged -= CollectionView_CurrentChanged; |
|||
CollectionView.CurrentChanging -= CollectionView_CurrentChanging; |
|||
} |
|||
|
|||
EventsWired = false; |
|||
} |
|||
|
|||
internal void WireEvents(IEnumerable value) |
|||
{ |
|||
if (value is INotifyCollectionChanged notifyingDataSource) |
|||
{ |
|||
notifyingDataSource.CollectionChanged += NotifyingDataSource_CollectionChanged; |
|||
} |
|||
|
|||
if (SortDescriptions != null) |
|||
{ |
|||
SortDescriptions.CollectionChanged += CollectionView_SortDescriptions_CollectionChanged; |
|||
} |
|||
|
|||
if (CollectionView != null) |
|||
{ |
|||
//Avalonia.Utilities.WeakSubscriptionManager.Subscribe(CollectionView, "CurrentChanged", CollectionView_CurrentChanged);
|
|||
CollectionView.CurrentChanged += CollectionView_CurrentChanged; |
|||
CollectionView.CurrentChanging += CollectionView_CurrentChanging; |
|||
} |
|||
|
|||
EventsWired = true; |
|||
} |
|||
|
|||
#endregion Internal Methods
|
|||
|
|||
#region Private methods
|
|||
|
|||
private void CollectionView_CurrentChanged(object sender, EventArgs e) |
|||
{ |
|||
if (_expectingCurrentChanged) |
|||
{ |
|||
// Committing Edit could cause our item to move to a group that no longer exists. In
|
|||
// this case, we need to update the item.
|
|||
if (_itemToSelectOnCurrentChanged is CollectionViewGroup collectionViewGroup) |
|||
{ |
|||
DataGridRowGroupInfo groupInfo = _owner.RowGroupInfoFromCollectionViewGroup(collectionViewGroup); |
|||
if (groupInfo == null) |
|||
{ |
|||
// Move to the next slot if the target slot isn't visible
|
|||
if (!_owner.IsSlotVisible(_backupSlotForCurrentChanged)) |
|||
{ |
|||
_backupSlotForCurrentChanged = _owner.GetNextVisibleSlot(_backupSlotForCurrentChanged); |
|||
} |
|||
// Move to the next best slot if we've moved past all the slots. This could happen if multiple
|
|||
// groups were removed.
|
|||
if (_backupSlotForCurrentChanged >= _owner.SlotCount) |
|||
{ |
|||
_backupSlotForCurrentChanged = _owner.GetPreviousVisibleSlot(_owner.SlotCount); |
|||
} |
|||
// Update the itemToSelect
|
|||
int newCurrentPosition = -1; |
|||
_itemToSelectOnCurrentChanged = _owner.ItemFromSlot(_backupSlotForCurrentChanged, ref newCurrentPosition); |
|||
} |
|||
} |
|||
|
|||
_owner.ProcessSelectionAndCurrency( |
|||
_columnForCurrentChanged, |
|||
_itemToSelectOnCurrentChanged, |
|||
_backupSlotForCurrentChanged, |
|||
_selectionActionForCurrentChanged, |
|||
_scrollForCurrentChanged); |
|||
} |
|||
else if (CollectionView != null) |
|||
{ |
|||
_owner.UpdateStateOnCurrentChanged(CollectionView.CurrentItem, CollectionView.CurrentPosition); |
|||
} |
|||
} |
|||
|
|||
private void CollectionView_CurrentChanging(object sender, CurrentChangingEventArgs e) |
|||
{ |
|||
if (_owner.NoCurrentCellChangeCount == 0 && |
|||
!_expectingCurrentChanged && |
|||
!CommittingEdit && |
|||
!_owner.CommitEdit()) |
|||
{ |
|||
// If CommitEdit failed, then the user has most likely input invalid data.
|
|||
// We should cancel the current change if we can, otherwise we have to abort the edit.
|
|||
if (e.IsCancelable) |
|||
{ |
|||
e.Cancel = true; |
|||
} |
|||
else |
|||
{ |
|||
_owner.CancelEdit(DataGridEditingUnit.Row, false); |
|||
} |
|||
} |
|||
} |
|||
|
|||
private void CollectionView_SortDescriptions_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) |
|||
{ |
|||
if (_owner.ColumnsItemsInternal.Count == 0) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
// refresh sort description
|
|||
foreach (DataGridColumn column in _owner.ColumnsItemsInternal) |
|||
{ |
|||
column.HeaderCell.ApplyState(); |
|||
} |
|||
} |
|||
|
|||
private void NotifyingDataSource_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) |
|||
{ |
|||
if (_owner.LoadingOrUnloadingRow) |
|||
{ |
|||
throw DataGridError.DataGrid.CannotChangeItemsWhenLoadingRows(); |
|||
} |
|||
switch (e.Action) |
|||
{ |
|||
case NotifyCollectionChangedAction.Add: |
|||
Debug.Assert(e.NewItems != null, "Unexpected NotifyCollectionChangedAction.Add notification"); |
|||
if (ShouldAutoGenerateColumns) |
|||
{ |
|||
// The columns are also affected (not just rows) in this case so we need to reset everything
|
|||
_owner.InitializeElements(false /*recycleRows*/); |
|||
} |
|||
else if (!IsGrouping) |
|||
{ |
|||
// If we're grouping then we handle this through the CollectionViewGroup notifications
|
|||
// According to WPF, Add is a single item operation
|
|||
Debug.Assert(e.NewItems.Count == 1); |
|||
_owner.InsertRowAt(e.NewStartingIndex); |
|||
} |
|||
break; |
|||
case NotifyCollectionChangedAction.Remove: |
|||
IList removedItems = e.OldItems; |
|||
if (removedItems == null || e.OldStartingIndex < 0) |
|||
{ |
|||
Debug.Assert(false, "Unexpected NotifyCollectionChangedAction.Remove notification"); |
|||
return; |
|||
} |
|||
if (!IsGrouping) |
|||
{ |
|||
// If we're grouping then we handle this through the CollectionViewGroup notifications
|
|||
// According to WPF, Remove is a single item operation
|
|||
foreach (object item in e.OldItems) |
|||
{ |
|||
Debug.Assert(item != null); |
|||
_owner.RemoveRowAt(e.OldStartingIndex, item); |
|||
} |
|||
} |
|||
break; |
|||
case NotifyCollectionChangedAction.Replace: |
|||
throw new NotSupportedException(); //
|
|||
|
|||
case NotifyCollectionChangedAction.Reset: |
|||
// Did the data type change during the reset? If not, we can recycle
|
|||
// the existing rows instead of having to clear them all. We still need to clear our cached
|
|||
// values for DataType and DataProperties, though, because the collection has been reset.
|
|||
Type previousDataType = _dataType; |
|||
_dataType = null; |
|||
if (previousDataType != DataType) |
|||
{ |
|||
ClearDataProperties(); |
|||
_owner.InitializeElements(false /*recycleRows*/); |
|||
} |
|||
else |
|||
{ |
|||
_owner.InitializeElements(!ShouldAutoGenerateColumns /*recycleRows*/); |
|||
} |
|||
break; |
|||
} |
|||
} |
|||
|
|||
|
|||
private void UpdateDataProperties() |
|||
{ |
|||
Type dataType = DataType; |
|||
|
|||
if (DataSource != null && dataType != null && !DataTypeIsPrimitive(dataType)) |
|||
{ |
|||
_dataProperties = dataType.GetProperties(BindingFlags.Public | BindingFlags.Instance); |
|||
Debug.Assert(_dataProperties != null); |
|||
} |
|||
else |
|||
{ |
|||
_dataProperties = null; |
|||
} |
|||
} |
|||
|
|||
#endregion Private Methods
|
|||
} |
|||
} |
|||
@ -0,0 +1,365 @@ |
|||
// (c) Copyright Microsoft Corporation.
|
|||
// This source is subject to the Microsoft Public License (Ms-PL).
|
|||
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
|
|||
// All other rights reserved.
|
|||
|
|||
using Avalonia.Media; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Diagnostics; |
|||
|
|||
namespace Avalonia.Controls |
|||
{ |
|||
internal class DataGridDisplayData |
|||
{ |
|||
private Stack<DataGridRow> _fullyRecycledRows; // list of Rows that have been fully recycled (Collapsed)
|
|||
private int _headScrollingElements; // index of the row in _scrollingRows that is the first displayed row
|
|||
private DataGrid _owner; |
|||
private Stack<DataGridRow> _recyclableRows; // list of Rows which have not been fully recycled (avoids Measure in several cases)
|
|||
private List<Control> _scrollingElements; // circular list of displayed elements
|
|||
private Stack<DataGridRowGroupHeader> _fullyRecycledGroupHeaders; // list of GroupHeaders that have been fully recycled (Collapsed)
|
|||
private Stack<DataGridRowGroupHeader> _recyclableGroupHeaders; // list of GroupHeaders which have not been fully recycled (avoids Measure in several cases)
|
|||
|
|||
public DataGridDisplayData(DataGrid owner) |
|||
{ |
|||
_owner = owner; |
|||
|
|||
ResetSlotIndexes(); |
|||
FirstDisplayedScrollingCol = -1; |
|||
LastTotallyDisplayedScrollingCol = -1; |
|||
|
|||
_scrollingElements = new List<Control>(); |
|||
_recyclableRows = new Stack<DataGridRow>(); |
|||
_fullyRecycledRows = new Stack<DataGridRow>(); |
|||
_recyclableGroupHeaders = new Stack<DataGridRowGroupHeader>(); |
|||
_fullyRecycledGroupHeaders = new Stack<DataGridRowGroupHeader>(); |
|||
} |
|||
|
|||
public int FirstDisplayedScrollingCol |
|||
{ |
|||
get; |
|||
set; |
|||
} |
|||
|
|||
public int FirstScrollingSlot |
|||
{ |
|||
get; |
|||
set; |
|||
} |
|||
|
|||
public int LastScrollingSlot |
|||
{ |
|||
get; |
|||
set; |
|||
} |
|||
|
|||
public int LastTotallyDisplayedScrollingCol |
|||
{ |
|||
get; |
|||
set; |
|||
} |
|||
|
|||
public int NumDisplayedScrollingElements |
|||
{ |
|||
get |
|||
{ |
|||
return _scrollingElements.Count; |
|||
} |
|||
} |
|||
|
|||
public int NumTotallyDisplayedScrollingElements |
|||
{ |
|||
get; |
|||
set; |
|||
} |
|||
|
|||
internal double PendingVerticalScrollHeight |
|||
{ |
|||
get; |
|||
set; |
|||
} |
|||
|
|||
internal void AddRecylableRow(DataGridRow row) |
|||
{ |
|||
Debug.Assert(!_recyclableRows.Contains(row)); |
|||
row.DetachFromDataGrid(true); |
|||
_recyclableRows.Push(row); |
|||
} |
|||
|
|||
internal DataGridRowGroupHeader GetUsedGroupHeader() |
|||
{ |
|||
if (_recyclableGroupHeaders.Count > 0) |
|||
{ |
|||
return _recyclableGroupHeaders.Pop(); |
|||
} |
|||
else if (_fullyRecycledGroupHeaders.Count > 0) |
|||
{ |
|||
// For fully recycled rows, we need to set the Visibility back to Visible
|
|||
DataGridRowGroupHeader groupHeader = _fullyRecycledGroupHeaders.Pop(); |
|||
groupHeader.IsVisible = true; |
|||
return groupHeader; |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
internal void AddRecylableRowGroupHeader(DataGridRowGroupHeader groupHeader) |
|||
{ |
|||
Debug.Assert(!_recyclableGroupHeaders.Contains(groupHeader)); |
|||
groupHeader.IsRecycled = true; |
|||
_recyclableGroupHeaders.Push(groupHeader); |
|||
} |
|||
|
|||
internal void ClearElements(bool recycle) |
|||
{ |
|||
ResetSlotIndexes(); |
|||
if (recycle) |
|||
{ |
|||
foreach (Control element in _scrollingElements) |
|||
{ |
|||
if (element is DataGridRow row) |
|||
{ |
|||
if (row.IsRecyclable) |
|||
{ |
|||
AddRecylableRow(row); |
|||
} |
|||
else |
|||
{ |
|||
row.Clip = new RectangleGeometry(); |
|||
} |
|||
} |
|||
else if (element is DataGridRowGroupHeader groupHeader) |
|||
{ |
|||
AddRecylableRowGroupHeader(groupHeader); |
|||
} |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
_recyclableRows.Clear(); |
|||
_fullyRecycledRows.Clear(); |
|||
_recyclableGroupHeaders.Clear(); |
|||
_fullyRecycledGroupHeaders.Clear(); |
|||
} |
|||
_scrollingElements.Clear(); |
|||
} |
|||
|
|||
internal void CorrectSlotsAfterDeletion(int slot, bool wasCollapsed) |
|||
{ |
|||
if (wasCollapsed) |
|||
{ |
|||
if (slot > FirstScrollingSlot) |
|||
{ |
|||
LastScrollingSlot--; |
|||
} |
|||
} |
|||
else if (_owner.IsSlotVisible(slot)) |
|||
{ |
|||
UnloadScrollingElement(slot, true /*updateSlotInformation*/, true /*wasDeleted*/); |
|||
} |
|||
// This cannot be an else condition because if there are 2 rows left, and you delete the first one
|
|||
// then these indexes need to be updated as well
|
|||
if (slot < FirstScrollingSlot) |
|||
{ |
|||
FirstScrollingSlot--; |
|||
LastScrollingSlot--; |
|||
} |
|||
} |
|||
|
|||
internal void CorrectSlotsAfterInsertion(int slot, Control element, bool isCollapsed) |
|||
{ |
|||
if (slot < FirstScrollingSlot) |
|||
{ |
|||
// The row was inserted above our viewport, just update our indexes
|
|||
FirstScrollingSlot++; |
|||
LastScrollingSlot++; |
|||
} |
|||
else if (isCollapsed && (slot <= LastScrollingSlot)) |
|||
{ |
|||
LastScrollingSlot++; |
|||
} |
|||
else if ((_owner.GetPreviousVisibleSlot(slot) <= LastScrollingSlot) || (LastScrollingSlot == -1)) |
|||
{ |
|||
Debug.Assert(element != null); |
|||
// The row was inserted in our viewport, add it as a scrolling row
|
|||
LoadScrollingSlot(slot, element, true /*updateSlotInformation*/); |
|||
} |
|||
} |
|||
|
|||
private int GetCircularListIndex(int slot, bool wrap) |
|||
{ |
|||
int index = slot - FirstScrollingSlot - _headScrollingElements - _owner.GetCollapsedSlotCount(FirstScrollingSlot, slot); |
|||
return wrap ? index % _scrollingElements.Count : index; |
|||
} |
|||
|
|||
internal void FullyRecycleElements() |
|||
{ |
|||
// Fully recycle Recycleable rows and transfer them to Recycled rows
|
|||
while (_recyclableRows.Count > 0) |
|||
{ |
|||
DataGridRow row = _recyclableRows.Pop(); |
|||
Debug.Assert(row != null); |
|||
row.IsVisible = false; |
|||
Debug.Assert(!_fullyRecycledRows.Contains(row)); |
|||
_fullyRecycledRows.Push(row); |
|||
} |
|||
// Fully recycle Recycleable GroupHeaders and transfer them to Recycled GroupHeaders
|
|||
while (_recyclableGroupHeaders.Count > 0) |
|||
{ |
|||
DataGridRowGroupHeader groupHeader = _recyclableGroupHeaders.Pop(); |
|||
Debug.Assert(groupHeader != null); |
|||
groupHeader.IsVisible = false; |
|||
Debug.Assert(!_fullyRecycledGroupHeaders.Contains(groupHeader)); |
|||
_fullyRecycledGroupHeaders.Push(groupHeader); |
|||
} |
|||
} |
|||
|
|||
internal Control GetDisplayedElement(int slot) |
|||
{ |
|||
Debug.Assert(slot >= FirstScrollingSlot); |
|||
Debug.Assert(slot <= LastScrollingSlot); |
|||
|
|||
return _scrollingElements[GetCircularListIndex(slot, true /*wrap*/)]; |
|||
} |
|||
|
|||
internal DataGridRow GetDisplayedRow(int rowIndex) |
|||
{ |
|||
|
|||
return GetDisplayedElement(_owner.SlotFromRowIndex(rowIndex)) as DataGridRow; |
|||
} |
|||
|
|||
// Returns an enumeration of the displayed scrolling rows in order starting with the FirstDisplayedScrollingRow
|
|||
internal IEnumerable<Control> GetScrollingElements() |
|||
{ |
|||
return GetScrollingElements(null); |
|||
} |
|||
|
|||
internal IEnumerable<Control> GetScrollingElements(Predicate<object> filter) |
|||
{ |
|||
for (int i = 0; i < _scrollingElements.Count; i++) |
|||
{ |
|||
Control element = _scrollingElements[(_headScrollingElements + i) % _scrollingElements.Count]; |
|||
if (filter == null || filter(element)) |
|||
{ |
|||
// _scrollingRows is a circular list that wraps
|
|||
yield return element; |
|||
} |
|||
} |
|||
} |
|||
|
|||
internal IEnumerable<Control> GetScrollingRows() |
|||
{ |
|||
return GetScrollingElements(element => element is DataGridRow); |
|||
} |
|||
|
|||
|
|||
internal DataGridRow GetUsedRow() |
|||
{ |
|||
if (_recyclableRows.Count > 0) |
|||
{ |
|||
return _recyclableRows.Pop(); |
|||
} |
|||
else if (_fullyRecycledRows.Count > 0) |
|||
{ |
|||
// For fully recycled rows, we need to set the Visibility back to Visible
|
|||
DataGridRow row = _fullyRecycledRows.Pop(); |
|||
row.IsVisible = true; |
|||
return row; |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
// Tracks the row at index rowIndex as a scrolling row
|
|||
internal void LoadScrollingSlot(int slot, Control element, bool updateSlotInformation) |
|||
{ |
|||
if (_scrollingElements.Count == 0) |
|||
{ |
|||
SetScrollingSlots(slot); |
|||
_scrollingElements.Add(element); |
|||
} |
|||
else |
|||
{ |
|||
// The slot should be adjacent to the other slots being displayed
|
|||
Debug.Assert(slot >= _owner.GetPreviousVisibleSlot(FirstScrollingSlot) && slot <= _owner.GetNextVisibleSlot(LastScrollingSlot)); |
|||
if (updateSlotInformation) |
|||
{ |
|||
if (slot < FirstScrollingSlot) |
|||
{ |
|||
FirstScrollingSlot = slot; |
|||
} |
|||
else |
|||
{ |
|||
LastScrollingSlot = _owner.GetNextVisibleSlot(LastScrollingSlot); |
|||
} |
|||
} |
|||
int insertIndex = GetCircularListIndex(slot, false /*wrap*/); |
|||
if (insertIndex > _scrollingElements.Count) |
|||
{ |
|||
// We need to wrap around from the bottom to the top of our circular list; as a result the head of the list moves forward
|
|||
insertIndex -= _scrollingElements.Count; |
|||
_headScrollingElements++; |
|||
} |
|||
_scrollingElements.Insert(insertIndex, element); |
|||
} |
|||
} |
|||
|
|||
private void ResetSlotIndexes() |
|||
{ |
|||
SetScrollingSlots(-1); |
|||
NumTotallyDisplayedScrollingElements = 0; |
|||
_headScrollingElements = 0; |
|||
} |
|||
|
|||
private void SetScrollingSlots(int newValue) |
|||
{ |
|||
FirstScrollingSlot = newValue; |
|||
LastScrollingSlot = newValue; |
|||
} |
|||
|
|||
// Stops tracking the element at the given slot as a scrolling element
|
|||
internal void UnloadScrollingElement(int slot, bool updateSlotInformation, bool wasDeleted) |
|||
{ |
|||
Debug.Assert(_owner.IsSlotVisible(slot)); |
|||
int elementIndex = GetCircularListIndex(slot, false /*wrap*/); |
|||
if (elementIndex > _scrollingElements.Count) |
|||
{ |
|||
// We need to wrap around from the top to the bottom of our circular list
|
|||
elementIndex -= _scrollingElements.Count; |
|||
_headScrollingElements--; |
|||
} |
|||
_scrollingElements.RemoveAt(elementIndex); |
|||
|
|||
if (updateSlotInformation) |
|||
{ |
|||
if (slot == FirstScrollingSlot && !wasDeleted) |
|||
{ |
|||
FirstScrollingSlot = _owner.GetNextVisibleSlot(FirstScrollingSlot); |
|||
} |
|||
else |
|||
{ |
|||
LastScrollingSlot = _owner.GetPreviousVisibleSlot(LastScrollingSlot); |
|||
} |
|||
if (LastScrollingSlot < FirstScrollingSlot) |
|||
{ |
|||
ResetSlotIndexes(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
#if DEBUG
|
|||
internal void PrintDisplay() |
|||
{ |
|||
foreach (Control element in GetScrollingElements()) |
|||
{ |
|||
if (element is DataGridRow row) |
|||
{ |
|||
Debug.WriteLine(String.Format(System.Globalization.CultureInfo.InvariantCulture, "Slot: {0} Row: {1} ", row.Slot, row.Index)); |
|||
} |
|||
else if (element is DataGridRowGroupHeader groupHeader) |
|||
{ |
|||
Debug.WriteLine(String.Format(System.Globalization.CultureInfo.InvariantCulture, "Slot: {0} GroupHeader: {1}", groupHeader.RowGroupInfo.Slot, groupHeader.RowGroupInfo.CollectionViewGroup.Key)); |
|||
} |
|||
} |
|||
} |
|||
#endif
|
|||
} |
|||
} |
|||
@ -0,0 +1,111 @@ |
|||
// (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; |
|||
|
|||
namespace Avalonia.Controls |
|||
{ |
|||
/// <summary>
|
|||
/// Used to specify action to take out of edit mode.
|
|||
/// </summary>
|
|||
/// <QualityBand>Mature</QualityBand>
|
|||
public enum DataGridEditAction |
|||
{ |
|||
/// <summary>
|
|||
/// Cancel the changes.
|
|||
/// </summary>
|
|||
Cancel, |
|||
|
|||
/// <summary>
|
|||
/// Commit edited value.
|
|||
/// </summary>
|
|||
Commit |
|||
} |
|||
|
|||
// Determines the location and visibility of the editing row.
|
|||
internal enum DataGridEditingRowLocation |
|||
{ |
|||
Bottom = 0, // The editing row is collapsed below the displayed rows
|
|||
Inline = 1, // The editing row is visible and displayed
|
|||
Top = 2 // The editing row is collapsed above the displayed rows
|
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Determines whether the inner cells' vertical/horizontal gridlines are shown or not.
|
|||
/// </summary>
|
|||
/// <QualityBand>Mature</QualityBand>
|
|||
[Flags] |
|||
public enum DataGridGridLinesVisibility |
|||
{ |
|||
None = 0, |
|||
Horizontal = 1, |
|||
Vertical = 2, |
|||
All = 3, |
|||
} |
|||
|
|||
/// <QualityBand>Mature</QualityBand>
|
|||
public enum DataGridEditingUnit |
|||
{ |
|||
Cell = 0, |
|||
Row = 1, |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Determines whether the row/column headers are shown or not.
|
|||
/// </summary>
|
|||
/// <QualityBand>Mature</QualityBand>
|
|||
[Flags] |
|||
public enum DataGridHeadersVisibility |
|||
{ |
|||
/// <summary>
|
|||
/// Show Row, Column, and Corner Headers
|
|||
/// </summary>
|
|||
All = Row | Column, |
|||
|
|||
/// <summary>
|
|||
/// Show only Column Headers with top-right corner Header
|
|||
/// </summary>
|
|||
Column = 0x01, |
|||
|
|||
/// <summary>
|
|||
/// Show only Row Headers with bottom-left corner
|
|||
/// </summary>
|
|||
Row = 0x02, |
|||
|
|||
/// <summary>
|
|||
/// Don’t show any Headers
|
|||
/// </summary>
|
|||
None = 0x00 |
|||
} |
|||
|
|||
public enum DataGridRowDetailsVisibilityMode |
|||
{ |
|||
Collapsed = 2, // Show no details. Developer is in charge of toggling visibility.
|
|||
Visible = 1, // Show the details section for all rows.
|
|||
VisibleWhenSelected = 0 // Show the details section only for the selected row(s).
|
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Determines the type of action to take when selecting items
|
|||
/// </summary>
|
|||
internal enum DataGridSelectionAction |
|||
{ |
|||
AddCurrentToSelection, |
|||
None, |
|||
RemoveCurrentFromSelection, |
|||
SelectCurrent, |
|||
SelectFromAnchorToCurrent |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Determines the selection model
|
|||
/// </summary>
|
|||
/// <QualityBand>Mature</QualityBand>
|
|||
public enum DataGridSelectionMode |
|||
{ |
|||
Extended = 0, |
|||
Single = 1 |
|||
} |
|||
} |
|||
@ -0,0 +1,199 @@ |
|||
// (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.Globalization; |
|||
|
|||
namespace Avalonia.Controls |
|||
{ |
|||
|
|||
internal static class DataGridError |
|||
{ |
|||
public static class DataGrid |
|||
{ |
|||
public static InvalidOperationException CannotChangeItemsWhenLoadingRows() |
|||
{ |
|||
return new InvalidOperationException("Items cannot be added, removed or reset while rows are loading or unloading."); |
|||
} |
|||
|
|||
public static InvalidOperationException CannotChangeColumnCollectionWhileAdjustingDisplayIndexes() |
|||
{ |
|||
return new InvalidOperationException("Column collection cannot be changed while adjusting display indexes."); |
|||
} |
|||
|
|||
public static InvalidOperationException ColumnCannotBeCollapsed() |
|||
{ |
|||
return new InvalidOperationException("Column cannot be collapsed."); |
|||
} |
|||
|
|||
public static InvalidOperationException ColumnCannotBeReassignedToDifferentDataGrid() |
|||
{ |
|||
return new InvalidOperationException("Column already belongs to a DataGrid instance and cannot be reassigned."); |
|||
} |
|||
|
|||
public static ArgumentException ColumnNotInThisDataGrid() |
|||
{ |
|||
return new ArgumentException("Provided column does not belong to this DataGrid."); |
|||
} |
|||
|
|||
public static ArgumentException ItemIsNotContainedInTheItemsSource(string paramName) |
|||
{ |
|||
return new ArgumentException("The item is not contained in the ItemsSource.", paramName); |
|||
} |
|||
|
|||
public static InvalidOperationException NoCurrentRow() |
|||
{ |
|||
return new InvalidOperationException("There is no current row. Operation cannot be completed."); |
|||
} |
|||
|
|||
public static InvalidOperationException NoOwningGrid(Type type) |
|||
{ |
|||
return new InvalidOperationException(Format("There is no instance of DataGrid assigned to this {0}. Operation cannot be completed.", type.FullName)); |
|||
} |
|||
|
|||
public static InvalidOperationException UnderlyingPropertyIsReadOnly(string paramName) |
|||
{ |
|||
return new InvalidOperationException(Format("{0} cannot be set because the underlying property is read only.", paramName)); |
|||
} |
|||
|
|||
public static ArgumentException ValueCannotBeSetToInfinity(string paramName) |
|||
{ |
|||
return new ArgumentException(Format("{0} cannot be set to infinity.", paramName)); |
|||
} |
|||
|
|||
public static ArgumentException ValueCannotBeSetToNAN(string paramName) |
|||
{ |
|||
return new ArgumentException(Format("{0} cannot be set to double.NAN.", paramName)); |
|||
} |
|||
|
|||
public static ArgumentNullException ValueCannotBeSetToNull(string paramName, string valueName) |
|||
{ |
|||
return new ArgumentNullException(paramName, Format("{0} cannot be set to a null value.", valueName)); |
|||
} |
|||
|
|||
public static ArgumentException ValueIsNotAnInstanceOf(string paramName, Type type) |
|||
{ |
|||
return new ArgumentException(paramName, Format("The value is not an instance of {0}.", type.FullName)); |
|||
} |
|||
|
|||
public static ArgumentException ValueIsNotAnInstanceOfEitherOr(string paramName, Type type1, Type type2) |
|||
{ |
|||
return new ArgumentException(paramName, Format("The value is not an instance of {0} or {1}.", type1.FullName, type2.FullName)); |
|||
} |
|||
|
|||
public static ArgumentOutOfRangeException ValueMustBeBetween(string paramName, string valueName, object lowValue, bool lowInclusive, object highValue, bool highInclusive) |
|||
{ |
|||
string message = null; |
|||
|
|||
if (lowInclusive && highInclusive) |
|||
{ |
|||
message = "{0} must be greater than or equal to {1} and less than or equal to {2}."; |
|||
} |
|||
else if (lowInclusive && !highInclusive) |
|||
{ |
|||
message = "{0} must be greater than or equal to {1} and less than {2}."; |
|||
} |
|||
else if (!lowInclusive && highInclusive) |
|||
{ |
|||
message = "{0} must be greater than {1} and less than or equal to {2}."; |
|||
} |
|||
else |
|||
{ |
|||
message = "{0} must be greater than {1} and less than {2}."; |
|||
} |
|||
|
|||
return new ArgumentOutOfRangeException(paramName, Format(message, valueName, lowValue, highValue)); |
|||
} |
|||
|
|||
public static ArgumentOutOfRangeException ValueMustBeGreaterThanOrEqualTo(string paramName, string valueName, object value) |
|||
{ |
|||
return new ArgumentOutOfRangeException(paramName, Format("{0} must be greater than or equal to {1}.", valueName, value)); |
|||
} |
|||
|
|||
public static ArgumentOutOfRangeException ValueMustBeLessThanOrEqualTo(string paramName, string valueName, object value) |
|||
{ |
|||
return new ArgumentOutOfRangeException(paramName, Format("{0} must be less than or equal to {1}.", valueName, value)); |
|||
} |
|||
|
|||
public static ArgumentOutOfRangeException ValueMustBeLessThan(string paramName, string valueName, object value) |
|||
{ |
|||
return new ArgumentOutOfRangeException(paramName, Format("{0} must be less than {1}.", valueName, value)); |
|||
} |
|||
|
|||
} |
|||
|
|||
//
|
|||
|
|||
|
|||
|
|||
|
|||
|
|||
|
|||
|
|||
|
|||
public static class DataGridColumnHeader |
|||
{ |
|||
public static NotSupportedException ContentDoesNotSupportUIElements() |
|||
{ |
|||
return new NotSupportedException("Content does not support Controls; use ContentTemplate instead."); |
|||
} |
|||
} |
|||
|
|||
public static class DataGridLength |
|||
{ |
|||
public static ArgumentException InvalidUnitType(string paramName) |
|||
{ |
|||
return new ArgumentException(Format("{0} is not a valid DataGridLengthUnitType.", paramName), paramName); |
|||
} |
|||
} |
|||
|
|||
public static class DataGridLengthConverter |
|||
{ |
|||
public static NotSupportedException CannotConvertFrom(string paramName) |
|||
{ |
|||
return new NotSupportedException(Format("DataGridLengthConverter cannot convert from {0}.", paramName)); |
|||
} |
|||
|
|||
public static NotSupportedException CannotConvertTo(string paramName) |
|||
{ |
|||
return new NotSupportedException(Format("Cannot convert from DataGridLength to {0}.", paramName)); |
|||
} |
|||
|
|||
public static NotSupportedException InvalidDataGridLength(string paramName) |
|||
{ |
|||
return new NotSupportedException(Format("Invalid DataGridLength.", paramName)); |
|||
} |
|||
} |
|||
|
|||
public static class DataGridRow |
|||
{ |
|||
public static InvalidOperationException InvalidRowIndexCannotCompleteOperation() |
|||
{ |
|||
return new InvalidOperationException("Invalid row index. Operation cannot be completed."); |
|||
} |
|||
} |
|||
|
|||
public static class DataGridSelectedItemsCollection |
|||
{ |
|||
public static InvalidOperationException CannotChangeSelectedItemsCollectionInSingleMode() |
|||
{ |
|||
return new InvalidOperationException("Can only change SelectedItems collection in Extended selection mode. Use SelectedItem property in Single selection mode."); |
|||
} |
|||
} |
|||
|
|||
public static class DataGridTemplateColumn |
|||
{ |
|||
public static TypeInitializationException MissingTemplateForType(Type type) |
|||
{ |
|||
return new TypeInitializationException(Format("Missing template. Cannot initialize {0}.", type.FullName), null); |
|||
} |
|||
} |
|||
|
|||
private static string Format(string formatString, params object[] args) |
|||
{ |
|||
return String.Format(CultureInfo.CurrentCulture, formatString, args); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,68 @@ |
|||
// (c) Copyright Microsoft Corporation.
|
|||
// This source is subject to the Microsoft Public License (Ms-PL).
|
|||
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
|
|||
// All other rights reserved.
|
|||
|
|||
using Avalonia.Interactivity; |
|||
|
|||
namespace Avalonia.Controls |
|||
{ |
|||
internal class DataGridFillerColumn : DataGridColumn |
|||
{ |
|||
|
|||
public DataGridFillerColumn(DataGrid owningGrid) |
|||
{ |
|||
IsReadOnly = true; |
|||
OwningGrid = owningGrid; |
|||
MinWidth = 0; |
|||
MaxWidth = int.MaxValue; |
|||
} |
|||
|
|||
internal double FillerWidth |
|||
{ |
|||
get; |
|||
set; |
|||
} |
|||
|
|||
// True if there is room for the filler column; otherwise, false
|
|||
internal bool IsActive |
|||
{ |
|||
get |
|||
{ |
|||
return FillerWidth > 0; |
|||
} |
|||
} |
|||
|
|||
// True if the FillerColumn's header cell is contained in the visual tree
|
|||
internal bool IsRepresented |
|||
{ |
|||
get; |
|||
set; |
|||
} |
|||
|
|||
internal override DataGridColumnHeader CreateHeader() |
|||
{ |
|||
DataGridColumnHeader headerCell = base.CreateHeader(); |
|||
if (headerCell != null) |
|||
{ |
|||
headerCell.IsEnabled = false; |
|||
} |
|||
return headerCell; |
|||
} |
|||
|
|||
protected override Control GenerateElement(DataGridCell cell, object dataItem) |
|||
{ |
|||
return null; |
|||
} |
|||
|
|||
protected override Control GenerateEditingElement(DataGridCell cell, object dataItem) |
|||
{ |
|||
return null; |
|||
} |
|||
|
|||
protected override object PrepareCellForEdit(Control editingElement, RoutedEventArgs editingEventArgs) |
|||
{ |
|||
return null; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,557 @@ |
|||
// (c) Copyright Microsoft Corporation.
|
|||
// This source is subject to the Microsoft Public License (Ms-PL).
|
|||
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
|
|||
// All other rights reserved.
|
|||
|
|||
using Avalonia.Utilities; |
|||
using System; |
|||
using System.ComponentModel; |
|||
using System.Globalization; |
|||
|
|||
namespace Avalonia.Controls |
|||
{ |
|||
public enum DataGridLengthUnitType |
|||
{ |
|||
Auto = 0, |
|||
Pixel = 1, |
|||
SizeToCells = 2, |
|||
SizeToHeader = 3, |
|||
Star = 4 |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Represents the lengths of elements within the <see cref="T:Avalonia.Controls.DataGrid" /> control.
|
|||
/// </summary>
|
|||
[TypeConverter(typeof(DataGridLengthConverter))] |
|||
public struct DataGridLength : IEquatable<DataGridLength> |
|||
{ |
|||
#region Data
|
|||
|
|||
private double _desiredValue; // desired value storage
|
|||
private double _displayValue; // display value storage
|
|||
private double _unitValue; // unit value storage
|
|||
private DataGridLengthUnitType _unitType; // unit type storage
|
|||
|
|||
// static instances of value invariant DataGridLengths
|
|||
private static readonly DataGridLength _auto = new DataGridLength(DATAGRIDLENGTH_DefaultValue, DataGridLengthUnitType.Auto); |
|||
private static readonly DataGridLength _sizeToCells = new DataGridLength(DATAGRIDLENGTH_DefaultValue, DataGridLengthUnitType.SizeToCells); |
|||
private static readonly DataGridLength _sizeToHeader = new DataGridLength(DATAGRIDLENGTH_DefaultValue, DataGridLengthUnitType.SizeToHeader); |
|||
|
|||
// WPF uses 1.0 as the default value as well
|
|||
internal const double DATAGRIDLENGTH_DefaultValue = 1.0; |
|||
|
|||
#endregion Data
|
|||
|
|||
|
|||
#region Constructors
|
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="T:Avalonia.Controls.DataGridLength" /> class.
|
|||
/// </summary>
|
|||
/// <param name="value"></param>
|
|||
public DataGridLength(double value) |
|||
: this(value, DataGridLengthUnitType.Pixel) |
|||
{ |
|||
} |
|||
/// <summary>
|
|||
/// Initializes to a specified value and unit.
|
|||
/// </summary>
|
|||
/// <param name="value">The value to hold.</param>
|
|||
/// <param name="type">The unit of <c>value</c>.</param>
|
|||
/// <remarks>
|
|||
/// <c>value</c> is ignored unless <c>type</c> is
|
|||
/// <c>DataGridLengthUnitType.Pixel</c> or
|
|||
/// <c>DataGridLengthUnitType.Star</c>
|
|||
/// </remarks>
|
|||
/// <exception cref="ArgumentException">
|
|||
/// If <c>value</c> parameter is <c>double.NaN</c>
|
|||
/// or <c>value</c> parameter is <c>double.NegativeInfinity</c>
|
|||
/// or <c>value</c> parameter is <c>double.PositiveInfinity</c>.
|
|||
/// </exception>
|
|||
public DataGridLength(double value, DataGridLengthUnitType type) |
|||
: this(value, type, (type == DataGridLengthUnitType.Pixel ? value : Double.NaN), (type == DataGridLengthUnitType.Pixel ? value : Double.NaN)) |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes to a specified value and unit.
|
|||
/// </summary>
|
|||
/// <param name="value">The value to hold.</param>
|
|||
/// <param name="type">The unit of <c>value</c>.</param>
|
|||
/// <param name="desiredValue"></param>
|
|||
/// <param name="displayValue"></param>
|
|||
/// <remarks>
|
|||
/// <c>value</c> is ignored unless <c>type</c> is
|
|||
/// <c>DataGridLengthUnitType.Pixel</c> or
|
|||
/// <c>DataGridLengthUnitType.Star</c>
|
|||
/// </remarks>
|
|||
/// <exception cref="ArgumentException">
|
|||
/// If <c>value</c> parameter is <c>double.NaN</c>
|
|||
/// or <c>value</c> parameter is <c>double.NegativeInfinity</c>
|
|||
/// or <c>value</c> parameter is <c>double.PositiveInfinity</c>.
|
|||
/// </exception>
|
|||
public DataGridLength(double value, DataGridLengthUnitType type, double desiredValue, double displayValue) |
|||
{ |
|||
if (double.IsNaN(value)) |
|||
{ |
|||
throw DataGridError.DataGrid.ValueCannotBeSetToNAN("value"); |
|||
} |
|||
if (double.IsInfinity(value)) |
|||
{ |
|||
throw DataGridError.DataGrid.ValueCannotBeSetToInfinity("value"); |
|||
} |
|||
if (double.IsInfinity(desiredValue)) |
|||
{ |
|||
throw DataGridError.DataGrid.ValueCannotBeSetToInfinity("desiredValue"); |
|||
} |
|||
if (double.IsInfinity(displayValue)) |
|||
{ |
|||
throw DataGridError.DataGrid.ValueCannotBeSetToInfinity("displayValue"); |
|||
} |
|||
if (value < 0) |
|||
{ |
|||
throw DataGridError.DataGrid.ValueMustBeGreaterThanOrEqualTo("value", "value", 0); |
|||
} |
|||
if (desiredValue < 0) |
|||
{ |
|||
throw DataGridError.DataGrid.ValueMustBeGreaterThanOrEqualTo("desiredValue", "desiredValue", 0); |
|||
} |
|||
if (displayValue < 0) |
|||
{ |
|||
throw DataGridError.DataGrid.ValueMustBeGreaterThanOrEqualTo("displayValue", "displayValue", 0); |
|||
} |
|||
|
|||
if (type != DataGridLengthUnitType.Auto && |
|||
type != DataGridLengthUnitType.SizeToCells && |
|||
type != DataGridLengthUnitType.SizeToHeader && |
|||
type != DataGridLengthUnitType.Star && |
|||
type != DataGridLengthUnitType.Pixel) |
|||
{ |
|||
throw DataGridError.DataGridLength.InvalidUnitType("type"); |
|||
} |
|||
|
|||
_desiredValue = desiredValue; |
|||
_displayValue = displayValue; |
|||
_unitValue = (type == DataGridLengthUnitType.Auto) ? DATAGRIDLENGTH_DefaultValue : value; |
|||
_unitType = type; |
|||
} |
|||
|
|||
#endregion Constructors
|
|||
|
|||
#region Properties
|
|||
|
|||
/// <summary>
|
|||
/// Gets a <see cref="T:Avalonia.Controls.DataGridLength" /> structure that represents the standard automatic sizing mode.
|
|||
/// </summary>
|
|||
/// <returns>
|
|||
/// A <see cref="T:Avalonia.Controls.DataGridLength" /> structure that represents the standard automatic sizing mode.
|
|||
/// </returns>
|
|||
public static DataGridLength Auto |
|||
{ |
|||
get |
|||
{ |
|||
return _auto; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns the desired value of this instance.
|
|||
/// </summary>
|
|||
public double DesiredValue |
|||
{ |
|||
get |
|||
{ |
|||
return _desiredValue; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns the display value of this instance.
|
|||
/// </summary>
|
|||
public double DisplayValue |
|||
{ |
|||
get |
|||
{ |
|||
return _displayValue; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns <c>true</c> if this DataGridLength instance holds
|
|||
/// an absolute (pixel) value.
|
|||
/// </summary>
|
|||
public bool IsAbsolute |
|||
{ |
|||
get |
|||
{ |
|||
return _unitType == DataGridLengthUnitType.Pixel; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns <c>true</c> if this DataGridLength instance is
|
|||
/// automatic (not specified).
|
|||
/// </summary>
|
|||
public bool IsAuto |
|||
{ |
|||
get |
|||
{ |
|||
return _unitType == DataGridLengthUnitType.Auto; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns <c>true</c> if this instance is to size to the cells of a column or row.
|
|||
/// </summary>
|
|||
public bool IsSizeToCells |
|||
{ |
|||
get |
|||
{ |
|||
return _unitType == DataGridLengthUnitType.SizeToCells; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns <c>true</c> if this instance is to size to the header of a column or row.
|
|||
/// </summary>
|
|||
public bool IsSizeToHeader |
|||
{ |
|||
get |
|||
{ |
|||
return _unitType == DataGridLengthUnitType.SizeToHeader; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns <c>true</c> if this DataGridLength instance holds a weighted proportion
|
|||
/// of available space.
|
|||
/// </summary>
|
|||
public bool IsStar |
|||
{ |
|||
get |
|||
{ |
|||
return _unitType == DataGridLengthUnitType.Star; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets a <see cref="T:Avalonia.Controls.DataGridLength" /> structure that represents the cell-based automatic sizing mode.
|
|||
/// </summary>
|
|||
/// <returns>
|
|||
/// A <see cref="T:Avalonia.Controls.DataGridLength" /> structure that represents the cell-based automatic sizing mode.
|
|||
/// </returns>
|
|||
public static DataGridLength SizeToCells |
|||
{ |
|||
get |
|||
{ |
|||
return _sizeToCells; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets a <see cref="T:Avalonia.Controls.DataGridLength" /> structure that represents the header-based automatic sizing mode.
|
|||
/// </summary>
|
|||
/// <returns>
|
|||
/// A <see cref="T:Avalonia.Controls.DataGridLength" /> structure that represents the header-based automatic sizing mode.
|
|||
/// </returns>
|
|||
public static DataGridLength SizeToHeader |
|||
{ |
|||
get |
|||
{ |
|||
return _sizeToHeader; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the <see cref="T:Avalonia.Controls.DataGridLengthUnitType" /> that represents the current sizing mode.
|
|||
/// </summary>
|
|||
public DataGridLengthUnitType UnitType |
|||
{ |
|||
get |
|||
{ |
|||
return _unitType; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the absolute value of the <see cref="T:Avalonia.Controls.DataGridLength" /> in pixels.
|
|||
/// </summary>
|
|||
/// <returns>
|
|||
/// The absolute value of the <see cref="T:Avalonia.Controls.DataGridLength" /> in pixels.
|
|||
/// </returns>
|
|||
public double Value |
|||
{ |
|||
get |
|||
{ |
|||
return _unitValue; |
|||
} |
|||
} |
|||
|
|||
#endregion Properties
|
|||
|
|||
#region Methods
|
|||
|
|||
/// <summary>
|
|||
/// Overloaded operator, compares 2 DataGridLength's.
|
|||
/// </summary>
|
|||
/// <param name="gl1">first DataGridLength to compare.</param>
|
|||
/// <param name="gl2">second DataGridLength to compare.</param>
|
|||
/// <returns>true if specified DataGridLength have same value,
|
|||
/// unit type, desired value, and display value.</returns>
|
|||
public static bool operator ==(DataGridLength gl1, DataGridLength gl2) |
|||
{ |
|||
return (gl1.UnitType == gl2.UnitType |
|||
&& gl1.Value == gl2.Value |
|||
&& gl1.DesiredValue == gl2.DesiredValue |
|||
&& gl1.DisplayValue == gl2.DisplayValue); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Overloaded operator, compares 2 DataGridLength's.
|
|||
/// </summary>
|
|||
/// <param name="gl1">first DataGridLength to compare.</param>
|
|||
/// <param name="gl2">second DataGridLength to compare.</param>
|
|||
/// <returns>true if specified DataGridLength have either different value,
|
|||
/// unit type, desired value, or display value.</returns>
|
|||
public static bool operator !=(DataGridLength gl1, DataGridLength gl2) |
|||
{ |
|||
return (gl1.UnitType != gl2.UnitType |
|||
|| gl1.Value != gl2.Value |
|||
|| gl1.DesiredValue != gl2.DesiredValue |
|||
|| gl1.DisplayValue != gl2.DisplayValue); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Compares this instance of DataGridLength with another instance.
|
|||
/// </summary>
|
|||
/// <param name="other">DataGridLength length instance to compare.</param>
|
|||
/// <returns><c>true</c> if this DataGridLength instance has the same value
|
|||
/// and unit type as gridLength.</returns>
|
|||
public bool Equals(DataGridLength other) |
|||
{ |
|||
return (this == other); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Compares this instance of DataGridLength with another object.
|
|||
/// </summary>
|
|||
/// <param name="obj">Reference to an object for comparison.</param>
|
|||
/// <returns><c>true</c> if this DataGridLength instance has the same value
|
|||
/// and unit type as oCompare.</returns>
|
|||
public override bool Equals(object obj) |
|||
{ |
|||
DataGridLength? dataGridLength = obj as DataGridLength?; |
|||
if (dataGridLength.HasValue) |
|||
{ |
|||
return (this == dataGridLength); |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns a unique hash code for this DataGridLength
|
|||
/// </summary>
|
|||
/// <returns>hash code</returns>
|
|||
public override int GetHashCode() |
|||
{ |
|||
return ((int)_unitValue + (int)_unitType) + (int)_desiredValue + (int)_displayValue; |
|||
} |
|||
|
|||
#endregion Methods
|
|||
} |
|||
/// <summary>
|
|||
/// DataGridLengthConverter - Converter class for converting instances of other types to and from DataGridLength instances.
|
|||
/// </summary>
|
|||
/// <QualityBand>Mature</QualityBand>
|
|||
public class DataGridLengthConverter : TypeConverter |
|||
{ |
|||
private static string _starSuffix = "*"; |
|||
private static string[] _valueInvariantUnitStrings = { "auto", "sizetocells", "sizetoheader" }; |
|||
private static DataGridLength[] _valueInvariantDataGridLengths = { DataGridLength.Auto, DataGridLength.SizeToCells, DataGridLength.SizeToHeader }; |
|||
|
|||
/// <summary>
|
|||
/// Checks whether or not this class can convert from a given type.
|
|||
/// </summary>
|
|||
/// <param name="context">
|
|||
/// An ITypeDescriptorContext that provides a format context.
|
|||
/// </param>
|
|||
/// <param name="sourceType">The Type being queried for support.</param>
|
|||
/// <returns>
|
|||
/// <c>true</c> if this converter can convert from the provided type,
|
|||
/// <c>false</c> otherwise.
|
|||
/// </returns>
|
|||
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) |
|||
{ |
|||
// We can only handle strings, integral and floating types
|
|||
TypeCode tc = Type.GetTypeCode(sourceType); |
|||
switch (tc) |
|||
{ |
|||
case TypeCode.String: |
|||
case TypeCode.Decimal: |
|||
case TypeCode.Single: |
|||
case TypeCode.Double: |
|||
case TypeCode.Int16: |
|||
case TypeCode.Int32: |
|||
case TypeCode.Int64: |
|||
case TypeCode.UInt16: |
|||
case TypeCode.UInt32: |
|||
case TypeCode.UInt64: |
|||
return true; |
|||
default: |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Checks whether or not this class can convert to a given type.
|
|||
/// </summary>
|
|||
/// <param name="context">
|
|||
/// An ITypeDescriptorContext that provides a format context.
|
|||
/// </param>
|
|||
/// <param name="destinationType">The Type being queried for support.</param>
|
|||
/// <returns>
|
|||
/// <c>true</c> if this converter can convert to the provided type,
|
|||
/// <c>false</c> otherwise.
|
|||
/// </returns>
|
|||
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) |
|||
{ |
|||
return destinationType == typeof(string); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Attempts to convert to a DataGridLength from the given object.
|
|||
/// </summary>
|
|||
/// <param name="context">
|
|||
/// An ITypeDescriptorContext that provides a format context.
|
|||
/// </param>
|
|||
/// <param name="culture">
|
|||
/// The CultureInfo to use for the conversion.
|
|||
/// </param>
|
|||
/// <param name="value">The object to convert to a GridLength.</param>
|
|||
/// <returns>
|
|||
/// The GridLength instance which was constructed.
|
|||
/// </returns>
|
|||
/// <exception cref="NotSupportedException">
|
|||
/// A NotSupportedException is thrown if the example object is null.
|
|||
/// </exception>
|
|||
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) |
|||
{ |
|||
// GridLengthConverter in WPF throws a NotSupportedException on a null value as well.
|
|||
if (value == null) |
|||
{ |
|||
throw DataGridError.DataGridLengthConverter.CannotConvertFrom("(null)"); |
|||
} |
|||
|
|||
if (value is string stringValue) |
|||
{ |
|||
stringValue = stringValue.Trim(); |
|||
|
|||
if (stringValue.EndsWith(_starSuffix, StringComparison.Ordinal)) |
|||
{ |
|||
string stringValueWithoutSuffix = stringValue.Substring(0, stringValue.Length - _starSuffix.Length); |
|||
|
|||
double starWeight; |
|||
if (string.IsNullOrEmpty(stringValueWithoutSuffix)) |
|||
{ |
|||
starWeight = 1; |
|||
} |
|||
else |
|||
{ |
|||
starWeight = Convert.ToDouble(stringValueWithoutSuffix, culture ?? CultureInfo.CurrentCulture); |
|||
} |
|||
|
|||
return new DataGridLength(starWeight, DataGridLengthUnitType.Star); |
|||
} |
|||
|
|||
for (int index = 0; index < _valueInvariantUnitStrings.Length; index++) |
|||
{ |
|||
if (stringValue.Equals(_valueInvariantUnitStrings[index], StringComparison.OrdinalIgnoreCase)) |
|||
{ |
|||
return _valueInvariantDataGridLengths[index]; |
|||
} |
|||
} |
|||
} |
|||
|
|||
// Conversion from numeric type, WPF lets Convert exceptions bubble out here as well
|
|||
double doubleValue = Convert.ToDouble(value, culture ?? CultureInfo.CurrentCulture); |
|||
if (double.IsNaN(doubleValue)) |
|||
{ |
|||
// WPF returns Auto in this case as well
|
|||
return DataGridLength.Auto; |
|||
} |
|||
else |
|||
{ |
|||
return new DataGridLength(doubleValue); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Attempts to convert a DataGridLength instance to the given type.
|
|||
/// </summary>
|
|||
/// <param name="context">
|
|||
/// An ITypeDescriptorContext that provides a format context.
|
|||
/// </param>
|
|||
/// <param name="culture">
|
|||
/// The CultureInfo to use for the conversion.
|
|||
/// </param>
|
|||
/// <param name="value">The DataGridLength to convert.</param>
|
|||
/// <param name="destinationType">The type to which to convert the DataGridLength instance.</param>
|
|||
/// <returns>
|
|||
/// The object which was constructed.
|
|||
/// </returns>
|
|||
/// <exception cref="ArgumentNullException">
|
|||
/// An ArgumentNullException is thrown if the example object is null.
|
|||
/// </exception>
|
|||
/// <exception cref="NotSupportedException">
|
|||
/// A NotSupportedException is thrown if the object is not null and is not a DataGridLength,
|
|||
/// or if the destinationType isn't one of the valid destination types.
|
|||
/// </exception>
|
|||
///<SecurityNote>
|
|||
/// Critical: calls InstanceDescriptor ctor which LinkDemands
|
|||
/// PublicOK: can only make an InstanceDescriptor for DataGridLength, not an arbitrary class
|
|||
///</SecurityNote>
|
|||
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) |
|||
{ |
|||
if (destinationType == null) |
|||
{ |
|||
throw new ArgumentNullException("destinationType"); |
|||
} |
|||
if (destinationType != typeof(string)) |
|||
{ |
|||
throw DataGridError.DataGridLengthConverter.CannotConvertTo(destinationType.ToString()); |
|||
} |
|||
DataGridLength? dataGridLength = value as DataGridLength?; |
|||
if (!dataGridLength.HasValue) |
|||
{ |
|||
throw DataGridError.DataGridLengthConverter.InvalidDataGridLength("value"); |
|||
} |
|||
else |
|||
{ |
|||
// Convert dataGridLength to a string
|
|||
switch (dataGridLength.Value.UnitType) |
|||
{ |
|||
// for Auto print out "Auto". value is always "1.0"
|
|||
case DataGridLengthUnitType.Auto: |
|||
return "Auto"; |
|||
|
|||
case DataGridLengthUnitType.SizeToHeader: |
|||
return "SizeToHeader"; |
|||
|
|||
case DataGridLengthUnitType.SizeToCells: |
|||
return "SizeToCells"; |
|||
|
|||
// Star has one special case when value is "1.0".
|
|||
// in this case drop value part and print only "Star"
|
|||
case DataGridLengthUnitType.Star: |
|||
return ( |
|||
DoubleUtil.AreClose(1.0, dataGridLength.Value.Value) |
|||
? _starSuffix |
|||
: Convert.ToString(dataGridLength.Value.Value, culture ?? CultureInfo.CurrentCulture) + DataGridLengthConverter._starSuffix); |
|||
|
|||
default: |
|||
return (Convert.ToString(dataGridLength.Value.Value, culture ?? CultureInfo.CurrentCulture)); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
File diff suppressed because it is too large
@ -0,0 +1,543 @@ |
|||
// (c) Copyright Microsoft Corporation.
|
|||
// This source is subject to the Microsoft Public License (Ms-PL).
|
|||
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
|
|||
// All other rights reserved.
|
|||
|
|||
using Avalonia.Controls.Primitives; |
|||
using Avalonia.Input; |
|||
using Avalonia.Media; |
|||
using System; |
|||
using System.Diagnostics; |
|||
using System.Reactive.Linq; |
|||
|
|||
namespace Avalonia.Controls |
|||
{ |
|||
public class DataGridRowGroupHeader : TemplatedControl |
|||
{ |
|||
private const string DATAGRIDROWGROUPHEADER_expanderButton = "ExpanderButton"; |
|||
private const string DATAGRIDROWGROUPHEADER_indentSpacer = "IndentSpacer"; |
|||
private const string DATAGRIDROWGROUPHEADER_itemCountElement = "ItemCountElement"; |
|||
private const string DATAGRIDROWGROUPHEADER_propertyNameElement = "PropertyNameElement"; |
|||
|
|||
private bool _areIsCheckedHandlersSuspended; |
|||
private ToggleButton _expanderButton; |
|||
private DataGridRowHeader _headerElement; |
|||
private Control _indentSpacer; |
|||
private TextBlock _itemCountElement; |
|||
private TextBlock _propertyNameElement; |
|||
private Panel _rootElement; |
|||
private double _totalIndent; |
|||
|
|||
|
|||
public static readonly StyledProperty<bool> IsItemCountVisibleProperty = |
|||
AvaloniaProperty.Register<DataGridRowGroupHeader, bool>(nameof(IsItemCountVisible)); |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets a value that indicates whether the item count is visible.
|
|||
/// </summary>
|
|||
public bool IsItemCountVisible |
|||
{ |
|||
get { return GetValue(IsItemCountVisibleProperty); } |
|||
set { SetValue(IsItemCountVisibleProperty, value); } |
|||
} |
|||
|
|||
|
|||
public static readonly StyledProperty<string> PropertyNameProperty = |
|||
AvaloniaProperty.Register<DataGridRowGroupHeader, string>(nameof(PropertyName)); |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the name of the property that this <see cref="T:Avalonia.Controls.DataGrid" /> row is bound to.
|
|||
/// </summary>
|
|||
public string PropertyName |
|||
{ |
|||
get { return GetValue(PropertyNameProperty); } |
|||
set { SetValue(PropertyNameProperty, value); } |
|||
} |
|||
|
|||
|
|||
public static readonly StyledProperty<bool> IsPropertyNameVisibleProperty = |
|||
AvaloniaProperty.Register<DataGridRowGroupHeader, bool>(nameof(IsPropertyNameVisible)); |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets a value that indicates whether the property name is visible.
|
|||
/// </summary>
|
|||
public bool IsPropertyNameVisible |
|||
{ |
|||
get { return GetValue(IsPropertyNameVisibleProperty); } |
|||
set { SetValue(IsPropertyNameVisibleProperty, value); } |
|||
} |
|||
|
|||
|
|||
public static readonly StyledProperty<double> SublevelIndentProperty = |
|||
AvaloniaProperty.Register<DataGridRowGroupHeader, double>( |
|||
nameof(SublevelIndent), |
|||
defaultValue: DataGrid.DATAGRID_defaultRowGroupSublevelIndent, |
|||
validate: ValidateSublevelIndent); |
|||
|
|||
private static double ValidateSublevelIndent(DataGridRowGroupHeader header, double value) |
|||
{ |
|||
// We don't need to revert to the old value if our input is bad because we never read this property value
|
|||
if (double.IsNaN(value)) |
|||
{ |
|||
throw DataGridError.DataGrid.ValueCannotBeSetToNAN(nameof(SublevelIndent)); |
|||
} |
|||
else if (double.IsInfinity(value)) |
|||
{ |
|||
throw DataGridError.DataGrid.ValueCannotBeSetToInfinity(nameof(SublevelIndent)); |
|||
} |
|||
else if (value < 0) |
|||
{ |
|||
throw DataGridError.DataGrid.ValueMustBeGreaterThanOrEqualTo(nameof(value), nameof(SublevelIndent), 0); |
|||
} |
|||
|
|||
return value; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets a value that indicates the amount that the
|
|||
/// children of the <see cref="T:Avalonia.Controls.RowGroupHeader" /> are indented.
|
|||
/// </summary>
|
|||
public double SublevelIndent |
|||
{ |
|||
get { return GetValue(SublevelIndentProperty); } |
|||
set { SetValue(SublevelIndentProperty, value); } |
|||
} |
|||
|
|||
private void OnSublevelIndentChanged(AvaloniaPropertyChangedEventArgs e) |
|||
{ |
|||
if (OwningGrid != null) |
|||
{ |
|||
OwningGrid.OnSublevelIndentUpdated(this, (double)e.NewValue); |
|||
} |
|||
} |
|||
|
|||
static DataGridRowGroupHeader() |
|||
{ |
|||
SublevelIndentProperty.Changed.AddClassHandler<DataGridRowGroupHeader>(x => x.OnSublevelIndentChanged); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Constructs a DataGridRowGroupHeader
|
|||
/// </summary>
|
|||
public DataGridRowGroupHeader() |
|||
{ |
|||
//DefaultStyleKey = typeof(DataGridRowGroupHeader);
|
|||
AddHandler(InputElement.PointerPressedEvent, (s, e) => DataGridRowGroupHeader_PointerPressed(e), handledEventsToo: true); |
|||
} |
|||
|
|||
#region Properties
|
|||
|
|||
internal DataGridRowHeader HeaderCell |
|||
{ |
|||
get |
|||
{ |
|||
return _headerElement; |
|||
} |
|||
} |
|||
|
|||
private bool IsCurrent |
|||
{ |
|||
get |
|||
{ |
|||
Debug.Assert(OwningGrid != null); |
|||
return (RowGroupInfo.Slot == OwningGrid.CurrentSlot); |
|||
} |
|||
} |
|||
|
|||
private bool IsMouseOver |
|||
{ |
|||
get; |
|||
set; |
|||
} |
|||
|
|||
internal bool IsRecycled |
|||
{ |
|||
get; |
|||
set; |
|||
} |
|||
|
|||
internal int Level |
|||
{ |
|||
get; |
|||
set; |
|||
} |
|||
|
|||
internal DataGrid OwningGrid |
|||
{ |
|||
get; |
|||
set; |
|||
} |
|||
|
|||
internal DataGridRowGroupInfo RowGroupInfo |
|||
{ |
|||
get; |
|||
set; |
|||
} |
|||
|
|||
internal double TotalIndent |
|||
{ |
|||
set |
|||
{ |
|||
_totalIndent = value; |
|||
if (_indentSpacer != null) |
|||
{ |
|||
_indentSpacer.Width = _totalIndent; |
|||
} |
|||
} |
|||
} |
|||
|
|||
#endregion Properties
|
|||
|
|||
#region Methods
|
|||
|
|||
private IDisposable _expanderButtonSubscription; |
|||
|
|||
protected override void OnTemplateApplied(TemplateAppliedEventArgs e) |
|||
{ |
|||
_rootElement = e.NameScope.Find<Panel>(DataGridRow.DATAGRIDROW_elementRoot); |
|||
|
|||
_expanderButtonSubscription?.Dispose(); |
|||
_expanderButton = e.NameScope.Find<ToggleButton>(DATAGRIDROWGROUPHEADER_expanderButton); |
|||
if(_expanderButton != null) |
|||
{ |
|||
EnsureExpanderButtonIsChecked(); |
|||
_expanderButtonSubscription = |
|||
_expanderButton.GetObservable(ToggleButton.IsCheckedProperty) |
|||
.Skip(1) |
|||
.Subscribe(v => OnExpanderButtonIsCheckedChanged(v)); |
|||
} |
|||
|
|||
_headerElement = e.NameScope.Find<DataGridRowHeader>(DataGridRow.DATAGRIDROW_elementRowHeader); |
|||
if(_headerElement != null) |
|||
{ |
|||
_headerElement.Owner = this; |
|||
EnsureHeaderVisibility(); |
|||
} |
|||
|
|||
_indentSpacer = e.NameScope.Find<Control>(DATAGRIDROWGROUPHEADER_indentSpacer); |
|||
if(_indentSpacer != null) |
|||
{ |
|||
_indentSpacer.Width = _totalIndent; |
|||
} |
|||
|
|||
_itemCountElement = e.NameScope.Find<TextBlock>(DATAGRIDROWGROUPHEADER_itemCountElement); |
|||
_propertyNameElement = e.NameScope.Find<TextBlock>(DATAGRIDROWGROUPHEADER_propertyNameElement); |
|||
UpdateTitleElements(); |
|||
|
|||
base.OnTemplateApplied(e); |
|||
} |
|||
|
|||
internal void ApplyHeaderStatus() |
|||
{ |
|||
if (_headerElement != null && OwningGrid.AreRowHeadersVisible) |
|||
{ |
|||
_headerElement.ApplyOwnerStatus(); |
|||
} |
|||
} |
|||
|
|||
//TODO Implement
|
|||
internal void ApplyState(bool useTransitions) |
|||
{ |
|||
/* |
|||
// Common States
|
|||
if (IsMouseOver) |
|||
{ |
|||
VisualStates.GoToState(this, useTransitions, VisualStates.StateMouseOver, VisualStates.StateNormal); |
|||
} |
|||
else |
|||
{ |
|||
VisualStates.GoToState(this, useTransitions, VisualStates.StateNormal); |
|||
} |
|||
|
|||
// Current States
|
|||
if (IsCurrent) |
|||
{ |
|||
VisualStates.GoToState(this, useTransitions, VisualStates.StateCurrent, VisualStates.StateRegular); |
|||
} |
|||
else |
|||
{ |
|||
VisualStates.GoToState(this, useTransitions, VisualStates.StateRegular); |
|||
} |
|||
|
|||
// Expanded States
|
|||
if (RowGroupInfo.CollectionViewGroup.ItemCount == 0) |
|||
{ |
|||
VisualStates.GoToState(this, useTransitions, VisualStates.StateEmpty); |
|||
} |
|||
else |
|||
{ |
|||
if (RowGroupInfo.Visibility == Visibility.Visible) |
|||
{ |
|||
VisualStates.GoToState(this, useTransitions, VisualStates.StateExpanded, VisualStates.StateEmpty); |
|||
} |
|||
else |
|||
{ |
|||
VisualStates.GoToState(this, useTransitions, VisualStates.StateCollapsed, VisualStates.StateEmpty); |
|||
} |
|||
}*/ |
|||
} |
|||
|
|||
protected override Size ArrangeOverride(Size finalSize) |
|||
{ |
|||
if (OwningGrid == null) |
|||
{ |
|||
return base.ArrangeOverride(finalSize); |
|||
} |
|||
|
|||
Size size = base.ArrangeOverride(finalSize); |
|||
if (_rootElement != null) |
|||
{ |
|||
if (OwningGrid.AreRowGroupHeadersFrozen) |
|||
{ |
|||
foreach (Control child in _rootElement.Children) |
|||
{ |
|||
child.Clip = null; |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
double frozenLeftEdge = 0; |
|||
foreach (Control child in _rootElement.Children) |
|||
{ |
|||
if (DataGridFrozenGrid.GetIsFrozen(child) && child.IsVisible) |
|||
{ |
|||
TranslateTransform transform = new TranslateTransform(); |
|||
// Automatic layout rounding doesn't apply to transforms so we need to Round this
|
|||
transform.X = Math.Round(OwningGrid.HorizontalOffset); |
|||
child.RenderTransform = transform; |
|||
|
|||
double childLeftEdge = child.Translate(this, new Point(child.Bounds.Width, 0)).X - transform.X; |
|||
frozenLeftEdge = Math.Max(frozenLeftEdge, childLeftEdge + OwningGrid.HorizontalOffset); |
|||
} |
|||
} |
|||
// Clip the non-frozen elements so they don't overlap the frozen ones
|
|||
foreach (Control child in _rootElement.Children) |
|||
{ |
|||
if (!DataGridFrozenGrid.GetIsFrozen(child)) |
|||
{ |
|||
EnsureChildClip(child, frozenLeftEdge); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
return size; |
|||
} |
|||
|
|||
internal void ClearFrozenStates() |
|||
{ |
|||
if (_rootElement != null) |
|||
{ |
|||
foreach (Control child in _rootElement.Children) |
|||
{ |
|||
child.RenderTransform = null; |
|||
} |
|||
} |
|||
} |
|||
|
|||
//TODO TabStop
|
|||
private void DataGridRowGroupHeader_PointerPressed(PointerPressedEventArgs e) |
|||
{ |
|||
if (OwningGrid != null && e.MouseButton == MouseButton.Left) |
|||
{ |
|||
if (OwningGrid.IsDoubleClickRecordsClickOnCall(this) && !e.Handled) |
|||
{ |
|||
ToggleExpandCollapse(!RowGroupInfo.IsVisible, true); |
|||
e.Handled = true; |
|||
} |
|||
else |
|||
{ |
|||
//if (!e.Handled && OwningGrid.IsTabStop)
|
|||
if (!e.Handled) |
|||
{ |
|||
OwningGrid.Focus(); |
|||
} |
|||
e.Handled = OwningGrid.UpdateStateOnMouseLeftButtonDown(e, OwningGrid.CurrentColumnIndex, RowGroupInfo.Slot, allowEdit: false); |
|||
} |
|||
} |
|||
} |
|||
|
|||
private void EnsureChildClip(Visual child, double frozenLeftEdge) |
|||
{ |
|||
double childLeftEdge = child.Translate(this, new Point(0, 0)).X; |
|||
if (frozenLeftEdge > childLeftEdge) |
|||
{ |
|||
double xClip = Math.Round(frozenLeftEdge - childLeftEdge); |
|||
var rg = new RectangleGeometry(); |
|||
rg.Rect = |
|||
new Rect(xClip, 0, |
|||
Math.Max(0, child.Bounds.Width - xClip), |
|||
child.Bounds.Height); |
|||
child.Clip = rg; |
|||
} |
|||
else |
|||
{ |
|||
child.Clip = null; |
|||
} |
|||
} |
|||
|
|||
internal void EnsureExpanderButtonIsChecked() |
|||
{ |
|||
if (_expanderButton != null && RowGroupInfo != null && RowGroupInfo.CollectionViewGroup != null && |
|||
RowGroupInfo.CollectionViewGroup.ItemCount != 0) |
|||
{ |
|||
SetIsCheckedNoCallBack(RowGroupInfo.IsVisible); |
|||
} |
|||
} |
|||
|
|||
//TODO Styles
|
|||
//internal void EnsureHeaderStyleAndVisibility(Style previousStyle)
|
|||
internal void EnsureHeaderVisibility() |
|||
{ |
|||
if (_headerElement != null && OwningGrid != null) |
|||
{ |
|||
_headerElement.IsVisible = OwningGrid.AreColumnHeadersVisible; |
|||
//if (OwningGrid.AreRowHeadersVisible)
|
|||
//{
|
|||
// _headerElement.EnsureStyle(previousStyle);
|
|||
// ;
|
|||
//}
|
|||
//else
|
|||
//{
|
|||
// _headerElement.IsVisible = false;
|
|||
//}
|
|||
} |
|||
} |
|||
|
|||
private void OnExpanderButtonIsCheckedChanged(bool? value) |
|||
{ |
|||
if(!_areIsCheckedHandlersSuspended) |
|||
{ |
|||
ToggleExpandCollapse(value ?? false, true); |
|||
} |
|||
} |
|||
|
|||
internal void LoadVisualsForDisplay() |
|||
{ |
|||
EnsureExpanderButtonIsChecked(); |
|||
|
|||
//EnsureHeaderStyleAndVisibility(null);
|
|||
EnsureHeaderVisibility(); |
|||
ApplyState(useTransitions: false); |
|||
ApplyHeaderStatus(); |
|||
} |
|||
|
|||
protected override void OnPointerEnter(PointerEventArgs e) |
|||
{ |
|||
if (IsEnabled) |
|||
{ |
|||
IsMouseOver = true; |
|||
ApplyState(useTransitions: true); |
|||
} |
|||
|
|||
base.OnPointerEnter(e); |
|||
} |
|||
|
|||
protected override void OnPointerLeave(PointerEventArgs e) |
|||
{ |
|||
if (IsEnabled) |
|||
{ |
|||
IsMouseOver = false; |
|||
ApplyState(useTransitions: true); |
|||
} |
|||
|
|||
base.OnPointerLeave(e); |
|||
} |
|||
|
|||
private void SetIsCheckedNoCallBack(bool value) |
|||
{ |
|||
if (_expanderButton != null && _expanderButton.IsChecked != value) |
|||
{ |
|||
_areIsCheckedHandlersSuspended = true; |
|||
try |
|||
{ |
|||
_expanderButton.IsChecked = value; |
|||
} |
|||
finally |
|||
{ |
|||
_areIsCheckedHandlersSuspended = false; |
|||
} |
|||
} |
|||
} |
|||
|
|||
internal void ToggleExpandCollapse(bool isVisible, bool setCurrent) |
|||
{ |
|||
if (RowGroupInfo.CollectionViewGroup.ItemCount != 0) |
|||
{ |
|||
if (OwningGrid == null) |
|||
{ |
|||
// Do these even if the OwningGrid is null in case it could improve the Designer experience for a standalone DataGridRowGroupHeader
|
|||
RowGroupInfo.IsVisible = isVisible; |
|||
} |
|||
else |
|||
{ |
|||
OwningGrid.OnRowGroupHeaderToggled(this, isVisible, setCurrent); |
|||
} |
|||
|
|||
EnsureExpanderButtonIsChecked(); |
|||
|
|||
ApplyState(true /*useTransitions*/); |
|||
} |
|||
} |
|||
|
|||
internal void UpdateTitleElements() |
|||
{ |
|||
if (_propertyNameElement != null) |
|||
{ |
|||
string txt; |
|||
if (string.IsNullOrWhiteSpace(PropertyName)) |
|||
txt = String.Empty; |
|||
else |
|||
txt = String.Format("{0}:", PropertyName); |
|||
_propertyNameElement.Text = txt; |
|||
} |
|||
if (_itemCountElement != null && RowGroupInfo != null && RowGroupInfo.CollectionViewGroup != null) |
|||
{ |
|||
string formatString; |
|||
if(RowGroupInfo.CollectionViewGroup.ItemCount == 1) |
|||
formatString = "({0} Item)"; |
|||
else |
|||
formatString = "({0} Items)"; |
|||
|
|||
|
|||
_itemCountElement.Text = String.Format(formatString, RowGroupInfo.CollectionViewGroup.ItemCount); |
|||
} |
|||
} |
|||
|
|||
#endregion Methods
|
|||
} |
|||
|
|||
/* |
|||
|
|||
#region HeaderStyle
|
|||
//TODO Styles
|
|||
|
|||
/// <summary>
|
|||
/// The style applied to the header cell of a <see cref="T:Avalonia.Controls.DataGridRowGroupHeader" />.
|
|||
/// </summary>
|
|||
public Style HeaderStyle |
|||
{ |
|||
get { return GetValue(HeaderStyleProperty) as Style; } |
|||
set { SetValue(HeaderStyleProperty, value); } |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Dependency Property for HeaderStyle
|
|||
/// </summary>
|
|||
public static readonly DependencyProperty HeaderStyleProperty = |
|||
DependencyProperty.Register( |
|||
"HeaderStyle", |
|||
typeof(Style), |
|||
typeof(DataGridRowGroupHeader), |
|||
new PropertyMetadata(OnHeaderStylePropertyChanged)); |
|||
|
|||
private static void OnHeaderStylePropertyChanged(AvaloniaObject d, DependencyPropertyChangedEventArgs e) |
|||
{ |
|||
DataGridRowGroupHeader groupHeader = d as DataGridRowGroupHeader; |
|||
if (groupHeader._headerElement != null) |
|||
{ |
|||
groupHeader._headerElement.EnsureStyle(e.OldValue as Style); |
|||
} |
|||
} |
|||
#endregion HeaderStyle
|
|||
*/ |
|||
} |
|||
@ -0,0 +1,57 @@ |
|||
// (c) Copyright Microsoft Corporation.
|
|||
// This source is subject to the Microsoft Public License (Ms-PL).
|
|||
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
|
|||
// All other rights reserved.
|
|||
|
|||
using Avalonia.Collections; |
|||
|
|||
namespace Avalonia.Controls |
|||
{ |
|||
|
|||
internal class DataGridRowGroupInfo |
|||
{ |
|||
public DataGridRowGroupInfo( |
|||
CollectionViewGroup collectionViewGroup, |
|||
bool isVisible, |
|||
int level, |
|||
int slot, |
|||
int lastSubItemSlot) |
|||
{ |
|||
CollectionViewGroup = collectionViewGroup; |
|||
IsVisible = isVisible; |
|||
Level = level; |
|||
Slot = slot; |
|||
LastSubItemSlot = lastSubItemSlot; |
|||
} |
|||
|
|||
public CollectionViewGroup CollectionViewGroup |
|||
{ |
|||
get; |
|||
private set; |
|||
} |
|||
|
|||
public int LastSubItemSlot |
|||
{ |
|||
get; |
|||
set; |
|||
} |
|||
|
|||
public int Level |
|||
{ |
|||
get; |
|||
private set; |
|||
} |
|||
|
|||
public int Slot |
|||
{ |
|||
get; |
|||
set; |
|||
} |
|||
|
|||
public bool IsVisible |
|||
{ |
|||
get; |
|||
set; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,502 @@ |
|||
// (c) Copyright Microsoft Corporation.
|
|||
// This source is subject to the Microsoft Public License (Ms-PL).
|
|||
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
|
|||
// All other rights reserved.
|
|||
|
|||
using Avalonia.Input; |
|||
using Avalonia.Media; |
|||
using System.Diagnostics; |
|||
|
|||
namespace Avalonia.Controls.Primitives |
|||
{ |
|||
/// <summary>
|
|||
/// Represents an individual <see cref="T:Avalonia.Controls.DataGrid" /> row header.
|
|||
/// </summary>
|
|||
public class DataGridRowHeader : ContentControl |
|||
{ |
|||
private const string DATAGRIDROWHEADER_elementRootName = "Root"; |
|||
private const double DATAGRIDROWHEADER_separatorThickness = 1; |
|||
|
|||
#region Data
|
|||
|
|||
private Control _rootElement; |
|||
|
|||
#endregion Data
|
|||
|
|||
#region Dependency Properties
|
|||
|
|||
|
|||
public static readonly StyledProperty<IBrush> SeparatorBrushProperty = |
|||
AvaloniaProperty.Register<DataGridRowHeader, IBrush>(nameof(SeparatorBrush)); |
|||
|
|||
public IBrush SeparatorBrush |
|||
{ |
|||
get { return GetValue(SeparatorBrushProperty); } |
|||
set { SetValue(SeparatorBrushProperty, value); } |
|||
} |
|||
|
|||
|
|||
public static readonly StyledProperty<bool> AreSeparatorsVisibleProperty = |
|||
AvaloniaProperty.Register<DataGridRowHeader, bool>( |
|||
nameof(AreSeparatorsVisible)); |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets a value indicating whether the row header separator lines are visible.
|
|||
/// </summary>
|
|||
public bool AreSeparatorsVisible |
|||
{ |
|||
get { return GetValue(AreSeparatorsVisibleProperty); } |
|||
set { SetValue(AreSeparatorsVisibleProperty, value); } |
|||
} |
|||
|
|||
#endregion
|
|||
|
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="T:Avalonia.Controls.Primitives.DataGridRowHeader" /> class.
|
|||
/// </summary>
|
|||
public DataGridRowHeader() |
|||
{ |
|||
AddHandler(PointerPressedEvent, DataGridRowHeader_PointerPressed, handledEventsToo: true); |
|||
} |
|||
|
|||
#region Properties
|
|||
|
|||
|
|||
internal Control Owner |
|||
{ |
|||
get; |
|||
set; |
|||
} |
|||
|
|||
private DataGridRow OwningRow => Owner as DataGridRow; |
|||
|
|||
private DataGridRowGroupHeader OwningRowGroupHeader => Owner as DataGridRowGroupHeader; |
|||
|
|||
private DataGrid OwningGrid |
|||
{ |
|||
get |
|||
{ |
|||
if (OwningRow != null) |
|||
{ |
|||
return OwningRow.OwningGrid; |
|||
} |
|||
else if (OwningRowGroupHeader != null) |
|||
{ |
|||
return OwningRowGroupHeader.OwningGrid; |
|||
} |
|||
return null; |
|||
} |
|||
} |
|||
|
|||
private int Slot |
|||
{ |
|||
get |
|||
{ |
|||
if (OwningRow != null) |
|||
{ |
|||
return OwningRow.Slot; |
|||
} |
|||
else if (OwningRowGroupHeader != null) |
|||
{ |
|||
return OwningRowGroupHeader.RowGroupInfo.Slot; |
|||
} |
|||
return -1; |
|||
} |
|||
} |
|||
|
|||
#endregion
|
|||
|
|||
#region Protected Methods
|
|||
|
|||
/// <summary>
|
|||
/// Builds the visual tree for the row header when a new template is applied.
|
|||
/// </summary>
|
|||
protected override void OnTemplateApplied(TemplateAppliedEventArgs e) |
|||
{ |
|||
base.OnTemplateApplied(e); |
|||
|
|||
_rootElement = e.NameScope.Find<Control>(DATAGRIDROWHEADER_elementRootName); |
|||
if (_rootElement != null) |
|||
{ |
|||
ApplyOwnerStatus(); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Measures the children of a <see cref="T:Avalonia.Controls.Primitives.DataGridRowHeader" /> to prepare for arranging them during the <see cref="M:System.Windows.FrameworkElement.ArrangeOverride(System.Windows.Size)" /> pass.
|
|||
/// </summary>
|
|||
/// <param name="availableSize">
|
|||
/// The available size that this element can give to child elements. Indicates an upper limit that child elements should not exceed.
|
|||
/// </param>
|
|||
/// <returns>
|
|||
/// The size that the <see cref="T:Avalonia.Controls.Primitives.DataGridRowHeader" /> determines it needs during layout, based on its calculations of child object allocated sizes.
|
|||
/// </returns>
|
|||
protected override Size MeasureOverride(Size availableSize) |
|||
{ |
|||
if (OwningRow == null || OwningGrid == null) |
|||
{ |
|||
return base.MeasureOverride(availableSize); |
|||
} |
|||
double measureHeight = double.IsNaN(OwningGrid.RowHeight) ? availableSize.Height : OwningGrid.RowHeight; |
|||
double measureWidth = double.IsNaN(OwningGrid.RowHeaderWidth) ? availableSize.Width : OwningGrid.RowHeaderWidth; |
|||
Size measuredSize = base.MeasureOverride(new Size(measureWidth, measureHeight)); |
|||
|
|||
// Auto grow the row header or force it to a fixed width based on the DataGrid's setting
|
|||
if (!double.IsNaN(OwningGrid.RowHeaderWidth) || measuredSize.Width < OwningGrid.ActualRowHeaderWidth) |
|||
{ |
|||
return new Size(OwningGrid.ActualRowHeaderWidth, measuredSize.Height); |
|||
} |
|||
|
|||
return measuredSize; |
|||
} |
|||
|
|||
#endregion Protected Methods
|
|||
|
|||
|
|||
#region Internal Methods
|
|||
|
|||
//TODO Implement
|
|||
internal void ApplyOwnerStatus() |
|||
{ |
|||
if (_rootElement != null && Owner != null && Owner.IsVisible) |
|||
{ |
|||
//byte idealStateMappingIndex = 0;
|
|||
|
|||
//if (OwningRow != null)
|
|||
//{
|
|||
// if (OwningRow.IsValid)
|
|||
// {
|
|||
// VisualStates.GoToState(this, true, VisualStates.StateRowValid);
|
|||
// }
|
|||
// else
|
|||
// {
|
|||
// VisualStates.GoToState(this, true, VisualStates.StateRowInvalid, VisualStates.StateRowValid);
|
|||
// }
|
|||
|
|||
// if (OwningGrid != null)
|
|||
// {
|
|||
// if (OwningGrid.CurrentSlot == OwningRow.Slot)
|
|||
// {
|
|||
// idealStateMappingIndex += 16;
|
|||
// }
|
|||
// if (OwningGrid.ContainsFocus)
|
|||
// {
|
|||
// idealStateMappingIndex += 1;
|
|||
// }
|
|||
// }
|
|||
// if (OwningRow.IsSelected || OwningRow.IsEditing)
|
|||
// {
|
|||
// idealStateMappingIndex += 8;
|
|||
// }
|
|||
// if (OwningRow.IsEditing)
|
|||
// {
|
|||
// idealStateMappingIndex += 4;
|
|||
// }
|
|||
// if (OwningRow.IsMouseOver)
|
|||
// {
|
|||
// idealStateMappingIndex += 2;
|
|||
// }
|
|||
//}
|
|||
//else if (OwningRowGroupHeader != null && OwningGrid != null && OwningGrid.CurrentSlot == OwningRowGroupHeader.RowGroupInfo.Slot)
|
|||
//{
|
|||
// idealStateMappingIndex += 16;
|
|||
//}
|
|||
|
|||
//byte stateCode = _idealStateMapping[idealStateMappingIndex];
|
|||
//Debug.Assert(stateCode != DATAGRIDROWHEADER_stateNullCode);
|
|||
|
|||
//string storyboardName;
|
|||
//while (stateCode != DATAGRIDROWHEADER_stateNullCode)
|
|||
//{
|
|||
// storyboardName = _stateNames[stateCode];
|
|||
// if (VisualStateManager.GoToState(this, storyboardName, animate) || VisualStateManager.GoToState(this, _legacyStateNames[stateCode], animate))
|
|||
// {
|
|||
// break;
|
|||
// }
|
|||
// else
|
|||
// {
|
|||
// // The state wasn't implemented so fall back to the next one
|
|||
// stateCode = _fallbackStateMapping[stateCode];
|
|||
// }
|
|||
//}
|
|||
} |
|||
} |
|||
|
|||
#endregion Internal Methods
|
|||
|
|||
|
|||
#region Private Methods
|
|||
|
|||
protected override void OnPointerEnter(PointerEventArgs e) |
|||
{ |
|||
if (OwningRow != null) |
|||
{ |
|||
OwningRow.IsMouseOver = true; |
|||
} |
|||
|
|||
base.OnPointerEnter(e); |
|||
} |
|||
protected override void OnPointerLeave(PointerEventArgs e) |
|||
{ |
|||
if (OwningRow != null) |
|||
{ |
|||
OwningRow.IsMouseOver = false; |
|||
} |
|||
|
|||
base.OnPointerLeave(e); |
|||
} |
|||
|
|||
//TODO TabStop
|
|||
private void DataGridRowHeader_PointerPressed(object sender, PointerPressedEventArgs e) |
|||
{ |
|||
if(e.MouseButton != MouseButton.Left) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
if (OwningGrid != null) |
|||
{ |
|||
if (!e.Handled) |
|||
//if (!e.Handled && OwningGrid.IsTabStop)
|
|||
{ |
|||
OwningGrid.Focus(); |
|||
} |
|||
if (OwningRow != null) |
|||
{ |
|||
Debug.Assert(sender is DataGridRowHeader); |
|||
Debug.Assert(sender == this); |
|||
e.Handled = OwningGrid.UpdateStateOnMouseLeftButtonDown(e, -1, Slot, false); |
|||
OwningGrid.UpdatedStateOnMouseLeftButtonDown = true; |
|||
} |
|||
} |
|||
} |
|||
|
|||
#endregion Private Methods
|
|||
|
|||
} |
|||
|
|||
/* |
|||
[TemplatePart(Name = DATAGRIDROWHEADER_elementRootName, Type = typeof(FrameworkElement))] |
|||
|
|||
[TemplateVisualState(Name = DATAGRIDROWHEADER_stateNormal, GroupName = VisualStates.GroupCommon)] |
|||
[TemplateVisualState(Name = DATAGRIDROWHEADER_stateNormalCurrentRow, GroupName = VisualStates.GroupCommon)] |
|||
[TemplateVisualState(Name = DATAGRIDROWHEADER_stateNormalEditingRow, GroupName = VisualStates.GroupCommon)] |
|||
[TemplateVisualState(Name = DATAGRIDROWHEADER_stateNormalEditingRowFocused, GroupName = VisualStates.GroupCommon)] |
|||
|
|||
[TemplateVisualState(Name = DATAGRIDROWHEADER_stateMouseOver, GroupName = VisualStates.GroupCommon)] |
|||
[TemplateVisualState(Name = DATAGRIDROWHEADER_stateMouseOverCurrentRow, GroupName = VisualStates.GroupCommon)] |
|||
[TemplateVisualState(Name = DATAGRIDROWHEADER_stateMouseOverEditingRow, GroupName = VisualStates.GroupCommon)] |
|||
[TemplateVisualState(Name = DATAGRIDROWHEADER_stateMouseOverEditingRowFocused, GroupName = VisualStates.GroupCommon)] |
|||
[TemplateVisualState(Name = DATAGRIDROWHEADER_stateMouseOverSelected, GroupName = VisualStates.GroupCommon)] |
|||
[TemplateVisualState(Name = DATAGRIDROWHEADER_stateMouseOverSelectedFocused, GroupName = VisualStates.GroupCommon)] |
|||
[TemplateVisualState(Name = DATAGRIDROWHEADER_stateMouseOverSelectedCurrentRow, GroupName = VisualStates.GroupCommon)] |
|||
[TemplateVisualState(Name = DATAGRIDROWHEADER_stateMouseOverSelectedCurrentRowFocused, GroupName = VisualStates.GroupCommon)] |
|||
|
|||
[TemplateVisualState(Name = DATAGRIDROWHEADER_stateSelected, GroupName = VisualStates.GroupCommon)] |
|||
[TemplateVisualState(Name = DATAGRIDROWHEADER_stateSelectedCurrentRow, GroupName = VisualStates.GroupCommon)] |
|||
[TemplateVisualState(Name = DATAGRIDROWHEADER_stateSelectedCurrentRowFocused, GroupName = VisualStates.GroupCommon)] |
|||
[TemplateVisualState(Name = DATAGRIDROWHEADER_stateSelectedFocused, GroupName = VisualStates.GroupCommon)] |
|||
|
|||
[TemplateVisualState(Name = VisualStates.StateRowInvalid, GroupName = VisualStates.GroupValidation)] |
|||
[TemplateVisualState(Name = VisualStates.StateRowValid, GroupName = VisualStates.GroupValidation)] |
|||
public partial class DataGridRowHeader : ContentControl |
|||
*/ |
|||
|
|||
#region Styles
|
|||
|
|||
//TODO Styles
|
|||
|
|||
/// <summary>
|
|||
/// Ensures that the correct Style is applied to this object.
|
|||
/// </summary>
|
|||
/// <param name="previousStyle">Caller's previous associated Style</param>
|
|||
/*internal void EnsureStyle(Style previousStyle) |
|||
{ |
|||
if (Style != null |
|||
&& (OwningRow != null && Style != OwningRow.HeaderStyle) |
|||
&& (OwningRowGroupHeader != null && Style != OwningRowGroupHeader.HeaderStyle) |
|||
&& (OwningGrid != null && Style != OwningGrid.RowHeaderStyle) |
|||
&& (Style != previousStyle)) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
Style style = null; |
|||
if (OwningRow != null) |
|||
{ |
|||
style = OwningRow.HeaderStyle; |
|||
} |
|||
if (style == null && OwningGrid != null) |
|||
{ |
|||
style = OwningGrid.RowHeaderStyle; |
|||
} |
|||
SetStyleWithType(style); |
|||
} */ |
|||
|
|||
#endregion
|
|||
|
|||
#region Constants
|
|||
/* |
|||
private const string DATAGRIDROWHEADER_stateMouseOver = "MouseOver"; |
|||
private const string DATAGRIDROWHEADER_stateMouseOverCurrentRow = "MouseOverCurrentRow"; |
|||
private const string DATAGRIDROWHEADER_stateMouseOverCurrentRowLegacy = "MouseOver CurrentRow"; |
|||
private const string DATAGRIDROWHEADER_stateMouseOverEditingRow = "MouseOverUnfocusedEditingRow"; |
|||
private const string DATAGRIDROWHEADER_stateMouseOverEditingRowLegacy = "MouseOver Unfocused EditingRow"; |
|||
private const string DATAGRIDROWHEADER_stateMouseOverEditingRowFocused = "MouseOverEditingRow"; |
|||
private const string DATAGRIDROWHEADER_stateMouseOverEditingRowFocusedLegacy = "MouseOver EditingRow"; |
|||
private const string DATAGRIDROWHEADER_stateMouseOverSelected = "MouseOverUnfocusedSelected"; |
|||
private const string DATAGRIDROWHEADER_stateMouseOverSelectedLegacy = "MouseOver Unfocused Selected"; |
|||
private const string DATAGRIDROWHEADER_stateMouseOverSelectedCurrentRow = "MouseOverUnfocusedCurrentRowSelected"; |
|||
private const string DATAGRIDROWHEADER_stateMouseOverSelectedCurrentRowLegacy = "MouseOver Unfocused CurrentRow Selected"; |
|||
private const string DATAGRIDROWHEADER_stateMouseOverSelectedCurrentRowFocused = "MouseOverCurrentRowSelected"; |
|||
private const string DATAGRIDROWHEADER_stateMouseOverSelectedCurrentRowFocusedLegacy = "MouseOver CurrentRow Selected"; |
|||
private const string DATAGRIDROWHEADER_stateMouseOverSelectedFocused = "MouseOverSelected"; |
|||
private const string DATAGRIDROWHEADER_stateMouseOverSelectedFocusedLegacy = "MouseOver Selected"; |
|||
private const string DATAGRIDROWHEADER_stateNormal = "Normal"; |
|||
private const string DATAGRIDROWHEADER_stateNormalCurrentRow = "NormalCurrentRow"; |
|||
private const string DATAGRIDROWHEADER_stateNormalCurrentRowLegacy = "Normal CurrentRow"; |
|||
private const string DATAGRIDROWHEADER_stateNormalEditingRow = "UnfocusedEditingRow"; |
|||
private const string DATAGRIDROWHEADER_stateNormalEditingRowLegacy = "Unfocused EditingRow"; |
|||
private const string DATAGRIDROWHEADER_stateNormalEditingRowFocusedLegacy = "NormalEditingRow"; |
|||
private const string DATAGRIDROWHEADER_stateNormalEditingRowFocused = "Normal EditingRow"; |
|||
private const string DATAGRIDROWHEADER_stateSelected = "UnfocusedSelected"; |
|||
private const string DATAGRIDROWHEADER_stateSelectedLegacy = "Unfocused Selected"; |
|||
private const string DATAGRIDROWHEADER_stateSelectedCurrentRow = "UnfocusedCurrentRowSelected"; |
|||
private const string DATAGRIDROWHEADER_stateSelectedCurrentRowLegacy = "Unfocused CurrentRow Selected"; |
|||
private const string DATAGRIDROWHEADER_stateSelectedCurrentRowFocused = "NormalCurrentRowSelected"; |
|||
private const string DATAGRIDROWHEADER_stateSelectedCurrentRowFocusedLegacy = "Normal CurrentRow Selected"; |
|||
private const string DATAGRIDROWHEADER_stateSelectedFocused = "NormalSelected"; |
|||
private const string DATAGRIDROWHEADER_stateSelectedFocusedLegacy = "Normal Selected"; |
|||
|
|||
private const byte DATAGRIDROWHEADER_stateMouseOverCode = 0; |
|||
private const byte DATAGRIDROWHEADER_stateMouseOverCurrentRowCode = 1; |
|||
private const byte DATAGRIDROWHEADER_stateMouseOverEditingRowCode = 2; |
|||
private const byte DATAGRIDROWHEADER_stateMouseOverEditingRowFocusedCode = 3; |
|||
private const byte DATAGRIDROWHEADER_stateMouseOverSelectedCode = 4; |
|||
private const byte DATAGRIDROWHEADER_stateMouseOverSelectedCurrentRowCode = 5; |
|||
private const byte DATAGRIDROWHEADER_stateMouseOverSelectedCurrentRowFocusedCode = 6; |
|||
private const byte DATAGRIDROWHEADER_stateMouseOverSelectedFocusedCode = 7; |
|||
private const byte DATAGRIDROWHEADER_stateNormalCode = 8; |
|||
private const byte DATAGRIDROWHEADER_stateNormalCurrentRowCode = 9; |
|||
private const byte DATAGRIDROWHEADER_stateNormalEditingRowCode = 10; |
|||
private const byte DATAGRIDROWHEADER_stateNormalEditingRowFocusedCode = 11; |
|||
private const byte DATAGRIDROWHEADER_stateSelectedCode = 12; |
|||
private const byte DATAGRIDROWHEADER_stateSelectedCurrentRowCode = 13; |
|||
private const byte DATAGRIDROWHEADER_stateSelectedCurrentRowFocusedCode = 14; |
|||
private const byte DATAGRIDROWHEADER_stateSelectedFocusedCode = 15; |
|||
private const byte DATAGRIDROWHEADER_stateNullCode = 255; |
|||
*/ |
|||
|
|||
/*private static byte[] _fallbackStateMapping = new byte[] { |
|||
DATAGRIDROWHEADER_stateNormalCode, |
|||
DATAGRIDROWHEADER_stateNormalCurrentRowCode, |
|||
DATAGRIDROWHEADER_stateMouseOverEditingRowFocusedCode, |
|||
DATAGRIDROWHEADER_stateNormalEditingRowFocusedCode, |
|||
DATAGRIDROWHEADER_stateMouseOverSelectedFocusedCode, |
|||
DATAGRIDROWHEADER_stateMouseOverSelectedCurrentRowFocusedCode, |
|||
DATAGRIDROWHEADER_stateSelectedFocusedCode, |
|||
DATAGRIDROWHEADER_stateSelectedFocusedCode, |
|||
DATAGRIDROWHEADER_stateNullCode, |
|||
DATAGRIDROWHEADER_stateNormalCode, |
|||
DATAGRIDROWHEADER_stateNormalEditingRowFocusedCode, |
|||
DATAGRIDROWHEADER_stateSelectedCurrentRowFocusedCode, |
|||
DATAGRIDROWHEADER_stateSelectedFocusedCode, |
|||
DATAGRIDROWHEADER_stateSelectedCurrentRowFocusedCode, |
|||
DATAGRIDROWHEADER_stateNormalCurrentRowCode, |
|||
DATAGRIDROWHEADER_stateNormalCode, |
|||
}; */ |
|||
|
|||
/*private static byte[] _idealStateMapping = new byte[] { |
|||
DATAGRIDROWHEADER_stateNormalCode, |
|||
DATAGRIDROWHEADER_stateNormalCode, |
|||
DATAGRIDROWHEADER_stateMouseOverCode, |
|||
DATAGRIDROWHEADER_stateMouseOverCode, |
|||
DATAGRIDROWHEADER_stateNullCode, |
|||
DATAGRIDROWHEADER_stateNullCode, |
|||
DATAGRIDROWHEADER_stateNullCode, |
|||
DATAGRIDROWHEADER_stateNullCode, |
|||
DATAGRIDROWHEADER_stateSelectedCode, |
|||
DATAGRIDROWHEADER_stateSelectedFocusedCode, |
|||
DATAGRIDROWHEADER_stateMouseOverSelectedCode, |
|||
DATAGRIDROWHEADER_stateMouseOverSelectedFocusedCode, |
|||
DATAGRIDROWHEADER_stateNormalEditingRowCode, |
|||
DATAGRIDROWHEADER_stateNormalEditingRowFocusedCode, |
|||
DATAGRIDROWHEADER_stateMouseOverEditingRowCode, |
|||
DATAGRIDROWHEADER_stateMouseOverEditingRowFocusedCode, |
|||
DATAGRIDROWHEADER_stateNormalCurrentRowCode, |
|||
DATAGRIDROWHEADER_stateNormalCurrentRowCode, |
|||
DATAGRIDROWHEADER_stateMouseOverCurrentRowCode, |
|||
DATAGRIDROWHEADER_stateMouseOverCurrentRowCode, |
|||
DATAGRIDROWHEADER_stateNullCode, |
|||
DATAGRIDROWHEADER_stateNullCode, |
|||
DATAGRIDROWHEADER_stateNullCode, |
|||
DATAGRIDROWHEADER_stateNullCode, |
|||
DATAGRIDROWHEADER_stateSelectedCurrentRowCode, |
|||
DATAGRIDROWHEADER_stateSelectedCurrentRowFocusedCode, |
|||
DATAGRIDROWHEADER_stateMouseOverSelectedCurrentRowCode, |
|||
DATAGRIDROWHEADER_stateMouseOverSelectedCurrentRowFocusedCode, |
|||
DATAGRIDROWHEADER_stateNormalEditingRowCode, |
|||
DATAGRIDROWHEADER_stateNormalEditingRowFocusedCode, |
|||
DATAGRIDROWHEADER_stateMouseOverEditingRowCode, |
|||
DATAGRIDROWHEADER_stateMouseOverEditingRowFocusedCode |
|||
};*/ |
|||
|
|||
// In SL 2, our state names had spaces. Going forward, we are removing the spaces but still
|
|||
// supporting the legacy state names
|
|||
/*private static string[] _legacyStateNames = new string[] |
|||
{ |
|||
DATAGRIDROWHEADER_stateMouseOver, |
|||
DATAGRIDROWHEADER_stateMouseOverCurrentRowLegacy, |
|||
DATAGRIDROWHEADER_stateMouseOverEditingRowLegacy, |
|||
DATAGRIDROWHEADER_stateMouseOverEditingRowFocusedLegacy, |
|||
DATAGRIDROWHEADER_stateMouseOverSelectedLegacy, |
|||
DATAGRIDROWHEADER_stateMouseOverSelectedCurrentRowLegacy, |
|||
DATAGRIDROWHEADER_stateMouseOverSelectedCurrentRowFocusedLegacy, |
|||
DATAGRIDROWHEADER_stateMouseOverSelectedFocusedLegacy, |
|||
DATAGRIDROWHEADER_stateNormal, |
|||
DATAGRIDROWHEADER_stateNormalCurrentRowLegacy, |
|||
DATAGRIDROWHEADER_stateNormalEditingRowLegacy, |
|||
DATAGRIDROWHEADER_stateNormalEditingRowFocusedLegacy, |
|||
DATAGRIDROWHEADER_stateSelectedLegacy, |
|||
DATAGRIDROWHEADER_stateSelectedCurrentRowLegacy, |
|||
DATAGRIDROWHEADER_stateSelectedCurrentRowFocusedLegacy, |
|||
DATAGRIDROWHEADER_stateSelectedFocusedLegacy |
|||
}; */ |
|||
|
|||
/*private static string[] _stateNames = new string[] |
|||
{ |
|||
DATAGRIDROWHEADER_stateMouseOver, |
|||
DATAGRIDROWHEADER_stateMouseOverCurrentRow, |
|||
DATAGRIDROWHEADER_stateMouseOverEditingRow, |
|||
DATAGRIDROWHEADER_stateMouseOverEditingRowFocused, |
|||
DATAGRIDROWHEADER_stateMouseOverSelected, |
|||
DATAGRIDROWHEADER_stateMouseOverSelectedCurrentRow, |
|||
DATAGRIDROWHEADER_stateMouseOverSelectedCurrentRowFocused, |
|||
DATAGRIDROWHEADER_stateMouseOverSelectedFocused, |
|||
DATAGRIDROWHEADER_stateNormal, |
|||
DATAGRIDROWHEADER_stateNormalCurrentRow, |
|||
DATAGRIDROWHEADER_stateNormalEditingRow, |
|||
DATAGRIDROWHEADER_stateNormalEditingRowFocused, |
|||
DATAGRIDROWHEADER_stateSelected, |
|||
DATAGRIDROWHEADER_stateSelectedCurrentRow, |
|||
DATAGRIDROWHEADER_stateSelectedCurrentRowFocused, |
|||
DATAGRIDROWHEADER_stateSelectedFocused |
|||
};*/ |
|||
|
|||
#endregion Constants
|
|||
|
|||
|
|||
|
|||
/// <summary>
|
|||
/// Creates AutomationPeer (<see cref="UIElement.OnCreateAutomationPeer"/>)
|
|||
/// </summary>
|
|||
/*protected override AutomationPeer OnCreateAutomationPeer() |
|||
{ |
|||
return new DataGridRowHeaderAutomationPeer(this); |
|||
} */ |
|||
|
|||
|
|||
|
|||
} |
|||
|
|||
File diff suppressed because it is too large
@ -0,0 +1,472 @@ |
|||
// (c) Copyright Microsoft Corporation.
|
|||
// This source is subject to the Microsoft Public License (Ms-PL).
|
|||
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
|
|||
// All other rights reserved.
|
|||
|
|||
using System; |
|||
using System.Diagnostics; |
|||
using System.Collections.Generic; |
|||
using System.Collections; |
|||
|
|||
namespace Avalonia.Controls |
|||
{ |
|||
internal class DataGridSelectedItemsCollection : IList |
|||
{ |
|||
private List<object> _oldSelectedItemsCache; |
|||
private IndexToValueTable<bool> _oldSelectedSlotsTable; |
|||
private List<object> _selectedItemsCache; |
|||
private IndexToValueTable<bool> _selectedSlotsTable; |
|||
|
|||
public DataGridSelectedItemsCollection(DataGrid owningGrid) |
|||
{ |
|||
OwningGrid = owningGrid; |
|||
_oldSelectedItemsCache = new List<object>(); |
|||
_oldSelectedSlotsTable = new IndexToValueTable<bool>(); |
|||
_selectedItemsCache = new List<object>(); |
|||
_selectedSlotsTable = new IndexToValueTable<bool>(); |
|||
} |
|||
|
|||
public object this[int index] |
|||
{ |
|||
get |
|||
{ |
|||
if (index < 0 || index >= _selectedSlotsTable.IndexCount) |
|||
{ |
|||
throw DataGridError.DataGrid.ValueMustBeBetween("index", "Index", 0, true, _selectedSlotsTable.IndexCount, false); |
|||
} |
|||
int slot = _selectedSlotsTable.GetNthIndex(index); |
|||
Debug.Assert(slot > -1); |
|||
return OwningGrid.DataConnection.GetDataItem(OwningGrid.RowIndexFromSlot(slot)); |
|||
} |
|||
set |
|||
{ |
|||
throw new NotSupportedException(); |
|||
} |
|||
} |
|||
|
|||
public bool IsFixedSize |
|||
{ |
|||
get |
|||
{ |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
public bool IsReadOnly |
|||
{ |
|||
get |
|||
{ |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
public int Add(object dataItem) |
|||
{ |
|||
if (OwningGrid.SelectionMode == DataGridSelectionMode.Single) |
|||
{ |
|||
throw DataGridError.DataGridSelectedItemsCollection.CannotChangeSelectedItemsCollectionInSingleMode(); |
|||
} |
|||
|
|||
int itemIndex = OwningGrid.DataConnection.IndexOf(dataItem); |
|||
if (itemIndex == -1) |
|||
{ |
|||
throw DataGridError.DataGrid.ItemIsNotContainedInTheItemsSource("dataItem"); |
|||
} |
|||
Debug.Assert(itemIndex >= 0); |
|||
|
|||
int slot = OwningGrid.SlotFromRowIndex(itemIndex); |
|||
if (_selectedSlotsTable.RangeCount == 0) |
|||
{ |
|||
OwningGrid.SelectedItem = dataItem; |
|||
} |
|||
else |
|||
{ |
|||
OwningGrid.SetRowSelection(slot, true /*isSelected*/, false /*setAnchorSlot*/); |
|||
} |
|||
return _selectedSlotsTable.IndexOf(slot); |
|||
} |
|||
|
|||
public void Clear() |
|||
{ |
|||
if (OwningGrid.SelectionMode == DataGridSelectionMode.Single) |
|||
{ |
|||
throw DataGridError.DataGridSelectedItemsCollection.CannotChangeSelectedItemsCollectionInSingleMode(); |
|||
} |
|||
|
|||
if (_selectedSlotsTable.RangeCount > 0) |
|||
{ |
|||
// Clearing the selection does not reset the potential current cell.
|
|||
if (!OwningGrid.CommitEdit(DataGridEditingUnit.Row, true /*exitEditing*/)) |
|||
{ |
|||
// Edited value couldn't be committed or aborted
|
|||
return; |
|||
} |
|||
OwningGrid.ClearRowSelection(true /*resetAnchorSlot*/); |
|||
} |
|||
} |
|||
|
|||
public bool Contains(object dataItem) |
|||
{ |
|||
int itemIndex = OwningGrid.DataConnection.IndexOf(dataItem); |
|||
if (itemIndex == -1) |
|||
{ |
|||
return false; |
|||
} |
|||
Debug.Assert(itemIndex >= 0); |
|||
|
|||
return ContainsSlot(OwningGrid.SlotFromRowIndex(itemIndex)); |
|||
} |
|||
|
|||
public int IndexOf(object dataItem) |
|||
{ |
|||
int itemIndex = OwningGrid.DataConnection.IndexOf(dataItem); |
|||
if (itemIndex == -1) |
|||
{ |
|||
return -1; |
|||
} |
|||
Debug.Assert(itemIndex >= 0); |
|||
int slot = OwningGrid.SlotFromRowIndex(itemIndex); |
|||
return _selectedSlotsTable.IndexOf(slot); |
|||
} |
|||
|
|||
public void Insert(int index, object dataItem) |
|||
{ |
|||
throw new NotSupportedException(); |
|||
} |
|||
|
|||
public void Remove(object dataItem) |
|||
{ |
|||
if (OwningGrid.SelectionMode == DataGridSelectionMode.Single) |
|||
{ |
|||
throw DataGridError.DataGridSelectedItemsCollection.CannotChangeSelectedItemsCollectionInSingleMode(); |
|||
} |
|||
|
|||
int itemIndex = OwningGrid.DataConnection.IndexOf(dataItem); |
|||
if (itemIndex == -1) |
|||
{ |
|||
return; |
|||
} |
|||
Debug.Assert(itemIndex >= 0); |
|||
|
|||
if (itemIndex == OwningGrid.CurrentSlot && |
|||
!OwningGrid.CommitEdit(DataGridEditingUnit.Row, true /*exitEditing*/)) |
|||
{ |
|||
// Edited value couldn't be committed or aborted
|
|||
return; |
|||
} |
|||
|
|||
OwningGrid.SetRowSelection(itemIndex, false /*isSelected*/, false /*setAnchorSlot*/); |
|||
} |
|||
|
|||
public void RemoveAt(int index) |
|||
{ |
|||
if (OwningGrid.SelectionMode == DataGridSelectionMode.Single) |
|||
{ |
|||
throw DataGridError.DataGridSelectedItemsCollection.CannotChangeSelectedItemsCollectionInSingleMode(); |
|||
} |
|||
|
|||
if (index < 0 || index >= _selectedSlotsTable.IndexCount) |
|||
{ |
|||
throw DataGridError.DataGrid.ValueMustBeBetween("index", "Index", 0, true, _selectedSlotsTable.IndexCount, false); |
|||
} |
|||
int rowIndex = _selectedSlotsTable.GetNthIndex(index); |
|||
Debug.Assert(rowIndex > -1); |
|||
|
|||
if (rowIndex == OwningGrid.CurrentSlot && |
|||
!OwningGrid.CommitEdit(DataGridEditingUnit.Row, true /*exitEditing*/)) |
|||
{ |
|||
// Edited value couldn't be committed or aborted
|
|||
return; |
|||
} |
|||
|
|||
OwningGrid.SetRowSelection(rowIndex, false /*isSelected*/, false /*setAnchorSlot*/); |
|||
} |
|||
|
|||
public int Count |
|||
{ |
|||
get |
|||
{ |
|||
return _selectedSlotsTable.IndexCount; |
|||
} |
|||
} |
|||
|
|||
public bool IsSynchronized |
|||
{ |
|||
get |
|||
{ |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
public object SyncRoot |
|||
{ |
|||
get |
|||
{ |
|||
return this; |
|||
} |
|||
} |
|||
|
|||
public void CopyTo(System.Array array, int index) |
|||
{ |
|||
//
|
|||
throw new NotImplementedException(); |
|||
} |
|||
|
|||
public IEnumerator GetEnumerator() |
|||
{ |
|||
Debug.Assert(OwningGrid != null); |
|||
Debug.Assert(OwningGrid.DataConnection != null); |
|||
Debug.Assert(_selectedSlotsTable != null); |
|||
|
|||
foreach (int slot in _selectedSlotsTable.GetIndexes()) |
|||
{ |
|||
int rowIndex = OwningGrid.RowIndexFromSlot(slot); |
|||
Debug.Assert(rowIndex > -1); |
|||
yield return OwningGrid.DataConnection.GetDataItem(rowIndex); |
|||
} |
|||
} |
|||
|
|||
internal DataGrid OwningGrid |
|||
{ |
|||
get; |
|||
private set; |
|||
} |
|||
|
|||
internal List<object> SelectedItemsCache |
|||
{ |
|||
get |
|||
{ |
|||
return _selectedItemsCache; |
|||
} |
|||
set |
|||
{ |
|||
_selectedItemsCache = value; |
|||
UpdateIndexes(); |
|||
} |
|||
} |
|||
|
|||
internal void ClearRows() |
|||
{ |
|||
_selectedSlotsTable.Clear(); |
|||
_selectedItemsCache.Clear(); |
|||
} |
|||
|
|||
internal bool ContainsSlot(int slot) |
|||
{ |
|||
return _selectedSlotsTable.Contains(slot); |
|||
} |
|||
|
|||
internal bool ContainsAll(int startSlot, int endSlot) |
|||
{ |
|||
int itemSlot = OwningGrid.RowGroupHeadersTable.GetNextGap(startSlot - 1); |
|||
while (itemSlot <= endSlot) |
|||
{ |
|||
// Skip over the RowGroupHeaderSlots
|
|||
int nextRowGroupHeaderSlot = OwningGrid.RowGroupHeadersTable.GetNextIndex(itemSlot); |
|||
int lastItemSlot = nextRowGroupHeaderSlot == -1 ? endSlot : Math.Min(endSlot, nextRowGroupHeaderSlot - 1); |
|||
if (!_selectedSlotsTable.ContainsAll(itemSlot, lastItemSlot)) |
|||
{ |
|||
return false; |
|||
} |
|||
itemSlot = OwningGrid.RowGroupHeadersTable.GetNextGap(lastItemSlot); |
|||
} |
|||
return true; |
|||
} |
|||
|
|||
// Called when an item is deleted from the ItemsSource as opposed to just being unselected
|
|||
internal void Delete(int slot, object item) |
|||
{ |
|||
if (_oldSelectedSlotsTable.Contains(slot)) |
|||
{ |
|||
OwningGrid.SelectionHasChanged = true; |
|||
} |
|||
DeleteSlot(slot); |
|||
_selectedItemsCache.Remove(item); |
|||
} |
|||
|
|||
internal void DeleteSlot(int slot) |
|||
{ |
|||
_selectedSlotsTable.RemoveIndex(slot); |
|||
_oldSelectedSlotsTable.RemoveIndex(slot); |
|||
} |
|||
|
|||
// Returns the inclusive index count between lowerBound and upperBound of all indexes with the given value
|
|||
internal int GetIndexCount(int lowerBound, int upperBound) |
|||
{ |
|||
return _selectedSlotsTable.GetIndexCount(lowerBound, upperBound, true); |
|||
} |
|||
|
|||
internal IEnumerable<int> GetIndexes() |
|||
{ |
|||
return _selectedSlotsTable.GetIndexes(); |
|||
} |
|||
|
|||
internal IEnumerable<int> GetSlots(int startSlot) |
|||
{ |
|||
return _selectedSlotsTable.GetIndexes(startSlot); |
|||
} |
|||
|
|||
internal SelectionChangedEventArgs GetSelectionChangedEventArgs() |
|||
{ |
|||
List<object> addedSelectedItems = new List<object>(); |
|||
List<object> removedSelectedItems = new List<object>(); |
|||
|
|||
// Compare the old selected indexes with the current selection to determine which items
|
|||
// have been added and removed since the last time this method was called
|
|||
foreach (int newSlot in _selectedSlotsTable.GetIndexes()) |
|||
{ |
|||
object newItem = OwningGrid.DataConnection.GetDataItem(OwningGrid.RowIndexFromSlot(newSlot)); |
|||
if (_oldSelectedSlotsTable.Contains(newSlot)) |
|||
{ |
|||
_oldSelectedSlotsTable.RemoveValue(newSlot); |
|||
_oldSelectedItemsCache.Remove(newItem); |
|||
} |
|||
else |
|||
{ |
|||
addedSelectedItems.Add(newItem); |
|||
} |
|||
} |
|||
foreach (object oldItem in _oldSelectedItemsCache) |
|||
{ |
|||
removedSelectedItems.Add(oldItem); |
|||
} |
|||
|
|||
// The current selection becomes the old selection
|
|||
_oldSelectedSlotsTable = _selectedSlotsTable.Copy(); |
|||
_oldSelectedItemsCache = new List<object>(_selectedItemsCache); |
|||
|
|||
return |
|||
new SelectionChangedEventArgs(DataGrid.SelectionChangedEvent, removedSelectedItems, addedSelectedItems) |
|||
{ |
|||
Source = OwningGrid |
|||
}; |
|||
} |
|||
|
|||
internal void InsertIndex(int slot) |
|||
{ |
|||
_selectedSlotsTable.InsertIndex(slot); |
|||
_oldSelectedSlotsTable.InsertIndex(slot); |
|||
|
|||
// It's possible that we're inserting an item that was just removed. If that's the case,
|
|||
// and the re-inserted item used to be selected, we want to update the _oldSelectedSlotsTable
|
|||
// to include the item's new index within the collection.
|
|||
int rowIndex = OwningGrid.RowIndexFromSlot(slot); |
|||
if (rowIndex != -1) |
|||
{ |
|||
object insertedItem = OwningGrid.DataConnection.GetDataItem(rowIndex); |
|||
if (insertedItem != null && _oldSelectedItemsCache.Contains(insertedItem)) |
|||
{ |
|||
_oldSelectedSlotsTable.AddValue(slot, true); |
|||
} |
|||
} |
|||
} |
|||
|
|||
internal void SelectSlot(int slot, bool select) |
|||
{ |
|||
if (OwningGrid.RowGroupHeadersTable.Contains(slot)) |
|||
{ |
|||
return; |
|||
} |
|||
if (select) |
|||
{ |
|||
if (!_selectedSlotsTable.Contains(slot)) |
|||
{ |
|||
_selectedItemsCache.Add(OwningGrid.DataConnection.GetDataItem(OwningGrid.RowIndexFromSlot(slot))); |
|||
} |
|||
_selectedSlotsTable.AddValue(slot, true); |
|||
} |
|||
else |
|||
{ |
|||
if (_selectedSlotsTable.Contains(slot)) |
|||
{ |
|||
_selectedItemsCache.Remove(OwningGrid.DataConnection.GetDataItem(OwningGrid.RowIndexFromSlot(slot))); |
|||
} |
|||
_selectedSlotsTable.RemoveValue(slot); |
|||
} |
|||
} |
|||
|
|||
internal void SelectSlots(int startSlot, int endSlot, bool select) |
|||
{ |
|||
int itemSlot = OwningGrid.RowGroupHeadersTable.GetNextGap(startSlot - 1); |
|||
int endItemSlot = OwningGrid.RowGroupHeadersTable.GetPreviousGap(endSlot + 1); |
|||
if (select) |
|||
{ |
|||
while (itemSlot <= endItemSlot) |
|||
{ |
|||
// Add the newly selected item slots by skipping over the RowGroupHeaderSlots
|
|||
int nextRowGroupHeaderSlot = OwningGrid.RowGroupHeadersTable.GetNextIndex(itemSlot); |
|||
int lastItemSlot = nextRowGroupHeaderSlot == -1 ? endItemSlot : Math.Min(endItemSlot, nextRowGroupHeaderSlot - 1); |
|||
for (int slot = itemSlot; slot <= lastItemSlot; slot++) |
|||
{ |
|||
if (!_selectedSlotsTable.Contains(slot)) |
|||
{ |
|||
_selectedItemsCache.Add(OwningGrid.DataConnection.GetDataItem(OwningGrid.RowIndexFromSlot(slot))); |
|||
} |
|||
} |
|||
_selectedSlotsTable.AddValues(itemSlot, lastItemSlot - itemSlot + 1, true); |
|||
itemSlot = OwningGrid.RowGroupHeadersTable.GetNextGap(lastItemSlot); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
while (itemSlot <= endItemSlot) |
|||
{ |
|||
// Remove the unselected item slots by skipping over the RowGroupHeaderSlots
|
|||
int nextRowGroupHeaderSlot = OwningGrid.RowGroupHeadersTable.GetNextIndex(itemSlot); |
|||
int lastItemSlot = nextRowGroupHeaderSlot == -1 ? endItemSlot : Math.Min(endItemSlot, nextRowGroupHeaderSlot - 1); |
|||
for (int slot = itemSlot; slot <= lastItemSlot; slot++) |
|||
{ |
|||
if (_selectedSlotsTable.Contains(slot)) |
|||
{ |
|||
_selectedItemsCache.Remove(OwningGrid.DataConnection.GetDataItem(OwningGrid.RowIndexFromSlot(slot))); |
|||
} |
|||
} |
|||
_selectedSlotsTable.RemoveValues(itemSlot, lastItemSlot - itemSlot + 1); |
|||
itemSlot = OwningGrid.RowGroupHeadersTable.GetNextGap(lastItemSlot); |
|||
} |
|||
} |
|||
} |
|||
|
|||
internal void UpdateIndexes() |
|||
{ |
|||
_oldSelectedSlotsTable.Clear(); |
|||
_selectedSlotsTable.Clear(); |
|||
|
|||
if (OwningGrid.DataConnection.DataSource == null) |
|||
{ |
|||
if (SelectedItemsCache.Count > 0) |
|||
{ |
|||
OwningGrid.SelectionHasChanged = true; |
|||
SelectedItemsCache.Clear(); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
List<object> tempSelectedItemsCache = new List<object>(); |
|||
foreach (object item in _selectedItemsCache) |
|||
{ |
|||
int index = OwningGrid.DataConnection.IndexOf(item); |
|||
if (index != -1) |
|||
{ |
|||
tempSelectedItemsCache.Add(item); |
|||
_selectedSlotsTable.AddValue(OwningGrid.SlotFromRowIndex(index), true); |
|||
} |
|||
} |
|||
foreach (object item in _oldSelectedItemsCache) |
|||
{ |
|||
int index = OwningGrid.DataConnection.IndexOf(item); |
|||
if (index == -1) |
|||
{ |
|||
OwningGrid.SelectionHasChanged = true; |
|||
} |
|||
else |
|||
{ |
|||
_oldSelectedSlotsTable.AddValue(OwningGrid.SlotFromRowIndex(index), true); |
|||
} |
|||
} |
|||
_selectedItemsCache = tempSelectedItemsCache; |
|||
} |
|||
|
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,401 @@ |
|||
// (c) Copyright Microsoft Corporation.
|
|||
// This source is subject to the Microsoft Public License (Ms-PL).
|
|||
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
|
|||
// All other rights reserved.
|
|||
|
|||
using Avalonia.Input; |
|||
using Avalonia.Interactivity; |
|||
using Avalonia.Media; |
|||
using System; |
|||
using System.ComponentModel; |
|||
using Avalonia.Layout; |
|||
|
|||
namespace Avalonia.Controls |
|||
{ |
|||
/// <summary>
|
|||
/// Represents a <see cref="T:Avalonia.Controls.DataGrid" /> column that hosts textual content in its cells.
|
|||
/// </summary>
|
|||
//[StyleTypedProperty(Property = "ElementStyle", StyleTargetType = typeof(TextBlock))]
|
|||
//[StyleTypedProperty(Property = "EditingElementStyle", StyleTargetType = typeof(TextBox))]
|
|||
public class DataGridTextColumn : DataGridBoundColumn |
|||
{ |
|||
#region Constants
|
|||
|
|||
//private const string DATAGRIDTEXTCOLUMN_fontFamilyName = "FontFamily";
|
|||
//private const string DATAGRIDTEXTCOLUMN_fontSizeName = "FontSize";
|
|||
//private const string DATAGRIDTEXTCOLUMN_fontStyleName = "FontStyle";
|
|||
//private const string DATAGRIDTEXTCOLUMN_fontWeightName = "FontWeight";
|
|||
//private const string DATAGRIDTEXTCOLUMN_foregroundName = "Foreground";
|
|||
|
|||
#endregion Constants
|
|||
|
|||
#region Data
|
|||
|
|||
private double? _fontSize; |
|||
private FontStyle? _fontStyle; |
|||
private FontWeight? _fontWeight; |
|||
private IBrush _foreground; |
|||
|
|||
#endregion Data
|
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="T:Avalonia.Controls.DataGridTextColumn" /> class.
|
|||
/// </summary>
|
|||
public DataGridTextColumn() |
|||
{ |
|||
BindingTarget = TextBox.TextProperty; |
|||
} |
|||
|
|||
#region DependencyProperties
|
|||
|
|||
|
|||
/// <summary>
|
|||
/// Identifies the FontFamily dependency property.
|
|||
/// </summary>
|
|||
public static readonly StyledProperty<string> FontFamilyProperty = |
|||
AvaloniaProperty.Register<DataGridTextColumn, string>(nameof(FontFamily)); |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the font name.
|
|||
/// </summary>
|
|||
public string FontFamily |
|||
{ |
|||
get { return GetValue(FontFamilyProperty); } |
|||
set { SetValue(FontFamilyProperty, value); } |
|||
} |
|||
|
|||
/*private void OnFontFamilyChanged(AvaloniaPropertyChangedEventArgs e) |
|||
{ |
|||
//FontFamilyProperty.Changed.AddClassHandler<DataGridTextColumn>(x => x.OnFontFamilyChanged);
|
|||
var oldValue = (string)e.OldValue; |
|||
var value = (string)e.NewValue; |
|||
}*/ |
|||
|
|||
/*private static void OnFontFamilyPropertyChanged(AvaloniaObject d, DependencyPropertyChangedEventArgs e) |
|||
{ |
|||
DataGridTextColumn textColumn = (DataGridTextColumn)d; |
|||
textColumn.NotifyPropertyChanged(DATAGRIDTEXTCOLUMN_fontFamilyName); |
|||
}*/ |
|||
|
|||
#endregion DependencyProperties
|
|||
|
|||
#region Public Properties
|
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the font size.
|
|||
/// </summary>
|
|||
// Use DefaultValue here so undo in the Designer will set this to NaN
|
|||
[DefaultValue(double.NaN)] |
|||
public double FontSize |
|||
{ |
|||
get |
|||
{ |
|||
return _fontSize ?? Double.NaN; |
|||
} |
|||
set |
|||
{ |
|||
if (_fontSize != value) |
|||
{ |
|||
_fontSize = value; |
|||
NotifyPropertyChanged(nameof(FontSize)); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the font style.
|
|||
/// </summary>
|
|||
public FontStyle FontStyle |
|||
{ |
|||
get |
|||
{ |
|||
return _fontStyle ?? FontStyle.Normal; |
|||
} |
|||
set |
|||
{ |
|||
if (_fontStyle != value) |
|||
{ |
|||
_fontStyle = value; |
|||
NotifyPropertyChanged(nameof(FontStyle)); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the font weight or thickness.
|
|||
/// </summary>
|
|||
public FontWeight FontWeight |
|||
{ |
|||
get |
|||
{ |
|||
return _fontWeight ?? FontWeight.Normal; |
|||
} |
|||
set |
|||
{ |
|||
if (_fontWeight != value) |
|||
{ |
|||
_fontWeight = value; |
|||
NotifyPropertyChanged(nameof(FontWeight)); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets a brush that describes the foreground of the column cells.
|
|||
/// </summary>
|
|||
public IBrush Foreground |
|||
{ |
|||
get |
|||
{ |
|||
return _foreground; |
|||
} |
|||
set |
|||
{ |
|||
if (_foreground != value) |
|||
{ |
|||
_foreground = value; |
|||
NotifyPropertyChanged(nameof(Foreground)); |
|||
} |
|||
} |
|||
} |
|||
|
|||
#endregion Public Properties
|
|||
|
|||
#region Protected Methods
|
|||
|
|||
/// <summary>
|
|||
/// Causes the column cell being edited to revert to the specified value.
|
|||
/// </summary>
|
|||
/// <param name="editingElement">The element that the column displays for a cell in editing mode.</param>
|
|||
/// <param name="uneditedValue">The previous, unedited value in the cell being edited.</param>
|
|||
protected override void CancelCellEdit(Control editingElement, object uneditedValue) |
|||
{ |
|||
if (editingElement is TextBox textBox) |
|||
{ |
|||
string uneditedString = uneditedValue as string; |
|||
textBox.Text = uneditedString ?? string.Empty; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets a <see cref="T:Avalonia.Controls.TextBox" /> control that is bound to the column's <see cref="P:Avalonia.Controls.DataGridBoundColumn.Binding" /> property value.
|
|||
/// </summary>
|
|||
/// <param name="cell">The cell that will contain the generated element.</param>
|
|||
/// <param name="dataItem">The data item represented by the row that contains the intended cell.</param>
|
|||
/// <returns>A new <see cref="T:Avalonia.Controls.TextBox" /> control that is bound to the column's <see cref="P:Avalonia.Controls.DataGridBoundColumn.Binding" /> property value.</returns>
|
|||
protected override Control GenerateEditingElementDirect(DataGridCell cell, object dataItem) |
|||
{ |
|||
var textBox = new TextBox |
|||
{ |
|||
VerticalAlignment = VerticalAlignment.Stretch, |
|||
Background = new SolidColorBrush(Colors.Transparent) |
|||
}; |
|||
|
|||
//if (DependencyProperty.UnsetValue != ReadLocalValue(DataGridTextColumn.FontFamilyProperty))
|
|||
if (IsSet(FontFamilyProperty)) |
|||
{ |
|||
textBox.FontFamily = FontFamily; |
|||
} |
|||
if (_fontSize.HasValue) |
|||
{ |
|||
textBox.FontSize = _fontSize.Value; |
|||
} |
|||
if (_fontStyle.HasValue) |
|||
{ |
|||
textBox.FontStyle = _fontStyle.Value; |
|||
} |
|||
if (_fontWeight.HasValue) |
|||
{ |
|||
textBox.FontWeight = _fontWeight.Value; |
|||
} |
|||
if (_foreground != null) |
|||
{ |
|||
textBox.Foreground = _foreground; |
|||
} |
|||
|
|||
return textBox; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets a read-only <see cref="T:Avalonia.Controls.TextBlock" /> element that is bound to the column's <see cref="P:Avalonia.Controls.DataGridBoundColumn.Binding" /> property value.
|
|||
/// </summary>
|
|||
/// <param name="cell">The cell that will contain the generated element.</param>
|
|||
/// <param name="dataItem">The data item represented by the row that contains the intended cell.</param>
|
|||
/// <returns>A new, read-only <see cref="T:Avalonia.Controls.TextBlock" /> element that is bound to the column's <see cref="P:Avalonia.Controls.DataGridBoundColumn.Binding" /> property value.</returns>
|
|||
protected override Control GenerateElement(DataGridCell cell, object dataItem) |
|||
{ |
|||
TextBlock textBlockElement = new TextBlock(); |
|||
textBlockElement.Margin = new Thickness(4); |
|||
textBlockElement.VerticalAlignment = VerticalAlignment.Center; |
|||
|
|||
if (IsSet(FontFamilyProperty)) |
|||
{ |
|||
textBlockElement.FontFamily = FontFamily; |
|||
} |
|||
if (_fontSize.HasValue) |
|||
{ |
|||
textBlockElement.FontSize = _fontSize.Value; |
|||
} |
|||
if (_fontStyle.HasValue) |
|||
{ |
|||
textBlockElement.FontStyle = _fontStyle.Value; |
|||
} |
|||
if (_fontWeight.HasValue) |
|||
{ |
|||
textBlockElement.FontWeight = _fontWeight.Value; |
|||
} |
|||
if (_foreground != null) |
|||
{ |
|||
textBlockElement.Foreground = _foreground; |
|||
} |
|||
if (Binding != null) |
|||
//if (Binding != null || !DesignerProperties.IsInDesignTool)
|
|||
{ |
|||
textBlockElement.Bind(TextBlock.TextProperty, Binding); |
|||
} |
|||
return textBlockElement; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Called when the cell in the column enters editing mode.
|
|||
/// </summary>
|
|||
/// <param name="editingElement">The element that the column displays for a cell in editing mode.</param>
|
|||
/// <param name="editingEventArgs">Information about the user gesture that is causing a cell to enter editing mode.</param>
|
|||
/// <returns>The unedited value. </returns>
|
|||
protected override object PrepareCellForEdit(Control editingElement, RoutedEventArgs editingEventArgs) |
|||
{ |
|||
if (editingElement is TextBox textBox) |
|||
{ |
|||
string uneditedText = textBox.Text ?? String.Empty; |
|||
int len = uneditedText.Length; |
|||
if (editingEventArgs is KeyEventArgs keyEventArgs && keyEventArgs.Key == Key.F2) |
|||
{ |
|||
// Put caret at the end of the text
|
|||
textBox.SelectionStart = len; |
|||
textBox.SelectionEnd = len; |
|||
} |
|||
else |
|||
{ |
|||
// Select all text
|
|||
textBox.SelectionStart = 0; |
|||
textBox.SelectionEnd = len; |
|||
textBox.CaretIndex = len; |
|||
} |
|||
|
|||
return uneditedText; |
|||
} |
|||
return string.Empty; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Called by the DataGrid control when this column asks for its elements to be
|
|||
/// updated, because a property changed.
|
|||
/// </summary>
|
|||
protected internal override void RefreshCellContent(Control element, string propertyName) |
|||
{ |
|||
if (element == null) |
|||
{ |
|||
throw new ArgumentNullException("element"); |
|||
} |
|||
|
|||
if(element is TextBox textBox) |
|||
{ |
|||
if (propertyName == nameof(FontFamily)) |
|||
{ |
|||
textBox.FontFamily = FontFamily; |
|||
} |
|||
else if (propertyName == nameof(FontSize)) |
|||
{ |
|||
SetTextFontSize(textBox, TextBox.FontSizeProperty); |
|||
} |
|||
else if (propertyName == nameof(FontStyle)) |
|||
{ |
|||
textBox.FontStyle = FontStyle; |
|||
} |
|||
else if (propertyName == nameof(FontWeight)) |
|||
{ |
|||
textBox.FontWeight = FontWeight; |
|||
} |
|||
else if (propertyName == nameof(Foreground)) |
|||
{ |
|||
textBox.Foreground = Foreground; |
|||
} |
|||
else |
|||
{ |
|||
if (FontFamily != null) |
|||
{ |
|||
textBox.FontFamily = FontFamily; |
|||
} |
|||
SetTextFontSize(textBox, TextBox.FontSizeProperty); |
|||
textBox.FontStyle = FontStyle; |
|||
textBox.FontWeight = FontWeight; |
|||
if (Foreground != null) |
|||
{ |
|||
textBox.Foreground = Foreground; |
|||
} |
|||
} |
|||
|
|||
} |
|||
else if (element is TextBlock textBlock) |
|||
{ |
|||
if (propertyName == nameof(FontFamily)) |
|||
{ |
|||
textBlock.FontFamily = FontFamily; |
|||
} |
|||
else if (propertyName == nameof(FontSize)) |
|||
{ |
|||
SetTextFontSize(textBlock, TextBlock.FontSizeProperty); |
|||
} |
|||
else if (propertyName == nameof(FontStyle)) |
|||
{ |
|||
textBlock.FontStyle = FontStyle; |
|||
} |
|||
else if (propertyName == nameof(FontWeight)) |
|||
{ |
|||
textBlock.FontWeight = FontWeight; |
|||
} |
|||
else if (propertyName == nameof(Foreground)) |
|||
{ |
|||
textBlock.Foreground = Foreground; |
|||
} |
|||
else |
|||
{ |
|||
if (FontFamily != null) |
|||
{ |
|||
textBlock.FontFamily = FontFamily; |
|||
} |
|||
SetTextFontSize(textBlock, TextBlock.FontSizeProperty); |
|||
textBlock.FontStyle = FontStyle; |
|||
textBlock.FontWeight = FontWeight; |
|||
if (Foreground != null) |
|||
{ |
|||
textBlock.Foreground = Foreground; |
|||
} |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
throw DataGridError.DataGrid.ValueIsNotAnInstanceOfEitherOr("element", typeof(TextBox), typeof(TextBlock)); |
|||
} |
|||
} |
|||
|
|||
#endregion Protected Methods
|
|||
|
|||
#region Private Methods
|
|||
|
|||
private void SetTextFontSize(AvaloniaObject textElement, AvaloniaProperty fontSizeProperty) |
|||
{ |
|||
double newFontSize = FontSize; |
|||
if (double.IsNaN(newFontSize)) |
|||
{ |
|||
textElement.ClearValue(fontSizeProperty); |
|||
} |
|||
else |
|||
{ |
|||
textElement.SetValue(fontSizeProperty, newFontSize); |
|||
} |
|||
} |
|||
|
|||
#endregion Private Methods
|
|||
} |
|||
} |
|||
@ -0,0 +1,39 @@ |
|||
// (c) Copyright Microsoft Corporation.
|
|||
// This source is subject to the Microsoft Public License (Ms-PL).
|
|||
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
|
|||
// All other rights reserved.
|
|||
|
|||
using Avalonia.Data.Converters; |
|||
using Avalonia.Utilities; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Globalization; |
|||
using System.Text; |
|||
|
|||
namespace Avalonia.Controls |
|||
{ |
|||
internal class DataGridValueConverter : IValueConverter |
|||
{ |
|||
public static DataGridValueConverter Instance = new DataGridValueConverter(); |
|||
|
|||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) |
|||
{ |
|||
return DefaultValueConverter.Instance.Convert(value, targetType, parameter, culture); |
|||
} |
|||
|
|||
// This suppresses a warning saying that we should use String.IsNullOrEmpty instead of a string
|
|||
// comparison, but in this case we want to explicitly check for Empty and not Null.
|
|||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) |
|||
{ |
|||
if (targetType != null && targetType.IsNullableType()) |
|||
{ |
|||
String strValue = value as String; |
|||
if (strValue == String.Empty) |
|||
{ |
|||
return null; |
|||
} |
|||
} |
|||
return DefaultValueConverter.Instance.ConvertBack(value, targetType, parameter, culture); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,541 @@ |
|||
// (c) Copyright Microsoft Corporation.
|
|||
// This source is subject to the Microsoft Public License (Ms-PL).
|
|||
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
|
|||
// All other rights reserved.
|
|||
|
|||
using Avalonia.Interactivity; |
|||
using System; |
|||
using System.ComponentModel; |
|||
using System.Diagnostics; |
|||
|
|||
namespace Avalonia.Controls |
|||
{ |
|||
/// <summary>
|
|||
/// Provides data for the <see cref="E:Avalonia.Controls.DataGrid.AutoGeneratingColumn" /> event.
|
|||
/// </summary>
|
|||
public class DataGridAutoGeneratingColumnEventArgs : CancelEventArgs |
|||
{ |
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="T:Avalonia.Controls.DataGridAutoGeneratingColumnEventArgs" /> class.
|
|||
/// </summary>
|
|||
/// <param name="propertyName">
|
|||
/// The name of the property bound to the generated column.
|
|||
/// </param>
|
|||
/// <param name="propertyType">
|
|||
/// The <see cref="T:System.Type" /> of the property bound to the generated column.
|
|||
/// </param>
|
|||
/// <param name="column">
|
|||
/// The generated column.
|
|||
/// </param>
|
|||
public DataGridAutoGeneratingColumnEventArgs(string propertyName, Type propertyType, DataGridColumn column) |
|||
{ |
|||
Column = column; |
|||
PropertyName = propertyName; |
|||
PropertyType = propertyType; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the generated column.
|
|||
/// </summary>
|
|||
public DataGridColumn Column |
|||
{ |
|||
get; |
|||
set; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the name of the property bound to the generated column.
|
|||
/// </summary>
|
|||
public string PropertyName |
|||
{ |
|||
get; |
|||
private set; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the <see cref="T:System.Type" /> of the property bound to the generated column.
|
|||
/// </summary>
|
|||
public Type PropertyType |
|||
{ |
|||
get; |
|||
private set; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Provides data for the <see cref="E:Avalonia.Controls.DataGrid.BeginningEdit" /> event.
|
|||
/// </summary>
|
|||
public class DataGridBeginningEditEventArgs : CancelEventArgs |
|||
{ |
|||
/// <summary>
|
|||
/// Initializes a new instance of the
|
|||
/// <see cref="T:Avalonia.Controls.DataGridBeginningEditEventArgs" /> class.
|
|||
/// </summary>
|
|||
/// <param name="column">
|
|||
/// The column that contains the cell to be edited.
|
|||
/// </param>
|
|||
/// <param name="row">
|
|||
/// The row that contains the cell to be edited.
|
|||
/// </param>
|
|||
/// <param name="editingEventArgs">
|
|||
/// Information about the user gesture that caused the cell to enter edit mode.
|
|||
/// </param>
|
|||
public DataGridBeginningEditEventArgs(DataGridColumn column, |
|||
DataGridRow row, |
|||
RoutedEventArgs editingEventArgs) |
|||
{ |
|||
this.Column = column; |
|||
this.Row = row; |
|||
this.EditingEventArgs = editingEventArgs; |
|||
} |
|||
|
|||
#region Properties
|
|||
|
|||
/// <summary>
|
|||
/// Gets the column that contains the cell to be edited.
|
|||
/// </summary>
|
|||
public DataGridColumn Column |
|||
{ |
|||
get; |
|||
private set; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets information about the user gesture that caused the cell to enter edit mode.
|
|||
/// </summary>
|
|||
public RoutedEventArgs EditingEventArgs |
|||
{ |
|||
get; |
|||
private set; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the row that contains the cell to be edited.
|
|||
/// </summary>
|
|||
public DataGridRow Row |
|||
{ |
|||
get; |
|||
private set; |
|||
} |
|||
|
|||
#endregion Properties
|
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Provides information just after a cell has exited editing mode.
|
|||
/// </summary>
|
|||
public class DataGridCellEditEndedEventArgs : EventArgs |
|||
{ |
|||
/// <summary>
|
|||
/// Instantiates a new instance of this class.
|
|||
/// </summary>
|
|||
/// <param name="column">The column of the cell that has just exited edit mode.</param>
|
|||
/// <param name="row">The row container of the cell container that has just exited edit mode.</param>
|
|||
/// <param name="editAction">The editing action that has been taken.</param>
|
|||
public DataGridCellEditEndedEventArgs(DataGridColumn column, DataGridRow row, DataGridEditAction editAction) |
|||
{ |
|||
Column = column; |
|||
Row = row; |
|||
EditAction = editAction; |
|||
} |
|||
|
|||
#region Properties
|
|||
|
|||
/// <summary>
|
|||
/// The column of the cell that has just exited edit mode.
|
|||
/// </summary>
|
|||
public DataGridColumn Column |
|||
{ |
|||
get; |
|||
private set; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// The edit action taken when leaving edit mode.
|
|||
/// </summary>
|
|||
public DataGridEditAction EditAction |
|||
{ |
|||
get; |
|||
private set; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// The row container of the cell container that has just exited edit mode.
|
|||
/// </summary>
|
|||
public DataGridRow Row |
|||
{ |
|||
get; |
|||
private set; |
|||
} |
|||
|
|||
#endregion Properties
|
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Provides information just before a cell exits editing mode.
|
|||
/// </summary>
|
|||
public class DataGridCellEditEndingEventArgs : CancelEventArgs |
|||
{ |
|||
/// <summary>
|
|||
/// Instantiates a new instance of this class.
|
|||
/// </summary>
|
|||
/// <param name="column">The column of the cell that is about to exit edit mode.</param>
|
|||
/// <param name="row">The row container of the cell container that is about to exit edit mode.</param>
|
|||
/// <param name="editingElement">The editing element within the cell.</param>
|
|||
/// <param name="editAction">The editing action that will be taken.</param>
|
|||
public DataGridCellEditEndingEventArgs(DataGridColumn column, |
|||
DataGridRow row, |
|||
Control editingElement, |
|||
DataGridEditAction editAction) |
|||
{ |
|||
Column = column; |
|||
Row = row; |
|||
EditingElement = editingElement; |
|||
EditAction = editAction; |
|||
} |
|||
|
|||
#region Properties
|
|||
|
|||
/// <summary>
|
|||
/// The column of the cell that is about to exit edit mode.
|
|||
/// </summary>
|
|||
public DataGridColumn Column |
|||
{ |
|||
get; |
|||
private set; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// The edit action to take when leaving edit mode.
|
|||
/// </summary>
|
|||
public DataGridEditAction EditAction |
|||
{ |
|||
get; |
|||
private set; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// The editing element within the cell.
|
|||
/// </summary>
|
|||
public Control EditingElement |
|||
{ |
|||
get; |
|||
private set; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// The row container of the cell container that is about to exit edit mode.
|
|||
/// </summary>
|
|||
public DataGridRow Row |
|||
{ |
|||
get; |
|||
private set; |
|||
} |
|||
|
|||
#endregion Properties
|
|||
} |
|||
|
|||
internal class DataGridCellEventArgs : EventArgs |
|||
{ |
|||
internal DataGridCellEventArgs(DataGridCell dataGridCell) |
|||
{ |
|||
Debug.Assert(dataGridCell != null); |
|||
this.Cell = dataGridCell; |
|||
} |
|||
|
|||
internal DataGridCell Cell |
|||
{ |
|||
get; |
|||
private set; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Provides data for <see cref="T:Avalonia.Controls.DataGrid" /> column-related events.
|
|||
/// </summary>
|
|||
public class DataGridColumnEventArgs : EventArgs |
|||
{ |
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="T:Avalonia.Controls.DataGridColumnEventArgs" /> class.
|
|||
/// </summary>
|
|||
/// <param name="column">The column that the event occurs for.</param>
|
|||
public DataGridColumnEventArgs(DataGridColumn column) |
|||
{ |
|||
Column = column ?? throw new ArgumentNullException(nameof(column)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the column that the event occurs for.
|
|||
/// </summary>
|
|||
public DataGridColumn Column |
|||
{ |
|||
get; |
|||
private set; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Provides data for the <see cref="E:Avalonia.Controls.DataGrid.ColumnReordering" /> event.
|
|||
/// </summary>
|
|||
public class DataGridColumnReorderingEventArgs : CancelEventArgs |
|||
{ |
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="T:Avalonia.Controls.DataGridColumnReorderingEventArgs" /> class.
|
|||
/// </summary>
|
|||
/// <param name="dataGridColumn"></param>
|
|||
public DataGridColumnReorderingEventArgs(DataGridColumn dataGridColumn) |
|||
{ |
|||
this.Column = dataGridColumn; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// The column being moved.
|
|||
/// </summary>
|
|||
public DataGridColumn Column |
|||
{ |
|||
get; |
|||
private set; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// The popup indicator displayed while dragging. If null and Handled = true, then do not display a tooltip.
|
|||
/// </summary>
|
|||
public Control DragIndicator |
|||
{ |
|||
get; |
|||
set; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// UIElement to display at the insertion position. If null and Handled = true, then do not display an insertion indicator.
|
|||
/// </summary>
|
|||
public Control DropLocationIndicator |
|||
{ |
|||
get; |
|||
set; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Provides data for <see cref="T:Avalonia.Controls.DataGrid" /> row-related events.
|
|||
/// </summary>
|
|||
public class DataGridRowEventArgs : EventArgs |
|||
{ |
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="T:Avalonia.Controls.DataGridRowEventArgs" /> class.
|
|||
/// </summary>
|
|||
/// <param name="dataGridRow">The row that the event occurs for.</param>
|
|||
public DataGridRowEventArgs(DataGridRow dataGridRow) |
|||
{ |
|||
this.Row = dataGridRow; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the row that the event occurs for.
|
|||
/// </summary>
|
|||
public DataGridRow Row |
|||
{ |
|||
get; |
|||
private set; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Provides information just before a row exits editing mode.
|
|||
/// </summary>
|
|||
/// <QualityBand>Preview</QualityBand>
|
|||
public class DataGridRowEditEndingEventArgs : CancelEventArgs |
|||
{ |
|||
/// <summary>
|
|||
/// Instantiates a new instance of this class.
|
|||
/// </summary>
|
|||
/// <param name="row">The row container of the cell container that is about to exit edit mode.</param>
|
|||
/// <param name="editAction">The editing action that will be taken.</param>
|
|||
public DataGridRowEditEndingEventArgs(DataGridRow row, DataGridEditAction editAction) |
|||
{ |
|||
this.Row = row; |
|||
this.EditAction = editAction; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// The editing action that will be taken.
|
|||
/// </summary>
|
|||
public DataGridEditAction EditAction |
|||
{ |
|||
get; |
|||
private set; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// The row container of the cell container that is about to exit edit mode.
|
|||
/// </summary>
|
|||
public DataGridRow Row |
|||
{ |
|||
get; |
|||
private set; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Provides information just after a row has exited edit mode.
|
|||
/// </summary>
|
|||
/// <QualityBand>Preview</QualityBand>
|
|||
public class DataGridRowEditEndedEventArgs : EventArgs |
|||
{ |
|||
/// <summary>
|
|||
/// Instantiates a new instance of this class.
|
|||
/// </summary>
|
|||
/// <param name="row">The row container of the cell container that has just exited edit mode.</param>
|
|||
/// <param name="editAction">The editing action that has been taken.</param>
|
|||
public DataGridRowEditEndedEventArgs(DataGridRow row, DataGridEditAction editAction) |
|||
{ |
|||
this.Row = row; |
|||
this.EditAction = editAction; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// The editing action that has been taken.
|
|||
/// </summary>
|
|||
public DataGridEditAction EditAction |
|||
{ |
|||
get; |
|||
private set; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// The row container of the cell container that has just exited edit mode.
|
|||
/// </summary>
|
|||
public DataGridRow Row |
|||
{ |
|||
get; |
|||
private set; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Provides data for the <see cref="E:Avalonia.Controls.DataGrid.PreparingCellForEdit" /> event.
|
|||
/// </summary>
|
|||
public class DataGridPreparingCellForEditEventArgs : EventArgs |
|||
{ |
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="T:Avalonia.Controls.DataGridPreparingCellForEditEventArgs" /> class.
|
|||
/// </summary>
|
|||
/// <param name="column">The column that contains the cell to be edited.</param>
|
|||
/// <param name="row">The row that contains the cell to be edited.</param>
|
|||
/// <param name="editingEventArgs">Information about the user gesture that caused the cell to enter edit mode.</param>
|
|||
/// <param name="editingElement">The element that the column displays for a cell in editing mode.</param>
|
|||
public DataGridPreparingCellForEditEventArgs(DataGridColumn column, |
|||
DataGridRow row, |
|||
RoutedEventArgs editingEventArgs, |
|||
Control editingElement) |
|||
{ |
|||
Column = column; |
|||
Row = row; |
|||
EditingEventArgs = editingEventArgs; |
|||
EditingElement = editingElement; |
|||
} |
|||
|
|||
#region Public Properties
|
|||
|
|||
/// <summary>
|
|||
/// Gets the column that contains the cell to be edited.
|
|||
/// </summary>
|
|||
public DataGridColumn Column |
|||
{ |
|||
get; |
|||
private set; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the element that the column displays for a cell in editing mode.
|
|||
/// </summary>
|
|||
public Control EditingElement |
|||
{ |
|||
get; |
|||
private set; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets information about the user gesture that caused the cell to enter edit mode.
|
|||
/// </summary>
|
|||
public RoutedEventArgs EditingEventArgs |
|||
{ |
|||
get; |
|||
private set; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the row that contains the cell to be edited.
|
|||
/// </summary>
|
|||
public DataGridRow Row |
|||
{ |
|||
get; |
|||
private set; |
|||
} |
|||
|
|||
#endregion
|
|||
} |
|||
|
|||
|
|||
/// <summary>
|
|||
/// EventArgs used for the DataGrid's LoadingRowGroup and UnloadingRowGroup events
|
|||
/// </summary>
|
|||
/// <QualityBand>Preview</QualityBand>
|
|||
public class DataGridRowGroupHeaderEventArgs : EventArgs |
|||
{ |
|||
/// <summary>
|
|||
/// Constructs a DataGridRowGroupHeaderEventArgs instance
|
|||
/// </summary>
|
|||
/// <param name="rowGroupHeader"></param>
|
|||
public DataGridRowGroupHeaderEventArgs(DataGridRowGroupHeader rowGroupHeader) |
|||
{ |
|||
RowGroupHeader = rowGroupHeader; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// DataGridRowGroupHeader associated with this instance
|
|||
/// </summary>
|
|||
public DataGridRowGroupHeader RowGroupHeader |
|||
{ |
|||
get; |
|||
private set; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Provides data for the <see cref="E:Avalonia.Controls.DataGrid.LoadingRowDetails" />, <see cref="E:Avalonia.Controls.DataGrid.UnloadingRowDetails" />,
|
|||
/// and <see cref="E:Avalonia.Controls.DataGrid.RowDetailsVisibilityChanged" /> events.
|
|||
/// </summary>
|
|||
/// <QualityBand>Mature</QualityBand>
|
|||
public class DataGridRowDetailsEventArgs : EventArgs |
|||
{ |
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="T:Avalonia.Controls.DataGridRowDetailsEventArgs" /> class.
|
|||
/// </summary>
|
|||
/// <param name="row">The row that the event occurs for.</param>
|
|||
/// <param name="detailsElement">The row details section as a framework element.</param>
|
|||
public DataGridRowDetailsEventArgs(DataGridRow row, IControl detailsElement) |
|||
{ |
|||
Row = row; |
|||
DetailsElement = detailsElement; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the row details section as a framework element.
|
|||
/// </summary>
|
|||
public IControl DetailsElement |
|||
{ |
|||
get; |
|||
private set; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the row that the event occurs for.
|
|||
/// </summary>
|
|||
public DataGridRow Row |
|||
{ |
|||
get; |
|||
private set; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,25 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
|
|||
namespace Avalonia.Controls |
|||
{ |
|||
internal static class Extensions |
|||
{ |
|||
internal static Point Translate(this Visual fromElement, Visual toElement, Point fromPoint) |
|||
{ |
|||
if (fromElement == toElement) |
|||
{ |
|||
return fromPoint; |
|||
} |
|||
else |
|||
{ |
|||
var transform = fromElement.TransformToVisual(toElement); |
|||
if (transform.HasValue) |
|||
return fromPoint.Transform(transform.Value); |
|||
else |
|||
return fromPoint; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,850 @@ |
|||
// (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.Collections.Generic; |
|||
using System.Diagnostics; |
|||
using System; |
|||
using System.Text; |
|||
|
|||
namespace Avalonia.Controls |
|||
{ |
|||
internal class IndexToValueTable<T> : IEnumerable<Range<T>> |
|||
{ |
|||
private List<Range<T>> _list; |
|||
|
|||
public IndexToValueTable() |
|||
{ |
|||
_list = new List<Range<T>>(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Total number of indices represented in the table
|
|||
/// </summary>
|
|||
public int IndexCount |
|||
{ |
|||
get |
|||
{ |
|||
int indexCount = 0; |
|||
foreach (Range<T> range in _list) |
|||
{ |
|||
indexCount += range.Count; |
|||
} |
|||
return indexCount; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns true if the table is empty
|
|||
/// </summary>
|
|||
public bool IsEmpty |
|||
{ |
|||
get |
|||
{ |
|||
return _list.Count == 0; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns the number of index ranges in the table
|
|||
/// </summary>
|
|||
public int RangeCount |
|||
{ |
|||
get |
|||
{ |
|||
return _list.Count; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Add a value with an associated index to the table
|
|||
/// </summary>
|
|||
/// <param name="index">Index where the value is to be added or updated</param>
|
|||
/// <param name="value">Value to add</param>
|
|||
public void AddValue(int index, T value) |
|||
{ |
|||
AddValues(index, 1, value); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Add multiples values with an associated start index to the table
|
|||
/// </summary>
|
|||
/// <param name="startIndex">index where first value is added</param>
|
|||
/// <param name="count">Total number of values to add (must be greater than 0)</param>
|
|||
/// <param name="value">Value to add</param>
|
|||
public void AddValues(int startIndex, int count, T value) |
|||
{ |
|||
Debug.Assert(count > 0); |
|||
AddValuesPrivate(startIndex, count, value, null); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Clears the index table
|
|||
/// </summary>
|
|||
public void Clear() |
|||
{ |
|||
_list.Clear(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns true if the given index is contained in the table
|
|||
/// </summary>
|
|||
/// <param name="index">index to search for</param>
|
|||
/// <returns>True if the index is contained in the table</returns>
|
|||
public bool Contains(int index) |
|||
{ |
|||
return IsCorrectRangeIndex(this.FindRangeIndex(index), index); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns true if the entire given index range is contained in the table
|
|||
/// </summary>
|
|||
/// <param name="startIndex">beginning of the range</param>
|
|||
/// <param name="endIndex">end of the range</param>
|
|||
/// <returns>True if the entire index range is present in the table</returns>
|
|||
public bool ContainsAll(int startIndex, int endIndex) |
|||
{ |
|||
int start = -1; |
|||
int end = -1; |
|||
|
|||
foreach (Range<T> range in _list) |
|||
{ |
|||
if (start == -1 && range.UpperBound >= startIndex) |
|||
{ |
|||
if (startIndex < range.LowerBound) |
|||
{ |
|||
return false; |
|||
} |
|||
start = startIndex; |
|||
end = range.UpperBound; |
|||
if (end >= endIndex) |
|||
{ |
|||
return true; |
|||
} |
|||
} |
|||
else if (start != -1) |
|||
{ |
|||
if (range.LowerBound > end + 1) |
|||
{ |
|||
return false; |
|||
} |
|||
end = range.UpperBound; |
|||
if (end >= endIndex) |
|||
{ |
|||
return true; |
|||
} |
|||
} |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns true if the given index is contained in the table with the the given value
|
|||
/// </summary>
|
|||
/// <param name="index">index to search for</param>
|
|||
/// <param name="value">value expected</param>
|
|||
/// <returns>true if the given index is contained in the table with the the given value</returns>
|
|||
public bool ContainsIndexAndValue(int index, T value) |
|||
{ |
|||
int lowerRangeIndex = this.FindRangeIndex(index); |
|||
return ((IsCorrectRangeIndex(lowerRangeIndex, index)) && (_list[lowerRangeIndex].ContainsValue(value))); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns a copy of this IndexToValueTable
|
|||
/// </summary>
|
|||
/// <returns>copy of this IndexToValueTable</returns>
|
|||
public IndexToValueTable<T> Copy() |
|||
{ |
|||
IndexToValueTable<T> copy = new IndexToValueTable<T>(); |
|||
foreach (Range<T> range in this._list) |
|||
{ |
|||
copy._list.Add(range.Copy()); |
|||
} |
|||
return copy; |
|||
} |
|||
|
|||
public int GetNextGap(int index) |
|||
{ |
|||
int targetIndex = index + 1; |
|||
int rangeIndex = FindRangeIndex(targetIndex); |
|||
if (IsCorrectRangeIndex(rangeIndex, targetIndex)) |
|||
{ |
|||
while (rangeIndex < _list.Count - 1 && _list[rangeIndex].UpperBound == _list[rangeIndex + 1].LowerBound - 1) |
|||
{ |
|||
rangeIndex++; |
|||
} |
|||
return _list[rangeIndex].UpperBound + 1; |
|||
} |
|||
else |
|||
{ |
|||
return targetIndex; |
|||
} |
|||
} |
|||
|
|||
public int GetNextIndex(int index) |
|||
{ |
|||
int targetIndex = index + 1; |
|||
int rangeIndex = FindRangeIndex(targetIndex); |
|||
if (IsCorrectRangeIndex(rangeIndex, targetIndex)) |
|||
{ |
|||
return targetIndex; |
|||
} |
|||
else |
|||
{ |
|||
rangeIndex++; |
|||
return rangeIndex < _list.Count ? _list[rangeIndex].LowerBound : -1; |
|||
} |
|||
} |
|||
|
|||
public int GetPreviousGap(int index) |
|||
{ |
|||
int targetIndex = index - 1; |
|||
int rangeIndex = FindRangeIndex(targetIndex); |
|||
if (IsCorrectRangeIndex(rangeIndex, targetIndex)) |
|||
{ |
|||
while (rangeIndex > 0 && _list[rangeIndex].LowerBound == _list[rangeIndex - 1].UpperBound + 1) |
|||
{ |
|||
rangeIndex--; |
|||
} |
|||
return _list[rangeIndex].LowerBound - 1; |
|||
} |
|||
else |
|||
{ |
|||
return targetIndex; |
|||
} |
|||
} |
|||
|
|||
public int GetPreviousIndex(int index) |
|||
{ |
|||
int targetIndex = index - 1; |
|||
int rangeIndex = FindRangeIndex(targetIndex); |
|||
if (IsCorrectRangeIndex(rangeIndex, targetIndex)) |
|||
{ |
|||
return targetIndex; |
|||
} |
|||
else |
|||
{ |
|||
return rangeIndex >= 0 && rangeIndex < _list.Count ? _list[rangeIndex].UpperBound : -1; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns the inclusive index count between lowerBound and upperBound of all indexes with the given value
|
|||
/// </summary>
|
|||
/// <param name="lowerBound">lowerBound criteria</param>
|
|||
/// <param name="upperBound">upperBound criteria</param>
|
|||
/// <param name="value">value to look for</param>
|
|||
/// <returns>Number of indexes contained in the table between lowerBound and upperBound (inclusive)</returns>
|
|||
public int GetIndexCount(int lowerBound, int upperBound, T value) |
|||
{ |
|||
Debug.Assert(upperBound >= lowerBound); |
|||
if (_list.Count == 0) |
|||
{ |
|||
return 0; |
|||
} |
|||
int count = 0; |
|||
int index = FindRangeIndex(lowerBound); |
|||
if (IsCorrectRangeIndex(index, lowerBound) && _list[index].ContainsValue(value)) |
|||
{ |
|||
count += _list[index].UpperBound - lowerBound + 1; |
|||
} |
|||
index++; |
|||
while (index < _list.Count && _list[index].UpperBound <= upperBound) |
|||
{ |
|||
if (_list[index].ContainsValue(value)) |
|||
{ |
|||
count += _list[index].Count; |
|||
} |
|||
index++; |
|||
} |
|||
if (index < _list.Count && IsCorrectRangeIndex(index, upperBound) && _list[index].ContainsValue(value)) |
|||
{ |
|||
count += upperBound - _list[index].LowerBound; |
|||
} |
|||
return count; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns the inclusive index count between lowerBound and upperBound
|
|||
/// </summary>
|
|||
/// <param name="lowerBound">lowerBound criteria</param>
|
|||
/// <param name="upperBound">upperBound criteria</param>
|
|||
/// <returns>Number of indexes contained in the table between lowerBound and upperBound (inclusive)</returns>
|
|||
public int GetIndexCount(int lowerBound, int upperBound) |
|||
{ |
|||
if (upperBound < lowerBound || _list.Count == 0) |
|||
{ |
|||
return 0; |
|||
} |
|||
int count = 0; |
|||
int index = this.FindRangeIndex(lowerBound); |
|||
if (IsCorrectRangeIndex(index, lowerBound)) |
|||
{ |
|||
count += _list[index].UpperBound - lowerBound + 1; |
|||
} |
|||
index++; |
|||
while (index < _list.Count && _list[index].UpperBound <= upperBound) |
|||
{ |
|||
count += _list[index].Count; |
|||
index++; |
|||
} |
|||
if (index < _list.Count && IsCorrectRangeIndex(index, upperBound)) |
|||
{ |
|||
count += upperBound - _list[index].LowerBound; |
|||
} |
|||
return count; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns the number indexes in this table after a given startingIndex but before
|
|||
/// reaching a gap of indexes of a given size
|
|||
/// </summary>
|
|||
/// <param name="startingIndex">Index to start at</param>
|
|||
/// <param name="gapSize">Size of index gap</param>
|
|||
/// <returns></returns>
|
|||
public int GetIndexCountBeforeGap(int startingIndex, int gapSize) |
|||
{ |
|||
if (_list.Count == 0) |
|||
{ |
|||
return 0; |
|||
} |
|||
|
|||
int count = 0; |
|||
int currentIndex = startingIndex; |
|||
int rangeIndex = 0; |
|||
int gap = 0; |
|||
while (gap <= gapSize && rangeIndex < _list.Count) |
|||
{ |
|||
gap += _list[rangeIndex].LowerBound - currentIndex; |
|||
if (gap <= gapSize) |
|||
{ |
|||
count += _list[rangeIndex].UpperBound - _list[rangeIndex].LowerBound + 1; |
|||
currentIndex = _list[rangeIndex].UpperBound + 1; |
|||
rangeIndex++; |
|||
} |
|||
} |
|||
return count; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns an enumerator that goes through the indexes present in the table
|
|||
/// </summary>
|
|||
/// <returns>an enumerator that enumerates the indexes present in the table</returns>
|
|||
public IEnumerable<int> GetIndexes() |
|||
{ |
|||
Debug.Assert(_list != null); |
|||
|
|||
foreach (Range<T> range in _list) |
|||
{ |
|||
for (int i = range.LowerBound; i <= range.UpperBound; i++) |
|||
{ |
|||
yield return i; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns all the indexes on or after a starting index
|
|||
/// </summary>
|
|||
/// <param name="startIndex">start index</param>
|
|||
/// <returns></returns>
|
|||
public IEnumerable<int> GetIndexes(int startIndex) |
|||
{ |
|||
Debug.Assert(_list != null); |
|||
|
|||
int rangeIndex = FindRangeIndex(startIndex); |
|||
if (rangeIndex == -1) |
|||
{ |
|||
rangeIndex++; |
|||
} |
|||
|
|||
while (rangeIndex < _list.Count) |
|||
{ |
|||
for (int i = _list[rangeIndex].LowerBound; i <= _list[rangeIndex].UpperBound; i++) |
|||
{ |
|||
if (i >= startIndex) |
|||
{ |
|||
yield return i; |
|||
} |
|||
} |
|||
rangeIndex++; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Return the index of the Nth element in the table.
|
|||
/// </summary>
|
|||
/// <param name="n">n</param>
|
|||
public int GetNthIndex(int n) |
|||
{ |
|||
Debug.Assert(n >= 0 && n < this.IndexCount); |
|||
int cumulatedEntries = 0; |
|||
foreach (Range<T> range in _list) |
|||
{ |
|||
if (cumulatedEntries + range.Count > n) |
|||
{ |
|||
return range.LowerBound + n - cumulatedEntries; |
|||
} |
|||
else |
|||
{ |
|||
cumulatedEntries += range.Count; |
|||
} |
|||
} |
|||
return -1; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns the value at a given index or the default value if the index is not in the table
|
|||
/// </summary>
|
|||
/// <param name="index">index to search for</param>
|
|||
/// <returns>the value at the given index or the default value if index is not in the table</returns>
|
|||
public T GetValueAt(int index) |
|||
{ |
|||
return GetValueAt(index, out bool found); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns the value at a given index or the default value if the index is not in the table
|
|||
/// </summary>
|
|||
/// <param name="index">index to search for</param>
|
|||
/// <param name="found">set to true by the method if the index was found; otherwise, false</param>
|
|||
/// <returns>the value at the given index or the default value if index is not in the table</returns>
|
|||
public T GetValueAt(int index, out bool found) |
|||
{ |
|||
int rangeIndex = this.FindRangeIndex(index); |
|||
if (this.IsCorrectRangeIndex(rangeIndex, index)) |
|||
{ |
|||
found = true; |
|||
return _list[rangeIndex].Value; |
|||
} |
|||
else |
|||
{ |
|||
found = false; |
|||
return default(T); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns an index's index within this table
|
|||
/// </summary>
|
|||
/// <param name="index"></param>
|
|||
/// <returns></returns>
|
|||
public int IndexOf(int index) |
|||
{ |
|||
int cumulatedIndexes = 0; |
|||
foreach (Range<T> range in _list) |
|||
{ |
|||
if (range.UpperBound >= index) |
|||
{ |
|||
cumulatedIndexes += index - range.LowerBound; |
|||
break; |
|||
} |
|||
else |
|||
{ |
|||
cumulatedIndexes += range.Count; |
|||
} |
|||
} |
|||
return cumulatedIndexes; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Inserts an index at the given location. This does not alter values in the table
|
|||
/// </summary>
|
|||
/// <param name="index">index location to insert an index</param>
|
|||
public void InsertIndex(int index) |
|||
{ |
|||
InsertIndexes(index, 1); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Inserts an index into the table with the given value
|
|||
/// </summary>
|
|||
/// <param name="index">index to insert</param>
|
|||
/// <param name="value">value for the index</param>
|
|||
public void InsertIndexAndValue(int index, T value) |
|||
{ |
|||
InsertIndexesAndValues(index, 1, value); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Inserts multiple indexes into the table. This does not alter Values in the table
|
|||
/// </summary>
|
|||
/// <param name="startIndex">first index to insert</param>
|
|||
/// <param name="count">total number of indexes to insert</param>
|
|||
public void InsertIndexes(int startIndex, int count) |
|||
{ |
|||
Debug.Assert(count > 0); |
|||
InsertIndexesPrivate(startIndex, count, this.FindRangeIndex(startIndex)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Inserts multiple indexes into the table with the given value
|
|||
/// </summary>
|
|||
/// <param name="startIndex">Index to insert first value</param>
|
|||
/// <param name="count">Total number of values to insert (must be greater than 0)</param>
|
|||
/// <param name="value">Value to insert</param>
|
|||
public void InsertIndexesAndValues(int startIndex, int count, T value) |
|||
{ |
|||
Debug.Assert(count > 0); |
|||
int lowerRangeIndex = this.FindRangeIndex(startIndex); |
|||
InsertIndexesPrivate(startIndex, count, lowerRangeIndex); |
|||
if ((lowerRangeIndex >= 0) && (_list[lowerRangeIndex].LowerBound > startIndex)) |
|||
{ |
|||
// Because of the insert, the original range no longer contains the startIndex
|
|||
lowerRangeIndex--; |
|||
} |
|||
AddValuesPrivate(startIndex, count, value, lowerRangeIndex); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Removes an index from the table. This does not alter Values in the table
|
|||
/// </summary>
|
|||
/// <param name="index">index to remove</param>
|
|||
public void RemoveIndex(int index) |
|||
{ |
|||
RemoveIndexes(index, 1); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Removes a value and its index from the table
|
|||
/// </summary>
|
|||
/// <param name="index">index to remove</param>
|
|||
public void RemoveIndexAndValue(int index) |
|||
{ |
|||
RemoveIndexesAndValues(index, 1); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Removes multiple indexes from the table. This does not alter Values in the table
|
|||
/// </summary>
|
|||
/// <param name="startIndex">first index to remove</param>
|
|||
/// <param name="count">total number of indexes to remove</param>
|
|||
public void RemoveIndexes(int startIndex, int count) |
|||
{ |
|||
int lowerRangeIndex = this.FindRangeIndex(startIndex); |
|||
if (lowerRangeIndex < 0) |
|||
{ |
|||
lowerRangeIndex = 0; |
|||
} |
|||
int i = lowerRangeIndex; |
|||
while (i < _list.Count) |
|||
{ |
|||
Range<T> range = _list[i]; |
|||
if (range.UpperBound >= startIndex) |
|||
{ |
|||
if (range.LowerBound >= startIndex + count) |
|||
{ |
|||
// Both bounds will remain after the removal
|
|||
range.LowerBound -= count; |
|||
range.UpperBound -= count; |
|||
} |
|||
else |
|||
{ |
|||
int currentIndex = i; |
|||
if (range.LowerBound <= startIndex) |
|||
{ |
|||
// Range gets split up
|
|||
if (range.UpperBound >= startIndex + count) |
|||
{ |
|||
i++; |
|||
_list.Insert(i, new Range<T>(startIndex, range.UpperBound - count, range.Value)); |
|||
} |
|||
range.UpperBound = startIndex - 1; |
|||
} |
|||
else |
|||
{ |
|||
range.LowerBound = startIndex; |
|||
range.UpperBound -= count; |
|||
} |
|||
if (RemoveRangeIfInvalid(range, currentIndex)) |
|||
{ |
|||
i--; |
|||
} |
|||
} |
|||
} |
|||
i++; |
|||
} |
|||
if (!this.Merge(lowerRangeIndex)) |
|||
{ |
|||
this.Merge(lowerRangeIndex + 1); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Removes multiple values and their indexes from the table
|
|||
/// </summary>
|
|||
/// <param name="startIndex">first index to remove</param>
|
|||
/// <param name="count">total number of indexes to remove</param>
|
|||
public void RemoveIndexesAndValues(int startIndex, int count) |
|||
{ |
|||
RemoveValues(startIndex, count); |
|||
RemoveIndexes(startIndex, count); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Removes a value from the table at the given index. This does not alter other indexes in the table.
|
|||
/// </summary>
|
|||
/// <param name="index">index where value should be removed</param>
|
|||
public void RemoveValue(int index) |
|||
{ |
|||
RemoveValues(index, 1); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Removes multiple values from the table. This does not alter other indexes in the table.
|
|||
/// </summary>
|
|||
/// <param name="startIndex">first index where values should be removed </param>
|
|||
/// <param name="count">total number of values to remove</param>
|
|||
public void RemoveValues(int startIndex, int count) |
|||
{ |
|||
Debug.Assert(count > 0); |
|||
|
|||
int lowerRangeIndex = this.FindRangeIndex(startIndex); |
|||
if (lowerRangeIndex < 0) |
|||
{ |
|||
lowerRangeIndex = 0; |
|||
} |
|||
while ((lowerRangeIndex < _list.Count) && (_list[lowerRangeIndex].UpperBound < startIndex)) |
|||
{ |
|||
lowerRangeIndex++; |
|||
} |
|||
if (lowerRangeIndex >= _list.Count || _list[lowerRangeIndex].LowerBound > startIndex + count - 1) |
|||
{ |
|||
// If all the values are above our below our values, we have nothing to remove
|
|||
return; |
|||
} |
|||
if (_list[lowerRangeIndex].LowerBound < startIndex) |
|||
{ |
|||
// Need to split this up
|
|||
_list.Insert(lowerRangeIndex, new Range<T>(_list[lowerRangeIndex].LowerBound, startIndex - 1, _list[lowerRangeIndex].Value)); |
|||
lowerRangeIndex++; |
|||
} |
|||
_list[lowerRangeIndex].LowerBound = startIndex + count; |
|||
if (!RemoveRangeIfInvalid(_list[lowerRangeIndex], lowerRangeIndex)) |
|||
{ |
|||
lowerRangeIndex++; |
|||
} |
|||
while ((lowerRangeIndex < _list.Count) && (_list[lowerRangeIndex].UpperBound < startIndex + count)) |
|||
{ |
|||
_list.RemoveAt(lowerRangeIndex); |
|||
} |
|||
if ((lowerRangeIndex < _list.Count) && (_list[lowerRangeIndex].UpperBound >= startIndex + count) && |
|||
(_list[lowerRangeIndex].LowerBound < startIndex + count)) |
|||
{ |
|||
// Chop off the start of the remaining Range if it contains values that we're removing
|
|||
_list[lowerRangeIndex].LowerBound = startIndex + count; |
|||
RemoveRangeIfInvalid(_list[lowerRangeIndex], lowerRangeIndex); |
|||
} |
|||
} |
|||
|
|||
private void AddValuesPrivate(int startIndex, int count, T value, int? startRangeIndex) |
|||
{ |
|||
Debug.Assert(count > 0); |
|||
|
|||
int endIndex = startIndex + count - 1; |
|||
Range<T> newRange = new Range<T>(startIndex, endIndex, value); |
|||
if (_list.Count == 0) |
|||
{ |
|||
_list.Add(newRange); |
|||
} |
|||
else |
|||
{ |
|||
int lowerRangeIndex = startRangeIndex ?? FindRangeIndex(startIndex); |
|||
Range<T> lowerRange = (lowerRangeIndex < 0) ? null : _list[lowerRangeIndex]; |
|||
if (lowerRange == null) |
|||
{ |
|||
if (lowerRangeIndex < 0) |
|||
{ |
|||
lowerRangeIndex = 0; |
|||
} |
|||
_list.Insert(lowerRangeIndex, newRange); |
|||
} |
|||
else |
|||
{ |
|||
if (!lowerRange.Value.Equals(value) && (lowerRange.UpperBound >= startIndex)) |
|||
{ |
|||
// Split up the range
|
|||
if (lowerRange.UpperBound > endIndex) |
|||
{ |
|||
_list.Insert(lowerRangeIndex + 1, new Range<T>(endIndex + 1, lowerRange.UpperBound, lowerRange.Value)); |
|||
} |
|||
lowerRange.UpperBound = startIndex - 1; |
|||
if (!RemoveRangeIfInvalid(lowerRange, lowerRangeIndex)) |
|||
{ |
|||
lowerRangeIndex++; |
|||
} |
|||
_list.Insert(lowerRangeIndex, newRange); |
|||
} |
|||
else |
|||
{ |
|||
_list.Insert(lowerRangeIndex + 1, newRange); |
|||
if (!Merge(lowerRangeIndex)) |
|||
{ |
|||
lowerRangeIndex++; |
|||
} |
|||
} |
|||
} |
|||
|
|||
// At this point the newRange has been inserted in the correct place, now we need to remove
|
|||
// any subsequent ranges that no longer make sense and possibly update the one at newRange.UpperBound
|
|||
int upperRangeIndex = lowerRangeIndex + 1; |
|||
while ((upperRangeIndex < _list.Count) && (_list[upperRangeIndex].UpperBound < endIndex)) |
|||
{ |
|||
_list.RemoveAt(upperRangeIndex); |
|||
} |
|||
if (upperRangeIndex < _list.Count) |
|||
{ |
|||
Range<T> upperRange = _list[upperRangeIndex]; |
|||
if (upperRange.LowerBound <= endIndex) |
|||
{ |
|||
// Update the range
|
|||
upperRange.LowerBound = endIndex + 1; |
|||
RemoveRangeIfInvalid(upperRange, upperRangeIndex); |
|||
} |
|||
Merge(lowerRangeIndex); |
|||
} |
|||
} |
|||
} |
|||
|
|||
// Returns the index of the range that contains the input or the range before if the input is not found
|
|||
private int FindRangeIndex(int index) |
|||
{ |
|||
if (_list.Count == 0) |
|||
{ |
|||
return -1; |
|||
} |
|||
|
|||
// Do a binary search for the index
|
|||
int front = 0; |
|||
int end = _list.Count - 1; |
|||
Range<T> range = null; |
|||
while (end > front) |
|||
{ |
|||
int median = (front + end) / 2; |
|||
range = _list[median]; |
|||
if (range.UpperBound < index) |
|||
{ |
|||
front = median + 1; |
|||
} |
|||
else if (range.LowerBound > index) |
|||
{ |
|||
end = median - 1; |
|||
} |
|||
else |
|||
{ |
|||
// we found it
|
|||
return median; |
|||
} |
|||
} |
|||
|
|||
if (front == end) |
|||
{ |
|||
range = _list[front]; |
|||
if (range.ContainsIndex(index) || (range.UpperBound < index)) |
|||
{ |
|||
// we found it or the index isn't there and we're one range before
|
|||
return front; |
|||
} |
|||
else |
|||
{ |
|||
// not found and we're one range after
|
|||
return front - 1; |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
// end is one index before front in this case so it's the range before
|
|||
return end; |
|||
} |
|||
} |
|||
|
|||
private bool Merge(int lowerRangeIndex) |
|||
{ |
|||
int upperRangeIndex = lowerRangeIndex + 1; |
|||
if ((lowerRangeIndex >= 0) && (upperRangeIndex < _list.Count)) |
|||
{ |
|||
Range<T> lowerRange = _list[lowerRangeIndex]; |
|||
Range<T> upperRange = _list[upperRangeIndex]; |
|||
if ((lowerRange.UpperBound + 1 >= upperRange.LowerBound) && (lowerRange.Value.Equals(upperRange.Value))) |
|||
{ |
|||
lowerRange.UpperBound = Math.Max(lowerRange.UpperBound, upperRange.UpperBound); |
|||
_list.RemoveAt(upperRangeIndex); |
|||
return true; |
|||
} |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
private void InsertIndexesPrivate(int startIndex, int count, int lowerRangeIndex) |
|||
{ |
|||
Debug.Assert(count > 0); |
|||
|
|||
// Same as AddRange after we fix the indicies affected by the insertion
|
|||
int startRangeIndex = (lowerRangeIndex >= 0) ? lowerRangeIndex : 0; |
|||
for (int i = startRangeIndex; i < _list.Count; i++) |
|||
{ |
|||
Range<T> range = _list[i]; |
|||
if (range.LowerBound >= startIndex) |
|||
{ |
|||
range.LowerBound += count; |
|||
} |
|||
else |
|||
{ |
|||
if (range.UpperBound >= startIndex) |
|||
{ |
|||
// Split up this range
|
|||
i++; |
|||
_list.Insert(i, new Range<T>(startIndex, range.UpperBound + count, range.Value)); |
|||
range.UpperBound = startIndex - 1; |
|||
continue; |
|||
} |
|||
} |
|||
|
|||
if (range.UpperBound >= startIndex) |
|||
{ |
|||
range.UpperBound += count; |
|||
} |
|||
} |
|||
} |
|||
|
|||
private bool IsCorrectRangeIndex(int rangeIndex, int index) |
|||
{ |
|||
return (-1 != rangeIndex) && (_list[rangeIndex].ContainsIndex(index)); |
|||
} |
|||
|
|||
private bool RemoveRangeIfInvalid(Range<T> range, int rangeIndex) |
|||
{ |
|||
if (range.UpperBound < range.LowerBound) |
|||
{ |
|||
_list.RemoveAt(rangeIndex); |
|||
return true; |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
public IEnumerator<Range<T>> GetEnumerator() |
|||
{ |
|||
return _list.GetEnumerator(); |
|||
} |
|||
|
|||
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() |
|||
{ |
|||
return _list.GetEnumerator(); |
|||
} |
|||
|
|||
#if DEBUG
|
|||
|
|||
public void PrintIndexes() |
|||
{ |
|||
Debug.WriteLine(this.IndexCount + " indexes"); |
|||
foreach (Range<T> range in _list) |
|||
{ |
|||
Debug.WriteLine(String.Format(System.Globalization.CultureInfo.InvariantCulture, "{0} - {1}", range.LowerBound, range.UpperBound)); |
|||
} |
|||
} |
|||
|
|||
#endif
|
|||
} |
|||
} |
|||
@ -0,0 +1,313 @@ |
|||
// (c) Copyright Microsoft Corporation.
|
|||
// This source is subject to the Microsoft Public License (Ms-PL).
|
|||
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
|
|||
// All other rights reserved.
|
|||
|
|||
using Avalonia.Media; |
|||
using Avalonia.Utilities; |
|||
using System; |
|||
using System.Diagnostics; |
|||
|
|||
namespace Avalonia.Controls.Primitives |
|||
{ |
|||
/// <summary>
|
|||
/// Used within the template of a <see cref="T:Avalonia.Controls.DataGrid" />
|
|||
/// to specify the location in the control's visual tree where the cells are to be added.
|
|||
/// </summary>
|
|||
public sealed class DataGridCellsPresenter : Panel |
|||
{ |
|||
private double _fillerLeftEdge; |
|||
|
|||
// The desired height needs to be cached due to column virtualization; otherwise, the cells
|
|||
// would grow and shrink as the DataGrid scrolls horizontally
|
|||
private double DesiredHeight |
|||
{ |
|||
get; |
|||
set; |
|||
} |
|||
|
|||
private DataGrid OwningGrid |
|||
{ |
|||
get |
|||
{ |
|||
return OwningRow?.OwningGrid; |
|||
} |
|||
} |
|||
|
|||
internal DataGridRow OwningRow |
|||
{ |
|||
get; |
|||
set; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Arranges the content of the <see cref="T:Avalonia.Controls.Primitives.DataGridCellsPresenter" />.
|
|||
/// </summary>
|
|||
/// <returns>
|
|||
/// The actual size used by the <see cref="T:Avalonia.Controls.Primitives.DataGridCellsPresenter" />.
|
|||
/// </returns>
|
|||
/// <param name="finalSize">
|
|||
/// The final area within the parent that this element should use to arrange itself and its children.
|
|||
/// </param>
|
|||
protected override Size ArrangeOverride(Size finalSize) |
|||
{ |
|||
if (OwningGrid == null) |
|||
{ |
|||
return base.ArrangeOverride(finalSize); |
|||
} |
|||
|
|||
if (OwningGrid.AutoSizingColumns) |
|||
{ |
|||
// When we initially load an auto-column, we have to wait for all the rows to be measured
|
|||
// before we know its final desired size. We need to trigger a new round of measures now
|
|||
// that the final sizes have been calculated.
|
|||
OwningGrid.AutoSizingColumns = false; |
|||
return base.ArrangeOverride(finalSize); |
|||
} |
|||
|
|||
double frozenLeftEdge = 0; |
|||
double scrollingLeftEdge = -OwningGrid.HorizontalOffset; |
|||
|
|||
double cellLeftEdge; |
|||
foreach (DataGridColumn column in OwningGrid.ColumnsInternal.GetVisibleColumns()) |
|||
{ |
|||
DataGridCell cell = OwningRow.Cells[column.Index]; |
|||
Debug.Assert(cell.OwningColumn == column); |
|||
Debug.Assert(column.IsVisible); |
|||
|
|||
if (column.IsFrozen) |
|||
{ |
|||
cellLeftEdge = frozenLeftEdge; |
|||
// This can happen before or after clipping because frozen cells aren't clipped
|
|||
frozenLeftEdge += column.ActualWidth; |
|||
} |
|||
else |
|||
{ |
|||
cellLeftEdge = scrollingLeftEdge; |
|||
} |
|||
if (cell.IsVisible) |
|||
{ |
|||
cell.Arrange(new Rect(cellLeftEdge, 0, column.LayoutRoundedWidth, finalSize.Height)); |
|||
EnsureCellClip(cell, column.ActualWidth, finalSize.Height, frozenLeftEdge, scrollingLeftEdge); |
|||
} |
|||
scrollingLeftEdge += column.ActualWidth; |
|||
column.IsInitialDesiredWidthDetermined = true; |
|||
} |
|||
|
|||
_fillerLeftEdge = scrollingLeftEdge; |
|||
// FillerColumn.Width == 0 when the filler column is not active
|
|||
OwningRow.FillerCell.Arrange(new Rect(_fillerLeftEdge, 0, OwningGrid.ColumnsInternal.FillerColumn.FillerWidth, finalSize.Height)); |
|||
|
|||
return finalSize; |
|||
} |
|||
|
|||
private static void EnsureCellClip(DataGridCell cell, double width, double height, double frozenLeftEdge, double cellLeftEdge) |
|||
{ |
|||
// Clip the cell only if it's scrolled under frozen columns. Unfortunately, we need to clip in this case
|
|||
// because cells could be transparent
|
|||
if (!cell.OwningColumn.IsFrozen && frozenLeftEdge > cellLeftEdge) |
|||
{ |
|||
RectangleGeometry rg = new RectangleGeometry(); |
|||
double xClip = Math.Round(Math.Min(width, frozenLeftEdge - cellLeftEdge)); |
|||
rg.Rect = new Rect(xClip, 0, Math.Max(0, width - xClip), height); |
|||
cell.Clip = rg; |
|||
} |
|||
else |
|||
{ |
|||
cell.Clip = null; |
|||
} |
|||
} |
|||
|
|||
private static void EnsureCellDisplay(DataGridCell cell, bool displayColumn) |
|||
{ |
|||
if (cell.IsCurrent) |
|||
{ |
|||
if (displayColumn) |
|||
{ |
|||
cell.IsVisible = true; |
|||
cell.Clip = null; |
|||
} |
|||
else |
|||
{ |
|||
// Clip
|
|||
RectangleGeometry rg = new RectangleGeometry(); |
|||
rg.Rect = Rect.Empty; |
|||
cell.Clip = rg; |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
cell.IsVisible = displayColumn; |
|||
} |
|||
} |
|||
|
|||
internal void EnsureFillerVisibility() |
|||
{ |
|||
DataGridFillerColumn fillerColumn = OwningGrid.ColumnsInternal.FillerColumn; |
|||
bool newVisibility = fillerColumn.IsActive; |
|||
if (OwningRow.FillerCell.IsVisible != newVisibility) |
|||
{ |
|||
OwningRow.FillerCell.IsVisible = newVisibility; |
|||
if (newVisibility) |
|||
{ |
|||
OwningRow.FillerCell.Arrange(new Rect(_fillerLeftEdge, 0, fillerColumn.FillerWidth, Bounds.Height)); |
|||
} |
|||
} |
|||
|
|||
// This must be done after the Filler visibility is determined. This also must be done
|
|||
// regardless of whether or not the filler visibility actually changed values because
|
|||
// we could scroll in a cell that didn't have EnsureGridLine called yet
|
|||
DataGridColumn lastVisibleColumn = OwningGrid.ColumnsInternal.LastVisibleColumn; |
|||
if (lastVisibleColumn != null) |
|||
{ |
|||
DataGridCell cell = OwningRow.Cells[lastVisibleColumn.Index]; |
|||
cell.EnsureGridLine(lastVisibleColumn); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Measures the children of a <see cref="T:Avalonia.Controls.Primitives.DataGridCellsPresenter" /> to
|
|||
/// prepare for arranging them during the <see cref="M:System.Windows.FrameworkElement.ArrangeOverride(System.Windows.Size)" /> pass.
|
|||
/// </summary>
|
|||
/// <param name="availableSize">
|
|||
/// The available size that this element can give to child elements. Indicates an upper limit that child elements should not exceed.
|
|||
/// </param>
|
|||
/// <returns>
|
|||
/// The size that the <see cref="T:Avalonia.Controls.Primitives.DataGridCellsPresenter" /> determines it needs during layout, based on its calculations of child object allocated sizes.
|
|||
/// </returns>
|
|||
protected override Size MeasureOverride(Size availableSize) |
|||
{ |
|||
if (OwningGrid == null) |
|||
{ |
|||
return base.MeasureOverride(availableSize); |
|||
} |
|||
|
|||
bool autoSizeHeight; |
|||
double measureHeight; |
|||
if (double.IsNaN(OwningGrid.RowHeight)) |
|||
{ |
|||
// No explicit height values were set so we can autosize
|
|||
autoSizeHeight = true; |
|||
measureHeight = double.PositiveInfinity; |
|||
} |
|||
else |
|||
{ |
|||
DesiredHeight = OwningGrid.RowHeight; |
|||
measureHeight = DesiredHeight; |
|||
autoSizeHeight = false; |
|||
} |
|||
|
|||
double frozenLeftEdge = 0; |
|||
double totalDisplayWidth = 0; |
|||
double scrollingLeftEdge = -OwningGrid.HorizontalOffset; |
|||
OwningGrid.ColumnsInternal.EnsureVisibleEdgedColumnsWidth(); |
|||
DataGridColumn lastVisibleColumn = OwningGrid.ColumnsInternal.LastVisibleColumn; |
|||
foreach (DataGridColumn column in OwningGrid.ColumnsInternal.GetVisibleColumns()) |
|||
{ |
|||
DataGridCell cell = OwningRow.Cells[column.Index]; |
|||
// Measure the entire first row to make the horizontal scrollbar more accurate
|
|||
bool shouldDisplayCell = ShouldDisplayCell(column, frozenLeftEdge, scrollingLeftEdge) || OwningRow.Index == 0; |
|||
EnsureCellDisplay(cell, shouldDisplayCell); |
|||
if (shouldDisplayCell) |
|||
{ |
|||
DataGridLength columnWidth = column.Width; |
|||
bool autoGrowWidth = columnWidth.IsSizeToCells || columnWidth.IsAuto; |
|||
if (column != lastVisibleColumn) |
|||
{ |
|||
cell.EnsureGridLine(lastVisibleColumn); |
|||
} |
|||
|
|||
// If we're not using star sizing or the current column can't be resized,
|
|||
// then just set the display width according to the column's desired width
|
|||
if (!OwningGrid.UsesStarSizing || (!column.ActualCanUserResize && !column.Width.IsStar)) |
|||
{ |
|||
// In the edge-case where we're given infinite width and we have star columns, the
|
|||
// star columns grow to their predefined limit of 10,000 (or their MaxWidth)
|
|||
double newDisplayWidth = column.Width.IsStar ? |
|||
Math.Min(column.ActualMaxWidth, DataGrid.DATAGRID_maximumStarColumnWidth) : |
|||
Math.Max(column.ActualMinWidth, Math.Min(column.ActualMaxWidth, column.Width.DesiredValue)); |
|||
column.SetWidthDisplayValue(newDisplayWidth); |
|||
} |
|||
|
|||
// If we're auto-growing the column based on the cell content, we want to measure it at its maximum value
|
|||
if (autoGrowWidth) |
|||
{ |
|||
cell.Measure(new Size(column.ActualMaxWidth, measureHeight)); |
|||
OwningGrid.AutoSizeColumn(column, cell.DesiredSize.Width); |
|||
column.ComputeLayoutRoundedWidth(totalDisplayWidth); |
|||
} |
|||
else if (!OwningGrid.UsesStarSizing) |
|||
{ |
|||
column.ComputeLayoutRoundedWidth(scrollingLeftEdge); |
|||
cell.Measure(new Size(column.LayoutRoundedWidth, measureHeight)); |
|||
} |
|||
|
|||
// We need to track the largest height in order to auto-size
|
|||
if (autoSizeHeight) |
|||
{ |
|||
DesiredHeight = Math.Max(DesiredHeight, cell.DesiredSize.Height); |
|||
} |
|||
} |
|||
|
|||
if (column.IsFrozen) |
|||
{ |
|||
frozenLeftEdge += column.ActualWidth; |
|||
} |
|||
scrollingLeftEdge += column.ActualWidth; |
|||
totalDisplayWidth += column.ActualWidth; |
|||
} |
|||
|
|||
// If we're using star sizing (and we're not waiting for an auto-column to finish growing)
|
|||
// then we will resize all the columns to fit the available space.
|
|||
if (OwningGrid.UsesStarSizing && !OwningGrid.AutoSizingColumns) |
|||
{ |
|||
double adjustment = OwningGrid.CellsWidth - totalDisplayWidth; |
|||
totalDisplayWidth += adjustment - OwningGrid.AdjustColumnWidths(0, adjustment, false); |
|||
|
|||
// Since we didn't know the final widths of the columns until we resized,
|
|||
// we waited until now to measure each cell
|
|||
double leftEdge = 0; |
|||
foreach (DataGridColumn column in OwningGrid.ColumnsInternal.GetVisibleColumns()) |
|||
{ |
|||
DataGridCell cell = OwningRow.Cells[column.Index]; |
|||
column.ComputeLayoutRoundedWidth(leftEdge); |
|||
cell.Measure(new Size(column.LayoutRoundedWidth, measureHeight)); |
|||
if (autoSizeHeight) |
|||
{ |
|||
DesiredHeight = Math.Max(DesiredHeight, cell.DesiredSize.Height); |
|||
} |
|||
leftEdge += column.ActualWidth; |
|||
} |
|||
} |
|||
|
|||
// Measure FillerCell, we're doing it unconditionally here because we don't know if we'll need the filler
|
|||
// column and we don't want to cause another Measure if we do
|
|||
OwningRow.FillerCell.Measure(new Size(double.PositiveInfinity, DesiredHeight)); |
|||
|
|||
OwningGrid.ColumnsInternal.EnsureVisibleEdgedColumnsWidth(); |
|||
return new Size(OwningGrid.ColumnsInternal.VisibleEdgedColumnsWidth, DesiredHeight); |
|||
} |
|||
|
|||
internal void Recycle() |
|||
{ |
|||
// Clear out the cached desired height so it is not reused for other rows
|
|||
DesiredHeight = 0; |
|||
} |
|||
|
|||
private bool ShouldDisplayCell(DataGridColumn column, double frozenLeftEdge, double scrollingLeftEdge) |
|||
{ |
|||
if (!column.IsVisible) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
scrollingLeftEdge += OwningGrid.HorizontalAdjustment; |
|||
double leftEdge = column.IsFrozen ? frozenLeftEdge : scrollingLeftEdge; |
|||
double rightEdge = leftEdge + column.ActualWidth; |
|||
return |
|||
DoubleUtil.GreaterThan(rightEdge, 0) && |
|||
DoubleUtil.LessThanOrClose(leftEdge, OwningGrid.CellsWidth) && |
|||
DoubleUtil.GreaterThan(rightEdge, frozenLeftEdge); // scrolling column covered up by frozen column(s)
|
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,395 @@ |
|||
// (c) Copyright Microsoft Corporation.
|
|||
// This source is subject to the Microsoft Public License (Ms-PL).
|
|||
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
|
|||
// All other rights reserved.
|
|||
|
|||
using Avalonia.Media; |
|||
using System; |
|||
using System.Diagnostics; |
|||
|
|||
namespace Avalonia.Controls.Primitives |
|||
{ |
|||
/// <summary>
|
|||
/// Used within the template of a <see cref="T:Avalonia.Controls.DataGrid" /> to specify the
|
|||
/// location in the control's visual tree where the column headers are to be added.
|
|||
/// </summary>
|
|||
/// <QualityBand>Mature</QualityBand>
|
|||
public sealed class DataGridColumnHeadersPresenter : Panel |
|||
{ |
|||
private Control _dragIndicator; |
|||
private Control _dropLocationIndicator; |
|||
|
|||
/// <summary>
|
|||
/// Tracks which column is currently being dragged.
|
|||
/// </summary>
|
|||
internal DataGridColumn DragColumn |
|||
{ |
|||
get; |
|||
set; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// The current drag indicator control. This value is null if no column is being dragged.
|
|||
/// </summary>
|
|||
internal Control DragIndicator |
|||
{ |
|||
get |
|||
{ |
|||
return _dragIndicator; |
|||
} |
|||
set |
|||
{ |
|||
if (value != _dragIndicator) |
|||
{ |
|||
if (Children.Contains(_dragIndicator)) |
|||
{ |
|||
Children.Remove(_dragIndicator); |
|||
} |
|||
_dragIndicator = value; |
|||
if (_dragIndicator != null) |
|||
{ |
|||
Children.Add(_dragIndicator); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// The distance, in pixels, that the DragIndicator should be positioned away from the corresponding DragColumn.
|
|||
/// </summary>
|
|||
internal Double DragIndicatorOffset |
|||
{ |
|||
get; |
|||
set; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// The drop location indicator control. This value is null if no column is being dragged.
|
|||
/// </summary>
|
|||
internal Control DropLocationIndicator |
|||
{ |
|||
get |
|||
{ |
|||
return _dropLocationIndicator; |
|||
} |
|||
set |
|||
{ |
|||
if (value != _dropLocationIndicator) |
|||
{ |
|||
if (Children.Contains(_dropLocationIndicator)) |
|||
{ |
|||
Children.Remove(_dropLocationIndicator); |
|||
} |
|||
_dropLocationIndicator = value; |
|||
if (_dropLocationIndicator != null) |
|||
{ |
|||
Children.Add(_dropLocationIndicator); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// The distance, in pixels, that the drop location indicator should be positioned away from the left edge
|
|||
/// of the ColumnsHeaderPresenter.
|
|||
/// </summary>
|
|||
internal double DropLocationIndicatorOffset |
|||
{ |
|||
get; |
|||
set; |
|||
} |
|||
|
|||
internal DataGrid OwningGrid |
|||
{ |
|||
get; |
|||
set; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Arranges the content of the <see cref="T:Avalonia.Controls.Primitives.DataGridColumnHeadersPresenter" />.
|
|||
/// </summary>
|
|||
/// <returns>
|
|||
/// The actual size used by the <see cref="T:Avalonia.Controls.Primitives.DataGridColumnHeadersPresenter" />.
|
|||
/// </returns>
|
|||
/// <param name="finalSize">
|
|||
/// The final area within the parent that this element should use to arrange itself and its children.
|
|||
/// </param>
|
|||
protected override Size ArrangeOverride(Size finalSize) |
|||
{ |
|||
if (OwningGrid == null) |
|||
{ |
|||
return base.ArrangeOverride(finalSize); |
|||
} |
|||
|
|||
if (OwningGrid.AutoSizingColumns) |
|||
{ |
|||
// When we initially load an auto-column, we have to wait for all the rows to be measured
|
|||
// before we know its final desired size. We need to trigger a new round of measures now
|
|||
// that the final sizes have been calculated.
|
|||
OwningGrid.AutoSizingColumns = false; |
|||
return base.ArrangeOverride(finalSize); |
|||
} |
|||
|
|||
double dragIndicatorLeftEdge = 0; |
|||
double frozenLeftEdge = 0; |
|||
double scrollingLeftEdge = -OwningGrid.HorizontalOffset; |
|||
foreach (DataGridColumn dataGridColumn in OwningGrid.ColumnsInternal.GetVisibleColumns()) |
|||
{ |
|||
DataGridColumnHeader columnHeader = dataGridColumn.HeaderCell; |
|||
Debug.Assert(columnHeader.OwningColumn == dataGridColumn); |
|||
|
|||
if (dataGridColumn.IsFrozen) |
|||
{ |
|||
columnHeader.Arrange(new Rect(frozenLeftEdge, 0, dataGridColumn.LayoutRoundedWidth, finalSize.Height)); |
|||
columnHeader.Clip = null; // The layout system could have clipped this becaues it's not aware of our render transform
|
|||
if (DragColumn == dataGridColumn && DragIndicator != null) |
|||
{ |
|||
dragIndicatorLeftEdge = frozenLeftEdge + DragIndicatorOffset; |
|||
} |
|||
frozenLeftEdge += dataGridColumn.ActualWidth; |
|||
} |
|||
else |
|||
{ |
|||
columnHeader.Arrange(new Rect(scrollingLeftEdge, 0, dataGridColumn.LayoutRoundedWidth, finalSize.Height)); |
|||
EnsureColumnHeaderClip(columnHeader, dataGridColumn.ActualWidth, finalSize.Height, frozenLeftEdge, scrollingLeftEdge); |
|||
if (DragColumn == dataGridColumn && DragIndicator != null) |
|||
{ |
|||
dragIndicatorLeftEdge = scrollingLeftEdge + DragIndicatorOffset; |
|||
} |
|||
} |
|||
scrollingLeftEdge += dataGridColumn.ActualWidth; |
|||
} |
|||
if (DragColumn != null) |
|||
{ |
|||
if (DragIndicator != null) |
|||
{ |
|||
EnsureColumnReorderingClip(DragIndicator, finalSize.Height, frozenLeftEdge, dragIndicatorLeftEdge); |
|||
DragIndicator.Arrange(new Rect(dragIndicatorLeftEdge, 0, DragIndicator.Bounds.Width, DragIndicator.Bounds.Height)); |
|||
} |
|||
if (DropLocationIndicator != null) |
|||
{ |
|||
EnsureColumnReorderingClip(DropLocationIndicator, finalSize.Height, frozenLeftEdge, DropLocationIndicatorOffset); |
|||
DropLocationIndicator.Arrange(new Rect(DropLocationIndicatorOffset, 0, DropLocationIndicator.Bounds.Width, DropLocationIndicator.Bounds.Height)); |
|||
} |
|||
} |
|||
|
|||
// Arrange filler
|
|||
OwningGrid.OnFillerColumnWidthNeeded(finalSize.Width); |
|||
DataGridFillerColumn fillerColumn = OwningGrid.ColumnsInternal.FillerColumn; |
|||
if (fillerColumn.FillerWidth > 0) |
|||
{ |
|||
fillerColumn.HeaderCell.IsVisible = true; |
|||
fillerColumn.HeaderCell.Arrange(new Rect(scrollingLeftEdge, 0, fillerColumn.FillerWidth, finalSize.Height)); |
|||
} |
|||
else |
|||
{ |
|||
fillerColumn.HeaderCell.IsVisible = false; |
|||
} |
|||
|
|||
// This needs to be updated after the filler column is configured
|
|||
DataGridColumn lastVisibleColumn = OwningGrid.ColumnsInternal.LastVisibleColumn; |
|||
if (lastVisibleColumn != null) |
|||
{ |
|||
lastVisibleColumn.HeaderCell.UpdateSeparatorVisibility(lastVisibleColumn); |
|||
} |
|||
return finalSize; |
|||
} |
|||
|
|||
private static void EnsureColumnHeaderClip(DataGridColumnHeader columnHeader, double width, double height, double frozenLeftEdge, double columnHeaderLeftEdge) |
|||
{ |
|||
// Clip the cell only if it's scrolled under frozen columns. Unfortunately, we need to clip in this case
|
|||
// because cells could be transparent
|
|||
if (frozenLeftEdge > columnHeaderLeftEdge) |
|||
{ |
|||
RectangleGeometry rg = new RectangleGeometry(); |
|||
double xClip = Math.Min(width, frozenLeftEdge - columnHeaderLeftEdge); |
|||
rg.Rect = new Rect(xClip, 0, width - xClip, height); |
|||
columnHeader.Clip = rg; |
|||
} |
|||
else |
|||
{ |
|||
columnHeader.Clip = null; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Clips the DragIndicator and DropLocationIndicator controls according to current ColumnHeaderPresenter constraints.
|
|||
/// </summary>
|
|||
/// <param name="control">The DragIndicator or DropLocationIndicator</param>
|
|||
/// <param name="height">The available height</param>
|
|||
/// <param name="frozenColumnsWidth">The width of the frozen column region</param>
|
|||
/// <param name="controlLeftEdge">The left edge of the control to clip</param>
|
|||
private void EnsureColumnReorderingClip(Control control, double height, double frozenColumnsWidth, double controlLeftEdge) |
|||
{ |
|||
double leftEdge = 0; |
|||
double rightEdge = OwningGrid.CellsWidth; |
|||
double width = control.Bounds.Width; |
|||
if (DragColumn.IsFrozen) |
|||
{ |
|||
// If we're dragging a frozen column, we want to clip the corresponding DragIndicator control when it goes
|
|||
// into the scrolling columns region, but not the DropLocationIndicator.
|
|||
if (control == DragIndicator) |
|||
{ |
|||
rightEdge = Math.Min(rightEdge, frozenColumnsWidth); |
|||
} |
|||
} |
|||
else if (OwningGrid.FrozenColumnCount > 0) |
|||
{ |
|||
// If we're dragging a scrolling column, we want to clip both the DragIndicator and the DropLocationIndicator
|
|||
// controls when they go into the frozen column range.
|
|||
leftEdge = frozenColumnsWidth; |
|||
} |
|||
RectangleGeometry rg = null; |
|||
if (leftEdge > controlLeftEdge) |
|||
{ |
|||
rg = new RectangleGeometry(); |
|||
double xClip = Math.Min(width, leftEdge - controlLeftEdge); |
|||
rg.Rect = new Rect(xClip, 0, width - xClip, height); |
|||
} |
|||
if (controlLeftEdge + width >= rightEdge) |
|||
{ |
|||
if (rg == null) |
|||
{ |
|||
rg = new RectangleGeometry(); |
|||
} |
|||
rg.Rect = new Rect(rg.Rect.X, rg.Rect.Y, Math.Max(0, rightEdge - controlLeftEdge - rg.Rect.X), height); |
|||
} |
|||
control.Clip = rg; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Measures the children of a <see cref="T:Avalonia.Controls.Primitives.DataGridColumnHeadersPresenter" /> to
|
|||
/// prepare for arranging them during the <see cref="M:System.Windows.FrameworkElement.ArrangeOverride(System.Windows.Size)" /> pass.
|
|||
/// </summary>
|
|||
/// <param name="availableSize">
|
|||
/// The available size that this element can give to child elements. Indicates an upper limit that child elements should not exceed.
|
|||
/// </param>
|
|||
/// <returns>
|
|||
/// The size that the <see cref="T:Avalonia.Controls.Primitives.DataGridColumnHeadersPresenter" /> determines it needs during layout, based on its calculations of child object allocated sizes.
|
|||
/// </returns>
|
|||
protected override Size MeasureOverride(Size availableSize) |
|||
{ |
|||
if (OwningGrid == null) |
|||
{ |
|||
return base.MeasureOverride(availableSize); |
|||
} |
|||
if (!OwningGrid.AreColumnHeadersVisible) |
|||
{ |
|||
return Size.Empty; |
|||
} |
|||
double height = OwningGrid.ColumnHeaderHeight; |
|||
bool autoSizeHeight; |
|||
if (double.IsNaN(height)) |
|||
{ |
|||
// No explicit height values were set so we can autosize
|
|||
height = 0; |
|||
autoSizeHeight = true; |
|||
} |
|||
else |
|||
{ |
|||
autoSizeHeight = false; |
|||
} |
|||
|
|||
double totalDisplayWidth = 0; |
|||
OwningGrid.ColumnsInternal.EnsureVisibleEdgedColumnsWidth(); |
|||
DataGridColumn lastVisibleColumn = OwningGrid.ColumnsInternal.LastVisibleColumn; |
|||
foreach (DataGridColumn column in OwningGrid.ColumnsInternal.GetVisibleColumns()) |
|||
{ |
|||
// Measure each column header
|
|||
bool autoGrowWidth = column.Width.IsAuto || column.Width.IsSizeToHeader; |
|||
DataGridColumnHeader columnHeader = column.HeaderCell; |
|||
if (column != lastVisibleColumn) |
|||
{ |
|||
columnHeader.UpdateSeparatorVisibility(lastVisibleColumn); |
|||
} |
|||
|
|||
// If we're not using star sizing or the current column can't be resized,
|
|||
// then just set the display width according to the column's desired width
|
|||
if (!OwningGrid.UsesStarSizing || (!column.ActualCanUserResize && !column.Width.IsStar)) |
|||
{ |
|||
// In the edge-case where we're given infinite width and we have star columns, the
|
|||
// star columns grow to their predefined limit of 10,000 (or their MaxWidth)
|
|||
double newDisplayWidth = column.Width.IsStar ? |
|||
Math.Min(column.ActualMaxWidth, DataGrid.DATAGRID_maximumStarColumnWidth) : |
|||
Math.Max(column.ActualMinWidth, Math.Min(column.ActualMaxWidth, column.Width.DesiredValue)); |
|||
column.SetWidthDisplayValue(newDisplayWidth); |
|||
} |
|||
|
|||
// If we're auto-growing the column based on the header content, we want to measure it at its maximum value
|
|||
if (autoGrowWidth) |
|||
{ |
|||
columnHeader.Measure(new Size(column.ActualMaxWidth, double.PositiveInfinity)); |
|||
OwningGrid.AutoSizeColumn(column, columnHeader.DesiredSize.Width); |
|||
column.ComputeLayoutRoundedWidth(totalDisplayWidth); |
|||
} |
|||
else if (!OwningGrid.UsesStarSizing) |
|||
{ |
|||
column.ComputeLayoutRoundedWidth(totalDisplayWidth); |
|||
columnHeader.Measure(new Size(column.LayoutRoundedWidth, double.PositiveInfinity)); |
|||
} |
|||
|
|||
// We need to track the largest height in order to auto-size
|
|||
if (autoSizeHeight) |
|||
{ |
|||
height = Math.Max(height, columnHeader.DesiredSize.Height); |
|||
} |
|||
totalDisplayWidth += column.ActualWidth; |
|||
} |
|||
|
|||
// If we're using star sizing (and we're not waiting for an auto-column to finish growing)
|
|||
// then we will resize all the columns to fit the available space.
|
|||
if (OwningGrid.UsesStarSizing && !OwningGrid.AutoSizingColumns) |
|||
{ |
|||
double adjustment = Double.IsPositiveInfinity(availableSize.Width) ? OwningGrid.CellsWidth : availableSize.Width - totalDisplayWidth; |
|||
totalDisplayWidth += adjustment - OwningGrid.AdjustColumnWidths(0, adjustment, false); |
|||
|
|||
// Since we didn't know the final widths of the columns until we resized,
|
|||
// we waited until now to measure each header
|
|||
double leftEdge = 0; |
|||
foreach (DataGridColumn column in OwningGrid.ColumnsInternal.GetVisibleColumns()) |
|||
{ |
|||
column.ComputeLayoutRoundedWidth(leftEdge); |
|||
column.HeaderCell.Measure(new Size(column.LayoutRoundedWidth, double.PositiveInfinity)); |
|||
if (autoSizeHeight) |
|||
{ |
|||
height = Math.Max(height, column.HeaderCell.DesiredSize.Height); |
|||
} |
|||
leftEdge += column.ActualWidth; |
|||
} |
|||
} |
|||
|
|||
// Add the filler column if it's not represented. We won't know whether we need it or not until Arrange
|
|||
DataGridFillerColumn fillerColumn = OwningGrid.ColumnsInternal.FillerColumn; |
|||
if (!fillerColumn.IsRepresented) |
|||
{ |
|||
Debug.Assert(!Children.Contains(fillerColumn.HeaderCell)); |
|||
fillerColumn.HeaderCell.AreSeparatorsVisible = false; |
|||
Children.Insert(OwningGrid.ColumnsInternal.Count, fillerColumn.HeaderCell); |
|||
fillerColumn.IsRepresented = true; |
|||
// Optimize for the case where we don't need the filler cell
|
|||
fillerColumn.HeaderCell.IsVisible = false; |
|||
} |
|||
fillerColumn.HeaderCell.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); |
|||
|
|||
if (DragIndicator != null) |
|||
{ |
|||
DragIndicator.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); |
|||
} |
|||
if (DropLocationIndicator != null) |
|||
{ |
|||
DropLocationIndicator.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); |
|||
} |
|||
|
|||
OwningGrid.ColumnsInternal.EnsureVisibleEdgedColumnsWidth(); |
|||
return new Size(OwningGrid.ColumnsInternal.VisibleEdgedColumnsWidth, height); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Creates AutomationPeer (<see cref="UIElement.OnCreateAutomationPeer"/>)
|
|||
/// </summary>
|
|||
//protected override AutomationPeer OnCreateAutomationPeer()
|
|||
//{
|
|||
// return new DataGridColumnHeadersPresenterAutomationPeer(this);
|
|||
//}
|
|||
} |
|||
} |
|||
@ -0,0 +1,141 @@ |
|||
// (c) Copyright Microsoft Corporation.
|
|||
// This source is subject to the Microsoft Public License (Ms-PL).
|
|||
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
|
|||
// All other rights reserved.
|
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
using Avalonia.Media; |
|||
|
|||
namespace Avalonia.Controls.Primitives |
|||
{ |
|||
/// <summary>
|
|||
/// Used within the template of a <see cref="T:Avalonia.Controls.DataGrid" /> to specify the location in the control's visual tree
|
|||
/// where the row details are to be added.
|
|||
/// </summary>
|
|||
/// <QualityBand>Mature</QualityBand>
|
|||
public sealed class DataGridDetailsPresenter : Panel |
|||
{ |
|||
public static readonly StyledProperty<double> ContentHeightProperty = |
|||
AvaloniaProperty.Register<DataGridDetailsPresenter, double>(nameof(ContentHeight)); |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the height of the content.
|
|||
/// </summary>
|
|||
/// <returns>
|
|||
/// The height of the content.
|
|||
/// </returns>
|
|||
public double ContentHeight |
|||
{ |
|||
get { return GetValue(ContentHeightProperty); } |
|||
set { SetValue(ContentHeightProperty, value); } |
|||
} |
|||
|
|||
internal DataGridRow OwningRow |
|||
{ |
|||
get; |
|||
set; |
|||
} |
|||
private DataGrid OwningGrid => OwningRow?.OwningGrid; |
|||
|
|||
|
|||
public DataGridDetailsPresenter() |
|||
{ |
|||
AffectsMeasure(ContentHeightProperty); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Arranges the content of the <see cref="T:Avalonia.Controls.Primitives.DataGridDetailsPresenter" />.
|
|||
/// </summary>
|
|||
/// <returns>
|
|||
/// The actual size used by the <see cref="T:Avalonia.Controls.Primitives.DataGridDetailsPresenter" />.
|
|||
/// </returns>
|
|||
/// <param name="finalSize">
|
|||
/// The final area within the parent that this element should use to arrange itself and its children.
|
|||
/// </param>
|
|||
protected override Size ArrangeOverride(Size finalSize) |
|||
{ |
|||
if (OwningGrid == null) |
|||
{ |
|||
return base.ArrangeOverride(finalSize); |
|||
} |
|||
double rowGroupSpacerWidth = OwningGrid.ColumnsInternal.RowGroupSpacerColumn.Width.Value; |
|||
double leftEdge = rowGroupSpacerWidth; |
|||
double xClip = OwningGrid.AreRowGroupHeadersFrozen ? rowGroupSpacerWidth : 0; |
|||
double width; |
|||
if (OwningGrid.AreRowDetailsFrozen) |
|||
{ |
|||
leftEdge += OwningGrid.HorizontalOffset; |
|||
width = OwningGrid.CellsWidth; |
|||
} |
|||
else |
|||
{ |
|||
xClip += OwningGrid.HorizontalOffset; |
|||
width = Math.Max(OwningGrid.CellsWidth, OwningGrid.ColumnsInternal.VisibleEdgedColumnsWidth); |
|||
} |
|||
// Details should not extend through the indented area
|
|||
width -= rowGroupSpacerWidth; |
|||
double height = Math.Max(0, double.IsNaN(ContentHeight) ? 0 : ContentHeight); |
|||
|
|||
foreach (Control child in Children) |
|||
{ |
|||
child.Arrange(new Rect(leftEdge, 0, width, height)); |
|||
} |
|||
|
|||
if (OwningGrid.AreRowDetailsFrozen) |
|||
{ |
|||
// Frozen Details should not be clipped, similar to frozen cells
|
|||
Clip = null; |
|||
} |
|||
else |
|||
{ |
|||
// Clip so Details doesn't obstruct elements to the left (the RowHeader by default) as we scroll to the right
|
|||
Clip = new RectangleGeometry |
|||
{ |
|||
Rect = new Rect(xClip, 0, Math.Max(0, width - xClip + rowGroupSpacerWidth), height) |
|||
}; |
|||
} |
|||
|
|||
return finalSize; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Measures the children of a <see cref="T:Avalonia.Controls.Primitives.DataGridDetailsPresenter" /> to
|
|||
/// prepare for arranging them during the <see cref="M:System.Windows.FrameworkElement.ArrangeOverride(System.Windows.Size)" /> pass.
|
|||
/// </summary>
|
|||
/// <param name="availableSize">
|
|||
/// The available size that this element can give to child elements. Indicates an upper limit that child elements should not exceed.
|
|||
/// </param>
|
|||
/// <returns>
|
|||
/// The size that the <see cref="T:Avalonia.Controls.Primitives.DataGridDetailsPresenter" /> determines it needs during layout, based on its calculations of child object allocated sizes.
|
|||
/// </returns>
|
|||
protected override Size MeasureOverride(Size availableSize) |
|||
{ |
|||
if (OwningGrid == null || Children.Count == 0) |
|||
{ |
|||
return Size.Empty; |
|||
} |
|||
|
|||
double desiredWidth = OwningGrid.AreRowDetailsFrozen ? |
|||
OwningGrid.CellsWidth : |
|||
Math.Max(OwningGrid.CellsWidth, OwningGrid.ColumnsInternal.VisibleEdgedColumnsWidth); |
|||
|
|||
desiredWidth -= OwningGrid.ColumnsInternal.RowGroupSpacerColumn.Width.Value; |
|||
|
|||
foreach (Control child in Children) |
|||
{ |
|||
child.Measure(new Size(desiredWidth, double.PositiveInfinity)); |
|||
} |
|||
|
|||
double desiredHeight = Math.Max(0, double.IsNaN(ContentHeight) ? 0 : ContentHeight); |
|||
|
|||
return new Size(desiredWidth, desiredHeight); |
|||
} |
|||
|
|||
//protected override AutomationPeer OnCreateAutomationPeer()
|
|||
//{
|
|||
// return new DataGridDetailsPresenterAutomationPeer(this);
|
|||
//}
|
|||
} |
|||
} |
|||
@ -0,0 +1,45 @@ |
|||
// (c) Copyright Microsoft Corporation.
|
|||
// This source is subject to the Microsoft Public License (Ms-PL).
|
|||
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
|
|||
// All other rights reserved.
|
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
|
|||
namespace Avalonia.Controls.Primitives |
|||
{ |
|||
/// <summary>
|
|||
/// Represents a non-scrollable grid that contains <see cref="T:Avalonia.Controls.DataGrid" /> row headers.
|
|||
/// </summary>
|
|||
public class DataGridFrozenGrid : Grid |
|||
{ |
|||
public static readonly AvaloniaProperty<bool> IsFrozenProperty = |
|||
AvaloniaProperty.RegisterAttached<DataGridFrozenGrid, Control, bool>("IsFrozen"); |
|||
|
|||
/// <summary>
|
|||
/// Gets a value that indicates whether the grid is frozen.
|
|||
/// </summary>
|
|||
/// <param name="element">
|
|||
/// The object to get the <see cref="P:Avalonia.Controls.Primitives.DataGridFrozenGrid.IsFrozen" /> value from.
|
|||
/// </param>
|
|||
/// <returns>true if the grid is frozen; otherwise, false. The default is true.</returns>
|
|||
public static bool GetIsFrozen(Control element) |
|||
{ |
|||
Contract.Requires<ArgumentNullException>(element != null); |
|||
return element.GetValue(IsFrozenProperty); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Sets a value that indicates whether the grid is frozen.
|
|||
/// </summary>
|
|||
/// <param name="element">The object to set the <see cref="P:Avalonia.Controls.Primitives.DataGridFrozenGrid.IsFrozen" /> value on.</param>
|
|||
/// <param name="value">true if <paramref name="element" /> is frozen; otherwise, false.</param>
|
|||
/// <exception cref="T:System.ArgumentNullException"><paramref name="element" /> is null.</exception>
|
|||
public static void SetIsFrozen(Control element, bool value) |
|||
{ |
|||
Contract.Requires<ArgumentNullException>(element != null); |
|||
element.SetValue(IsFrozenProperty, value); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,167 @@ |
|||
// (c) Copyright Microsoft Corporation.
|
|||
// This source is subject to the Microsoft Public License (Ms-PL).
|
|||
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
|
|||
// All other rights reserved.
|
|||
|
|||
using Avalonia.Media; |
|||
using System; |
|||
using System.Diagnostics; |
|||
|
|||
namespace Avalonia.Controls.Primitives |
|||
{ |
|||
/// <summary>
|
|||
/// Used within the template of a <see cref="T:Avalonia.Controls.DataGrid" /> to specify the
|
|||
/// location in the control's visual tree where the rows are to be added.
|
|||
/// </summary>
|
|||
public sealed class DataGridRowsPresenter : Panel |
|||
{ |
|||
internal DataGrid OwningGrid |
|||
{ |
|||
get; |
|||
set; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Arranges the content of the <see cref="T:Avalonia.Controls.Primitives.DataGridRowsPresenter" />.
|
|||
/// </summary>
|
|||
/// <returns>
|
|||
/// The actual size used by the <see cref="T:Avalonia.Controls.Primitives.DataGridRowsPresenter" />.
|
|||
/// </returns>
|
|||
/// <param name="finalSize">
|
|||
/// The final area within the parent that this element should use to arrange itself and its children.
|
|||
/// </param>
|
|||
protected override Size ArrangeOverride(Size finalSize) |
|||
{ |
|||
if (finalSize.Height == 0 || OwningGrid == null) |
|||
{ |
|||
return base.ArrangeOverride(finalSize); |
|||
} |
|||
|
|||
OwningGrid.OnFillerColumnWidthNeeded(finalSize.Width); |
|||
|
|||
double rowDesiredWidth = OwningGrid.ColumnsInternal.VisibleEdgedColumnsWidth + OwningGrid.ColumnsInternal.FillerColumn.FillerWidth; |
|||
double topEdge = -OwningGrid.NegVerticalOffset; |
|||
foreach (Control element in OwningGrid.DisplayData.GetScrollingElements()) |
|||
{ |
|||
if (element is DataGridRow row) |
|||
{ |
|||
Debug.Assert(row.Index != -1); // A displayed row should always have its index
|
|||
|
|||
// Visibility for all filler cells needs to be set in one place. Setting it individually in
|
|||
// each CellsPresenter causes an NxN layout cycle (see DevDiv Bugs 211557)
|
|||
row.EnsureFillerVisibility(); |
|||
row.Arrange(new Rect(-OwningGrid.HorizontalOffset, topEdge, rowDesiredWidth, element.DesiredSize.Height)); |
|||
} |
|||
else if (element is DataGridRowGroupHeader groupHeader) |
|||
{ |
|||
double leftEdge = (OwningGrid.AreRowGroupHeadersFrozen) ? 0 : -OwningGrid.HorizontalOffset; |
|||
groupHeader.Arrange(new Rect(leftEdge, topEdge, rowDesiredWidth - leftEdge, element.DesiredSize.Height)); |
|||
} |
|||
|
|||
topEdge += element.DesiredSize.Height; |
|||
} |
|||
|
|||
double finalHeight = Math.Max(topEdge + OwningGrid.NegVerticalOffset, finalSize.Height); |
|||
|
|||
// Clip the RowsPresenter so rows cannot overlap other elements in certain styling scenarios
|
|||
var rg = new RectangleGeometry |
|||
{ |
|||
Rect = new Rect(0, 0, finalSize.Width, finalHeight) |
|||
}; |
|||
Clip = rg; |
|||
|
|||
return new Size(finalSize.Width, finalHeight); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Measures the children of a <see cref="T:Avalonia.Controls.Primitives.DataGridRowsPresenter" /> to
|
|||
/// prepare for arranging them during the <see cref="M:System.Windows.FrameworkElement.ArrangeOverride(System.Windows.Size)" /> pass.
|
|||
/// </summary>
|
|||
/// <param name="availableSize">
|
|||
/// The available size that this element can give to child elements. Indicates an upper limit that child elements should not exceed.
|
|||
/// </param>
|
|||
/// <returns>
|
|||
/// The size that the <see cref="T:Avalonia.Controls.Primitives.DataGridRowsPresenter" /> determines it needs during layout, based on its calculations of child object allocated sizes.
|
|||
/// </returns>
|
|||
protected override Size MeasureOverride(Size availableSize) |
|||
{ |
|||
if (availableSize.Height == 0 || OwningGrid == null) |
|||
{ |
|||
return base.MeasureOverride(availableSize); |
|||
} |
|||
|
|||
// If the Width of our RowsPresenter changed then we need to invalidate our rows
|
|||
bool invalidateRows = (!OwningGrid.RowsPresenterAvailableSize.HasValue || availableSize.Width != OwningGrid.RowsPresenterAvailableSize.Value.Width) |
|||
&& !double.IsInfinity(availableSize.Width); |
|||
|
|||
// The DataGrid uses the RowsPresenter available size in order to autogrow
|
|||
// and calculate the scrollbars
|
|||
OwningGrid.RowsPresenterAvailableSize = availableSize; |
|||
|
|||
OwningGrid.OnRowsMeasure(); |
|||
|
|||
double totalHeight = -OwningGrid.NegVerticalOffset; |
|||
double totalCellsWidth = OwningGrid.ColumnsInternal.VisibleEdgedColumnsWidth; |
|||
|
|||
double headerWidth = 0; |
|||
foreach (Control element in OwningGrid.DisplayData.GetScrollingElements()) |
|||
{ |
|||
DataGridRow row = element as DataGridRow; |
|||
if (row != null) |
|||
{ |
|||
if (invalidateRows) |
|||
{ |
|||
row.InvalidateMeasure(); |
|||
} |
|||
} |
|||
|
|||
element.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); |
|||
|
|||
if (row != null && row.HeaderCell != null) |
|||
{ |
|||
headerWidth = Math.Max(headerWidth, row.HeaderCell.DesiredSize.Width); |
|||
} |
|||
else if (element is DataGridRowGroupHeader groupHeader && groupHeader.HeaderCell != null) |
|||
{ |
|||
headerWidth = Math.Max(headerWidth, groupHeader.HeaderCell.DesiredSize.Width); |
|||
} |
|||
|
|||
totalHeight += element.DesiredSize.Height; |
|||
} |
|||
|
|||
OwningGrid.RowHeadersDesiredWidth = headerWidth; |
|||
// Could be positive infinity depending on the DataGrid's bounds
|
|||
OwningGrid.AvailableSlotElementRoom = availableSize.Height - totalHeight; |
|||
|
|||
totalHeight = Math.Max(0, totalHeight); |
|||
|
|||
return new Size(totalCellsWidth + headerWidth, totalHeight); |
|||
} |
|||
|
|||
// / <summary>
|
|||
// / Creates AutomationPeer (<see cref="UIElement.OnCreateAutomationPeer"/>)
|
|||
// / </summary>
|
|||
//Automation
|
|||
//protected override AutomationPeer OnCreateAutomationPeer()
|
|||
//{
|
|||
// return new DataGridRowsPresenterAutomationPeer(this);
|
|||
//}
|
|||
|
|||
#if DEBUG
|
|||
internal void PrintChildren() |
|||
{ |
|||
foreach (Control element in Children) |
|||
{ |
|||
if (element is DataGridRow row) |
|||
{ |
|||
Debug.WriteLine(String.Format(System.Globalization.CultureInfo.InvariantCulture, "Slot: {0} Row: {1} Visibility: {2} ", row.Slot, row.Index, row.IsVisible)); |
|||
} |
|||
else if (element is DataGridRowGroupHeader groupHeader) |
|||
{ |
|||
Debug.WriteLine(String.Format(System.Globalization.CultureInfo.InvariantCulture, "Slot: {0} GroupHeader: {1} Visibility: {2}", groupHeader.RowGroupInfo.Slot, groupHeader.RowGroupInfo.CollectionViewGroup.Key, groupHeader.IsVisible)); |
|||
} |
|||
} |
|||
} |
|||
#endif
|
|||
} |
|||
} |
|||
@ -0,0 +1,8 @@ |
|||
using System.Reflection; |
|||
using System.Runtime.CompilerServices; |
|||
using Avalonia.Metadata; |
|||
|
|||
[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Controls")] |
|||
[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Controls.Primitives")] |
|||
//Avalonia.Controls.Primitives
|
|||
//Avalonia.Controls.Primitives
|
|||
@ -0,0 +1,69 @@ |
|||
// (c) Copyright Microsoft Corporation.
|
|||
// This source is subject to the Microsoft Public License (Ms-PL).
|
|||
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
|
|||
// All other rights reserved.
|
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
|
|||
namespace Avalonia.Controls |
|||
{ |
|||
internal class Range<T> |
|||
{ |
|||
public Range(int lowerBound, int upperBound, T value) |
|||
{ |
|||
LowerBound = lowerBound; |
|||
UpperBound = upperBound; |
|||
Value = value; |
|||
} |
|||
|
|||
public int Count |
|||
{ |
|||
get |
|||
{ |
|||
return UpperBound - LowerBound + 1; |
|||
} |
|||
} |
|||
|
|||
public int LowerBound |
|||
{ |
|||
get; |
|||
set; |
|||
} |
|||
|
|||
public int UpperBound |
|||
{ |
|||
get; |
|||
set; |
|||
} |
|||
|
|||
public T Value |
|||
{ |
|||
get; |
|||
set; |
|||
} |
|||
|
|||
public bool ContainsIndex(int index) |
|||
{ |
|||
return (LowerBound <= index) && (UpperBound >= index); |
|||
} |
|||
|
|||
public bool ContainsValue(object value) |
|||
{ |
|||
if (Value == null) |
|||
{ |
|||
return value == null; |
|||
} |
|||
else |
|||
{ |
|||
return Value.Equals(value); |
|||
} |
|||
} |
|||
|
|||
public Range<T> Copy() |
|||
{ |
|||
return new Range<T>(LowerBound, UpperBound, Value); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,136 @@ |
|||
// (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; |
|||
|
|||
namespace Avalonia.Utilities |
|||
{ |
|||
internal static class DoubleUtil |
|||
{ |
|||
internal const double DBL_EPSILON = 1e-6; |
|||
|
|||
/// <summary>
|
|||
/// AreClose - Returns whether or not two doubles are "close". That is, whether or
|
|||
/// not they are within epsilon of each other. Note that this epsilon is proportional
|
|||
/// to the numbers themselves to that AreClose survives scalar multiplication.
|
|||
/// There are plenty of ways for this to return false even for numbers which
|
|||
/// are theoretically identical, so no code calling this should fail to work if this
|
|||
/// returns false. This is important enough to repeat:
|
|||
/// NB: NO CODE CALLING THIS FUNCTION SHOULD DEPEND ON ACCURATE RESULTS - this should be
|
|||
/// used for optimizations *only*.
|
|||
/// </summary>
|
|||
/// <returns>
|
|||
/// bool - the result of the AreClose comparison.
|
|||
/// </returns>
|
|||
/// <param name="value1"> The first double to compare. </param>
|
|||
/// <param name="value2"> The second double to compare. </param>
|
|||
public static bool AreClose(double value1, double value2) |
|||
{ |
|||
//in case they are Infinities (then epsilon check does not work)
|
|||
if (value1 == value2) return true; |
|||
// This computes (|value1-value2| / (|value1| + |value2| + 10.0)) < DBL_EPSILON
|
|||
double eps = (Math.Abs(value1) + Math.Abs(value2) + 10.0) * DBL_EPSILON; |
|||
double delta = value1 - value2; |
|||
return (-eps < delta) && (eps > delta); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// GreaterThan - Returns whether or not the first double is greater than the second double.
|
|||
/// That is, whether or not the first is strictly greater than *and* not within epsilon of
|
|||
/// the other number. Note that this epsilon is proportional to the numbers themselves
|
|||
/// to that AreClose survives scalar multiplication. Note,
|
|||
/// There are plenty of ways for this to return false even for numbers which
|
|||
/// are theoretically identical, so no code calling this should fail to work if this
|
|||
/// returns false. This is important enough to repeat:
|
|||
/// NB: NO CODE CALLING THIS FUNCTION SHOULD DEPEND ON ACCURATE RESULTS - this should be
|
|||
/// used for optimizations *only*.
|
|||
/// </summary>
|
|||
/// <returns>
|
|||
/// bool - the result of the GreaterThan comparison.
|
|||
/// </returns>
|
|||
/// <param name="value1"> The first double to compare. </param>
|
|||
/// <param name="value2"> The second double to compare. </param>
|
|||
public static bool GreaterThan(double value1, double value2) |
|||
{ |
|||
return (value1 > value2) && !AreClose(value1, value2); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// GreaterThanOrClose - Returns whether or not the first double is greater than or close to
|
|||
/// the second double. That is, whether or not the first is strictly greater than or within
|
|||
/// epsilon of the other number. Note that this epsilon is proportional to the numbers
|
|||
/// themselves to that AreClose survives scalar multiplication. Note,
|
|||
/// There are plenty of ways for this to return false even for numbers which
|
|||
/// are theoretically identical, so no code calling this should fail to work if this
|
|||
/// returns false. This is important enough to repeat:
|
|||
/// NB: NO CODE CALLING THIS FUNCTION SHOULD DEPEND ON ACCURATE RESULTS - this should be
|
|||
/// used for optimizations *only*.
|
|||
/// </summary>
|
|||
/// <returns>
|
|||
/// bool - the result of the GreaterThanOrClose comparison.
|
|||
/// </returns>
|
|||
/// <param name="value1"> The first double to compare. </param>
|
|||
/// <param name="value2"> The second double to compare. </param>
|
|||
public static bool GreaterThanOrClose(double value1, double value2) |
|||
{ |
|||
return (value1 > value2) || AreClose(value1, value2); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// IsZero - Returns whether or not the double is "close" to 0. Same as AreClose(double, 0),
|
|||
/// but this is faster.
|
|||
/// </summary>
|
|||
/// <returns>
|
|||
/// bool - the result of the IsZero comparison.
|
|||
/// </returns>
|
|||
/// <param name="value"> The double to compare to 0. </param>
|
|||
public static bool IsZero(double value) |
|||
{ |
|||
return Math.Abs(value) < 10.0 * DBL_EPSILON; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// LessThan - Returns whether or not the first double is less than the second double.
|
|||
/// That is, whether or not the first is strictly less than *and* not within epsilon of
|
|||
/// the other number. Note that this epsilon is proportional to the numbers themselves
|
|||
/// to that AreClose survives scalar multiplication. Note,
|
|||
/// There are plenty of ways for this to return false even for numbers which
|
|||
/// are theoretically identical, so no code calling this should fail to work if this
|
|||
/// returns false. This is important enough to repeat:
|
|||
/// NB: NO CODE CALLING THIS FUNCTION SHOULD DEPEND ON ACCURATE RESULTS - this should be
|
|||
/// used for optimizations *only*.
|
|||
/// </summary>
|
|||
/// <returns>
|
|||
/// bool - the result of the LessThan comparison.
|
|||
/// </returns>
|
|||
/// <param name="value1"> The first double to compare. </param>
|
|||
/// <param name="value2"> The second double to compare. </param>
|
|||
public static bool LessThan(double value1, double value2) |
|||
{ |
|||
return (value1 < value2) && !AreClose(value1, value2); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// LessThanOrClose - Returns whether or not the first double is less than or close to
|
|||
/// the second double. That is, whether or not the first is strictly less than or within
|
|||
/// epsilon of the other number. Note that this epsilon is proportional to the numbers
|
|||
/// themselves to that AreClose survives scalar multiplication. Note,
|
|||
/// There are plenty of ways for this to return false even for numbers which
|
|||
/// are theoretically identical, so no code calling this should fail to work if this
|
|||
/// returns false. This is important enough to repeat:
|
|||
/// NB: NO CODE CALLING THIS FUNCTION SHOULD DEPEND ON ACCURATE RESULTS - this should be
|
|||
/// used for optimizations *only*.
|
|||
/// </summary>
|
|||
/// <returns>
|
|||
/// bool - the result of the LessThanOrClose comparison.
|
|||
/// </returns>
|
|||
/// <param name="value1"> The first double to compare. </param>
|
|||
/// <param name="value2"> The second double to compare. </param>
|
|||
public static bool LessThanOrClose(double value1, double value2) |
|||
{ |
|||
return (value1 < value2) || AreClose(value1, value2); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
// (c) Copyright Microsoft Corporation.
|
|||
// This source is subject to the Microsoft Public License (Ms-PL).
|
|||
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
|
|||
// All other rights reserved.
|
|||
|
|||
using Avalonia.Input; |
|||
|
|||
namespace Avalonia.Controls.Primitives |
|||
{ |
|||
internal static class KeyboardHelper |
|||
{ |
|||
public static void GetMetaKeyState(InputModifiers modifiers, out bool ctrl, out bool shift) |
|||
{ |
|||
ctrl = (modifiers & InputModifiers.Control) == InputModifiers.Control; |
|||
shift = (modifiers & InputModifiers.Shift) == InputModifiers.Shift; |
|||
} |
|||
public static void GetMetaKeyState(InputModifiers modifiers, out bool ctrl, out bool shift, out bool alt) |
|||
{ |
|||
ctrl = (modifiers & InputModifiers.Control) == InputModifiers.Control; |
|||
shift = (modifiers & InputModifiers.Shift) == InputModifiers.Shift; |
|||
alt = (modifiers & InputModifiers.Alt) == InputModifiers.Alt; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,524 @@ |
|||
// (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 System.Collections.Generic; |
|||
using System.ComponentModel; |
|||
using System.ComponentModel.DataAnnotations; |
|||
using System.Diagnostics; |
|||
using System.Globalization; |
|||
using System.Reflection; |
|||
|
|||
namespace Avalonia.Utilities |
|||
{ |
|||
internal static class TypeHelper |
|||
{ |
|||
internal const char LeftIndexerToken = '['; |
|||
internal const char PropertyNameSeparator = '.'; |
|||
internal const char RightIndexerToken = ']'; |
|||
|
|||
private static Type FindGenericType(Type definition, Type type) |
|||
{ |
|||
while ((type != null) && (type != typeof(object))) |
|||
{ |
|||
if (type.IsGenericType && (type.GetGenericTypeDefinition() == definition)) |
|||
{ |
|||
return type; |
|||
} |
|||
if (definition.IsInterface) |
|||
{ |
|||
foreach (Type type2 in type.GetInterfaces()) |
|||
{ |
|||
Type type3 = FindGenericType(definition, type2); |
|||
if (type3 != null) |
|||
{ |
|||
return type3; |
|||
} |
|||
} |
|||
} |
|||
type = type.BaseType; |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Finds an int or string indexer in the specified collection of members, where int indexers take priority
|
|||
/// over string indexers. If found, this method will return the associated PropertyInfo and set the out index
|
|||
/// argument to its appropriate value. If not found, the return value will be null, as will the index.
|
|||
/// </summary>
|
|||
/// <param name="members">Collection of members to search through for an indexer.</param>
|
|||
/// <param name="stringIndex">String value of indexer argument.</param>
|
|||
/// <param name="index">Resultant index value.</param>
|
|||
/// <returns>Indexer PropertyInfo if found, null otherwise.</returns>
|
|||
private static PropertyInfo FindIndexerInMembers(MemberInfo[] members, string stringIndex, out object[] index) |
|||
{ |
|||
index = null; |
|||
ParameterInfo[] parameters; |
|||
PropertyInfo stringIndexer = null; |
|||
|
|||
foreach (PropertyInfo pi in members) |
|||
{ |
|||
if (pi == null) |
|||
{ |
|||
continue; |
|||
} |
|||
|
|||
// Only a single parameter is supported and it must be a string or Int32 value.
|
|||
parameters = pi.GetIndexParameters(); |
|||
if (parameters.Length > 1) |
|||
{ |
|||
continue; |
|||
} |
|||
|
|||
if (parameters[0].ParameterType == typeof(int)) |
|||
{ |
|||
int intIndex = -1; |
|||
if (Int32.TryParse(stringIndex.Trim(), NumberStyles.None, CultureInfo.InvariantCulture, out intIndex)) |
|||
{ |
|||
index = new object[] { intIndex }; |
|||
return pi; |
|||
} |
|||
} |
|||
|
|||
// If string indexer is found save it, in case there is an int indexer.
|
|||
if (parameters[0].ParameterType == typeof(string)) |
|||
{ |
|||
index = new object[] { stringIndex }; |
|||
stringIndexer = pi; |
|||
} |
|||
} |
|||
|
|||
return stringIndexer; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the default member name that is used for an indexer (e.g. "Item").
|
|||
/// </summary>
|
|||
/// <param name="type">Type to check.</param>
|
|||
/// <returns>Default member name.</returns>
|
|||
private static string GetDefaultMemberName(this Type type) |
|||
{ |
|||
object[] attributes = type.GetCustomAttributes(typeof(DefaultMemberAttribute), true); |
|||
if (attributes != null && attributes.Length == 1) |
|||
{ |
|||
DefaultMemberAttribute defaultMemberAttribute = attributes[0] as DefaultMemberAttribute; |
|||
return defaultMemberAttribute.MemberName; |
|||
} |
|||
else |
|||
{ |
|||
return null; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Finds the PropertyInfo for the specified property path within this Type, and returns
|
|||
/// the value of GetShortName on its DisplayAttribute, if one exists. GetShortName will return
|
|||
/// the value of Name if there is no ShortName specified.
|
|||
/// </summary>
|
|||
/// <param name="type">Type to search</param>
|
|||
/// <param name="propertyPath">property path</param>
|
|||
/// <returns>DisplayAttribute.ShortName if it exists, null otherwise</returns>
|
|||
internal static string GetDisplayName(this Type type, string propertyPath) |
|||
{ |
|||
PropertyInfo propertyInfo = type.GetNestedProperty(propertyPath); |
|||
if (propertyInfo != null) |
|||
{ |
|||
object[] attributes = propertyInfo.GetCustomAttributes(typeof(DisplayAttribute), true); |
|||
if (attributes != null && attributes.Length > 0) |
|||
{ |
|||
Debug.Assert(attributes.Length == 1); |
|||
if (attributes[0] is DisplayAttribute displayAttribute) |
|||
{ |
|||
return displayAttribute.GetShortName(); |
|||
} |
|||
} |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
internal static Type GetEnumerableItemType(this Type enumerableType) |
|||
{ |
|||
Type type = FindGenericType(typeof(IEnumerable<>), enumerableType); |
|||
if (type != null) |
|||
{ |
|||
return type.GetGenericArguments()[0]; |
|||
} |
|||
return enumerableType; |
|||
} |
|||
|
|||
|
|||
/// <summary>
|
|||
/// Retrieves the value and type of a property. That property can be nested and its path
|
|||
/// can include indexers. Each element of the path needs to be a public instance property.
|
|||
/// </summary>
|
|||
/// <param name="parentType">The parent Type</param>
|
|||
/// <param name="propertyPath">Property path</param>
|
|||
/// <param name="exception">Potential exception</param>
|
|||
/// <param name="item">Parent item which will be set to the property value if non-null.</param>
|
|||
/// <returns></returns>
|
|||
private static PropertyInfo GetNestedProperty(this Type parentType, string propertyPath, out Exception exception, ref object item) |
|||
{ |
|||
exception = null; |
|||
if (parentType == null || String.IsNullOrEmpty(propertyPath)) |
|||
{ |
|||
item = null; |
|||
return null; |
|||
} |
|||
|
|||
Type type = parentType; |
|||
PropertyInfo propertyInfo = null; |
|||
List<string> propertyNames = SplitPropertyPath(propertyPath); |
|||
for (int i = 0; i < propertyNames.Count; i++) |
|||
{ |
|||
// if we can't find the property or it is not of the correct type,
|
|||
// treat it as a null value
|
|||
propertyInfo = type.GetPropertyOrIndexer(propertyNames[i], out object[] index); |
|||
if (propertyInfo == null) |
|||
{ |
|||
item = null; |
|||
return null; |
|||
} |
|||
|
|||
if (!propertyInfo.CanRead) |
|||
{ |
|||
exception = |
|||
new InvalidOperationException( |
|||
$"The property named '{propertyNames[i]}' on type '{type.GetTypeName()}' cannot be read."); |
|||
item = null; |
|||
return null; |
|||
} |
|||
|
|||
if (item != null) |
|||
{ |
|||
item = propertyInfo.GetValue(item, index); |
|||
} |
|||
type = propertyInfo.PropertyType.GetNonNullableType(); |
|||
} |
|||
|
|||
return propertyInfo; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Finds the leaf PropertyInfo for the specified property path, and returns its value
|
|||
/// if the item is non-null.
|
|||
/// </summary>
|
|||
/// <param name="parentType">Type to search.</param>
|
|||
/// <param name="propertyPath">Property path.</param>
|
|||
/// <param name="item">Parent item which will be set to the property value if non-null.</param>
|
|||
/// <returns>The PropertyInfo.</returns>
|
|||
internal static PropertyInfo GetNestedProperty(this Type parentType, string propertyPath, ref object item) |
|||
{ |
|||
return parentType.GetNestedProperty(propertyPath, out Exception ex, ref item); |
|||
} |
|||
|
|||
internal static PropertyInfo GetNestedProperty(this Type parentType, string propertyPath) |
|||
{ |
|||
if (parentType != null) |
|||
{ |
|||
object item = null; |
|||
return parentType.GetNestedProperty(propertyPath, ref item); |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns the friendly name for a type
|
|||
/// </summary>
|
|||
/// <param name="type">The type to get the name from</param>
|
|||
/// <returns>Textual representation of the input type</returns>
|
|||
internal static string GetTypeName(this Type type) |
|||
{ |
|||
Type baseType = type.GetNonNullableType(); |
|||
string s = baseType.Name; |
|||
if (type != baseType) |
|||
{ |
|||
s += '?'; |
|||
} |
|||
return s; |
|||
} |
|||
|
|||
internal static Type GetNestedPropertyType(this Type parentType, string propertyPath) |
|||
{ |
|||
if (parentType == null || String.IsNullOrEmpty(propertyPath)) |
|||
{ |
|||
return parentType; |
|||
} |
|||
|
|||
PropertyInfo propertyInfo = parentType.GetNestedProperty(propertyPath); |
|||
if (propertyInfo != null) |
|||
{ |
|||
return propertyInfo.PropertyType; |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Retrieves the value of a property. That property can be nested and its path can
|
|||
/// include indexers. Each element of the path needs to be a public instance property.
|
|||
/// The return value will either be of type propertyType or it will be null.
|
|||
/// </summary>
|
|||
/// <param name="item">Object that exposes the property</param>
|
|||
/// <param name="propertyPath">Property path</param>
|
|||
/// <param name="propertyType">Property type</param>
|
|||
/// <param name="exception">Potential exception</param>
|
|||
/// <returns>Property value</returns>
|
|||
internal static object GetNestedPropertyValue(object item, string propertyPath, Type propertyType, out Exception exception) |
|||
{ |
|||
exception = null; |
|||
|
|||
// if the item is null, treat the property value as null
|
|||
if (item == null) |
|||
{ |
|||
return null; |
|||
} |
|||
|
|||
// if the propertyPath is null or empty, return the item
|
|||
if (String.IsNullOrEmpty(propertyPath)) |
|||
{ |
|||
return item; |
|||
} |
|||
|
|||
object propertyValue = item; |
|||
Type itemType = item.GetType(); |
|||
if (itemType != null) |
|||
{ |
|||
PropertyInfo propertyInfo = itemType.GetNestedProperty(propertyPath, out exception, ref propertyValue); |
|||
if (propertyInfo != null && propertyInfo.PropertyType != propertyType) |
|||
{ |
|||
return null; |
|||
} |
|||
} |
|||
return propertyValue; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the value of a given property path on a particular data item.
|
|||
/// </summary>
|
|||
/// <param name="item">Parent data item.</param>
|
|||
/// <param name="propertyPath">Property path.</param>
|
|||
/// <returns>Value.</returns>
|
|||
internal static object GetNestedPropertyValue(object item, string propertyPath) |
|||
{ |
|||
if (item != null) |
|||
{ |
|||
Type parentType = item.GetType(); |
|||
if (String.IsNullOrEmpty(propertyPath)) |
|||
{ |
|||
return item; |
|||
} |
|||
else if (parentType != null) |
|||
{ |
|||
object nestedValue = item; |
|||
parentType.GetNestedProperty(propertyPath, ref nestedValue); |
|||
return nestedValue; |
|||
} |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
internal static Type GetNonNullableType(this Type type) |
|||
{ |
|||
if (IsNullableType(type)) |
|||
{ |
|||
return type.GetGenericArguments()[0]; |
|||
} |
|||
return type; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns the PropertyInfo for the specified property path. If the property path
|
|||
/// refers to an indexer (e.g. "[abc]"), then the index out parameter will be set to the value
|
|||
/// specified in the property path. This method only supports indexers with a single parameter
|
|||
/// that is either an int or a string. Int parameters take priority over string parameters.
|
|||
/// </summary>
|
|||
/// <param name="type">Type to search.</param>
|
|||
/// <param name="propertyPath">Property path.</param>
|
|||
/// <param name="index">Set to the index if return value is an indexer, otherwise null.</param>
|
|||
/// <returns>PropertyInfo for either a property or an indexer.</returns>
|
|||
internal static PropertyInfo GetPropertyOrIndexer(this Type type, string propertyPath, out object[] index) |
|||
{ |
|||
index = null; |
|||
if (string.IsNullOrEmpty(propertyPath) || propertyPath[0] != LeftIndexerToken) |
|||
{ |
|||
// Return the default value of GetProperty if the first character is not an indexer token.
|
|||
return type.GetProperty(propertyPath); |
|||
} |
|||
|
|||
if (propertyPath.Length < 2 || propertyPath[propertyPath.Length - 1] != RightIndexerToken) |
|||
{ |
|||
// Return null if the indexer does not meet the standard format (i.e. "[x]").
|
|||
return null; |
|||
} |
|||
|
|||
PropertyInfo indexer = null; |
|||
string stringIndex = propertyPath.Substring(1, propertyPath.Length - 2); |
|||
indexer = FindIndexerInMembers(type.GetDefaultMembers(), stringIndex, out index); |
|||
if (indexer != null) |
|||
{ |
|||
// We found the indexer, so return it.
|
|||
return indexer; |
|||
} |
|||
|
|||
if (typeof(IList).IsAssignableFrom(type)) |
|||
{ |
|||
// If the object is of type IList, try to use its default indexer.
|
|||
indexer = FindIndexerInMembers(typeof(IList).GetDefaultMembers(), stringIndex, out index); |
|||
} |
|||
|
|||
return indexer; |
|||
} |
|||
|
|||
internal static bool IsEnumerableType(this Type enumerableType) |
|||
{ |
|||
return (FindGenericType(typeof(IEnumerable<>), enumerableType) != null); |
|||
} |
|||
|
|||
internal static bool IsNullableType(this Type type) |
|||
{ |
|||
return (((type != null) && type.IsGenericType) && (type.GetGenericTypeDefinition() == typeof(Nullable<>))); |
|||
} |
|||
|
|||
internal static bool IsNullableEnum(this Type type) |
|||
{ |
|||
return type.IsNullableType() && |
|||
type.GetGenericArguments().Length == 1 && |
|||
type.GetGenericArguments()[0].IsEnum; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// If the specified property is an indexer, this method will prepend the object's
|
|||
/// default member name to it (e.g. "[foo]" returns "Item[foo]").
|
|||
/// </summary>
|
|||
/// <param name="item">Declaring data item.</param>
|
|||
/// <param name="property">Property name.</param>
|
|||
/// <returns>Property with default member name prepended, or property if unchanged.</returns>
|
|||
internal static string PrependDefaultMemberName(object item, string property) |
|||
{ |
|||
if (item != null && !string.IsNullOrEmpty(property) && property[0] == TypeHelper.LeftIndexerToken) |
|||
{ |
|||
// The leaf property name is an indexer, so add the default member name.
|
|||
Type declaringType = item.GetType(); |
|||
if (declaringType != null) |
|||
{ |
|||
string defaultMemberName = declaringType.GetNonNullableType().GetDefaultMemberName(); |
|||
if (!string.IsNullOrEmpty(defaultMemberName)) |
|||
{ |
|||
return defaultMemberName + property; |
|||
} |
|||
} |
|||
} |
|||
return property; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// If the specified property is an indexer, this method will remove the object's
|
|||
/// default member name from it (e.g. "Item[foo]" returns "[foo]").
|
|||
/// </summary>
|
|||
/// <param name="property">Property name.</param>
|
|||
/// <returns>Property with default member name removed, or property if unchanged.</returns>
|
|||
internal static string RemoveDefaultMemberName(string property) |
|||
{ |
|||
if (!string.IsNullOrEmpty(property) && property[property.Length - 1] == TypeHelper.RightIndexerToken) |
|||
{ |
|||
// The property is an indexer, so remove the default member name.
|
|||
int leftIndexerToken = property.IndexOf(TypeHelper.LeftIndexerToken); |
|||
if (leftIndexerToken >= 0) |
|||
{ |
|||
return property.Substring(leftIndexerToken); |
|||
} |
|||
} |
|||
return property; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns a list of substrings where each one represents a single property within a nested
|
|||
/// property path which may include indexers. For example, the string "abc.d[efg][h].ijk"
|
|||
/// would return the substrings: "abc", "d", "[efg]", "[h]", and "ijk".
|
|||
/// </summary>
|
|||
/// <param name="propertyPath">Path to split.</param>
|
|||
/// <returns>List of property substrings.</returns>
|
|||
internal static List<string> SplitPropertyPath(string propertyPath) |
|||
{ |
|||
List<string> propertyPaths = new List<string>(); |
|||
if (!string.IsNullOrEmpty(propertyPath)) |
|||
{ |
|||
int startIndex = 0; |
|||
for (int index = 0; index < propertyPath.Length; index++) |
|||
{ |
|||
if (propertyPath[index] == PropertyNameSeparator) |
|||
{ |
|||
propertyPaths.Add(propertyPath.Substring(startIndex, index - startIndex)); |
|||
startIndex = index + 1; |
|||
} |
|||
else if (startIndex != index && propertyPath[index] == LeftIndexerToken) |
|||
{ |
|||
propertyPaths.Add(propertyPath.Substring(startIndex, index - startIndex)); |
|||
startIndex = index; |
|||
} |
|||
else if (index == propertyPath.Length - 1) |
|||
{ |
|||
propertyPaths.Add(propertyPath.Substring(startIndex)); |
|||
} |
|||
} |
|||
} |
|||
return propertyPaths; |
|||
} |
|||
|
|||
|
|||
/// <summary>
|
|||
/// Checks a MemberInfo object (e.g. a Type or PropertyInfo) for the ReadOnly attribute
|
|||
/// and returns the value of IsReadOnly if it exists.
|
|||
/// </summary>
|
|||
/// <param name="memberInfo">MemberInfo to check</param>
|
|||
/// <returns>true if MemberInfo is read-only, false otherwise</returns>
|
|||
internal static bool GetIsReadOnly(this MemberInfo memberInfo) |
|||
{ |
|||
if (memberInfo != null) |
|||
{ |
|||
// Check if ReadOnlyAttribute is defined on the member
|
|||
object[] attributes = memberInfo.GetCustomAttributes(typeof(ReadOnlyAttribute), true); |
|||
if (attributes != null && attributes.Length > 0) |
|||
{ |
|||
ReadOnlyAttribute readOnlyAttribute = attributes[0] as ReadOnlyAttribute; |
|||
Debug.Assert(readOnlyAttribute != null); |
|||
return readOnlyAttribute.IsReadOnly; |
|||
} |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
internal static Type GetItemType(this IEnumerable list) |
|||
{ |
|||
Type listType = list.GetType(); |
|||
Type itemType = null; |
|||
|
|||
// if it's a generic enumerable, we get the generic type
|
|||
|
|||
// Unfortunately, if data source is fed from a bare IEnumerable, TypeHelper will report an element type of object,
|
|||
// which is not particularly interesting. We deal with it further on.
|
|||
if (listType.IsEnumerableType()) |
|||
{ |
|||
itemType = listType.GetEnumerableItemType(); |
|||
} |
|||
|
|||
// Bare IEnumerables mean that result type will be object. In that case, we try to get something more interesting
|
|||
if (itemType == null || itemType == typeof(object)) |
|||
{ |
|||
// We haven't located a type yet.. try a different approach.
|
|||
// Does the list have anything in it?
|
|||
|
|||
IEnumerator en = list.GetEnumerator(); |
|||
if (en.MoveNext() && en.Current != null) |
|||
{ |
|||
return en.Current.GetType(); |
|||
} |
|||
} |
|||
|
|||
// if we're null at this point, give up
|
|||
return itemType; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,60 @@ |
|||
// (c) Copyright Microsoft Corporation.
|
|||
// This source is subject to the Microsoft Public License (Ms-PL).
|
|||
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
|
|||
// All other rights reserved.
|
|||
|
|||
using Avalonia.Input; |
|||
using Avalonia.VisualTree; |
|||
using Avalonia.Controls; |
|||
|
|||
namespace Avalonia.Utilities |
|||
{ |
|||
internal static class TreeHelper |
|||
{ |
|||
/// <summary>
|
|||
/// Walks the visual tree to determine if a particular child is contained within a parent Visual.
|
|||
/// </summary>
|
|||
/// <param name="element">Parent Visual</param>
|
|||
/// <param name="child">Child Visual</param>
|
|||
/// <returns>True if the parent element contains the child</returns>
|
|||
internal static bool ContainsChild(this IVisual element, IVisual child) |
|||
{ |
|||
if (element != null) |
|||
{ |
|||
while (child != null) |
|||
{ |
|||
if (child == element) |
|||
{ |
|||
return true; |
|||
} |
|||
|
|||
// Walk up the visual tree. If we hit the root, try using the framework element's
|
|||
// parent. We do this because Popups behave differently with respect to the visual tree,
|
|||
// and it could have a parent even if the VisualTreeHelper doesn't find it.
|
|||
IVisual parent = child.GetVisualParent(); |
|||
if (parent == null) |
|||
{ |
|||
if (child is IControl childElement) |
|||
{ |
|||
parent = childElement.Parent; |
|||
} |
|||
} |
|||
child = parent; |
|||
} |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Walks the visual tree to determine if the currently focused element is contained within
|
|||
/// a parent AvaloniaObject. The FocusManager's Current property is used to determine
|
|||
/// the currently focused element, which is updated synchronously.
|
|||
/// </summary>
|
|||
/// <param name="element">Parent Visual</param>
|
|||
/// <returns>True if the currently focused element is within the visual tree of the parent</returns>
|
|||
internal static bool ContainsFocusedElement(this IVisual element) |
|||
{ |
|||
return (element == null) ? false : element.ContainsChild(FocusManager.Instance.Current); |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue