diff --git a/README.md b/README.md index e3b05e4..b847dd3 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,55 @@ -# Building-Variance-Indicators-in-.NET-MAUI-Toolkit-Charts-to-Visualize-Natural-Gas-Price-Volatility - Discover techniques to visualize natural gas price volatility with dynamic variance indicators using .NET MAUI Toolkit Charts from Syncfusion. Learn how to implement custom arrow indicators with percentage labels by extending range column series to deliver comprehensive energy market trend visualization for better insights +# Visualizing Natural Gas Price Volatility with Custom Variance Indicators in .NET MAUI + +Discover how to build sophisticated price volatility visualizations using variance indicators with [Syncfusion's .NET MAUI Toolkit Charts](https://www.syncfusion.com/maui-controls/maui-cartesian-charts). + +## Overview + +Energy market analysis demands clear visualization of both absolute price levels and price volatility. This project demonstrates how to create custom variance indicators that show percentage changes between time periods with directional arrows and color-coding, enhancing standard column charts for more insightful analysis. + +The implementation leverages custom renderers in .NET MAUI Toolkit to create specialized variance indicators that visually represent price movement direction and magnitude. This approach is valuable for energy traders, market analysts, and financial professionals tracking commodity volatility. + +## Key Features + +### Advanced Variance Visualization + +- Display both price levels and percentage changes in a single view +- Highlight price increases and decreases with color-coded indicators +- Show precise percentage changes between consecutive time periods +- Indicate direction with custom arrow elements + +### Custom Chart Extensions + +- Extend [RangeColumnSeries](https://help.syncfusion.com/cr/maui-toolkit/Syncfusion.Maui.Toolkit.Charts.RangeColumnSeries.html) for specialized variance indicators +- Implement custom `RangeColumnSegmentExt` for dynamic rendering +- Calculate volatility metrics on the fly +- Position visual elements based on data characteristics + +### Interactive Data Analysis + +- Access detailed data through custom tooltips +- Compare price levels across multiple time periods +- Identify volatility patterns with intuitive visual cues +- Assess market stability through visual trend analysis + +## Technologies Used + +- **.NET MAUI:** Cross-platform UI framework +- **Syncfusion .NET MAUI Toolkit:** Advanced charting capabilities +- **Custom Renderers:** Enhanced visualization components +- **C# & XAML:** Implementation languages + +## Syncfusion Controls Used + +This project showcases the powerful customization of Syncfusion control: + +- **[SfCartesianChart](https://help.syncfusion.com/cr/maui-toolkit/Syncfusion.Maui.Toolkit.Charts.SfCartesianChart.html):** Foundation for price visualization + +![VarianceIndicators](https://github.com/user-attachments/assets/4930c677-6b33-4830-bc96-555def1157db) + +## Troubleshooting + +### Path Too Long Exception + +If you are facing a "Path too long" exception when building this example project, close Visual Studio and rename the repository to a shorter name before building the project. + +For a step-by-step procedure, refer to the blog: \ No newline at end of file diff --git a/VarianceIndicators/App.xaml b/VarianceIndicators/App.xaml new file mode 100644 index 0000000..1e4b1a6 --- /dev/null +++ b/VarianceIndicators/App.xaml @@ -0,0 +1,14 @@ + + + + + + + + + + + diff --git a/VarianceIndicators/App.xaml.cs b/VarianceIndicators/App.xaml.cs new file mode 100644 index 0000000..21a36a8 --- /dev/null +++ b/VarianceIndicators/App.xaml.cs @@ -0,0 +1,15 @@ +namespace VarianceIndicators +{ + public partial class App : Application + { + public App() + { + InitializeComponent(); + } + + protected override Window CreateWindow(IActivationState? activationState) + { + return new Window(new AppShell()); + } + } +} \ No newline at end of file diff --git a/VarianceIndicators/AppShell.xaml b/VarianceIndicators/AppShell.xaml new file mode 100644 index 0000000..2562959 --- /dev/null +++ b/VarianceIndicators/AppShell.xaml @@ -0,0 +1,13 @@ + + + + + + diff --git a/VarianceIndicators/AppShell.xaml.cs b/VarianceIndicators/AppShell.xaml.cs new file mode 100644 index 0000000..0d83599 --- /dev/null +++ b/VarianceIndicators/AppShell.xaml.cs @@ -0,0 +1,10 @@ +namespace VarianceIndicators +{ + public partial class AppShell : Shell + { + public AppShell() + { + InitializeComponent(); + } + } +} diff --git a/VarianceIndicators/CustomCharts/CustomCharts.cs b/VarianceIndicators/CustomCharts/CustomCharts.cs new file mode 100644 index 0000000..6a6268d --- /dev/null +++ b/VarianceIndicators/CustomCharts/CustomCharts.cs @@ -0,0 +1,110 @@ +using Syncfusion.Maui.Toolkit.Charts; +using Syncfusion.Maui.Toolkit.Graphics.Internals; +using System.Collections.ObjectModel; +using Font = Microsoft.Maui.Graphics.Font; + +namespace VarianceIndicators +{ + public partial class RangeColumnSeriesExt : RangeColumnSeries + { + protected override ChartSegment CreateSegment() + { + return new RangeColumnSegmentExt(); + } + } + + public partial class RangeColumnSegmentExt : RangeColumnSegment + { + float offsetPercentage = 0.2f; + float topArrowTextGap = 1.0f; + float bottomArrowTextGap = 1.0f; + + protected override void Draw(ICanvas canvas) + { + // Call base layout first + base.OnLayout(); + + // Skip drawing if segment isn't properly laid out or data is missing + if (float.IsNaN(Left) || float.IsNaN(Top) || float.IsNaN(Right) || float.IsNaN(Bottom) || + Series is not RangeColumnSeriesExt || Item is not ChartDataModel dataPoint) + return; + + // Skip last segment or if there's no change + var itemsSource = Series.ItemsSource as ObservableCollection; + + if (Index == itemsSource?.Count - 1 || dataPoint.Low == dataPoint.High) + return; + + // Calculate change direction and percentage + bool isIncreasing = dataPoint.Low < dataPoint.High; + double changePercent = Math.Abs((dataPoint.High - dataPoint.Low) / dataPoint.Low) * 100; + + // Set color based on direction + Color arrowColor = isIncreasing ? Colors.Green : Colors.Red; + + // Ensure we have a valid drawing canvas + if (canvas == null) return; + + // Save canvas state before drawing + canvas.SaveState(); + + float arrowSize = 6; + +#if IOS + offsetPercentage = 0.1f; + topArrowTextGap = 2.0f; +#elif MACCATALYST + topArrowTextGap = 2.0f; +#elif WINDOWS + topArrowTextGap = 3.0f; +#elif ANDROID + bottomArrowTextGap = 1.5f; +#endif + + float columnWidth = Right - Left; + float offsetX = columnWidth * offsetPercentage; + float centerX = (Left + Right) / 2 + offsetX; + + // Draw line first + canvas.StrokeColor = Colors.Black; + canvas.StrokeSize = 1f; + canvas.DrawLine(centerX, Top, centerX, Bottom); + + // Draw arrow + var arrowPath = new PathF(); + + // Draw arrow based on direction + if (isIncreasing) + { + arrowPath.MoveTo(centerX, Top); + arrowPath.LineTo(centerX - arrowSize, Top + arrowSize); + arrowPath.LineTo(centerX + arrowSize, Top + arrowSize); + } + else + { + arrowPath.MoveTo(centerX, Bottom); + arrowPath.LineTo(centerX - arrowSize, Bottom - arrowSize); + arrowPath.LineTo(centerX + arrowSize, Bottom - arrowSize); + } + + arrowPath.Close(); + canvas.FillColor = Colors.Black; + canvas.FillPath(arrowPath); + + // Draw percentage text with clear formatting + string percentText = isIncreasing ? + $"{changePercent:0}%" : + $"- {changePercent:0}%"; + + var labelStyle = new ChartAxisLabelStyle() { FontSize = 12, TextColor = arrowColor }; + var font = Font.Default; + + // Measure text size using canvas directly + SizeF textSize = canvas.GetStringSize(percentText, font, (float)labelStyle.FontSize); + float textY = (float)(isIncreasing ? Top - (textSize.Height * topArrowTextGap) : Bottom + (textSize.Height * bottomArrowTextGap)); + + canvas.DrawText(percentText, centerX - (textSize.Width / 2), textY, labelStyle); + canvas.RestoreState(); + } + } +} diff --git a/VarianceIndicators/MainPage.xaml b/VarianceIndicators/MainPage.xaml new file mode 100644 index 0000000..49c119a --- /dev/null +++ b/VarianceIndicators/MainPage.xaml @@ -0,0 +1,120 @@ + + + + + + + + + M24,17.000001L24,26.000002 27,26.000002 27,17.000001z M22,15.000001L29,15.000001 29,28.000002 22,28.000002z M6,14.000001L6,26.000002 9,26.000002 9,14.000001z M4,12.000001L11,12.000001 11,28.000002 4,28.000002z M15,4.0000005L15,26 18,26 18,4.0000005z M13,2.0000005L20,2.0000005 20,28 13,28z M0,0L2,0 2,30 32,30 32,32 0,32z + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/VarianceIndicators/MainPage.xaml.cs b/VarianceIndicators/MainPage.xaml.cs new file mode 100644 index 0000000..1406ef4 --- /dev/null +++ b/VarianceIndicators/MainPage.xaml.cs @@ -0,0 +1,11 @@ + +namespace VarianceIndicators +{ + public partial class MainPage : ContentPage + { + public MainPage() + { + InitializeComponent(); + } + } +} diff --git a/VarianceIndicators/MauiProgram.cs b/VarianceIndicators/MauiProgram.cs new file mode 100644 index 0000000..e84cced --- /dev/null +++ b/VarianceIndicators/MauiProgram.cs @@ -0,0 +1,27 @@ +using Microsoft.Extensions.Logging; +using Syncfusion.Maui.Toolkit.Hosting; + +namespace VarianceIndicators +{ + public static class MauiProgram + { + public static MauiApp CreateMauiApp() + { + var builder = MauiApp.CreateBuilder(); + builder + .UseMauiApp() + .ConfigureSyncfusionToolkit() + .ConfigureFonts(fonts => + { + fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular"); + fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold"); + }); + +#if DEBUG + builder.Logging.AddDebug(); +#endif + + return builder.Build(); + } + } +} diff --git a/VarianceIndicators/Model/ChartDataModel.cs b/VarianceIndicators/Model/ChartDataModel.cs new file mode 100644 index 0000000..755d4f9 --- /dev/null +++ b/VarianceIndicators/Model/ChartDataModel.cs @@ -0,0 +1,10 @@ +namespace VarianceIndicators +{ + public class ChartDataModel + { + public DateTime Year { get; set; } + public double YValue { get; set; } + public double High { get; set; } + public double Low { get; set; } + } +} diff --git a/VarianceIndicators/Platforms/Android/AndroidManifest.xml b/VarianceIndicators/Platforms/Android/AndroidManifest.xml new file mode 100644 index 0000000..e9937ad --- /dev/null +++ b/VarianceIndicators/Platforms/Android/AndroidManifest.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/VarianceIndicators/Platforms/Android/MainActivity.cs b/VarianceIndicators/Platforms/Android/MainActivity.cs new file mode 100644 index 0000000..8280af3 --- /dev/null +++ b/VarianceIndicators/Platforms/Android/MainActivity.cs @@ -0,0 +1,11 @@ +using Android.App; +using Android.Content.PM; +using Android.OS; + +namespace VarianceIndicators +{ + [Activity(Theme = "@style/Maui.SplashTheme", MainLauncher = true,ScreenOrientation = ScreenOrientation.Landscape, LaunchMode = LaunchMode.SingleTop, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize | ConfigChanges.Density)] + public class MainActivity : MauiAppCompatActivity + { + } +} diff --git a/VarianceIndicators/Platforms/Android/MainApplication.cs b/VarianceIndicators/Platforms/Android/MainApplication.cs new file mode 100644 index 0000000..1f4ba89 --- /dev/null +++ b/VarianceIndicators/Platforms/Android/MainApplication.cs @@ -0,0 +1,16 @@ +using Android.App; +using Android.Runtime; + +namespace VarianceIndicators +{ + [Application] + public class MainApplication : MauiApplication + { + public MainApplication(IntPtr handle, JniHandleOwnership ownership) + : base(handle, ownership) + { + } + + protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp(); + } +} diff --git a/VarianceIndicators/Platforms/Android/Resources/values/colors.xml b/VarianceIndicators/Platforms/Android/Resources/values/colors.xml new file mode 100644 index 0000000..c04d749 --- /dev/null +++ b/VarianceIndicators/Platforms/Android/Resources/values/colors.xml @@ -0,0 +1,6 @@ + + + #512BD4 + #2B0B98 + #2B0B98 + \ No newline at end of file diff --git a/VarianceIndicators/Platforms/MacCatalyst/AppDelegate.cs b/VarianceIndicators/Platforms/MacCatalyst/AppDelegate.cs new file mode 100644 index 0000000..1584c9d --- /dev/null +++ b/VarianceIndicators/Platforms/MacCatalyst/AppDelegate.cs @@ -0,0 +1,10 @@ +using Foundation; + +namespace VarianceIndicators +{ + [Register("AppDelegate")] + public class AppDelegate : MauiUIApplicationDelegate + { + protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp(); + } +} diff --git a/VarianceIndicators/Platforms/MacCatalyst/Entitlements.plist b/VarianceIndicators/Platforms/MacCatalyst/Entitlements.plist new file mode 100644 index 0000000..de4adc9 --- /dev/null +++ b/VarianceIndicators/Platforms/MacCatalyst/Entitlements.plist @@ -0,0 +1,14 @@ + + + + + + + com.apple.security.app-sandbox + + + com.apple.security.network.client + + + + diff --git a/VarianceIndicators/Platforms/MacCatalyst/Info.plist b/VarianceIndicators/Platforms/MacCatalyst/Info.plist new file mode 100644 index 0000000..7268977 --- /dev/null +++ b/VarianceIndicators/Platforms/MacCatalyst/Info.plist @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + UIDeviceFamily + + 2 + + UIRequiredDeviceCapabilities + + arm64 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + XSAppIconAssets + Assets.xcassets/appicon.appiconset + + diff --git a/VarianceIndicators/Platforms/MacCatalyst/Program.cs b/VarianceIndicators/Platforms/MacCatalyst/Program.cs new file mode 100644 index 0000000..1f78dc9 --- /dev/null +++ b/VarianceIndicators/Platforms/MacCatalyst/Program.cs @@ -0,0 +1,16 @@ +using ObjCRuntime; +using UIKit; + +namespace VarianceIndicators +{ + public class Program + { + // This is the main entry point of the application. + static void Main(string[] args) + { + // if you want to use a different Application Delegate class from "AppDelegate" + // you can specify it here. + UIApplication.Main(args, null, typeof(AppDelegate)); + } + } +} diff --git a/VarianceIndicators/Platforms/Tizen/Main.cs b/VarianceIndicators/Platforms/Tizen/Main.cs new file mode 100644 index 0000000..c437b9b --- /dev/null +++ b/VarianceIndicators/Platforms/Tizen/Main.cs @@ -0,0 +1,17 @@ +using System; +using Microsoft.Maui; +using Microsoft.Maui.Hosting; + +namespace VarianceIndicators +{ + internal class Program : MauiApplication + { + protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp(); + + static void Main(string[] args) + { + var app = new Program(); + app.Run(args); + } + } +} diff --git a/VarianceIndicators/Platforms/Tizen/tizen-manifest.xml b/VarianceIndicators/Platforms/Tizen/tizen-manifest.xml new file mode 100644 index 0000000..cadcbe9 --- /dev/null +++ b/VarianceIndicators/Platforms/Tizen/tizen-manifest.xml @@ -0,0 +1,15 @@ + + + + + + maui-appicon-placeholder + + + + + http://tizen.org/privilege/internet + + + + \ No newline at end of file diff --git a/VarianceIndicators/Platforms/Windows/App.xaml b/VarianceIndicators/Platforms/Windows/App.xaml new file mode 100644 index 0000000..dca3a01 --- /dev/null +++ b/VarianceIndicators/Platforms/Windows/App.xaml @@ -0,0 +1,8 @@ + + + diff --git a/VarianceIndicators/Platforms/Windows/App.xaml.cs b/VarianceIndicators/Platforms/Windows/App.xaml.cs new file mode 100644 index 0000000..89c6ab9 --- /dev/null +++ b/VarianceIndicators/Platforms/Windows/App.xaml.cs @@ -0,0 +1,25 @@ +using Microsoft.UI.Xaml; + +// To learn more about WinUI, the WinUI project structure, +// and more about our project templates, see: http://aka.ms/winui-project-info. + +namespace VarianceIndicators.WinUI +{ + /// + /// Provides application-specific behavior to supplement the default Application class. + /// + public partial class App : MauiWinUIApplication + { + /// + /// Initializes the singleton application object. This is the first line of authored code + /// executed, and as such is the logical equivalent of main() or WinMain(). + /// + public App() + { + this.InitializeComponent(); + } + + protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp(); + } + +} diff --git a/VarianceIndicators/Platforms/Windows/Package.appxmanifest b/VarianceIndicators/Platforms/Windows/Package.appxmanifest new file mode 100644 index 0000000..905b059 --- /dev/null +++ b/VarianceIndicators/Platforms/Windows/Package.appxmanifest @@ -0,0 +1,46 @@ + + + + + + + + + $placeholder$ + User Name + $placeholder$.png + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/VarianceIndicators/Platforms/Windows/app.manifest b/VarianceIndicators/Platforms/Windows/app.manifest new file mode 100644 index 0000000..5db589e --- /dev/null +++ b/VarianceIndicators/Platforms/Windows/app.manifest @@ -0,0 +1,15 @@ + + + + + + + + true/PM + PerMonitorV2, PerMonitor + + + diff --git a/VarianceIndicators/Platforms/iOS/AppDelegate.cs b/VarianceIndicators/Platforms/iOS/AppDelegate.cs new file mode 100644 index 0000000..1584c9d --- /dev/null +++ b/VarianceIndicators/Platforms/iOS/AppDelegate.cs @@ -0,0 +1,10 @@ +using Foundation; + +namespace VarianceIndicators +{ + [Register("AppDelegate")] + public class AppDelegate : MauiUIApplicationDelegate + { + protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp(); + } +} diff --git a/VarianceIndicators/Platforms/iOS/Info.plist b/VarianceIndicators/Platforms/iOS/Info.plist new file mode 100644 index 0000000..0e89139 --- /dev/null +++ b/VarianceIndicators/Platforms/iOS/Info.plist @@ -0,0 +1,31 @@ + + + + + LSRequiresIPhoneOS + + UIDeviceFamily + + 1 + 2 + + UIRequiredDeviceCapabilities + + arm64 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + XSAppIconAssets + Assets.xcassets/appicon.appiconset + + diff --git a/VarianceIndicators/Platforms/iOS/Program.cs b/VarianceIndicators/Platforms/iOS/Program.cs new file mode 100644 index 0000000..1f78dc9 --- /dev/null +++ b/VarianceIndicators/Platforms/iOS/Program.cs @@ -0,0 +1,16 @@ +using ObjCRuntime; +using UIKit; + +namespace VarianceIndicators +{ + public class Program + { + // This is the main entry point of the application. + static void Main(string[] args) + { + // if you want to use a different Application Delegate class from "AppDelegate" + // you can specify it here. + UIApplication.Main(args, null, typeof(AppDelegate)); + } + } +} diff --git a/VarianceIndicators/Platforms/iOS/Resources/PrivacyInfo.xcprivacy b/VarianceIndicators/Platforms/iOS/Resources/PrivacyInfo.xcprivacy new file mode 100644 index 0000000..24ab3b4 --- /dev/null +++ b/VarianceIndicators/Platforms/iOS/Resources/PrivacyInfo.xcprivacy @@ -0,0 +1,51 @@ + + + + + + NSPrivacyAccessedAPITypes + + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategoryFileTimestamp + NSPrivacyAccessedAPITypeReasons + + C617.1 + + + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategorySystemBootTime + NSPrivacyAccessedAPITypeReasons + + 35F9.1 + + + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategoryDiskSpace + NSPrivacyAccessedAPITypeReasons + + E174.1 + + + + + + diff --git a/VarianceIndicators/Properties/launchSettings.json b/VarianceIndicators/Properties/launchSettings.json new file mode 100644 index 0000000..4f85793 --- /dev/null +++ b/VarianceIndicators/Properties/launchSettings.json @@ -0,0 +1,8 @@ +{ + "profiles": { + "Windows Machine": { + "commandName": "Project", + "nativeDebugging": false + } + } +} \ No newline at end of file diff --git a/VarianceIndicators/Resources/AppIcon/appicon.svg b/VarianceIndicators/Resources/AppIcon/appicon.svg new file mode 100644 index 0000000..9d63b65 --- /dev/null +++ b/VarianceIndicators/Resources/AppIcon/appicon.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/VarianceIndicators/Resources/AppIcon/appiconfg.svg b/VarianceIndicators/Resources/AppIcon/appiconfg.svg new file mode 100644 index 0000000..21dfb25 --- /dev/null +++ b/VarianceIndicators/Resources/AppIcon/appiconfg.svg @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/VarianceIndicators/Resources/Fonts/OpenSans-Regular.ttf b/VarianceIndicators/Resources/Fonts/OpenSans-Regular.ttf new file mode 100644 index 0000000..33b3e0d Binary files /dev/null and b/VarianceIndicators/Resources/Fonts/OpenSans-Regular.ttf differ diff --git a/VarianceIndicators/Resources/Fonts/OpenSans-Semibold.ttf b/VarianceIndicators/Resources/Fonts/OpenSans-Semibold.ttf new file mode 100644 index 0000000..a1f8571 Binary files /dev/null and b/VarianceIndicators/Resources/Fonts/OpenSans-Semibold.ttf differ diff --git a/VarianceIndicators/Resources/Images/dotnet_bot.png b/VarianceIndicators/Resources/Images/dotnet_bot.png new file mode 100644 index 0000000..1d1b981 Binary files /dev/null and b/VarianceIndicators/Resources/Images/dotnet_bot.png differ diff --git a/VarianceIndicators/Resources/Raw/AboutAssets.txt b/VarianceIndicators/Resources/Raw/AboutAssets.txt new file mode 100644 index 0000000..89dc758 --- /dev/null +++ b/VarianceIndicators/Resources/Raw/AboutAssets.txt @@ -0,0 +1,15 @@ +Any raw assets you want to be deployed with your application can be placed in +this directory (and child directories). Deployment of the asset to your application +is automatically handled by the following `MauiAsset` Build Action within your `.csproj`. + + + +These files will be deployed with your package and will be accessible using Essentials: + + async Task LoadMauiAsset() + { + using var stream = await FileSystem.OpenAppPackageFileAsync("AboutAssets.txt"); + using var reader = new StreamReader(stream); + + var contents = reader.ReadToEnd(); + } diff --git a/VarianceIndicators/Resources/Splash/splash.svg b/VarianceIndicators/Resources/Splash/splash.svg new file mode 100644 index 0000000..21dfb25 --- /dev/null +++ b/VarianceIndicators/Resources/Splash/splash.svg @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/VarianceIndicators/Resources/Styles/Colors.xaml b/VarianceIndicators/Resources/Styles/Colors.xaml new file mode 100644 index 0000000..30307a5 --- /dev/null +++ b/VarianceIndicators/Resources/Styles/Colors.xaml @@ -0,0 +1,45 @@ + + + + + + + #512BD4 + #ac99ea + #242424 + #DFD8F7 + #9880e5 + #2B0B98 + + White + Black + #D600AA + #190649 + #1f1f1f + + #E1E1E1 + #C8C8C8 + #ACACAC + #919191 + #6E6E6E + #404040 + #212121 + #141414 + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/VarianceIndicators/Resources/Styles/Styles.xaml b/VarianceIndicators/Resources/Styles/Styles.xaml new file mode 100644 index 0000000..86f574d --- /dev/null +++ b/VarianceIndicators/Resources/Styles/Styles.xaml @@ -0,0 +1,451 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/VarianceIndicators/VarianceIndicators.csproj b/VarianceIndicators/VarianceIndicators.csproj new file mode 100644 index 0000000..e630f37 --- /dev/null +++ b/VarianceIndicators/VarianceIndicators.csproj @@ -0,0 +1,68 @@ + + + + net9.0-android;net9.0-ios;net9.0-maccatalyst + $(TargetFrameworks);net9.0-windows10.0.19041.0 + + + + + + + Exe + VarianceIndicators + true + true + enable + enable + + + VarianceIndicators + + + com.companyname.varianceindicators + + + 1.0 + 1 + + + None + + 15.0 + 15.0 + 21.0 + 10.0.17763.0 + 10.0.17763.0 + 6.5 + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/VarianceIndicators/VarianceIndicators.sln b/VarianceIndicators/VarianceIndicators.sln new file mode 100644 index 0000000..455674b --- /dev/null +++ b/VarianceIndicators/VarianceIndicators.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.14.36221.1 d17.14 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VarianceIndicators", "VarianceIndicators.csproj", "{1CFB3319-66D3-4CFE-921E-A003331A8866}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {1CFB3319-66D3-4CFE-921E-A003331A8866}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1CFB3319-66D3-4CFE-921E-A003331A8866}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1CFB3319-66D3-4CFE-921E-A003331A8866}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1CFB3319-66D3-4CFE-921E-A003331A8866}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {2F1D96E3-F7A4-44EC-9973-A0CADA6FFF89} + EndGlobalSection +EndGlobal diff --git a/VarianceIndicators/ViewModel/ChartViewModel.cs b/VarianceIndicators/ViewModel/ChartViewModel.cs new file mode 100644 index 0000000..aa1a562 --- /dev/null +++ b/VarianceIndicators/ViewModel/ChartViewModel.cs @@ -0,0 +1,54 @@ + +using System.Collections.ObjectModel; + +namespace VarianceIndicators +{ + public class ChartViewModel + { + public ObservableCollection Data { get; set; } + + public ChartViewModel() + { + // Create the data collection + Data = new ObservableCollection(); + + AddDataPoint(new DateTime(2014, 1, 1), 4.71); + AddDataPoint(new DateTime(2016, 1, 1), 2.28); + AddDataPoint(new DateTime(2018, 1, 1), 3.87); + AddDataPoint(new DateTime(2020, 1, 1), 2.02); + AddDataPoint(new DateTime(2022, 1, 1), 4.38); + AddDataPoint(new DateTime(2024, 1, 1), 3.18); + + // Process data to calculate variance between points + ProcessData(); + } + + private void AddDataPoint(DateTime year, double value) + { + Data.Add(new ChartDataModel + { + Year = year, + YValue = value, + // Initialize to same value - will be updated later + Low = value, + High = value + }); + } + + private void ProcessData() + { + // Skip processing if we have 0 or 1 data points + if (Data.Count <= 1) return; + + // For each point (except last), set high value to next point's value + for (int i = 0; i < Data.Count - 1; i++) + { + Data[i].Low = Data[i].YValue; + Data[i].High = Data[i + 1].YValue; + } + } + } + +} + +