57 changed files with 33674 additions and 0 deletions
@ -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-build5800-beta" /> |
||||
|
<PackageReference Include="Avalonia.Desktop" Version="0.6.2-build5800-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,125 @@ |
|||||
|
<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="DataGrid"> |
||||
|
<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="Grouping"> |
||||
|
<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="Editable"> |
||||
|
<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> |
||||
|
|
||||
|
</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-*" /> |
||||
|
</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,927 @@ |
|||||
|
<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"> |
||||
|
|
||||
|
<!--TODO: Validation and Focus--> |
||||
|
<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}"/> |
||||
|
|
||||
|
<Path Name="SortIcon" |
||||
|
Grid.Column="1" |
||||
|
Fill="#FF444444" |
||||
|
HorizontalAlignment="Left" |
||||
|
VerticalAlignment="Center" |
||||
|
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 "/> |
||||
|
|
||||
|
</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> |
||||
|
--> |
||||
|
|
||||
|
|
||||
|
<Style Selector="dg|DataGridColumnHeader:dragIndicator"> |
||||
|
<Setter Property="Opacity" Value="0.5"/> |
||||
|
</Style> |
||||
|
|
||||
|
<Style Selector="dg|DataGridColumnHeader /template/ Path#SortIcon"> |
||||
|
<Setter Property="IsVisible" Value="False"/> |
||||
|
<Setter Property="RenderTransform"> |
||||
|
<Setter.Value> |
||||
|
<ScaleTransform ScaleX="0.9" ScaleY="0.9" /> |
||||
|
</Setter.Value> |
||||
|
</Setter> |
||||
|
</Style> |
||||
|
<Style Selector="dg|DataGridColumnHeader:sortascending /template/ Path#SortIcon"> |
||||
|
<Setter Property="IsVisible" Value="True"/> |
||||
|
</Style> |
||||
|
<Style Selector="dg|DataGridColumnHeader:sortdescending /template/ Path#SortIcon"> |
||||
|
<Setter Property="IsVisible" Value="True"/> |
||||
|
<Setter Property="RenderTransform"> |
||||
|
<Setter.Value> |
||||
|
<ScaleTransform ScaleX="0.9" ScaleY="-0.9" /> |
||||
|
</Setter.Value> |
||||
|
</Setter> |
||||
|
</Style> |
||||
|
|
||||
|
|
||||
|
<!--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"/> |
||||
|
|
||||
|
<prim:DataGridRowHeader Grid.RowSpan="3" Name="PART_RowHeader" prim:DataGridFrozenGrid.IsFrozen="True" /> |
||||
|
<prim:DataGridCellsPresenter Grid.Column="1" Name="PART_CellsPresenter" prim:DataGridFrozenGrid.IsFrozen="True" /> |
||||
|
<prim:DataGridDetailsPresenter Grid.Row="1" Grid.Column="1" Name="PART_DetailsPresenter"/> |
||||
|
<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"/> |
||||
|
<Setter Property="Fill" Value="#FFBADDE9" /> |
||||
|
</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" 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> |
||||
|
<Style Selector="dg|DataGridRowGroupHeader /template/ ToggleButton#ExpanderButton"> |
||||
|
<Setter Property="Template"> |
||||
|
<ControlTemplate> |
||||
|
<Border Grid.Column="0" Width="20" Height="20" Background="Transparent" HorizontalAlignment="Center" VerticalAlignment="Center"> |
||||
|
<Path Fill="Black" |
||||
|
HorizontalAlignment="Center" |
||||
|
VerticalAlignment="Center" |
||||
|
Data="M 0 2 L 4 6 L 0 10 Z" /> |
||||
|
</Border> |
||||
|
</ControlTemplate> |
||||
|
</Setter> |
||||
|
</Style> |
||||
|
<Style Selector="dg|DataGridRowGroupHeader /template/ ToggleButton#ExpanderButton:checked /template/ Path"> |
||||
|
<Setter Property="RenderTransform"> |
||||
|
<RotateTransform Angle="90" /> |
||||
|
</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="DropLocationIndicatorTemplate"> |
||||
|
<Template> |
||||
|
<Rectangle Fill="#FF3F4346" Width="2"/> |
||||
|
</Template> |
||||
|
</Setter> |
||||
|
<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-*" /> |
||||
|
</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,312 @@ |
|||||
|
// (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
|
||||
|
//TODO Validation
|
||||
|
protected sealed override IControl GenerateEditingElement(DataGridCell cell, object dataItem, out ICellEditBinding editBinding) |
||||
|
{ |
||||
|
IControl element = GenerateEditingElementDirect(cell, dataItem); |
||||
|
editBinding = null; |
||||
|
|
||||
|
if (Binding != null) |
||||
|
//if (Binding != null || !DesignerProperties.IsInDesignTool)
|
||||
|
{ |
||||
|
//var t1 = Binding.Initiate(textBox, BindingTarget, anchor: null, enableDataValidation: true);
|
||||
|
//BindingOperations.Apply(textBox, BindingTarget, t1, null);
|
||||
|
|
||||
|
editBinding = BindEditingElement(element, BindingTarget, Binding); |
||||
|
//element.Bind(BindingTarget, Binding);
|
||||
|
//textBox.SetBinding(BindingTarget, Binding);
|
||||
|
} |
||||
|
|
||||
|
return element; |
||||
|
} |
||||
|
|
||||
|
private static ICellEditBinding BindEditingElement(IAvaloniaObject target, AvaloniaProperty property, IBinding binding) |
||||
|
{ |
||||
|
var result = binding.Initiate(target, property, enableDataValidation: true); |
||||
|
|
||||
|
if (result != null) |
||||
|
{ |
||||
|
if(result.Subject != null) |
||||
|
{ |
||||
|
var bindingHelper = new CellEditBinding(result.Subject); |
||||
|
var instanceBinding = new InstancedBinding(bindingHelper.InternalSubject, result.Mode, result.Priority); |
||||
|
|
||||
|
BindingOperations.Apply(target, property, instanceBinding, null); |
||||
|
return bindingHelper; |
||||
|
} |
||||
|
|
||||
|
BindingOperations.Apply(target, property, result, null); |
||||
|
} |
||||
|
|
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
/* |
||||
|
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 IControl 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; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/* |
||||
|
|
||||
|
[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 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,335 @@ |
|||||
|
// (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) |
||||
|
{ |
||||
|
// OwningGrid is null for TopLeftHeaderCell and TopRightHeaderCell because they have no OwningRow
|
||||
|
if (OwningGrid != null) |
||||
|
{ |
||||
|
OwningGrid.OnCellPointerPressed(new DataGridCellPointerPressedEventArgs(this, OwningRow, OwningColumn, e)); |
||||
|
if (e.MouseButton == MouseButton.Left) |
||||
|
{ |
||||
|
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(IControl 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 IControl 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 IControl 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(IControl 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(IControl 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,70 @@ |
|||||
|
// (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 Avalonia.Utilities; |
||||
|
|
||||
|
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 IControl GenerateElement(DataGridCell cell, object dataItem) |
||||
|
{ |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
protected override IControl GenerateEditingElement(DataGridCell cell, object dataItem, out ICellEditBinding editBinding) |
||||
|
{ |
||||
|
editBinding = null; |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
protected override object PrepareCellForEdit(IControl 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 if(RowGroupInfo.IsVisible != isVisible) |
||||
|
{ |
||||
|
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,79 @@ |
|||||
|
// (c) Copyright Microsoft Corporation.
|
||||
|
// This source is subject to the Microsoft Public License (Ms-PL).
|
||||
|
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
|
||||
|
// All other rights reserved.
|
||||
|
|
||||
|
using Avalonia.Controls.Templates; |
||||
|
using Avalonia.Input; |
||||
|
using Avalonia.Interactivity; |
||||
|
using Avalonia.Media; |
||||
|
using Avalonia.Utilities; |
||||
|
|
||||
|
namespace Avalonia.Controls |
||||
|
{ |
||||
|
public class DataGridTemplateColumn : DataGridColumn |
||||
|
{ |
||||
|
IDataTemplate _cellTemplate; |
||||
|
|
||||
|
public static readonly DirectProperty<DataGridTemplateColumn, IDataTemplate> CellTemplateProperty = |
||||
|
AvaloniaProperty.RegisterDirect<DataGridTemplateColumn, IDataTemplate>( |
||||
|
nameof(CellTemplate), |
||||
|
o => o.CellTemplate, |
||||
|
(o, v) => o.CellTemplate = v); |
||||
|
|
||||
|
public IDataTemplate CellTemplate |
||||
|
{ |
||||
|
get { return _cellTemplate; } |
||||
|
set { SetAndRaise(CellTemplateProperty, ref _cellTemplate, value); } |
||||
|
} |
||||
|
|
||||
|
private void OnCellTemplateChanged(AvaloniaPropertyChangedEventArgs e) |
||||
|
{ |
||||
|
//CellTemplateProperty.Changed.AddClassHandler<DataGridTemplateColumn>(x => x.OnCellTemplateChanged);
|
||||
|
var oldValue = (IDataTemplate)e.OldValue; |
||||
|
var value = (IDataTemplate)e.NewValue; |
||||
|
} |
||||
|
|
||||
|
public DataGridTemplateColumn() |
||||
|
{ |
||||
|
IsReadOnly = true; |
||||
|
} |
||||
|
|
||||
|
protected override IControl GenerateElement(DataGridCell cell, object dataItem) |
||||
|
{ |
||||
|
if(CellTemplate != null) |
||||
|
{ |
||||
|
return CellTemplate.Build(dataItem); |
||||
|
} |
||||
|
if (Design.IsDesignMode) |
||||
|
{ |
||||
|
return null; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
throw DataGridError.DataGridTemplateColumn.MissingTemplateForType(typeof(DataGridTemplateColumn)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected override IControl GenerateEditingElement(DataGridCell cell, object dataItem, out ICellEditBinding binding) |
||||
|
{ |
||||
|
binding = null; |
||||
|
return GenerateElement(cell, dataItem); |
||||
|
} |
||||
|
|
||||
|
protected override object PrepareCellForEdit(IControl editingElement, RoutedEventArgs editingEventArgs) |
||||
|
{ |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
protected internal override void RefreshCellContent(IControl element, string propertyName) |
||||
|
{ |
||||
|
if(propertyName == nameof(CellTemplate) && element.Parent is DataGridCell cell) |
||||
|
{ |
||||
|
cell.Content = GenerateElement(cell, cell.DataContext); |
||||
|
} |
||||
|
|
||||
|
base.RefreshCellContent(element, propertyName); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,403 @@ |
|||||
|
// (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(IControl 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 IControl 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 IControl GenerateElement(DataGridCell cell, object dataItem) |
||||
|
{ |
||||
|
TextBlock textBlockElement = new TextBlock |
||||
|
{ |
||||
|
Margin = new Thickness(4), |
||||
|
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(IControl 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(IControl 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,588 @@ |
|||||
|
// (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 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 after the cell has been pressed.
|
||||
|
/// </summary>
|
||||
|
public class DataGridCellPointerPressedEventArgs : EventArgs |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Instantiates a new instance of this class.
|
||||
|
/// </summary>
|
||||
|
/// <param name="cell">The cell that has been pressed.</param>
|
||||
|
/// <param name="row">The row container of the cell that has been pressed.</param>
|
||||
|
/// <param name="column">The column of the cell that has been pressed.</param>
|
||||
|
/// <param name="e">The pointer action that has been taken.</param>
|
||||
|
public DataGridCellPointerPressedEventArgs(DataGridCell cell, |
||||
|
DataGridRow row, |
||||
|
DataGridColumn column, |
||||
|
PointerPressedEventArgs e) |
||||
|
{ |
||||
|
Cell = cell; |
||||
|
Row = row; |
||||
|
Column = column; |
||||
|
PointerPressedEventArgs = e; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The cell that has been pressed.
|
||||
|
/// </summary>
|
||||
|
public DataGridCell Cell { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The row container of the cell that has been pressed.
|
||||
|
/// </summary>
|
||||
|
public DataGridRow Row { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The column of the cell that has been pressed.
|
||||
|
/// </summary>
|
||||
|
public DataGridColumn Column { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The pointer action that has been taken.
|
||||
|
/// </summary>
|
||||
|
public PointerPressedEventArgs PointerPressedEventArgs { get; } |
||||
|
} |
||||
|
|
||||
|
|
||||
|
/// <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 IControl 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,404 @@ |
|||||
|
// (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 IControl _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 IControl 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); |
||||
|
|
||||
|
var height = DragIndicator.Bounds.Height; |
||||
|
if (height <= 0) |
||||
|
height = DragIndicator.DesiredSize.Height; |
||||
|
|
||||
|
DragIndicator.Arrange(new Rect(dragIndicatorLeftEdge, 0, DragIndicator.Bounds.Width, height)); |
||||
|
} |
||||
|
if (DropLocationIndicator != null) |
||||
|
{ |
||||
|
if (DropLocationIndicator is Control element) |
||||
|
{ |
||||
|
EnsureColumnReorderingClip(element, 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,191 @@ |
|||||
|
// (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; |
||||
|
} |
||||
|
|
||||
|
private double _measureHeightOffset = 0; |
||||
|
private double CalculateEstimatedAvailableHeight(Size availableSize) |
||||
|
{ |
||||
|
if(!Double.IsPositiveInfinity(availableSize.Height)) |
||||
|
{ |
||||
|
return availableSize.Height + _measureHeightOffset; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
return availableSize.Height; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <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); |
||||
|
} |
||||
|
|
||||
|
if(OwningGrid.RowsPresenterAvailableSize.HasValue) |
||||
|
{ |
||||
|
var availableHeight = OwningGrid.RowsPresenterAvailableSize.Value.Height; |
||||
|
if(!Double.IsPositiveInfinity(availableHeight)) |
||||
|
{ |
||||
|
_measureHeightOffset = finalSize.Height - availableHeight; |
||||
|
OwningGrid.RowsPresenterEstimatedAvailableHeight = finalSize.Height; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
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.RowsPresenterEstimatedAvailableHeight = CalculateEstimatedAvailableHeight(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,160 @@ |
|||||
|
using Avalonia.Data; |
||||
|
using Avalonia.Reactive; |
||||
|
using System; |
||||
|
using System.ComponentModel.DataAnnotations; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Reactive.Subjects; |
||||
|
using System.Text; |
||||
|
|
||||
|
namespace Avalonia.Utilities |
||||
|
{ |
||||
|
public interface ICellEditBinding |
||||
|
{ |
||||
|
bool IsValid { get; } |
||||
|
IEnumerable<Exception> ValidationErrors { get; } |
||||
|
IObservable<bool> ValidationChanged { get; } |
||||
|
bool CommitEdit(); |
||||
|
} |
||||
|
|
||||
|
internal class CellEditBinding : ICellEditBinding |
||||
|
{ |
||||
|
private readonly Subject<bool> _changedSubject = new Subject<bool>(); |
||||
|
private readonly List<Exception> _validationErrors = new List<Exception>(); |
||||
|
private readonly SubjectWrapper _inner; |
||||
|
|
||||
|
public bool IsValid => _validationErrors.Count <= 0; |
||||
|
public IEnumerable<Exception> ValidationErrors => _validationErrors; |
||||
|
public IObservable<bool> ValidationChanged => _changedSubject; |
||||
|
public ISubject<object> InternalSubject => _inner; |
||||
|
|
||||
|
public CellEditBinding(ISubject<object> bindingSourceSubject) |
||||
|
{ |
||||
|
_inner = new SubjectWrapper(bindingSourceSubject, this); |
||||
|
} |
||||
|
|
||||
|
private void AlterValidationErrors(Action<List<Exception>> action) |
||||
|
{ |
||||
|
var wasValid = IsValid; |
||||
|
action(_validationErrors); |
||||
|
var isValid = IsValid; |
||||
|
|
||||
|
if(!isValid || !wasValid) |
||||
|
{ |
||||
|
_changedSubject.OnNext(isValid); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public bool CommitEdit() |
||||
|
{ |
||||
|
_inner.CommitEdit(); |
||||
|
return IsValid; |
||||
|
} |
||||
|
|
||||
|
class SubjectWrapper : LightweightObservableBase<object>, ISubject<object>, IDisposable |
||||
|
{ |
||||
|
private readonly ISubject<object> _sourceSubject; |
||||
|
private readonly CellEditBinding _editBinding; |
||||
|
private IDisposable _subscription; |
||||
|
private object _controlValue; |
||||
|
private bool _isControlValueSet = false; |
||||
|
private bool _settingSourceValue = false; |
||||
|
|
||||
|
public SubjectWrapper(ISubject<object> bindingSourceSubject, CellEditBinding editBinding) |
||||
|
{ |
||||
|
_sourceSubject = bindingSourceSubject; |
||||
|
_editBinding = editBinding; |
||||
|
} |
||||
|
|
||||
|
private void SetSourceValue(object value) |
||||
|
{ |
||||
|
_settingSourceValue = true; |
||||
|
|
||||
|
_sourceSubject.OnNext(value); |
||||
|
|
||||
|
_settingSourceValue = false; |
||||
|
} |
||||
|
private void SetControlValue(object value) |
||||
|
{ |
||||
|
PublishNext(value); |
||||
|
} |
||||
|
|
||||
|
private void OnValidationError(BindingNotification notification) |
||||
|
{ |
||||
|
if(notification.Error != null) |
||||
|
{ |
||||
|
_editBinding.AlterValidationErrors(errors => |
||||
|
{ |
||||
|
errors.Clear(); |
||||
|
var unpackedErrors = ValidationUtil.UnpackException(notification.Error); |
||||
|
if(unpackedErrors != null) |
||||
|
errors.AddRange(unpackedErrors); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
private void OnControlValueUpdated(object value) |
||||
|
{ |
||||
|
_controlValue = value; |
||||
|
_isControlValueSet = true; |
||||
|
|
||||
|
if(!_editBinding.IsValid) |
||||
|
{ |
||||
|
SetSourceValue(value); |
||||
|
} |
||||
|
} |
||||
|
private void OnSourceValueUpdated(object value) |
||||
|
{ |
||||
|
void OnValidValue(object val) |
||||
|
{ |
||||
|
SetControlValue(val); |
||||
|
_editBinding.AlterValidationErrors(errors => errors.Clear()); |
||||
|
} |
||||
|
|
||||
|
if (value is BindingNotification notification) |
||||
|
{ |
||||
|
if (notification.ErrorType != BindingErrorType.None) |
||||
|
OnValidationError(notification); |
||||
|
else |
||||
|
OnValidValue(value); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
OnValidValue(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; |
||||
|
} |
||||
|
public void CommitEdit() |
||||
|
{ |
||||
|
if(_isControlValueSet) |
||||
|
SetSourceValue(_controlValue); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -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); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,168 @@ |
|||||
|
// (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.ComponentModel.DataAnnotations; |
||||
|
using System.Text; |
||||
|
using System.Threading; |
||||
|
using System.Linq; |
||||
|
using Avalonia.Data; |
||||
|
|
||||
|
namespace Avalonia.Utilities |
||||
|
{ |
||||
|
internal static class ValidationUtil |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Searches a ValidationResult for the specified target member name. If the target is null
|
||||
|
/// or empty, this method will return true if there are no member names at all.
|
||||
|
/// </summary>
|
||||
|
/// <param name="validationResult">ValidationResult to search.</param>
|
||||
|
/// <param name="target">Member name to search for.</param>
|
||||
|
/// <returns>True if found.</returns>
|
||||
|
public static bool ContainsMemberName(this ValidationResult validationResult, string target) |
||||
|
{ |
||||
|
int memberNameCount = 0; |
||||
|
foreach (string memberName in validationResult.MemberNames) |
||||
|
{ |
||||
|
if (string.Equals(target, memberName)) |
||||
|
{ |
||||
|
return true; |
||||
|
} |
||||
|
memberNameCount++; |
||||
|
} |
||||
|
return (memberNameCount == 0 && string.IsNullOrEmpty(target)); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Finds an equivalent ValidationResult if one exists.
|
||||
|
/// </summary>
|
||||
|
/// <param name="collection">ValidationResults to search through.</param>
|
||||
|
/// <param name="target">ValidationResult to find.</param>
|
||||
|
/// <returns>Equal ValidationResult if found, null otherwise.</returns>
|
||||
|
public static ValidationResult FindEqualValidationResult(this ICollection<ValidationResult> collection, ValidationResult target) |
||||
|
{ |
||||
|
foreach (ValidationResult oldValidationResult in collection) |
||||
|
{ |
||||
|
if (oldValidationResult.ErrorMessage == target.ErrorMessage) |
||||
|
{ |
||||
|
bool movedOld = true; |
||||
|
bool movedTarget = true; |
||||
|
IEnumerator<string> oldEnumerator = oldValidationResult.MemberNames.GetEnumerator(); |
||||
|
IEnumerator<string> targetEnumerator = target.MemberNames.GetEnumerator(); |
||||
|
while (movedOld && movedTarget) |
||||
|
{ |
||||
|
movedOld = oldEnumerator.MoveNext(); |
||||
|
movedTarget = targetEnumerator.MoveNext(); |
||||
|
|
||||
|
if (!movedOld && !movedTarget) |
||||
|
{ |
||||
|
return oldValidationResult; |
||||
|
} |
||||
|
if (movedOld != movedTarget || oldEnumerator.Current != targetEnumerator.Current) |
||||
|
{ |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
public static bool IsValid(this ValidationResult result) |
||||
|
{ |
||||
|
return result == null || result == ValidationResult.Success; |
||||
|
} |
||||
|
|
||||
|
public static IEnumerable<Exception> UnpackException(Exception exception) |
||||
|
{ |
||||
|
if (exception != null) |
||||
|
{ |
||||
|
var aggregate = exception as AggregateException; |
||||
|
var exceptions = aggregate == null ? |
||||
|
(IEnumerable<Exception>)new[] { exception } : |
||||
|
aggregate.InnerExceptions; |
||||
|
var filtered = exceptions.Where(x => !(x is BindingChainException)).ToList(); |
||||
|
|
||||
|
if (filtered.Count > 0) |
||||
|
{ |
||||
|
return filtered; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Determines whether the collection contains an equivalent ValidationResult
|
||||
|
/// </summary>
|
||||
|
/// <param name="collection">ValidationResults to search through</param>
|
||||
|
/// <param name="target">ValidationResult to search for</param>
|
||||
|
/// <returns></returns>
|
||||
|
public static bool ContainsEqualValidationResult(this ICollection<ValidationResult> collection, ValidationResult target) |
||||
|
{ |
||||
|
return (collection.FindEqualValidationResult(target) != null); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Adds a new ValidationResult to the collection if an equivalent does not exist.
|
||||
|
/// </summary>
|
||||
|
/// <param name="collection">ValidationResults to search through</param>
|
||||
|
/// <param name="value">ValidationResult to add</param>
|
||||
|
public static void AddIfNew(this ICollection<ValidationResult> collection, ValidationResult value) |
||||
|
{ |
||||
|
if (!collection.ContainsEqualValidationResult(value)) |
||||
|
{ |
||||
|
collection.Add(value); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private static bool ExceptionsMatch(Exception e1, Exception e2) |
||||
|
{ |
||||
|
return e1.Message == e2.Message; |
||||
|
} |
||||
|
public static void AddExceptionIfNew(this ICollection<Exception> collection, Exception value) |
||||
|
{ |
||||
|
if(!collection.Any(e => ExceptionsMatch(e, value))) |
||||
|
{ |
||||
|
collection.Add(value); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Performs an action and catches any non-critical exceptions.
|
||||
|
/// </summary>
|
||||
|
/// <param name="action">Action to perform</param>
|
||||
|
public static void CatchNonCriticalExceptions(Action action) |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
action(); |
||||
|
} |
||||
|
catch (Exception exception) |
||||
|
{ |
||||
|
if (IsCriticalException(exception)) |
||||
|
{ |
||||
|
throw; |
||||
|
} |
||||
|
// Catch any non-critical exceptions
|
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Determines if the specified exception is un-recoverable.
|
||||
|
/// </summary>
|
||||
|
/// <param name="exception">The exception.</param>
|
||||
|
/// <returns>True if the process cannot be recovered from the exception.</returns>
|
||||
|
public static bool IsCriticalException(Exception exception) |
||||
|
{ |
||||
|
return (exception is OutOfMemoryException) || |
||||
|
(exception is StackOverflowException) || |
||||
|
(exception is AccessViolationException) || |
||||
|
(exception is ThreadAbortException); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue