diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..7d9f5d3 --- /dev/null +++ b/AUTHORS @@ -0,0 +1,15 @@ +# This is the official list of OxyPlot authors for copyright purposes. +# This file is distinct from the CONTRIBUTORS file. +# See the latter for an explanation. + +# Names should be added to this file as +# Name or Organization +# The email address is not required for organizations. + +# Please keep the list sorted. +# Please notify the first person on the list to be added here. + +Oystein Bjorke +DNV GL AS +LECO® Corporation +TrainerRoad, LLC \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..3d255ff --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,8 @@ +# Change Log +All notable changes to this project will be documented in this file. + +## [Unreleased] +### Added +- + +[Unreleased]: https://github.com/oxyplot/oxyplot-contrib/compare/v0.0.1...HEAD diff --git a/CONTRIBUTORS b/CONTRIBUTORS new file mode 100644 index 0000000..6ec34fd --- /dev/null +++ b/CONTRIBUTORS @@ -0,0 +1,138 @@ +# This is the official list of people who have contributed +# to the OxyPlot repository. +# The AUTHORS file lists the copyright holders; this file +# lists people. + +# People submitting code should be listed in this file (by email address). + +# Names should be added to this file like so: +# Name + +# Please keep the list sorted. + +Alexei Shcherbakov +Anders Musikka +Auriou +Bartłomiej Szypelow +benjaminrupp +Benoit Blanchon <> +br +brantheman +Brannon King +Bryan Freeman +Brian Lim +Caleb Clarke +Carlos Anderson +Carlos Teixeira +Chase Long +Choden Konigsmark +classicboss302 +csabar +Curt Mullin +Cyril Martin +Dan Aizenstros +danpaul88 +darrelbrown +David Funk +David Laundav +David Wong +DJDAS +DNV GL AS +Doeharrrck +Don Syme +DotNetDoctor +efontana2 +elliatab +Elmar Strittmatter +episage +eric +Federico Coppola +Francois Botha +Frank Tore Sæther +Garrett +Geert van Horrik +Gimly +Herman Eldering +Iain Nicol +Ilja Nosik +Ilya Skriblovsky +Iurii Gazin +Jānis Kiršteins +jaykul +Jeremy Koritzinsky +Jeremie Magnette +jezza323 +Johan +Johan20D +Jonathan Arweck +Jonathan Shore +julien.bataille +Just Slon +Kaplas80 +kc1212 +kenny_evoleap +Kenny Nygaard +Kevin Crowell +Kyle Pulvermacher +LECO® Corporation +Levi Botelho +Linquize +lsowen +Luka B +Nils Haferkemper +Matt Williams +Matthew Leibowitz +Memphisch +Mendel Monteiro-Beckerman +Menno Deij - van Rijswijk +methdotnet +Mikant +mirolev +Mitch-Connor +moes_leco +moljac +mroth +mrtncls +Oleg Tarasov +Oystein Bjorke +Patrice Marin +Peter-B- +Philippe AURIOU +Piotr Warzocha +Poul Erik Venø +Régis Boudin +Rik Borger +ryang +Sarah Müller +Senen Fernandez +Scott W Harden +Shankar Mathiah Nanjundan +Shun-ichi Goto +Soarc +Stefan Rado +stefan-schweiger +Steve Hoelzer +Surfin Bird +Sven Dummis +Taldoras +Tandy Carmichael +Tasos Stamadianos +Thorsten Claff +thepretender +tephyrnex +Thomas Ibel +Tomasz Cielecki +ToplandJ +twsl <45483159+twsI@users.noreply.github.com> +Udo Liess +VisualMelon +vhoehn +Vsevolod Kukol +Xavier +zur003 +Markus Ebner +Duncan Robertson +LauXjpn +R. Usamentiaga +Dmytro Shaurin +Rustam Sayfutdinov \ No newline at end of file diff --git a/Source/OxyPlot.Windows/OxyPlot.Windows.nuspec b/OxyPlot.Windows.nuspec similarity index 75% rename from Source/OxyPlot.Windows/OxyPlot.Windows.nuspec rename to OxyPlot.Windows.nuspec index 5f41f91..f1ab370 100644 --- a/Source/OxyPlot.Windows/OxyPlot.Windows.nuspec +++ b/OxyPlot.Windows.nuspec @@ -3,7 +3,7 @@ OxyPlot.Windows OxyPlot for Windows apps - $version$ + 2.1.0 Oystein Bjorke OxyPlot is a plotting library for .NET. This package targets the Universal Windows Platform (UAP10). @@ -14,13 +14,13 @@ en-US winrt uap uap10 UWP win10 plotting plot charting chart - + - - - + + + @@ -28,4 +28,4 @@ - + \ No newline at end of file diff --git a/OxyPlot.Windows.sln b/OxyPlot.Windows.sln new file mode 100644 index 0000000..881859f --- /dev/null +++ b/OxyPlot.Windows.sln @@ -0,0 +1,74 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29403.142 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OxyPlot.Windows", "OxyPlot.Windows\OxyPlot.Windows.csproj", "{4F5B1D20-EA97-4DBB-94E1-8D86B4152847}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimpleDemo", "OxyPlot.Windows\Examples\Windows\SimpleDemo\SimpleDemo.csproj", "{FAC17BDF-4003-4359-A366-2E2F380E1BB5}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|ARM = Debug|ARM + Debug|ARM64 = Debug|ARM64 + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|ARM = Release|ARM + Release|ARM64 = Release|ARM64 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {4F5B1D20-EA97-4DBB-94E1-8D86B4152847}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4F5B1D20-EA97-4DBB-94E1-8D86B4152847}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4F5B1D20-EA97-4DBB-94E1-8D86B4152847}.Debug|ARM.ActiveCfg = Debug|ARM + {4F5B1D20-EA97-4DBB-94E1-8D86B4152847}.Debug|ARM.Build.0 = Debug|ARM + {4F5B1D20-EA97-4DBB-94E1-8D86B4152847}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {4F5B1D20-EA97-4DBB-94E1-8D86B4152847}.Debug|ARM64.Build.0 = Debug|ARM64 + {4F5B1D20-EA97-4DBB-94E1-8D86B4152847}.Debug|x64.ActiveCfg = Debug|x64 + {4F5B1D20-EA97-4DBB-94E1-8D86B4152847}.Debug|x64.Build.0 = Debug|x64 + {4F5B1D20-EA97-4DBB-94E1-8D86B4152847}.Debug|x86.ActiveCfg = Debug|x86 + {4F5B1D20-EA97-4DBB-94E1-8D86B4152847}.Debug|x86.Build.0 = Debug|x86 + {4F5B1D20-EA97-4DBB-94E1-8D86B4152847}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4F5B1D20-EA97-4DBB-94E1-8D86B4152847}.Release|Any CPU.Build.0 = Release|Any CPU + {4F5B1D20-EA97-4DBB-94E1-8D86B4152847}.Release|ARM.ActiveCfg = Release|ARM + {4F5B1D20-EA97-4DBB-94E1-8D86B4152847}.Release|ARM.Build.0 = Release|ARM + {4F5B1D20-EA97-4DBB-94E1-8D86B4152847}.Release|ARM64.ActiveCfg = Release|ARM64 + {4F5B1D20-EA97-4DBB-94E1-8D86B4152847}.Release|ARM64.Build.0 = Release|ARM64 + {4F5B1D20-EA97-4DBB-94E1-8D86B4152847}.Release|x64.ActiveCfg = Release|x64 + {4F5B1D20-EA97-4DBB-94E1-8D86B4152847}.Release|x64.Build.0 = Release|x64 + {4F5B1D20-EA97-4DBB-94E1-8D86B4152847}.Release|x86.ActiveCfg = Release|x86 + {4F5B1D20-EA97-4DBB-94E1-8D86B4152847}.Release|x86.Build.0 = Release|x86 + {FAC17BDF-4003-4359-A366-2E2F380E1BB5}.Debug|Any CPU.ActiveCfg = Debug|x64 + {FAC17BDF-4003-4359-A366-2E2F380E1BB5}.Debug|Any CPU.Build.0 = Debug|x64 + {FAC17BDF-4003-4359-A366-2E2F380E1BB5}.Debug|ARM.ActiveCfg = Debug|ARM + {FAC17BDF-4003-4359-A366-2E2F380E1BB5}.Debug|ARM.Build.0 = Debug|ARM + {FAC17BDF-4003-4359-A366-2E2F380E1BB5}.Debug|ARM.Deploy.0 = Debug|ARM + {FAC17BDF-4003-4359-A366-2E2F380E1BB5}.Debug|ARM64.ActiveCfg = Debug|x86 + {FAC17BDF-4003-4359-A366-2E2F380E1BB5}.Debug|x64.ActiveCfg = Debug|x64 + {FAC17BDF-4003-4359-A366-2E2F380E1BB5}.Debug|x64.Build.0 = Debug|x64 + {FAC17BDF-4003-4359-A366-2E2F380E1BB5}.Debug|x64.Deploy.0 = Debug|x64 + {FAC17BDF-4003-4359-A366-2E2F380E1BB5}.Debug|x86.ActiveCfg = Debug|x86 + {FAC17BDF-4003-4359-A366-2E2F380E1BB5}.Debug|x86.Build.0 = Debug|x86 + {FAC17BDF-4003-4359-A366-2E2F380E1BB5}.Debug|x86.Deploy.0 = Debug|x86 + {FAC17BDF-4003-4359-A366-2E2F380E1BB5}.Release|Any CPU.ActiveCfg = Release|x86 + {FAC17BDF-4003-4359-A366-2E2F380E1BB5}.Release|ARM.ActiveCfg = Release|ARM + {FAC17BDF-4003-4359-A366-2E2F380E1BB5}.Release|ARM.Build.0 = Release|ARM + {FAC17BDF-4003-4359-A366-2E2F380E1BB5}.Release|ARM.Deploy.0 = Release|ARM + {FAC17BDF-4003-4359-A366-2E2F380E1BB5}.Release|ARM64.ActiveCfg = Release|x86 + {FAC17BDF-4003-4359-A366-2E2F380E1BB5}.Release|x64.ActiveCfg = Release|x64 + {FAC17BDF-4003-4359-A366-2E2F380E1BB5}.Release|x64.Build.0 = Release|x64 + {FAC17BDF-4003-4359-A366-2E2F380E1BB5}.Release|x64.Deploy.0 = Release|x64 + {FAC17BDF-4003-4359-A366-2E2F380E1BB5}.Release|x86.ActiveCfg = Release|x86 + {FAC17BDF-4003-4359-A366-2E2F380E1BB5}.Release|x86.Build.0 = Release|x86 + {FAC17BDF-4003-4359-A366-2E2F380E1BB5}.Release|x86.Deploy.0 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {64FB9F5C-FD83-4AF6-967F-20344209E367} + EndGlobalSection +EndGlobal diff --git a/Source/OxyPlot.Windows/Converters/OxyColorConverter.cs b/OxyPlot.Windows/Converters/OxyColorConverter.cs similarity index 100% rename from Source/OxyPlot.Windows/Converters/OxyColorConverter.cs rename to OxyPlot.Windows/Converters/OxyColorConverter.cs diff --git a/OxyPlot.Windows/Converters/OxyColorConverters.cs b/OxyPlot.Windows/Converters/OxyColorConverters.cs new file mode 100644 index 0000000..31d5686 --- /dev/null +++ b/OxyPlot.Windows/Converters/OxyColorConverters.cs @@ -0,0 +1,85 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// +// Converts from OxyPlot colors to Windows.UI.Color and vice versa. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace OxyPlot.Windows +{ + using System; + + using global::Windows.UI; + using global::Windows.UI.Xaml.Data; + using global::Windows.UI.Xaml.Media; + + /// + /// Converts from OxyPlot colors to Windows.UI.Color and vice versa. + /// + public sealed class OxyColorConverter : IValueConverter + { + /// + /// The convert. + /// + /// The value. + /// The target type. + /// The parameter. + /// The language. + /// The converted value. + public object Convert(object value, Type targetType, object parameter, string language) + { + if (!(value is OxyColor)) + { + return null; + } + + var color = (OxyColor)value; + Color col = Color.FromArgb(color.A, color.R, color.G, color.B); + if (targetType == typeof(Color)) + { + return col; + } + + if (targetType == typeof(Brush)) + { + Brush ret = new SolidColorBrush(col); + return ret; + } + + return null; + } + + /// + /// Converts back. + /// + /// The value. + /// The target type. + /// The parameter. + /// The language. + /// The converted value. + public object ConvertBack(object value, Type targetType, object parameter, string language) + { + if (targetType != typeof(OxyColor)) + { + return null; + } + + if (value is Color) + { + var color = (Color)value; + return OxyColor.FromArgb(color.A, color.R, color.G, color.B); + } + + var scb = value as SolidColorBrush; + if (scb != null) + { + var color = scb.Color; + return OxyColor.FromArgb(color.A, color.R, color.G, color.B); + } + + return null; + } + } +} diff --git a/Source/OxyPlot.Windows/Converters/ThicknessConverter.cs b/OxyPlot.Windows/Converters/ThicknessConverter.cs similarity index 97% rename from Source/OxyPlot.Windows/Converters/ThicknessConverter.cs rename to OxyPlot.Windows/Converters/ThicknessConverter.cs index 9f8fe92..538bee3 100644 --- a/Source/OxyPlot.Windows/Converters/ThicknessConverter.cs +++ b/OxyPlot.Windows/Converters/ThicknessConverter.cs @@ -19,7 +19,7 @@ namespace OxyPlot.Windows /// /// This is used to convert BorderThickness properties to Path.StrokeThickness (double). /// The maximum thickness value is used. - public class ThicknessConverter : IValueConverter + public sealed class ThicknessConverter : IValueConverter { /// /// Converts the specified value. @@ -56,4 +56,4 @@ public object ConvertBack(object value, Type targetType, object parameter, strin return null; } } -} \ No newline at end of file +} diff --git a/Source/Examples/Windows/SimpleDemo/App.xaml b/OxyPlot.Windows/Examples/Windows/SimpleDemo/App.xaml similarity index 100% rename from Source/Examples/Windows/SimpleDemo/App.xaml rename to OxyPlot.Windows/Examples/Windows/SimpleDemo/App.xaml diff --git a/Source/Examples/Windows/SimpleDemo/App.xaml.cs b/OxyPlot.Windows/Examples/Windows/SimpleDemo/App.xaml.cs similarity index 100% rename from Source/Examples/Windows/SimpleDemo/App.xaml.cs rename to OxyPlot.Windows/Examples/Windows/SimpleDemo/App.xaml.cs diff --git a/Source/Examples/Windows/SimpleDemo/Assets/LockScreenLogo.scale-200.png b/OxyPlot.Windows/Examples/Windows/SimpleDemo/Assets/LockScreenLogo.scale-200.png similarity index 100% rename from Source/Examples/Windows/SimpleDemo/Assets/LockScreenLogo.scale-200.png rename to OxyPlot.Windows/Examples/Windows/SimpleDemo/Assets/LockScreenLogo.scale-200.png diff --git a/Source/Examples/Windows/SimpleDemo/Assets/SplashScreen.scale-200.png b/OxyPlot.Windows/Examples/Windows/SimpleDemo/Assets/SplashScreen.scale-200.png similarity index 100% rename from Source/Examples/Windows/SimpleDemo/Assets/SplashScreen.scale-200.png rename to OxyPlot.Windows/Examples/Windows/SimpleDemo/Assets/SplashScreen.scale-200.png diff --git a/Source/Examples/Windows/SimpleDemo/Assets/Square150x150Logo.scale-200.png b/OxyPlot.Windows/Examples/Windows/SimpleDemo/Assets/Square150x150Logo.scale-200.png similarity index 100% rename from Source/Examples/Windows/SimpleDemo/Assets/Square150x150Logo.scale-200.png rename to OxyPlot.Windows/Examples/Windows/SimpleDemo/Assets/Square150x150Logo.scale-200.png diff --git a/Source/Examples/Windows/SimpleDemo/Assets/Square44x44Logo.scale-200.png b/OxyPlot.Windows/Examples/Windows/SimpleDemo/Assets/Square44x44Logo.scale-200.png similarity index 100% rename from Source/Examples/Windows/SimpleDemo/Assets/Square44x44Logo.scale-200.png rename to OxyPlot.Windows/Examples/Windows/SimpleDemo/Assets/Square44x44Logo.scale-200.png diff --git a/Source/Examples/Windows/SimpleDemo/Assets/Square44x44Logo.targetsize-24_altform-unplated.png b/OxyPlot.Windows/Examples/Windows/SimpleDemo/Assets/Square44x44Logo.targetsize-24_altform-unplated.png similarity index 100% rename from Source/Examples/Windows/SimpleDemo/Assets/Square44x44Logo.targetsize-24_altform-unplated.png rename to OxyPlot.Windows/Examples/Windows/SimpleDemo/Assets/Square44x44Logo.targetsize-24_altform-unplated.png diff --git a/Source/Examples/Windows/SimpleDemo/Assets/StoreLogo.png b/OxyPlot.Windows/Examples/Windows/SimpleDemo/Assets/StoreLogo.png similarity index 100% rename from Source/Examples/Windows/SimpleDemo/Assets/StoreLogo.png rename to OxyPlot.Windows/Examples/Windows/SimpleDemo/Assets/StoreLogo.png diff --git a/Source/Examples/Windows/SimpleDemo/Assets/Wide310x150Logo.scale-200.png b/OxyPlot.Windows/Examples/Windows/SimpleDemo/Assets/Wide310x150Logo.scale-200.png similarity index 100% rename from Source/Examples/Windows/SimpleDemo/Assets/Wide310x150Logo.scale-200.png rename to OxyPlot.Windows/Examples/Windows/SimpleDemo/Assets/Wide310x150Logo.scale-200.png diff --git a/Source/Examples/Windows/SimpleDemo/MainPage.xaml b/OxyPlot.Windows/Examples/Windows/SimpleDemo/MainPage.xaml similarity index 79% rename from Source/Examples/Windows/SimpleDemo/MainPage.xaml rename to OxyPlot.Windows/Examples/Windows/SimpleDemo/MainPage.xaml index b95fdb1..a72fe6d 100644 --- a/Source/Examples/Windows/SimpleDemo/MainPage.xaml +++ b/OxyPlot.Windows/Examples/Windows/SimpleDemo/MainPage.xaml @@ -11,7 +11,7 @@ - - + + diff --git a/Source/Examples/Windows/SimpleDemo/MainPage.xaml.cs b/OxyPlot.Windows/Examples/Windows/SimpleDemo/MainPage.xaml.cs similarity index 100% rename from Source/Examples/Windows/SimpleDemo/MainPage.xaml.cs rename to OxyPlot.Windows/Examples/Windows/SimpleDemo/MainPage.xaml.cs diff --git a/OxyPlot.Windows/Examples/Windows/SimpleDemo/MainViewModel.cs b/OxyPlot.Windows/Examples/Windows/SimpleDemo/MainViewModel.cs new file mode 100644 index 0000000..34adcd8 --- /dev/null +++ b/OxyPlot.Windows/Examples/Windows/SimpleDemo/MainViewModel.cs @@ -0,0 +1,50 @@ +namespace SimpleDemo +{ + using OxyPlot; + using OxyPlot.Axes; + using OxyPlot.Series; + using System.Collections.Generic; + using System.ComponentModel; + + public class MainViewModel : INotifyPropertyChanged + { + public MainViewModel() + { + var model = new PlotModel { Title = "Hello Universal Windows" }; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left }); + LineSeries lineSeries = new LineSeries { Title = "LineSeries", MarkerType = MarkerType.Circle }; + lineSeries.Points.Add(new DataPoint(0, 0)); + lineSeries.Points.Add(new DataPoint(10, 18)); + lineSeries.Points.Add(new DataPoint(20, 12)); + lineSeries.Points.Add(new DataPoint(30, 8)); + lineSeries.Points.Add(new DataPoint(40, 15)); + + model.Series.Add(lineSeries); + model.InvalidatePlot(true); + this.Model = model; + } + + private PlotModel _model; + + public event PropertyChangedEventHandler PropertyChanged; + + public virtual void OnPropertyChanged(string propName) + { + this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName)); + } + + public PlotModel Model + { + get => _model; + private set + { + if (!object.ReferenceEquals(_model, value)) + { + _model = value; + OnPropertyChanged("Model"); + } + } + } + } +} \ No newline at end of file diff --git a/Source/Examples/Windows/SimpleDemo/Package.appxmanifest b/OxyPlot.Windows/Examples/Windows/SimpleDemo/Package.appxmanifest similarity index 93% rename from Source/Examples/Windows/SimpleDemo/Package.appxmanifest rename to OxyPlot.Windows/Examples/Windows/SimpleDemo/Package.appxmanifest index 05761f4..5a6c0e6 100644 --- a/Source/Examples/Windows/SimpleDemo/Package.appxmanifest +++ b/OxyPlot.Windows/Examples/Windows/SimpleDemo/Package.appxmanifest @@ -8,14 +8,14 @@ SimpleDemo - objo + Yoshiteru Kageyama Assets\StoreLogo.png diff --git a/Source/Examples/Windows/SimpleDemo/Properties/AssemblyInfo.cs b/OxyPlot.Windows/Examples/Windows/SimpleDemo/Properties/AssemblyInfo.cs similarity index 100% rename from Source/Examples/Windows/SimpleDemo/Properties/AssemblyInfo.cs rename to OxyPlot.Windows/Examples/Windows/SimpleDemo/Properties/AssemblyInfo.cs diff --git a/Source/Examples/Windows/SimpleDemo/Properties/Default.rd.xml b/OxyPlot.Windows/Examples/Windows/SimpleDemo/Properties/Default.rd.xml similarity index 100% rename from Source/Examples/Windows/SimpleDemo/Properties/Default.rd.xml rename to OxyPlot.Windows/Examples/Windows/SimpleDemo/Properties/Default.rd.xml diff --git a/Source/Examples/Windows/SimpleDemo/SimpleDemo.csproj b/OxyPlot.Windows/Examples/Windows/SimpleDemo/SimpleDemo.csproj similarity index 65% rename from Source/Examples/Windows/SimpleDemo/SimpleDemo.csproj rename to OxyPlot.Windows/Examples/Windows/SimpleDemo/SimpleDemo.csproj index 46106ea..004a1a2 100644 --- a/Source/Examples/Windows/SimpleDemo/SimpleDemo.csproj +++ b/OxyPlot.Windows/Examples/Windows/SimpleDemo/SimpleDemo.csproj @@ -11,13 +11,16 @@ SimpleDemo en-US UAP - 10.0.18362.0 + 10.0.19041.0 10.0.18362.0 14 512 {A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} true - SimpleDemo_TemporaryKey.pfx + + + 86BEAB295D9391A135A1168090375BE01A5C11D4 + True true @@ -75,6 +78,7 @@ false prompt true + false bin\x64\Release\ @@ -105,6 +109,7 @@ Designer + @@ -127,24 +132,92 @@ Designer - - - 5.2.3 - - - - - {5aa87152-20d5-48f7-ad0f-6e6baa61fc25} - OxyPlot.Windows - - - {7a0b35c0-dd17-4964-8e9a-44d6cecdc692} - OxyPlot - - + + + 4.7.0.9 + + + 1.0.31 + + + 5.0.9 + + + 5.0.0 + + + 3.11.0 + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + 6.2.12 + + + 7.1.0 + + + 7.1.0 + + + + 2.1.0 + + + 6.2.12 + + + 2.0.4 + + + 5.0.0 + + + 4.5.4 + + + 4.3.0 + + + 5.0.0 + + + 4.3.0 + + + 4.3.1 + + + 5.0.0 + + + 4.3.0 + + + 4.7.0 + + + 4.7.0 + + + 4.3.0 + + + 4.5.0 + + + 1.0.1 + + 14.0 + + true + + + SimpleDemo_TemporaryKey.pfx + + \ No newline at end of file diff --git a/OxyPlot.Windows/OxyPlot.Windows.csproj.user b/OxyPlot.Windows/OxyPlot.Windows.csproj.user new file mode 100644 index 0000000..9b86104 --- /dev/null +++ b/OxyPlot.Windows/OxyPlot.Windows.csproj.user @@ -0,0 +1,6 @@ + + + + ShowAllFiles + + \ No newline at end of file diff --git a/OxyPlot.Windows/OxyPlot.Windows.keypair.snk b/OxyPlot.Windows/OxyPlot.Windows.keypair.snk new file mode 100644 index 0000000..8a93b8b Binary files /dev/null and b/OxyPlot.Windows/OxyPlot.Windows.keypair.snk differ diff --git a/OxyPlot.Windows/OxyPlot.Windows.nuspec b/OxyPlot.Windows/OxyPlot.Windows.nuspec new file mode 100644 index 0000000..c422d48 --- /dev/null +++ b/OxyPlot.Windows/OxyPlot.Windows.nuspec @@ -0,0 +1,32 @@ + + + + OxyPlot.Windows + OxyPlot for Windows apps + 2.1.0 + Oystein Bjorke + OxyPlot is a plotting library for .NET. This package targets the Universal Windows Platform (UAP10). + + https://raw.githubusercontent.com/oxyplot/oxyplot/master/LICENSE + http://oxyplot.org/ + https://raw.githubusercontent.com/oxyplot/oxyplot/develop/Icons/OxyPlot_128.png + false + en-US + winrt uap uap10 UWP win10 plotting plot charting chart + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OxyPlot.Windows/OxyPlot.Windows.public.snk b/OxyPlot.Windows/OxyPlot.Windows.public.snk new file mode 100644 index 0000000..0812ff5 Binary files /dev/null and b/OxyPlot.Windows/OxyPlot.Windows.public.snk differ diff --git a/Source/OxyPlot.Windows/PlotView.cs b/OxyPlot.Windows/PlotView.cs similarity index 90% rename from Source/OxyPlot.Windows/PlotView.cs rename to OxyPlot.Windows/PlotView.cs index 976d110..266c719 100644 --- a/Source/OxyPlot.Windows/PlotView.cs +++ b/OxyPlot.Windows/PlotView.cs @@ -26,10 +26,10 @@ namespace OxyPlot.Windows using global::Windows.UI.Xaml.Media.Imaging; /// - /// Represents a control that displays a . + /// /// [TemplatePart(Name = PartGrid, Type = typeof(Grid))] - public class PlotView : Control, IPlotView + public class PlotView : Control, OxyPlot.IPlotView { /// /// Identifies the dependency property. @@ -73,7 +73,6 @@ public class PlotView : Control, IPlotView /// Flags if the cursor is not implemented (Windows Phone). /// private static bool cursorNotImplemented; - /// /// The Grid PART constant. /// @@ -167,7 +166,6 @@ public PlotView() this.ManipulationMode = ManipulationModes.Scale | ManipulationModes.TranslateX | ManipulationModes.TranslateY; } - /// /// Gets or sets the PlotView controller. /// @@ -286,6 +284,7 @@ Model IView.ActualModel } } + /// /// Gets the actual model. /// @@ -335,9 +334,6 @@ public IPlotController ActualController } } - /// - /// Hides the tracker. - /// public void HideTracker() { if (this.currentTracker != null) @@ -347,21 +343,18 @@ public void HideTracker() } } - /// - /// Hides the zoom rectangle. - /// public void HideZoomRectangle() { this.zoomRectangle.Visibility = Visibility.Collapsed; } /// - /// Invalidate the PlotView (not blocking the UI thread) + /// update a plot view. /// - /// if set to true, the data collections will be updated. - public void InvalidatePlot(bool update = true) + /// if the data in a plot view must be updated. + public void InvalidatePlot(bool updateData = true) { - this.UpdateModel(update); + this.UpdateModel(updateData); if (DesignMode.DesignModeEnabled) { @@ -379,10 +372,19 @@ public void InvalidatePlot(bool update = true) } /// - /// Sets the cursor. + /// + /// + /// + public void SetClipboardText(string text) + { + throw new NotImplementedException(); + } + + /// + /// Set the type of cursor. /// - /// The cursor. - public void SetCursorType(CursorType cursor) + /// cursor type + public void SetCursorType(CursorType cursorType) { if (cursorNotImplemented) { @@ -391,7 +393,7 @@ public void SetCursorType(CursorType cursor) } var type = CoreCursorType.Arrow; - switch (cursor) + switch (cursorType) { case CursorType.Default: type = CoreCursorType.Arrow; @@ -409,7 +411,6 @@ public void SetCursorType(CursorType cursor) type = CoreCursorType.SizeNorthwestSoutheast; break; } - // TODO: determine if creating a CoreCursor is possible, do not use exception try { @@ -423,9 +424,9 @@ public void SetCursorType(CursorType cursor) } /// - /// Shows the tracker. + /// Show a tracker. /// - /// The tracker data. + /// public void ShowTracker(TrackerHitResult trackerHitResult) { if (trackerHitResult == null) @@ -466,19 +467,18 @@ public void ShowTracker(TrackerHitResult trackerHitResult) } /// - /// Shows the zoom rectangle. + /// Show a zoom rectangle. /// - /// The rectangle. - public void ShowZoomRectangle(OxyRect r) + /// + public void ShowZoomRectangle(OxyRect rectangle) { - this.zoomRectangle.Width = r.Width; - this.zoomRectangle.Height = r.Height; - Canvas.SetLeft(this.zoomRectangle, r.Left); - Canvas.SetTop(this.zoomRectangle, r.Top); + this.zoomRectangle.Width = rectangle.Width; + this.zoomRectangle.Height = rectangle.Height; + Canvas.SetLeft(this.zoomRectangle, rectangle.Left); + Canvas.SetTop(this.zoomRectangle, rectangle.Top); this.zoomRectangle.Template = this.ZoomRectangleTemplate; this.zoomRectangle.Visibility = Visibility.Visible; } - /// /// Renders the PlotView to a bitmap. /// @@ -791,44 +791,44 @@ protected override void OnPointerExited(PointerRoutedEventArgs e) e.Handled = this.ActualController.HandleMouseLeave(this, e.ToMouseEventArgs(this)); } - /// - /// A one time condition for update visuals so it is called no matter the state of the control - /// Currently with out this, the plotview on Xamarin Forms UWP does not render until the app's window resizes - /// - private bool isUpdateVisualsCalledOnce = false; - - /// - /// Provides the behavior for the Arrange pass of layout. Classes can override this method to define their own Arrange pass behavior. - /// - /// The final area within the parent that this object should use to arrange itself and its children. - /// The actual size that is used after the element is arranged in layout. - protected override Size ArrangeOverride(Size finalSize) - { - if (this.ActualWidth > 0 && this.ActualHeight > 0) - { - if (Interlocked.CompareExchange(ref this.isPlotInvalidated, 0, 1) == 1) - { - this.UpdateVisuals(); - } - } - - //see summary for isUpdateVisualsCalledOnce - if (!isUpdateVisualsCalledOnce) - { - this.UpdateVisuals(); - - isUpdateVisualsCalledOnce = true; - } - - return base.ArrangeOverride(finalSize); - } - - /// - /// Called when the property is changed. - /// - /// The sender. - /// The instance containing the event data. - private static void ModelChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) + /// + /// A one time condition for update visuals so it is called no matter the state of the control + /// Currently with out this, the plotview on Xamarin Forms UWP does not render until the app's window resizes + /// + private bool isUpdateVisualsCalledOnce = false; + + /// + /// Provides the behavior for the Arrange pass of layout. Classes can override this method to define their own Arrange pass behavior. + /// + /// The final area within the parent that this object should use to arrange itself and its children. + /// The actual size that is used after the element is arranged in layout. + protected override Size ArrangeOverride(Size finalSize) + { + if (this.ActualWidth > 0 && this.ActualHeight > 0) + { + if (Interlocked.CompareExchange(ref this.isPlotInvalidated, 0, 1) == 1) + { + this.UpdateVisuals(); + } + } + + //see summary for isUpdateVisualsCalledOnce + if (!isUpdateVisualsCalledOnce) + { + this.UpdateVisuals(); + + isUpdateVisualsCalledOnce = true; + } + + return base.ArrangeOverride(finalSize); + } + + /// + /// Called when the property is changed. + /// + /// The sender. + /// The instance containing the event data. + private static void ModelChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) { ((PlotView)sender).OnModelChanged(); } @@ -914,7 +914,7 @@ private void UpdateVisuals() if (this.ActualModel != null) { - ((IPlotModel)this.ActualModel).Render(this.renderContext, this.canvas.ActualWidth, this.canvas.ActualHeight); + ((IPlotModel)this.ActualModel).Render(this.renderContext, ClientArea); } } @@ -939,4 +939,4 @@ private void BeginInvoke(Action action) } } } -} \ No newline at end of file +} diff --git a/OxyPlot.Windows/Properties/AssemblyInfo.cs b/OxyPlot.Windows/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..d67b685 --- /dev/null +++ b/OxyPlot.Windows/Properties/AssemblyInfo.cs @@ -0,0 +1,31 @@ +using System.Resources; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// アセンブリに関する一般情報は以下の属性セットをとおして制御されます。 +// アセンブリに関連付けられている情報を変更するには、 +// これらの属性値を変更してください。 +[assembly: AssemblyTitle("OxyPlot.Windows")] +[assembly: AssemblyDescription("Graph library for .NET")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("OxyPlot.Windows")] +[assembly: AssemblyCopyright("Copyright © 2021")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// アセンブリのバージョン情報は次の 4 つの値で構成されています: +// +// メジャー バージョン +// マイナー バージョン +// ビルド番号 +// リビジョン +// +// すべての値を指定するか、次を使用してビルド番号とリビジョン番号を既定に設定できます +// 既定値にすることができます: +//[アセンブリ: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("2.1.0.0")] +[assembly: AssemblyFileVersion("2.1.0.0")] +[assembly: ComVisible(false)] +[assembly: NeutralResourcesLanguage("en")] diff --git a/Source/OxyPlot.Windows/Properties/OxyPlot.Windows.rd.xml b/OxyPlot.Windows/Properties/OxyPlot.Windows.rd.xml similarity index 100% rename from Source/OxyPlot.Windows/Properties/OxyPlot.Windows.rd.xml rename to OxyPlot.Windows/Properties/OxyPlot.Windows.rd.xml diff --git a/OxyPlot.Windows/Properties/OxyPlot.Windows2.rd.xml b/OxyPlot.Windows/Properties/OxyPlot.Windows2.rd.xml new file mode 100644 index 0000000..e89e21a --- /dev/null +++ b/OxyPlot.Windows/Properties/OxyPlot.Windows2.rd.xml @@ -0,0 +1,33 @@ + + + + + + + + + diff --git a/OxyPlot.Windows/Properties/PublishProfiles/FolderProfile.pubxml b/OxyPlot.Windows/Properties/PublishProfiles/FolderProfile.pubxml new file mode 100644 index 0000000..a015706 --- /dev/null +++ b/OxyPlot.Windows/Properties/PublishProfiles/FolderProfile.pubxml @@ -0,0 +1,12 @@ + + + + + Release + Any CPU + bin\Release\net4.8\publish\ + FileSystem + + \ No newline at end of file diff --git a/OxyPlot.Windows/Properties/PublishProfiles/FolderProfile.pubxml.user b/OxyPlot.Windows/Properties/PublishProfiles/FolderProfile.pubxml.user new file mode 100644 index 0000000..b642d7a --- /dev/null +++ b/OxyPlot.Windows/Properties/PublishProfiles/FolderProfile.pubxml.user @@ -0,0 +1,9 @@ + + + + + False|2021-10-05T05:34:21.1603930Z; + + \ No newline at end of file diff --git a/Source/OxyPlot.Windows/RenderContext.cs b/OxyPlot.Windows/RenderContext.cs similarity index 80% rename from Source/OxyPlot.Windows/RenderContext.cs rename to OxyPlot.Windows/RenderContext.cs index 5dc685a..8fd3078 100644 --- a/Source/OxyPlot.Windows/RenderContext.cs +++ b/OxyPlot.Windows/RenderContext.cs @@ -7,25 +7,26 @@ // // -------------------------------------------------------------------------------------------------------------------- +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices.WindowsRuntime; +using System.Threading.Tasks; + +using global::Windows.Foundation; +using global::Windows.Storage.Streams; +using global::Windows.UI; +using global::Windows.UI.Text; +using global::Windows.UI.Xaml; +using global::Windows.UI.Xaml.Controls; +using global::Windows.UI.Xaml.Media; +using global::Windows.UI.Xaml.Media.Imaging; +using global::Windows.UI.Xaml.Shapes; +using OxyPlot.Core.Drawing; +using Path = global::Windows.UI.Xaml.Shapes.Path; + namespace OxyPlot.Windows { - using System; - using System.Collections.Generic; - using System.Linq; - using System.Runtime.InteropServices.WindowsRuntime; - using System.Threading.Tasks; - - using global::Windows.Foundation; - using global::Windows.Storage.Streams; - using global::Windows.UI.Text; - using global::Windows.UI.Xaml; - using global::Windows.UI.Xaml.Controls; - using global::Windows.UI.Xaml.Media; - using global::Windows.UI.Xaml.Media.Imaging; - using global::Windows.UI.Xaml.Shapes; - - using Path = global::Windows.UI.Xaml.Shapes.Path; - /// /// Implements for . /// @@ -66,6 +67,8 @@ public class RenderContext : IRenderContext /// private bool clip; + private readonly Stack clipStack; + /// /// Initializes a new instance of the class. /// @@ -76,6 +79,7 @@ public RenderContext(Canvas canvas) this.Width = canvas.ActualWidth; this.Height = canvas.ActualHeight; this.RendersToScreen = true; + this.clipStack = new Stack(); } /// @@ -108,6 +112,12 @@ public bool PaintBackground /// true if the context renders to screen; otherwise, false. public bool RendersToScreen { get; set; } + + /// + /// Gets the number of clipped. + /// + public int ClipCount => this.clipStack.Count; + /// /// Draws an ellipse. /// @@ -115,7 +125,9 @@ public bool PaintBackground /// The fill color. /// The stroke color. /// The thickness. - public void DrawEllipse(OxyRect rect, OxyColor fill, OxyColor stroke, double thickness) + /// The rendering mode. + public void DrawEllipse(OxyRect rect, OxyColor fill, OxyColor stroke, + double thickness, EdgeRenderingMode edgeRenderingMode) { var el = new Ellipse { @@ -148,7 +160,8 @@ public void DrawEllipse(OxyRect rect, OxyColor fill, OxyColor stroke, double thi /// The fill color. /// The stroke color. /// The stroke thickness. - public void DrawEllipses(IList rectangles, OxyColor fill, OxyColor stroke, double thickness) + /// The rendering mode. + public void DrawEllipses(IList rectangles, OxyColor fill, OxyColor stroke, double thickness, EdgeRenderingMode edgeRenderingMode) { var path = new Path { @@ -183,28 +196,23 @@ public void DrawEllipses(IList rectangles, OxyColor fill, OxyColor stro /// The points. /// The stroke color. /// The stroke thickness. + /// The rendering mode. /// The dash array. /// The line join type. - /// if set to true the shape will be aliased. - public void DrawLine( - IList points, - OxyColor stroke, - double thickness, - double[] dashArray, - LineJoin lineJoin, - bool aliased) + public void DrawLine(IList points, OxyColor stroke, double thickness, EdgeRenderingMode edgeRenderingMode, + double[] dashArray = null, LineJoin lineJoin = LineJoin.Miter) { var e = new Polyline { CompositeMode = ElementCompositeMode.SourceOver }; - this.SetStroke(e, stroke, thickness, lineJoin, dashArray, aliased); - - var pc = new PointCollection(); + this.SetStroke(e, stroke, thickness, lineJoin, dashArray, false); + PointCollection pc = new PointCollection(); foreach (var p in points) { - pc.Add(p.ToPoint(aliased)); + Point pnt = new Point(p.X, p.Y); + pc.Add(pnt); } e.Points = pc; @@ -219,23 +227,18 @@ public void DrawLine( /// The points. /// The stroke color. /// The stroke thickness. + /// The rendering mode. /// The dash array. /// The line join type. - /// if set to true the shape will be aliased. - public void DrawLineSegments( - IList points, - OxyColor stroke, - double thickness, - double[] dashArray, - LineJoin lineJoin, - bool aliased) + public void DrawLineSegments(IList points, OxyColor stroke, double thickness, + EdgeRenderingMode edgeRenderingMode, double[] dashArray = null, LineJoin lineJoin = LineJoin.Miter) { var path = new Path { CompositeMode = ElementCompositeMode.SourceOver }; - this.SetStroke(path, stroke, thickness, lineJoin, dashArray, aliased); + this.SetStroke(path, stroke, thickness, lineJoin, dashArray); var pg = new PathGeometry(); for (int i = 0; i + 1 < points.Count; i += 2) { @@ -251,8 +254,10 @@ public void DrawLineSegments( // Add(line); // continue; // } - var figure = new PathFigure { StartPoint = points[i].ToPoint(aliased), IsClosed = false }; - figure.Segments.Add(new LineSegment { Point = points[i + 1].ToPoint(aliased) }); + Point pnt1 = new Point(points[i].X, points[i].Y); + Point pnt2 = new Point(points[i + 1].X, points[i + 1].Y); + var figure = new PathFigure { StartPoint = pnt1, IsClosed = false }; + figure.Segments.Add(new LineSegment { Point = pnt2 }); pg.Figures.Add(figure); } @@ -267,34 +272,29 @@ public void DrawLineSegments( /// The fill color. /// The stroke color. /// The stroke thickness. + /// The rendering mode. /// The dash array. /// The line join type. - /// if set to true the shape will be aliased. - public void DrawPolygon( - IList points, - OxyColor fill, - OxyColor stroke, - double thickness, - double[] dashArray, - LineJoin lineJoin, - bool aliased) + public void DrawPolygon(IList points, OxyColor fill, OxyColor stroke, + double thickness, EdgeRenderingMode edgeRenderingMode, double[] dashArray = null, LineJoin lineJoin = LineJoin.Miter) { var po = new Polygon { CompositeMode = ElementCompositeMode.SourceOver }; - this.SetStroke(po, stroke, thickness, lineJoin, dashArray, aliased); + this.SetStroke(po, stroke, thickness, lineJoin, dashArray); if (fill.IsVisible()) { po.Fill = this.GetCachedBrush(fill); } - var pc = new PointCollection(); + PointCollection pc = new PointCollection(); foreach (var p in points) { - pc.Add(p.ToPoint(aliased)); + Point pnt = new Point(p.X, p.Y); + pc.Add(pnt); } po.Points = pc; @@ -310,24 +310,19 @@ public void DrawPolygon( /// The fill color. /// The stroke color. /// The stroke thickness. + /// The rendering mode. /// The dash array. /// The line join type. - /// if set to true the shape will be aliased. - public void DrawPolygons( - IList> polygons, - OxyColor fill, - OxyColor stroke, - double thickness, - double[] dashArray, - LineJoin lineJoin, - bool aliased) + public void DrawPolygons(IList> polygons, OxyColor fill, OxyColor stroke, + double thickness, EdgeRenderingMode edgeRenderingMode, double[] dashArray = null, LineJoin lineJoin = LineJoin.Miter) + { var path = new Path { CompositeMode = ElementCompositeMode.SourceOver }; - this.SetStroke(path, stroke, thickness, lineJoin, dashArray, aliased); + this.SetStroke(path, stroke, thickness, lineJoin, dashArray); if (fill.IsVisible()) { path.Fill = this.GetCachedBrush(fill); @@ -340,14 +335,15 @@ public void DrawPolygons( bool first = true; foreach (var p in polygon) { + Point pnt = new Point(p.X, p.Y); if (first) { - figure.StartPoint = p.ToPoint(aliased); + figure.StartPoint = pnt; first = false; } else { - figure.Segments.Add(new LineSegment { Point = p.ToPoint(aliased) }); + figure.Segments.Add(new LineSegment { Point = pnt }); } } @@ -361,11 +357,13 @@ public void DrawPolygons( /// /// Draws the rectangle. /// - /// The rectangle. + /// The rectangle. /// The fill color. /// The stroke color. /// The stroke thickness. - public void DrawRectangle(OxyRect rect, OxyColor fill, OxyColor stroke, double thickness) + /// Rendering mode. + public void DrawRectangle(OxyRect rectangle, OxyColor fill, OxyColor stroke, + double thickness, EdgeRenderingMode edgeRenderingMode) { var el = new Rectangle { @@ -374,20 +372,22 @@ public void DrawRectangle(OxyRect rect, OxyColor fill, OxyColor stroke, double t if (stroke.IsVisible()) { - el.Stroke = new SolidColorBrush(stroke.ToColor()); + Color col = Color.FromArgb(stroke.A, stroke.R, stroke.G, stroke.B); + el.Stroke = new SolidColorBrush(col); el.StrokeThickness = thickness; } if (fill.IsVisible()) { - el.Fill = new SolidColorBrush(fill.ToColor()); + Color col = Color.FromArgb(fill.A, fill.R, fill.G, fill.B); + el.Fill = new SolidColorBrush(col); } - el.Width = rect.Width; - el.Height = rect.Height; - Canvas.SetLeft(el, rect.Left); - Canvas.SetTop(el, rect.Top); - this.Add(el, rect.Left, rect.Top); + el.Width = rectangle.Width; + el.Height = rectangle.Height; + Canvas.SetLeft(el, rectangle.Left); + Canvas.SetTop(el, rectangle.Top); + this.Add(el, rectangle.Left, rectangle.Top); } /// @@ -398,7 +398,9 @@ public void DrawRectangle(OxyRect rect, OxyColor fill, OxyColor stroke, double t /// The fill color. /// The stroke color. /// The stroke thickness. - public void DrawRectangles(IList rectangles, OxyColor fill, OxyColor stroke, double thickness) + /// The rendering mode. + public void DrawRectangles(IList rectangles, OxyColor fill, OxyColor stroke, + double thickness, EdgeRenderingMode edgeRenderingMode) { var path = new Path { @@ -414,7 +416,8 @@ public void DrawRectangles(IList rectangles, OxyColor fill, OxyColor st var gg = new GeometryGroup { FillRule = FillRule.Nonzero }; foreach (var rect in rectangles) { - gg.Children.Add(new RectangleGeometry { Rect = rect.ToRect(true) }); + Rect r = rect.ToRect(false); + gg.Children.Add(new RectangleGeometry { Rect = new Rect(r.X, r.Y, r.Width, r.Height) }); } path.Data = gg; @@ -635,7 +638,8 @@ public void DrawImage( /// True if the clipping rectangle was set. public bool SetClip(OxyRect clippingRect) { - this.clipRect = clippingRect.ToRect(false); + Rect rect = clippingRect.ToRect(false); + this.clipRect = new Rect(rect.X, rect.Y, rect.Width, rect.Height); this.clip = true; return true; } @@ -689,7 +693,17 @@ private static DoubleCollection CreateDashArrayCollection(IEnumerable da /// A private static FontWeight GetFontWeight(double fontWeight) { - return fontWeight > OxyPlot.FontWeights.Normal ? FontWeights.Bold : FontWeights.Normal; + FontWeight fw; + + if (fontWeight > OxyPlot.FontWeights.Normal) + { + fw = new FontWeight() { Weight = (ushort)FontWeights.Bold }; + } + else + { + fw = new FontWeight() { Weight = (ushort)FontWeights.Normal }; + } + return fw; } /// @@ -767,7 +781,7 @@ private void SetStroke( shape.StrokeLineJoin = PenLineJoin.Bevel; break; - // The default StrokeLineJoin is Miter + // The default StrokeLineJoin is Miter } shape.StrokeThickness = thickness; @@ -838,5 +852,55 @@ private static async Task ConvertToRandomAccessStream(byte[ randomAccessStream.Seek(0); return randomAccessStream; } + /// + /// Push a Clip data. + /// + /// + public void PushClip(OxyRect clippingRectangle) + { + if (this.clipStack.Count > 0) + { + OxyRect currentClippingRectangle = this.clipStack.Peek(); + OxyRect newClippingRectangle = clippingRectangle.Intersect(currentClippingRectangle); + if (!currentClippingRectangle.Equals(newClippingRectangle)) + { + this.ResetClip(); + this.SetClip(newClippingRectangle); + } + + this.clipStack.Push(newClippingRectangle); + } + else + { + this.SetClip(clippingRectangle); + this.clipStack.Push(clippingRectangle); + } + } + + /// + /// Pop a Clip data. + /// + public void PopClip() + { + if (this.clipStack.Count == 0) + { + throw new InvalidOperationException($"Unbalanced call to {nameof(PopClip)}"); + } + + OxyRect currentClippingRectangle = this.clipStack.Pop(); + if (this.clipStack.Count > 0) + { + OxyRect newClippingRectangle = this.clipStack.Peek(); + if (!newClippingRectangle.Equals(currentClippingRectangle)) + { + this.ResetClip(); + this.SetClip(newClippingRectangle); + } + } + else + { + this.ResetClip(); + } + } } } \ No newline at end of file diff --git a/Source/OxyPlot.Windows/Themes/Generic.xaml b/OxyPlot.Windows/Themes/Generic.xaml similarity index 99% rename from Source/OxyPlot.Windows/Themes/Generic.xaml rename to OxyPlot.Windows/Themes/Generic.xaml index a2249ba..2ba0663 100644 --- a/Source/OxyPlot.Windows/Themes/Generic.xaml +++ b/OxyPlot.Windows/Themes/Generic.xaml @@ -1,4 +1,4 @@ - diff --git a/Source/OxyPlot.Windows/Tracker/TrackerControl.cs b/OxyPlot.Windows/Tracker/TrackerControl.cs similarity index 98% rename from Source/OxyPlot.Windows/Tracker/TrackerControl.cs rename to OxyPlot.Windows/Tracker/TrackerControl.cs index 854412a..ed52db4 100644 --- a/Source/OxyPlot.Windows/Tracker/TrackerControl.cs +++ b/OxyPlot.Windows/Tracker/TrackerControl.cs @@ -525,16 +525,16 @@ private void UpdatePositionAndBorder() Size contentSize = this.contentContainer.DesiredSize; this.contentContainer.RenderTransform = new TranslateTransform - { - X = dx * contentSize.Width, - Y = dy * contentSize.Height - }; + { + X = dx * contentSize.Width, + Y = dy * contentSize.Height + }; #if WPF ScreenPoint pos = this.Position; #endif #if SILVERLIGHT || NETFX_CORE - Point pos = this.Position.ToPoint(true); + Point pos = new Point(this.Position.X, this.Position.Y); #endif if (this.horizontalLine != null) @@ -588,12 +588,12 @@ private Geometry CreateBorderGeometry( var rect = new Rect( ha == HorizontalAlignment.Left ? m : 0, va == VerticalAlignment.Top ? m : 0, width, height); margin = new Thickness - { - Left = ha == HorizontalAlignment.Left ? m : 0, - Top = va == VerticalAlignment.Top ? m : 0, - Right = ha == HorizontalAlignment.Right ? m : 0, - Bottom = va == VerticalAlignment.Bottom ? m : 0 - }; + { + Left = ha == HorizontalAlignment.Left ? m : 0, + Top = va == VerticalAlignment.Top ? m : 0, + Right = ha == HorizontalAlignment.Right ? m : 0, + Bottom = va == VerticalAlignment.Bottom ? m : 0 + }; return new RectangleGeometry { Rect = rect /*, RadiusX = this.CornerRadius, RadiusY = this.CornerRadius*/ }; } @@ -749,4 +749,4 @@ private Geometry CreatePointerBorderGeometry( return new PathGeometry { Figures = new PathFigureCollection { pf } }; } } -} \ No newline at end of file +} diff --git a/Source/OxyPlot.Windows/Tracker/TrackerDefinition.cs b/OxyPlot.Windows/Tracker/TrackerDefinition.cs similarity index 99% rename from Source/OxyPlot.Windows/Tracker/TrackerDefinition.cs rename to OxyPlot.Windows/Tracker/TrackerDefinition.cs index 0a4f9ac..94a9254 100644 --- a/Source/OxyPlot.Windows/Tracker/TrackerDefinition.cs +++ b/OxyPlot.Windows/Tracker/TrackerDefinition.cs @@ -67,4 +67,4 @@ public ControlTemplate TrackerTemplate } } } -} \ No newline at end of file +} diff --git a/Source/OxyPlot.Windows/Utilities/ConverterExtensions.cs b/OxyPlot.Windows/Utilities/ConverterExtensions.cs similarity index 99% rename from Source/OxyPlot.Windows/Utilities/ConverterExtensions.cs rename to OxyPlot.Windows/Utilities/ConverterExtensions.cs index 3243c80..85200e2 100644 --- a/Source/OxyPlot.Windows/Utilities/ConverterExtensions.cs +++ b/OxyPlot.Windows/Utilities/ConverterExtensions.cs @@ -553,4 +553,4 @@ public static ScreenPoint[] ToScreenPointArray(this Point[] points) return pts; } } -} \ No newline at end of file +} diff --git a/Source/OxyPlot.Windows/Utilities/MouseButtonHelper.cs b/OxyPlot.Windows/Utilities/MouseButtonHelper.cs similarity index 99% rename from Source/OxyPlot.Windows/Utilities/MouseButtonHelper.cs rename to OxyPlot.Windows/Utilities/MouseButtonHelper.cs index cdd2f66..56f2893 100644 --- a/Source/OxyPlot.Windows/Utilities/MouseButtonHelper.cs +++ b/OxyPlot.Windows/Utilities/MouseButtonHelper.cs @@ -89,4 +89,4 @@ private static double Distance(this Point pointA, Point pointB) return Math.Sqrt((x * x) + (y * y)); } } -} \ No newline at end of file +} diff --git a/OxyPlot.Windows/Utilities/OxyPlot.Windows.2.1.0.nupkg b/OxyPlot.Windows/Utilities/OxyPlot.Windows.2.1.0.nupkg new file mode 100644 index 0000000..7ff5865 Binary files /dev/null and b/OxyPlot.Windows/Utilities/OxyPlot.Windows.2.1.0.nupkg differ diff --git a/OxyPlot.Windows/Utilities/OxyPlot.Windows.csproj b/OxyPlot.Windows/Utilities/OxyPlot.Windows.csproj new file mode 100644 index 0000000..aeb88e7 --- /dev/null +++ b/OxyPlot.Windows/Utilities/OxyPlot.Windows.csproj @@ -0,0 +1,165 @@ + + + + + Debug + AnyCPU + {4F5B1D20-EA97-4DBB-94E1-8D86B4152847} + Library + Properties + OxyPlot.Windows + OxyPlot.Windows + ja-JP + UAP + 10.0.19041.0 + 10.0.17763.0 + 14 + 512 + {A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE;NETFX_CORE;WINDOWS_UWP + prompt + 4 + + + x86 + true + bin\x86\Debug\ + DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP + ;2008 + full + false + prompt + + + x86 + bin\x86\Release\ + TRACE;NETFX_CORE;WINDOWS_UWP + true + ;2008 + pdbonly + false + prompt + + + ARM + true + bin\ARM\Debug\ + DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP + ;2008 + full + false + prompt + + + ARM + bin\ARM\Release\ + TRACE;NETFX_CORE;WINDOWS_UWP + true + ;2008 + pdbonly + false + prompt + + + ARM64 + true + bin\ARM64\Debug\ + DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP + ;2008 + full + false + prompt + + + ARM64 + bin\ARM64\Release\ + TRACE;NETFX_CORE;WINDOWS_UWP + true + ;2008 + pdbonly + false + prompt + + + x64 + true + bin\x64\Debug\ + DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP + ;2008 + full + false + prompt + + + x64 + bin\x64\Release\ + TRACE;NETFX_CORE;WINDOWS_UWP + true + ;2008 + pdbonly + false + prompt + + + PackageReference + + + + + + + + + + + + + + + + 6.2.12 + + + 2.1.0 + + + 2.1.0 + + + + + + + + Designer + MSBuild:Compile + + + + 14.0 + + + + \ No newline at end of file diff --git a/OxyPlot.Windows/Utilities/OxyPlot.Windows.csproj.user b/OxyPlot.Windows/Utilities/OxyPlot.Windows.csproj.user new file mode 100644 index 0000000..9b86104 --- /dev/null +++ b/OxyPlot.Windows/Utilities/OxyPlot.Windows.csproj.user @@ -0,0 +1,6 @@ + + + + ShowAllFiles + + \ No newline at end of file diff --git a/OxyPlot.Windows/Utilities/OxyPlot.Windows.nuspec b/OxyPlot.Windows/Utilities/OxyPlot.Windows.nuspec new file mode 100644 index 0000000..c422d48 --- /dev/null +++ b/OxyPlot.Windows/Utilities/OxyPlot.Windows.nuspec @@ -0,0 +1,32 @@ + + + + OxyPlot.Windows + OxyPlot for Windows apps + 2.1.0 + Oystein Bjorke + OxyPlot is a plotting library for .NET. This package targets the Universal Windows Platform (UAP10). + + https://raw.githubusercontent.com/oxyplot/oxyplot/master/LICENSE + http://oxyplot.org/ + https://raw.githubusercontent.com/oxyplot/oxyplot/develop/Icons/OxyPlot_128.png + false + en-US + winrt uap uap10 UWP win10 plotting plot charting chart + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OxyPlot.Windows/Utilities/PlotView.cs b/OxyPlot.Windows/Utilities/PlotView.cs new file mode 100644 index 0000000..266c719 --- /dev/null +++ b/OxyPlot.Windows/Utilities/PlotView.cs @@ -0,0 +1,942 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// +// Represents a control that displays a . +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace OxyPlot.Windows +{ + using System; + using System.Collections.ObjectModel; + using System.Linq; + using System.Threading; + + using global::Windows.ApplicationModel; + using global::Windows.ApplicationModel.DataTransfer; + using global::Windows.Devices.Input; + using global::Windows.Foundation; + using global::Windows.System; + using global::Windows.UI.Core; + using global::Windows.UI.Xaml; + using global::Windows.UI.Xaml.Controls; + using global::Windows.UI.Xaml.Input; + using global::Windows.UI.Xaml.Media.Imaging; + + /// + /// + /// + [TemplatePart(Name = PartGrid, Type = typeof(Grid))] + public class PlotView : Control, OxyPlot.IPlotView + { + /// + /// Identifies the dependency property. + /// + public static readonly DependencyProperty ControllerProperty = + DependencyProperty.Register("Controller", typeof(IPlotController), typeof(PlotView), new PropertyMetadata(null)); + + /// + /// Identifies the dependency property. + /// + public static readonly DependencyProperty DefaultTrackerTemplateProperty = + DependencyProperty.Register( + "DefaultTrackerTemplate", typeof(ControlTemplate), typeof(PlotView), new PropertyMetadata(null)); + + /// + /// Identifies the dependency property. + /// + public static readonly DependencyProperty HandleRightClicksProperty = + DependencyProperty.Register("HandleRightClicks", typeof(bool), typeof(PlotView), new PropertyMetadata(true)); + + /// + /// Identifies the dependency property. + /// + public static readonly DependencyProperty IsMouseWheelEnabledProperty = + DependencyProperty.Register("IsMouseWheelEnabled", typeof(bool), typeof(PlotView), new PropertyMetadata(true)); + + /// + /// Identifies the dependency property. + /// + public static readonly DependencyProperty ModelProperty = DependencyProperty.Register( + "Model", typeof(PlotModel), typeof(PlotView), new PropertyMetadata(null, ModelChanged)); + + /// + /// Identifies the dependency property. + /// + public static readonly DependencyProperty ZoomRectangleTemplateProperty = + DependencyProperty.Register( + "ZoomRectangleTemplate", typeof(ControlTemplate), typeof(PlotView), new PropertyMetadata(null)); + + /// + /// Flags if the cursor is not implemented (Windows Phone). + /// + private static bool cursorNotImplemented; + /// + /// The Grid PART constant. + /// + private const string PartGrid = "PART_Grid"; + + /// + /// The model lock. + /// + private readonly object modelLock = new object(); + + /// + /// The tracker definitions. + /// + private readonly ObservableCollection trackerDefinitions; + + /// + /// The canvas. + /// + private Canvas canvas; + + /// + /// The current model. + /// + private PlotModel currentModel; + + /// + /// The current tracker. + /// + private FrameworkElement currentTracker; + + /// + /// The grid. + /// + private Grid grid; + + /// + /// The default controller. + /// + private IPlotController defaultController; + + /// + /// The state of the Alt key. + /// + private bool isAltPressed; + + /// + /// The state of the Windows key. + /// + private bool isWindowsPressed; + + /// + /// The state of the Control key. + /// + private bool isControlPressed; + + /// + /// The is PlotView invalidated. + /// + private int isPlotInvalidated; + + /// + /// The is shift pressed. + /// + private bool isShiftPressed; + + /// + /// The overlays. + /// + private Canvas overlays; + + /// + /// The render context + /// + private RenderContext renderContext; + + /// + /// The zoom control. + /// + private ContentControl zoomRectangle; + + /// + /// Initializes a new instance of the class. + /// + public PlotView() + { + this.DefaultStyleKey = typeof(PlotView); + + this.trackerDefinitions = new ObservableCollection(); + this.Loaded += this.OnLoaded; + this.SizeChanged += this.OnSizeChanged; + this.ManipulationMode = ManipulationModes.Scale | ManipulationModes.TranslateX + | ManipulationModes.TranslateY; + } + /// + /// Gets or sets the PlotView controller. + /// + /// The PlotView controller. + public IPlotController Controller + { + get { return (IPlotController)this.GetValue(ControllerProperty); } + set { this.SetValue(ControllerProperty, value); } + } + + /// + /// Gets or sets the default tracker template. + /// + public ControlTemplate DefaultTrackerTemplate + { + get + { + return (ControlTemplate)this.GetValue(DefaultTrackerTemplateProperty); + } + + set + { + this.SetValue(DefaultTrackerTemplateProperty, value); + } + } + + /// + /// Gets or sets a value indicating whether to handle right clicks. + /// + public bool HandleRightClicks + { + get + { + return (bool)this.GetValue(HandleRightClicksProperty); + } + + set + { + this.SetValue(HandleRightClicksProperty, value); + } + } + + /// + /// Gets or sets a value indicating whether IsMouseWheelEnabled. + /// + public bool IsMouseWheelEnabled + { + get + { + return (bool)this.GetValue(IsMouseWheelEnabledProperty); + } + + set + { + this.SetValue(IsMouseWheelEnabledProperty, value); + } + } + + /// + /// Gets or sets the to show. + /// + /// The . + public PlotModel Model + { + get + { + return (PlotModel)this.GetValue(ModelProperty); + } + + set + { + this.SetValue(ModelProperty, value); + } + } + + /// + /// Gets or sets the zoom rectangle template. + /// + /// The zoom rectangle template. + public ControlTemplate ZoomRectangleTemplate + { + get + { + return (ControlTemplate)this.GetValue(ZoomRectangleTemplateProperty); + } + + set + { + this.SetValue(ZoomRectangleTemplateProperty, value); + } + } + + /// + /// Gets the tracker definitions. + /// + /// The tracker definitions. + public ObservableCollection TrackerDefinitions + { + get + { + return this.trackerDefinitions; + } + } + + /// + /// Gets the actual model in the view. + /// + /// + /// The actual model. + /// + Model IView.ActualModel + { + get + { + return this.Model; + } + } + + + /// + /// Gets the actual model. + /// + /// The actual model. + public PlotModel ActualModel + { + get + { + return this.currentModel; + } + } + + /// + /// Gets the actual controller. + /// + /// + /// The actual . + /// + IController IView.ActualController + { + get + { + return this.ActualController; + } + } + + /// + /// Gets the coordinates of the client area of the view. + /// + public OxyRect ClientArea + { + get + { + return new OxyRect(0, 0, this.ActualWidth, this.ActualHeight); + } + } + + /// + /// Gets the actual PlotView controller. + /// + /// The actual PlotView controller. + public IPlotController ActualController + { + get + { + return this.Controller ?? (this.defaultController ?? (this.defaultController = new PlotController())); + } + } + + public void HideTracker() + { + if (this.currentTracker != null) + { + this.overlays.Children.Remove(this.currentTracker); + this.currentTracker = null; + } + } + + public void HideZoomRectangle() + { + this.zoomRectangle.Visibility = Visibility.Collapsed; + } + + /// + /// update a plot view. + /// + /// if the data in a plot view must be updated. + public void InvalidatePlot(bool updateData = true) + { + this.UpdateModel(updateData); + + if (DesignMode.DesignModeEnabled) + { + this.InvalidateArrange(); + return; + } + + if (Interlocked.CompareExchange(ref this.isPlotInvalidated, 1, 0) == 0) + { + // Invalidate the arrange state for the element. + // After the invalidation, the element will have its layout updated, + // which will occur asynchronously unless subsequently forced by UpdateLayout. + this.BeginInvoke(this.InvalidateArrange); + } + } + + /// + /// + /// + /// + public void SetClipboardText(string text) + { + throw new NotImplementedException(); + } + + /// + /// Set the type of cursor. + /// + /// cursor type + public void SetCursorType(CursorType cursorType) + { + if (cursorNotImplemented) + { + // setting the cursor has failed in a previous attempt, see code below + return; + } + + var type = CoreCursorType.Arrow; + switch (cursorType) + { + case CursorType.Default: + type = CoreCursorType.Arrow; + break; + case CursorType.Pan: + type = CoreCursorType.Hand; + break; + case CursorType.ZoomHorizontal: + type = CoreCursorType.SizeWestEast; + break; + case CursorType.ZoomVertical: + type = CoreCursorType.SizeNorthSouth; + break; + case CursorType.ZoomRectangle: + type = CoreCursorType.SizeNorthwestSoutheast; + break; + } + // TODO: determine if creating a CoreCursor is possible, do not use exception + try + { + var newCursor = new CoreCursor(type, 1); // this line throws an exception on Windows Phone + Window.Current.CoreWindow.PointerCursor = newCursor; + } + catch (NotImplementedException) + { + cursorNotImplemented = true; + } + } + + /// + /// Show a tracker. + /// + /// + public void ShowTracker(TrackerHitResult trackerHitResult) + { + if (trackerHitResult == null) + { + this.HideTracker(); + return; + } + + var trackerTemplate = this.DefaultTrackerTemplate; + if (trackerHitResult.Series != null && !string.IsNullOrEmpty(trackerHitResult.Series.TrackerKey)) + { + var match = this.TrackerDefinitions.FirstOrDefault(t => t.TrackerKey == trackerHitResult.Series.TrackerKey); + if (match != null) + { + trackerTemplate = match.TrackerTemplate; + } + } + + if (trackerTemplate == null) + { + this.HideTracker(); + return; + } + + var tracker = new ContentControl { Template = trackerTemplate }; + + if (tracker != this.currentTracker) + { + this.HideTracker(); + this.overlays.Children.Add(tracker); + this.currentTracker = tracker; + } + + if (this.currentTracker != null) + { + this.currentTracker.DataContext = trackerHitResult; + } + } + + /// + /// Show a zoom rectangle. + /// + /// + public void ShowZoomRectangle(OxyRect rectangle) + { + this.zoomRectangle.Width = rectangle.Width; + this.zoomRectangle.Height = rectangle.Height; + Canvas.SetLeft(this.zoomRectangle, rectangle.Left); + Canvas.SetTop(this.zoomRectangle, rectangle.Top); + this.zoomRectangle.Template = this.ZoomRectangleTemplate; + this.zoomRectangle.Visibility = Visibility.Visible; + } + /// + /// Renders the PlotView to a bitmap. + /// + /// A bitmap. + public WriteableBitmap ToBitmap() + { + throw new NotImplementedException(); + + // var bmp = new RenderTargetBitmap( + // (int)this.ActualWidth, (int)this.ActualHeight, 96, 96, PixelFormats.Pbgra32); + // bmp.Render(this); + // return bmp; + } + + /// + /// Stores text on the clipboard. + /// + /// The text. + void IPlotView.SetClipboardText(string text) + { + var pkg = new DataPackage(); + pkg.SetText(text); + + // TODO: Clipboard.SetContent(pkg); + } + + /// + /// Invoked whenever application code or internal processes (such as a rebuilding layout pass) call ApplyTemplate. In simplest terms, this means the method is called just before a UI element displays in your app. Override this method to influence the default post-template logic of a class. + /// + protected override void OnApplyTemplate() + { + base.OnApplyTemplate(); + + this.grid = this.GetTemplateChild(PartGrid) as Grid; + if (this.grid == null) + { + return; + } + + this.canvas = new Canvas { IsHitTestVisible = false }; + this.grid.Children.Add(this.canvas); + this.canvas.UpdateLayout(); + + this.renderContext = new RenderContext(this.canvas); + + this.overlays = new Canvas(); + this.grid.Children.Add(this.overlays); + + this.zoomRectangle = new ContentControl(); + this.overlays.Children.Add(this.zoomRectangle); + } + + /// + /// Called before the KeyDown event occurs. + /// + /// The data for the event. + protected override void OnKeyDown(KeyRoutedEventArgs e) + { + switch (e.Key) + { + case VirtualKey.Control: + this.isControlPressed = true; + break; + case VirtualKey.Shift: + this.isShiftPressed = true; + break; + case VirtualKey.Menu: + this.isAltPressed = true; + break; + case VirtualKey.LeftWindows: + case VirtualKey.RightWindows: + this.isWindowsPressed = true; + break; + } + + var modifiers = OxyModifierKeys.None; + if (this.isControlPressed) + { + modifiers |= OxyModifierKeys.Control; + } + + if (this.isAltPressed) + { + modifiers |= OxyModifierKeys.Control; + } + + if (this.isShiftPressed) + { + modifiers |= OxyModifierKeys.Shift; + } + + if (this.isWindowsPressed) + { + modifiers |= OxyModifierKeys.Windows; + } + + if (e.Handled) + { + return; + } + + var args = new OxyKeyEventArgs + { + Key = e.Key.Convert(), + ModifierKeys = modifiers, + }; + + e.Handled = this.ActualController.HandleKeyDown(this, args); + } + + /// + /// Called before the KeyUp event occurs. + /// + /// The data for the event. + protected override void OnKeyUp(KeyRoutedEventArgs e) + { + base.OnKeyUp(e); + switch (e.Key) + { + case VirtualKey.Control: + this.isControlPressed = false; + break; + case VirtualKey.Shift: + this.isShiftPressed = false; + break; + case VirtualKey.Menu: + this.isAltPressed = false; + break; + case VirtualKey.LeftWindows: + case VirtualKey.RightWindows: + this.isWindowsPressed = false; + break; + } + } + + /// + /// Called before the ManipulationStarted event occurs. + /// + /// Event data for the event. + protected override void OnManipulationStarted(ManipulationStartedRoutedEventArgs e) + { + base.OnManipulationStarted(e); + + if (e.Handled) + { + return; + } + + if (e.PointerDeviceType == PointerDeviceType.Touch) + { + this.Focus(FocusState.Pointer); + e.Handled = this.ActualController.HandleTouchStarted(this, e.ToTouchEventArgs(this)); + } + } + + /// + /// Called before the ManipulationDelta event occurs. + /// + /// Event data for the event. + protected override void OnManipulationDelta(ManipulationDeltaRoutedEventArgs e) + { + base.OnManipulationDelta(e); + + if (e.Handled) + { + return; + } + + if (e.PointerDeviceType == PointerDeviceType.Touch) + { + e.Handled = this.ActualController.HandleTouchDelta(this, e.ToTouchEventArgs(this)); + } + } + + /// + /// Called before the ManipulationCompleted event occurs. + /// + /// Event data for the event. + protected override void OnManipulationCompleted(ManipulationCompletedRoutedEventArgs e) + { + base.OnManipulationCompleted(e); + + if (e.Handled) + { + return; + } + + if (e.PointerDeviceType == PointerDeviceType.Touch) + { + e.Handled = this.ActualController.HandleTouchCompleted(this, e.ToTouchEventArgs(this)); + } + } + + /// + /// Called before the PointerPressed event occurs. + /// + /// Event data for the event. + protected override void OnPointerPressed(PointerRoutedEventArgs e) + { + base.OnPointerPressed(e); + + if (e.Handled) + { + return; + } + + if (e.Pointer.PointerDeviceType == PointerDeviceType.Mouse) + { + this.Focus(FocusState.Pointer); + this.CapturePointer(e.Pointer); + + e.Handled = this.ActualController.HandleMouseDown(this, e.ToMouseDownEventArgs(this)); + } + else if (e.Pointer.PointerDeviceType == PointerDeviceType.Touch) + { + this.Focus(FocusState.Pointer); + + e.Handled = this.ActualController.HandleTouchStarted(this, e.ToTouchEventArgs(this)); + } + } + + /// + /// Called before the PointerMoved event occurs. + /// + /// Event data for the event. + protected override void OnPointerMoved(PointerRoutedEventArgs e) + { + base.OnPointerMoved(e); + + if (e.Handled) + { + return; + } + + if (e.Pointer.PointerDeviceType == PointerDeviceType.Mouse) + { + e.Handled = this.ActualController.HandleMouseMove(this, e.ToMouseEventArgs(this)); + } + + // Note: don't handle touch here, this is also handled when moving over when a touch device + } + + /// + /// Called before the PointerReleased event occurs. + /// + /// Event data for the event. + protected override void OnPointerReleased(PointerRoutedEventArgs e) + { + base.OnPointerReleased(e); + + if (e.Handled) + { + return; + } + + if (e.Pointer.PointerDeviceType == PointerDeviceType.Mouse) + { + this.ReleasePointerCapture(e.Pointer); + e.Handled = this.ActualController.HandleMouseUp(this, e.ToMouseEventArgs(this)); + } + else if (e.Pointer.PointerDeviceType == PointerDeviceType.Touch) + { + e.Handled = this.ActualController.HandleTouchCompleted(this, e.ToTouchEventArgs(this)); + } + } + + /// + /// Called before the PointerWheelChanged event occurs. + /// + /// Event data for the event. + protected override void OnPointerWheelChanged(PointerRoutedEventArgs e) + { + base.OnPointerWheelChanged(e); + + if (e.Handled || !this.IsMouseWheelEnabled) + { + return; + } + + e.Handled = this.ActualController.HandleMouseWheel(this, e.ToMouseWheelEventArgs(this)); + } + + /// + /// Called before the PointerEntered event occurs. + /// + /// Event data for the event. + protected override void OnPointerEntered(PointerRoutedEventArgs e) + { + base.OnPointerEntered(e); + if (e.Handled) + { + return; + } + + e.Handled = this.ActualController.HandleMouseEnter(this, e.ToMouseEventArgs(this)); + } + + /// + /// Called before the PointerExited event occurs. + /// + /// Event data for the event. + protected override void OnPointerExited(PointerRoutedEventArgs e) + { + base.OnPointerExited(e); + if (e.Handled) + { + return; + } + + e.Handled = this.ActualController.HandleMouseLeave(this, e.ToMouseEventArgs(this)); + } + + /// + /// A one time condition for update visuals so it is called no matter the state of the control + /// Currently with out this, the plotview on Xamarin Forms UWP does not render until the app's window resizes + /// + private bool isUpdateVisualsCalledOnce = false; + + /// + /// Provides the behavior for the Arrange pass of layout. Classes can override this method to define their own Arrange pass behavior. + /// + /// The final area within the parent that this object should use to arrange itself and its children. + /// The actual size that is used after the element is arranged in layout. + protected override Size ArrangeOverride(Size finalSize) + { + if (this.ActualWidth > 0 && this.ActualHeight > 0) + { + if (Interlocked.CompareExchange(ref this.isPlotInvalidated, 0, 1) == 1) + { + this.UpdateVisuals(); + } + } + + //see summary for isUpdateVisualsCalledOnce + if (!isUpdateVisualsCalledOnce) + { + this.UpdateVisuals(); + + isUpdateVisualsCalledOnce = true; + } + + return base.ArrangeOverride(finalSize); + } + + /// + /// Called when the property is changed. + /// + /// The sender. + /// The instance containing the event data. + private static void ModelChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) + { + ((PlotView)sender).OnModelChanged(); + } + + /// + /// Called when the control is loaded. + /// + /// The sender. + /// The instance containing the event data. + private void OnLoaded(object sender, RoutedEventArgs e) + { + // Make sure InvalidateArrange is called when the PlotView is invalidated + Interlocked.Exchange(ref this.isPlotInvalidated, 0); + this.InvalidatePlot(); + } + + /// + /// Called when the model is changed. + /// + private void OnModelChanged() + { + lock (this.modelLock) + { + if (this.currentModel != null) + { + ((IPlotModel)this.currentModel).AttachPlotView(null); + this.currentModel = null; + } + + if (this.Model != null) + { + ((IPlotModel)this.Model).AttachPlotView(this); + this.currentModel = this.Model; + } + } + + this.InvalidatePlot(); + } + + /// + /// Called when the size of the control is changed. + /// + /// The sender. + /// The instance containing the event data. + private void OnSizeChanged(object sender, SizeChangedEventArgs e) + { + this.InvalidatePlot(false); + } + + /// + /// Updates the model. + /// + /// if set to true, the data collections will be updated. + private void UpdateModel(bool update) + { + if (this.ActualModel != null) + { + ((IPlotModel)this.ActualModel).Update(update); + } + } + + /// + /// Updates the visuals. + /// + private void UpdateVisuals() + { + if (this.canvas == null || this.renderContext == null) + { + return; + } + + // Clear the canvas + this.canvas.Children.Clear(); + + if (this.ActualModel != null && !this.ActualModel.Background.IsUndefined()) + { + this.canvas.Background = this.ActualModel.Background.ToBrush(); + } + else + { + this.canvas.Background = null; + } + + if (this.ActualModel != null) + { + ((IPlotModel)this.ActualModel).Render(this.renderContext, ClientArea); + } + } + + /// + /// Invokes the specified action on the UI Thread (without blocking the calling thread). + /// + /// The action. + private void BeginInvoke(Action action) + { + if (!this.Dispatcher.HasThreadAccess) + { + // TODO: Fix warning? + // Because this call is not awaited, execution of the current method continues before the call is completed. + // Consider applying the 'await' operator to the result of the call. +#pragma warning disable 4014 + this.Dispatcher.RunAsync(CoreDispatcherPriority.Low, () => action()); +#pragma warning restore 4014 + } + else + { + action(); + } + } + } +} diff --git a/OxyPlot.Windows/Utilities/RenderContext.cs b/OxyPlot.Windows/Utilities/RenderContext.cs new file mode 100644 index 0000000..8fd3078 --- /dev/null +++ b/OxyPlot.Windows/Utilities/RenderContext.cs @@ -0,0 +1,906 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// +// Implements for . +// +// -------------------------------------------------------------------------------------------------------------------- + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices.WindowsRuntime; +using System.Threading.Tasks; + +using global::Windows.Foundation; +using global::Windows.Storage.Streams; +using global::Windows.UI; +using global::Windows.UI.Text; +using global::Windows.UI.Xaml; +using global::Windows.UI.Xaml.Controls; +using global::Windows.UI.Xaml.Media; +using global::Windows.UI.Xaml.Media.Imaging; +using global::Windows.UI.Xaml.Shapes; +using OxyPlot.Core.Drawing; +using Path = global::Windows.UI.Xaml.Shapes.Path; + +namespace OxyPlot.Windows +{ + /// + /// Implements for . + /// + public class RenderContext : IRenderContext + { + /// + /// The brush cache. + /// + private readonly Dictionary brushCache = new Dictionary(); + + /// + /// The canvas. + /// + private readonly Canvas canvas; + + /// + /// The images in use + /// + private readonly HashSet imagesInUse = new HashSet(); + + /// + /// The image cache + /// + private readonly Dictionary imageCache = new Dictionary(); + + /// + /// The current tool tip + /// + private string currentToolTip; + + /// + /// The clip rectangle. + /// + private Rect clipRect; + + /// + /// The clip flag. + /// + private bool clip; + + private readonly Stack clipStack; + + /// + /// Initializes a new instance of the class. + /// + /// The canvas. + public RenderContext(Canvas canvas) + { + this.canvas = canvas; + this.Width = canvas.ActualWidth; + this.Height = canvas.ActualHeight; + this.RendersToScreen = true; + this.clipStack = new Stack(); + } + + /// + /// Gets the height. + /// + /// The height. + public double Height { get; private set; } + + /// + /// Gets a value indicating whether to paint the background. + /// + /// true if the background should be painted; otherwise, false. + public bool PaintBackground + { + get + { + return false; + } + } + + /// + /// Gets the width. + /// + /// The width. + public double Width { get; private set; } + + /// + /// Gets or sets a value indicating whether the context renders to screen. + /// + /// true if the context renders to screen; otherwise, false. + public bool RendersToScreen { get; set; } + + + /// + /// Gets the number of clipped. + /// + public int ClipCount => this.clipStack.Count; + + /// + /// Draws an ellipse. + /// + /// The rectangle. + /// The fill color. + /// The stroke color. + /// The thickness. + /// The rendering mode. + public void DrawEllipse(OxyRect rect, OxyColor fill, OxyColor stroke, + double thickness, EdgeRenderingMode edgeRenderingMode) + { + var el = new Ellipse + { + CompositeMode = ElementCompositeMode.SourceOver + }; + + if (stroke.IsVisible()) + { + el.Stroke = new SolidColorBrush(stroke.ToColor()); + el.StrokeThickness = thickness; + } + + if (fill.IsVisible()) + { + el.Fill = new SolidColorBrush(fill.ToColor()); + } + + el.Width = rect.Width; + el.Height = rect.Height; + Canvas.SetLeft(el, rect.Left); + Canvas.SetTop(el, rect.Top); + this.Add(el, rect.Left, rect.Top); + } + + /// + /// Draws the collection of ellipses, where all have the same stroke and fill. + /// This performs better than calling DrawEllipse multiple times. + /// + /// The rectangles. + /// The fill color. + /// The stroke color. + /// The stroke thickness. + /// The rendering mode. + public void DrawEllipses(IList rectangles, OxyColor fill, OxyColor stroke, double thickness, EdgeRenderingMode edgeRenderingMode) + { + var path = new Path + { + CompositeMode = ElementCompositeMode.SourceOver + }; + + this.SetStroke(path, stroke, thickness); + if (fill.IsVisible()) + { + path.Fill = this.GetCachedBrush(fill); + } + + var gg = new GeometryGroup { FillRule = FillRule.Nonzero }; + foreach (var rect in rectangles) + { + gg.Children.Add( + new EllipseGeometry + { + Center = new Point(rect.Left + (rect.Width / 2), rect.Top + (rect.Height / 2)), + RadiusX = rect.Width / 2, + RadiusY = rect.Height / 2 + }); + } + + path.Data = gg; + this.Add(path); + } + + /// + /// Draws the polyline from the specified points. + /// + /// The points. + /// The stroke color. + /// The stroke thickness. + /// The rendering mode. + /// The dash array. + /// The line join type. + public void DrawLine(IList points, OxyColor stroke, double thickness, EdgeRenderingMode edgeRenderingMode, + double[] dashArray = null, LineJoin lineJoin = LineJoin.Miter) + { + var e = new Polyline + { + CompositeMode = ElementCompositeMode.SourceOver + }; + + this.SetStroke(e, stroke, thickness, lineJoin, dashArray, false); + PointCollection pc = new PointCollection(); + foreach (var p in points) + { + Point pnt = new Point(p.X, p.Y); + pc.Add(pnt); + } + + e.Points = pc; + + this.Add(e); + } + + /// + /// Draws the multiple line segments defined by points (0,1) (2,3) (4,5) etc. + /// This should have better performance than calling DrawLine for each segment. + /// + /// The points. + /// The stroke color. + /// The stroke thickness. + /// The rendering mode. + /// The dash array. + /// The line join type. + public void DrawLineSegments(IList points, OxyColor stroke, double thickness, + EdgeRenderingMode edgeRenderingMode, double[] dashArray = null, LineJoin lineJoin = LineJoin.Miter) + { + var path = new Path + { + CompositeMode = ElementCompositeMode.SourceOver + }; + + this.SetStroke(path, stroke, thickness, lineJoin, dashArray); + var pg = new PathGeometry(); + for (int i = 0; i + 1 < points.Count; i += 2) + { + // if (points[i].Y==points[i+1].Y) + // { + // var line = new Line(); + + // line.X1 = 0.5+(int)points[i].X; + // line.X2 = 0.5+(int)points[i+1].X; + // line.Y1 = 0.5+(int)points[i].Y; + // line.Y2 = 0.5+(int)points[i+1].Y; + // SetStroke(line, OxyColors.DarkRed, thickness, lineJoin, dashArray, aliased); + // Add(line); + // continue; + // } + Point pnt1 = new Point(points[i].X, points[i].Y); + Point pnt2 = new Point(points[i + 1].X, points[i + 1].Y); + var figure = new PathFigure { StartPoint = pnt1, IsClosed = false }; + figure.Segments.Add(new LineSegment { Point = pnt2 }); + pg.Figures.Add(figure); + } + + path.Data = pg; + this.Add(path); + } + + /// + /// Draws the polygon from the specified points. The polygon can have stroke and/or fill. + /// + /// The points. + /// The fill color. + /// The stroke color. + /// The stroke thickness. + /// The rendering mode. + /// The dash array. + /// The line join type. + public void DrawPolygon(IList points, OxyColor fill, OxyColor stroke, + double thickness, EdgeRenderingMode edgeRenderingMode, double[] dashArray = null, LineJoin lineJoin = LineJoin.Miter) + { + var po = new Polygon + { + CompositeMode = ElementCompositeMode.SourceOver + }; + + this.SetStroke(po, stroke, thickness, lineJoin, dashArray); + + if (fill.IsVisible()) + { + po.Fill = this.GetCachedBrush(fill); + } + + PointCollection pc = new PointCollection(); + foreach (var p in points) + { + Point pnt = new Point(p.X, p.Y); + pc.Add(pnt); + } + + po.Points = pc; + + this.Add(po); + } + + /// + /// Draws a collection of polygons, where all polygons have the same stroke and fill. + /// This performs better than calling DrawPolygon multiple times. + /// + /// The polygons. + /// The fill color. + /// The stroke color. + /// The stroke thickness. + /// The rendering mode. + /// The dash array. + /// The line join type. + public void DrawPolygons(IList> polygons, OxyColor fill, OxyColor stroke, + double thickness, EdgeRenderingMode edgeRenderingMode, double[] dashArray = null, LineJoin lineJoin = LineJoin.Miter) + + { + var path = new Path + { + CompositeMode = ElementCompositeMode.SourceOver + }; + + this.SetStroke(path, stroke, thickness, lineJoin, dashArray); + if (fill.IsVisible()) + { + path.Fill = this.GetCachedBrush(fill); + } + + var pg = new PathGeometry { FillRule = FillRule.Nonzero }; + foreach (var polygon in polygons) + { + var figure = new PathFigure { IsClosed = true }; + bool first = true; + foreach (var p in polygon) + { + Point pnt = new Point(p.X, p.Y); + if (first) + { + figure.StartPoint = pnt; + first = false; + } + else + { + figure.Segments.Add(new LineSegment { Point = pnt }); + } + } + + pg.Figures.Add(figure); + } + + path.Data = pg; + this.Add(path); + } + + /// + /// Draws the rectangle. + /// + /// The rectangle. + /// The fill color. + /// The stroke color. + /// The stroke thickness. + /// Rendering mode. + public void DrawRectangle(OxyRect rectangle, OxyColor fill, OxyColor stroke, + double thickness, EdgeRenderingMode edgeRenderingMode) + { + var el = new Rectangle + { + CompositeMode = ElementCompositeMode.SourceOver + }; + + if (stroke.IsVisible()) + { + Color col = Color.FromArgb(stroke.A, stroke.R, stroke.G, stroke.B); + el.Stroke = new SolidColorBrush(col); + el.StrokeThickness = thickness; + } + + if (fill.IsVisible()) + { + Color col = Color.FromArgb(fill.A, fill.R, fill.G, fill.B); + el.Fill = new SolidColorBrush(col); + } + + el.Width = rectangle.Width; + el.Height = rectangle.Height; + Canvas.SetLeft(el, rectangle.Left); + Canvas.SetTop(el, rectangle.Top); + this.Add(el, rectangle.Left, rectangle.Top); + } + + /// + /// Draws a collection of rectangles, where all have the same stroke and fill. + /// This performs better than calling DrawRectangle multiple times. + /// + /// The rectangles. + /// The fill color. + /// The stroke color. + /// The stroke thickness. + /// The rendering mode. + public void DrawRectangles(IList rectangles, OxyColor fill, OxyColor stroke, + double thickness, EdgeRenderingMode edgeRenderingMode) + { + var path = new Path + { + CompositeMode = ElementCompositeMode.SourceOver + }; + + this.SetStroke(path, stroke, thickness); + if (fill.IsVisible()) + { + path.Fill = this.GetCachedBrush(fill); + } + + var gg = new GeometryGroup { FillRule = FillRule.Nonzero }; + foreach (var rect in rectangles) + { + Rect r = rect.ToRect(false); + gg.Children.Add(new RectangleGeometry { Rect = new Rect(r.X, r.Y, r.Width, r.Height) }); + } + + path.Data = gg; + this.Add(path); + } + + /// + /// Draws the text. + /// + /// The position. + /// The text. + /// The fill color. + /// The font family. + /// Size of the font. + /// The font weight. + /// The rotation angle. + /// The horizontal alignment. + /// The vertical alignment. + /// The maximum size of the text. + public void DrawText( + ScreenPoint p, + string text, + OxyColor fill, + string fontFamily, + double fontSize, + double fontWeight, + double rotate, + OxyPlot.HorizontalAlignment halign, + OxyPlot.VerticalAlignment valign, + OxySize? maxSize) + { + var tb = new TextBlock { Text = text, Foreground = new SolidColorBrush(fill.ToColor()) }; + + // tb.SetValue(TextOptions.TextHintingModeProperty, TextHintingMode.Animated); + if (fontFamily != null) + { + tb.FontFamily = new FontFamily(fontFamily); + } + + if (fontSize > 0) + { + tb.FontSize = fontSize; + } + + tb.FontWeight = GetFontWeight(fontWeight); + + tb.Measure(new Size(1000, 1000)); + var size = new Size(tb.ActualWidth, tb.ActualHeight); + if (maxSize != null) + { + if (size.Width > maxSize.Value.Width) + { + size.Width = maxSize.Value.Width; + } + + if (size.Height > maxSize.Value.Height) + { + size.Height = maxSize.Value.Height; + } + + tb.Clip = new RectangleGeometry { Rect = new Rect(0, 0, size.Width, size.Height) }; + } + + double dx = 0; + if (halign == OxyPlot.HorizontalAlignment.Center) + { + dx = -size.Width / 2; + } + + if (halign == OxyPlot.HorizontalAlignment.Right) + { + dx = -size.Width; + } + + double dy = 0; + if (valign == OxyPlot.VerticalAlignment.Middle) + { + dy = -size.Height / 2; + } + + if (valign == OxyPlot.VerticalAlignment.Bottom) + { + dy = -size.Height; + } + + var transform = new TransformGroup(); + transform.Children.Add(new TranslateTransform { X = (int)dx, Y = (int)dy }); + if (!rotate.Equals(0)) + { + transform.Children.Add(new RotateTransform { Angle = rotate }); + } + + transform.Children.Add(new TranslateTransform { X = (int)p.X, Y = (int)p.Y }); + tb.RenderTransform = transform; + this.ApplyTooltip(tb); + + if (this.clip) + { + // add a clipping container that is not rotated + var c = new Canvas(); + c.Children.Add(tb); + this.Add(c); + } + else + { + this.Add(tb); + } + } + + /// + /// Measures the text. + /// + /// The text. + /// The font family. + /// Size of the font. + /// The font weight. + /// The text size. + public OxySize MeasureText(string text, string fontFamily, double fontSize, double fontWeight) + { + if (string.IsNullOrEmpty(text)) + { + return OxySize.Empty; + } + + var tb = new TextBlock { Text = text }; + + if (fontFamily != null) + { + tb.FontFamily = new FontFamily(fontFamily); + } + + if (fontSize > 0) + { + tb.FontSize = fontSize; + } + + tb.FontWeight = GetFontWeight(fontWeight); + + tb.Measure(new Size(1000, 1000)); + + return new OxySize(tb.ActualWidth, tb.ActualHeight); + } + + /// + /// Sets the tool tip for the following items. + /// + /// The text in the tooltip. + public void SetToolTip(string text) + { + this.currentToolTip = text; + } + + /// + /// Draws the specified portion of the specified at the specified location and with the specified size. + /// + /// The source. + /// The x-coordinate of the upper-left corner of the portion of the source image to draw. + /// The y-coordinate of the upper-left corner of the portion of the source image to draw. + /// Width of the portion of the source image to draw. + /// Height of the portion of the source image to draw. + /// The x-coordinate of the upper-left corner of drawn image. + /// The y-coordinate of the upper-left corner of drawn image. + /// The width of the drawn image. + /// The height of the drawn image. + /// The opacity. + /// interpolate if set to true. + public void DrawImage( + OxyImage source, + double srcX, + double srcY, + double srcWidth, + double srcHeight, + double destX, + double destY, + double destWidth, + double destHeight, + double opacity, + bool interpolate) + { + if (destWidth <= 0 || destHeight <= 0 || srcWidth <= 0 || srcHeight <= 0) + { + return; + } + + var image = new Image(); + var bmp = this.GetImageSource(source); + + if (srcX.Equals(0) && srcY.Equals(0) && srcWidth.Equals(bmp.PixelWidth) && srcHeight.Equals(bmp.PixelHeight)) + { + // do not crop + } + else + { + // TODO: cropped image not available in Silverlight?? + // bmp = new CroppedBitmap(bmp, new Int32Rect((int)srcX, (int)srcY, (int)srcWidth, (int)srcHeight)); + return; + } + + image.Opacity = opacity; + image.Width = destWidth; + image.Height = destHeight; + image.Stretch = Stretch.Fill; + + // TODO: not available in Silverlight?? + // RenderOptions.SetBitmapScalingMode(image, interpolate ? BitmapScalingMode.HighQuality : BitmapScalingMode.NearestNeighbor); + // Canvas.SetLeft(image, x); + // Canvas.SetTop(image, y); + image.RenderTransform = new TranslateTransform { X = destX, Y = destY }; + image.Source = bmp; + this.ApplyTooltip(image); + this.Add(image, destX, destY); + } + + /// + /// Sets the clipping rectangle. + /// + /// The clipping rectangle. + /// True if the clipping rectangle was set. + public bool SetClip(OxyRect clippingRect) + { + Rect rect = clippingRect.ToRect(false); + this.clipRect = new Rect(rect.X, rect.Y, rect.Width, rect.Height); + this.clip = true; + return true; + } + + /// + /// Resets the clipping rectangle. + /// + public void ResetClip() + { + this.clip = false; + } + + /// + /// Cleans up resources not in use. + /// + /// This method is called at the end of each rendering. + public void CleanUp() + { + // Find the images in the cache that has not been used since last call to this method + var imagesToRelease = this.imageCache.Keys.Where(i => !this.imagesInUse.Contains(i)).ToList(); + + // Remove the images from the cache + foreach (var i in imagesToRelease) + { + this.imageCache.Remove(i); + } + + this.imagesInUse.Clear(); + } + + /// + /// Creates the dash array collection. + /// + /// The dash array. + /// A DoubleCollection. + private static DoubleCollection CreateDashArrayCollection(IEnumerable dashArray) + { + var dac = new DoubleCollection(); + foreach (double v in dashArray) + { + dac.Add(v); + } + + return dac; + } + + /// + /// Gets the font weight. + /// + /// The font weight. + /// A + private static FontWeight GetFontWeight(double fontWeight) + { + FontWeight fw; + + if (fontWeight > OxyPlot.FontWeights.Normal) + { + fw = new FontWeight() { Weight = (ushort)FontWeights.Bold }; + } + else + { + fw = new FontWeight() { Weight = (ushort)FontWeights.Normal }; + } + return fw; + } + + /// + /// Adds the specified element to the canvas. + /// + /// The element. + /// The clip offset X. + /// The clip offset Y. + private void Add(UIElement element, double clipOffsetX = 0, double clipOffsetY = 0) + { + if (this.clip) + { + this.ApplyClip(element, clipOffsetX, clipOffsetY); + } + + this.canvas.Children.Add(element); + } + + /// + /// Applies the tooltip to the specified element. + /// + /// The element. + private void ApplyTooltip(DependencyObject element) + { + if (!string.IsNullOrEmpty(this.currentToolTip)) + { + ToolTipService.SetToolTip(element, this.currentToolTip); + } + } + + /// + /// Gets the cached brush. + /// + /// The stroke. + /// The brush. + private Brush GetCachedBrush(OxyColor stroke) + { + Brush brush; + if (!this.brushCache.TryGetValue(stroke, out brush)) + { + brush = new SolidColorBrush(stroke.ToColor()); + this.brushCache.Add(stroke, brush); + } + + return brush; + } + + /// + /// Sets the stroke of the specified shape. + /// + /// The shape. + /// The stroke. + /// The thickness. + /// The line join. + /// The dash array. + /// aliased if set to true. + private void SetStroke( + Shape shape, + OxyColor stroke, + double thickness, + LineJoin lineJoin = LineJoin.Miter, + IEnumerable dashArray = null, + bool aliased = false) + { + if (stroke.IsVisible() && thickness > 0) + { + shape.Stroke = this.GetCachedBrush(stroke); + + switch (lineJoin) + { + case LineJoin.Round: + shape.StrokeLineJoin = PenLineJoin.Round; + break; + case LineJoin.Bevel: + shape.StrokeLineJoin = PenLineJoin.Bevel; + break; + + // The default StrokeLineJoin is Miter + } + + shape.StrokeThickness = thickness; + + if (dashArray != null) + { + shape.StrokeDashArray = CreateDashArrayCollection(dashArray); + } + + if (aliased) + { + // shape.UseLayoutRounding = aliased; + } + } + } + + /// + /// Applies the clip rectangle. + /// + /// The image. + /// The x offset of the element. + /// The y offset of the element. + private void ApplyClip(UIElement image, double x, double y) + { + image.Clip = new RectangleGeometry { Rect = new Rect(this.clipRect.X - x, this.clipRect.Y - y, this.clipRect.Width, this.clipRect.Height) }; + } + + /// + /// Gets the bitmap source. + /// + /// The image. + /// The bitmap source. + private BitmapSource GetImageSource(OxyImage image) + { + if (image == null) + { + return null; + } + + if (!this.imagesInUse.Contains(image)) + { + this.imagesInUse.Add(image); + } + + BitmapSource src; + if (this.imageCache.TryGetValue(image, out src)) + { + return src; + } + + var bitmapImage = new BitmapImage(); + var imageStream = ConvertToRandomAccessStream(image.GetData()).GetAwaiter().GetResult(); + bitmapImage.SetSource(imageStream); + this.imageCache.Add(image, bitmapImage); + return bitmapImage; + } + + /// + /// Converts the specified byte array to a . + /// + /// + /// + private static async Task ConvertToRandomAccessStream(byte[] buffer) + { + //https://stackoverflow.com/questions/16397509/how-to-convert-byte-array-to-inmemoryrandomaccessstream-or-irandomaccessstream-i + var randomAccessStream = new InMemoryRandomAccessStream(); + await randomAccessStream.WriteAsync(buffer.AsBuffer()); + randomAccessStream.Seek(0); + return randomAccessStream; + } + /// + /// Push a Clip data. + /// + /// + public void PushClip(OxyRect clippingRectangle) + { + if (this.clipStack.Count > 0) + { + OxyRect currentClippingRectangle = this.clipStack.Peek(); + OxyRect newClippingRectangle = clippingRectangle.Intersect(currentClippingRectangle); + if (!currentClippingRectangle.Equals(newClippingRectangle)) + { + this.ResetClip(); + this.SetClip(newClippingRectangle); + } + + this.clipStack.Push(newClippingRectangle); + } + else + { + this.SetClip(clippingRectangle); + this.clipStack.Push(clippingRectangle); + } + } + + /// + /// Pop a Clip data. + /// + public void PopClip() + { + if (this.clipStack.Count == 0) + { + throw new InvalidOperationException($"Unbalanced call to {nameof(PopClip)}"); + } + + OxyRect currentClippingRectangle = this.clipStack.Pop(); + if (this.clipStack.Count > 0) + { + OxyRect newClippingRectangle = this.clipStack.Peek(); + if (!newClippingRectangle.Equals(currentClippingRectangle)) + { + this.ResetClip(); + this.SetClip(newClippingRectangle); + } + } + else + { + this.ResetClip(); + } + } + } +} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..6be5183 --- /dev/null +++ b/README.md @@ -0,0 +1,16 @@ +## OxyPlot is a cross-platform plotting library for .NET +### Function +You can draw a graph like below in a .NET UWP application. +![Plot](https://oxyplot.github.io/public/images/normal-distributions.png) + +### Background +* This library contains some adjustment for Universal Windows Platform that are to be published as the NuGet package OxyPlot.Windows. +* This library was created simply to use the OxyPlot.Core version 2.1.0. I worked for just adjusting the existing oxyplot-uwp repository in oxyplot, so I'm not sure right now whether you can use all the functionarities of the oxyplot with this library. +* The current NuGet OxyPlot.Windows package is 2.0.0-unstable1035, which will be out of date in the near future because the OxyPlot.Core version 2.1.0 is totally different from version 2.0.0. I don't know how this package were built as the original repository oxyplot/oxyplot-uwp itself couldn't make its package. + +### Getting started + +1. Use the NuGet package manager to add a reference to OxyPlot (OxyPlot.Windows package can be made in this repository). +2. Add a `PlotView` to your user interface +3. Create a `PlotModel` in your code +4. Bind the `PlotModel` to the `Model` property of your `PlotView` diff --git a/Source/Examples/Windows/SimpleDemo/MainViewModel.cs b/Source/Examples/Windows/SimpleDemo/MainViewModel.cs deleted file mode 100644 index 2bb27e4..0000000 --- a/Source/Examples/Windows/SimpleDemo/MainViewModel.cs +++ /dev/null @@ -1,27 +0,0 @@ -namespace SimpleDemo -{ - using OxyPlot; - using OxyPlot.Axes; - using OxyPlot.Series; - - public class MainViewModel - { - public MainViewModel() - { - var model = new PlotModel { Title = "Hello Universal Windows" }; - model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom }); - model.Axes.Add(new LinearAxis { Position = AxisPosition.Left }); - var lineSeries = new LineSeries { Title = "LineSeries", MarkerType = MarkerType.Circle }; - lineSeries.Points.Add(new DataPoint(0, 0)); - lineSeries.Points.Add(new DataPoint(10, 18)); - lineSeries.Points.Add(new DataPoint(20, 12)); - lineSeries.Points.Add(new DataPoint(30, 8)); - lineSeries.Points.Add(new DataPoint(40, 15)); - - model.Series.Add(lineSeries); - this.Model = model; - } - - public PlotModel Model { get; private set; } - } -} \ No newline at end of file diff --git a/Source/OxyPlot.Windows.sln b/Source/OxyPlot.Windows.sln deleted file mode 100644 index a26f024..0000000 --- a/Source/OxyPlot.Windows.sln +++ /dev/null @@ -1,117 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.29403.142 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Examples", "Examples", "{07CE67B3-8AA2-4572-865F-B3F7FA80CE19}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OxyPlot", "OxyPlot\OxyPlot.csproj", "{7A0B35C0-DD17-4964-8E9A-44D6CECDC692}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ExampleLibrary", "Examples\ExampleLibrary\ExampleLibrary.csproj", "{FACB89E5-53A5-4748-9F5B-E0714EBB37B2}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OxyPlot.Windows", "OxyPlot.Windows\OxyPlot.Windows.csproj", "{5AA87152-20D5-48F7-AD0F-6E6BAA61FC25}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimpleDemo", "Examples\Windows\SimpleDemo\SimpleDemo.csproj", "{FAC17BDF-4003-4359-A366-2E2F380E1BB5}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Debug|ARM = Debug|ARM - Debug|ARM64 = Debug|ARM64 - Debug|x64 = Debug|x64 - Debug|x86 = Debug|x86 - Release|Any CPU = Release|Any CPU - Release|ARM = Release|ARM - Release|ARM64 = Release|ARM64 - Release|x64 = Release|x64 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {7A0B35C0-DD17-4964-8E9A-44D6CECDC692}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7A0B35C0-DD17-4964-8E9A-44D6CECDC692}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7A0B35C0-DD17-4964-8E9A-44D6CECDC692}.Debug|ARM.ActiveCfg = Debug|Any CPU - {7A0B35C0-DD17-4964-8E9A-44D6CECDC692}.Debug|ARM64.ActiveCfg = Debug|Any CPU - {7A0B35C0-DD17-4964-8E9A-44D6CECDC692}.Debug|ARM64.Build.0 = Debug|Any CPU - {7A0B35C0-DD17-4964-8E9A-44D6CECDC692}.Debug|x64.ActiveCfg = Debug|Any CPU - {7A0B35C0-DD17-4964-8E9A-44D6CECDC692}.Debug|x86.ActiveCfg = Debug|Any CPU - {7A0B35C0-DD17-4964-8E9A-44D6CECDC692}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7A0B35C0-DD17-4964-8E9A-44D6CECDC692}.Release|Any CPU.Build.0 = Release|Any CPU - {7A0B35C0-DD17-4964-8E9A-44D6CECDC692}.Release|ARM.ActiveCfg = Release|Any CPU - {7A0B35C0-DD17-4964-8E9A-44D6CECDC692}.Release|ARM.Build.0 = Release|Any CPU - {7A0B35C0-DD17-4964-8E9A-44D6CECDC692}.Release|ARM64.ActiveCfg = Release|Any CPU - {7A0B35C0-DD17-4964-8E9A-44D6CECDC692}.Release|ARM64.Build.0 = Release|Any CPU - {7A0B35C0-DD17-4964-8E9A-44D6CECDC692}.Release|x64.ActiveCfg = Release|Any CPU - {7A0B35C0-DD17-4964-8E9A-44D6CECDC692}.Release|x64.Build.0 = Release|Any CPU - {7A0B35C0-DD17-4964-8E9A-44D6CECDC692}.Release|x86.ActiveCfg = Release|Any CPU - {7A0B35C0-DD17-4964-8E9A-44D6CECDC692}.Release|x86.Build.0 = Release|Any CPU - {FACB89E5-53A5-4748-9F5B-E0714EBB37B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {FACB89E5-53A5-4748-9F5B-E0714EBB37B2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {FACB89E5-53A5-4748-9F5B-E0714EBB37B2}.Debug|ARM.ActiveCfg = Debug|Any CPU - {FACB89E5-53A5-4748-9F5B-E0714EBB37B2}.Debug|ARM64.ActiveCfg = Debug|Any CPU - {FACB89E5-53A5-4748-9F5B-E0714EBB37B2}.Debug|ARM64.Build.0 = Debug|Any CPU - {FACB89E5-53A5-4748-9F5B-E0714EBB37B2}.Debug|x64.ActiveCfg = Debug|Any CPU - {FACB89E5-53A5-4748-9F5B-E0714EBB37B2}.Debug|x86.ActiveCfg = Debug|Any CPU - {FACB89E5-53A5-4748-9F5B-E0714EBB37B2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {FACB89E5-53A5-4748-9F5B-E0714EBB37B2}.Release|Any CPU.Build.0 = Release|Any CPU - {FACB89E5-53A5-4748-9F5B-E0714EBB37B2}.Release|ARM.ActiveCfg = Release|Any CPU - {FACB89E5-53A5-4748-9F5B-E0714EBB37B2}.Release|ARM.Build.0 = Release|Any CPU - {FACB89E5-53A5-4748-9F5B-E0714EBB37B2}.Release|ARM64.ActiveCfg = Release|Any CPU - {FACB89E5-53A5-4748-9F5B-E0714EBB37B2}.Release|ARM64.Build.0 = Release|Any CPU - {FACB89E5-53A5-4748-9F5B-E0714EBB37B2}.Release|x64.ActiveCfg = Release|Any CPU - {FACB89E5-53A5-4748-9F5B-E0714EBB37B2}.Release|x64.Build.0 = Release|Any CPU - {FACB89E5-53A5-4748-9F5B-E0714EBB37B2}.Release|x86.ActiveCfg = Release|Any CPU - {FACB89E5-53A5-4748-9F5B-E0714EBB37B2}.Release|x86.Build.0 = Release|Any CPU - {5AA87152-20D5-48F7-AD0F-6E6BAA61FC25}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {5AA87152-20D5-48F7-AD0F-6E6BAA61FC25}.Debug|Any CPU.Build.0 = Debug|Any CPU - {5AA87152-20D5-48F7-AD0F-6E6BAA61FC25}.Debug|ARM.ActiveCfg = Debug|Any CPU - {5AA87152-20D5-48F7-AD0F-6E6BAA61FC25}.Debug|ARM.Build.0 = Debug|Any CPU - {5AA87152-20D5-48F7-AD0F-6E6BAA61FC25}.Debug|ARM64.ActiveCfg = Debug|Any CPU - {5AA87152-20D5-48F7-AD0F-6E6BAA61FC25}.Debug|ARM64.Build.0 = Debug|Any CPU - {5AA87152-20D5-48F7-AD0F-6E6BAA61FC25}.Debug|x64.ActiveCfg = Debug|Any CPU - {5AA87152-20D5-48F7-AD0F-6E6BAA61FC25}.Debug|x64.Build.0 = Debug|Any CPU - {5AA87152-20D5-48F7-AD0F-6E6BAA61FC25}.Debug|x86.ActiveCfg = Debug|Any CPU - {5AA87152-20D5-48F7-AD0F-6E6BAA61FC25}.Debug|x86.Build.0 = Debug|Any CPU - {5AA87152-20D5-48F7-AD0F-6E6BAA61FC25}.Release|Any CPU.ActiveCfg = Release|Any CPU - {5AA87152-20D5-48F7-AD0F-6E6BAA61FC25}.Release|Any CPU.Build.0 = Release|Any CPU - {5AA87152-20D5-48F7-AD0F-6E6BAA61FC25}.Release|ARM.ActiveCfg = Release|Any CPU - {5AA87152-20D5-48F7-AD0F-6E6BAA61FC25}.Release|ARM.Build.0 = Release|Any CPU - {5AA87152-20D5-48F7-AD0F-6E6BAA61FC25}.Release|ARM64.ActiveCfg = Release|Any CPU - {5AA87152-20D5-48F7-AD0F-6E6BAA61FC25}.Release|ARM64.Build.0 = Release|Any CPU - {5AA87152-20D5-48F7-AD0F-6E6BAA61FC25}.Release|x64.ActiveCfg = Release|Any CPU - {5AA87152-20D5-48F7-AD0F-6E6BAA61FC25}.Release|x64.Build.0 = Release|Any CPU - {5AA87152-20D5-48F7-AD0F-6E6BAA61FC25}.Release|x86.ActiveCfg = Release|Any CPU - {5AA87152-20D5-48F7-AD0F-6E6BAA61FC25}.Release|x86.Build.0 = Release|Any CPU - {FAC17BDF-4003-4359-A366-2E2F380E1BB5}.Debug|Any CPU.ActiveCfg = Debug|x86 - {FAC17BDF-4003-4359-A366-2E2F380E1BB5}.Debug|ARM.ActiveCfg = Debug|ARM - {FAC17BDF-4003-4359-A366-2E2F380E1BB5}.Debug|ARM.Build.0 = Debug|ARM - {FAC17BDF-4003-4359-A366-2E2F380E1BB5}.Debug|ARM.Deploy.0 = Debug|ARM - {FAC17BDF-4003-4359-A366-2E2F380E1BB5}.Debug|ARM64.ActiveCfg = Debug|x86 - {FAC17BDF-4003-4359-A366-2E2F380E1BB5}.Debug|x64.ActiveCfg = Debug|x64 - {FAC17BDF-4003-4359-A366-2E2F380E1BB5}.Debug|x64.Build.0 = Debug|x64 - {FAC17BDF-4003-4359-A366-2E2F380E1BB5}.Debug|x64.Deploy.0 = Debug|x64 - {FAC17BDF-4003-4359-A366-2E2F380E1BB5}.Debug|x86.ActiveCfg = Debug|x86 - {FAC17BDF-4003-4359-A366-2E2F380E1BB5}.Debug|x86.Build.0 = Debug|x86 - {FAC17BDF-4003-4359-A366-2E2F380E1BB5}.Debug|x86.Deploy.0 = Debug|x86 - {FAC17BDF-4003-4359-A366-2E2F380E1BB5}.Release|Any CPU.ActiveCfg = Release|x86 - {FAC17BDF-4003-4359-A366-2E2F380E1BB5}.Release|ARM.ActiveCfg = Release|ARM - {FAC17BDF-4003-4359-A366-2E2F380E1BB5}.Release|ARM.Build.0 = Release|ARM - {FAC17BDF-4003-4359-A366-2E2F380E1BB5}.Release|ARM.Deploy.0 = Release|ARM - {FAC17BDF-4003-4359-A366-2E2F380E1BB5}.Release|ARM64.ActiveCfg = Release|x86 - {FAC17BDF-4003-4359-A366-2E2F380E1BB5}.Release|x64.ActiveCfg = Release|x64 - {FAC17BDF-4003-4359-A366-2E2F380E1BB5}.Release|x64.Build.0 = Release|x64 - {FAC17BDF-4003-4359-A366-2E2F380E1BB5}.Release|x64.Deploy.0 = Release|x64 - {FAC17BDF-4003-4359-A366-2E2F380E1BB5}.Release|x86.ActiveCfg = Release|x86 - {FAC17BDF-4003-4359-A366-2E2F380E1BB5}.Release|x86.Build.0 = Release|x86 - {FAC17BDF-4003-4359-A366-2E2F380E1BB5}.Release|x86.Deploy.0 = Release|x86 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {FACB89E5-53A5-4748-9F5B-E0714EBB37B2} = {07CE67B3-8AA2-4572-865F-B3F7FA80CE19} - {FAC17BDF-4003-4359-A366-2E2F380E1BB5} = {07CE67B3-8AA2-4572-865F-B3F7FA80CE19} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {183609A0-795E-4886-8165-1377A3A0B948} - EndGlobalSection -EndGlobal diff --git a/Source/OxyPlot.Windows/OxyPlot.Windows.csproj b/Source/OxyPlot.Windows/OxyPlot.Windows.csproj deleted file mode 100644 index ea0b5a3..0000000 --- a/Source/OxyPlot.Windows/OxyPlot.Windows.csproj +++ /dev/null @@ -1,21 +0,0 @@ - - - uap10.0 - OxyPlot is a plotting library for .NET. This is package contains the UWP implementation. - MIT - Copyright (c) 2014 OxyPlot contributors - https://oxyplot.github.io/ - OxyPlot_128.png - plotting plot charting chart - git - https://github.com/oxyplot/oxyplot.git - True - true - - - - - - - - diff --git a/Source/OxyPlot.Windows/OxyPlot.Windows.snk b/Source/OxyPlot.Windows/OxyPlot.Windows.snk deleted file mode 100644 index 1a8edea..0000000 Binary files a/Source/OxyPlot.Windows/OxyPlot.Windows.snk and /dev/null differ diff --git a/Source/OxyPlot.Windows/Properties/AssemblyInfo.cs b/Source/OxyPlot.Windows/Properties/AssemblyInfo.cs deleted file mode 100644 index d587aff..0000000 --- a/Source/OxyPlot.Windows/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("OxyPlot.Windows")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("OxyPlot.Windows")] -[assembly: AssemblyCopyright("Copyright © 2017")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] -[assembly: ComVisible(false)] \ No newline at end of file