├── .gitignore
├── Images
├── BizUnity.jpeg
├── StockPlot3.gif
├── StockPlot5.gif
├── StockPlot6.gif
├── ouinex.png
└── stockplot4.gif
├── README.md
├── StockPlot.Charts
├── Controls
│ ├── AvaPlot.axaml
│ ├── AvaPlot.axaml.cs
│ ├── PropertyGrid.axaml
│ ├── PropertyGrid.axaml.cs
│ ├── StockChart.axaml
│ ├── StockChart.axaml.cs
│ ├── SubIndicator.axaml
│ └── SubIndicator.axaml.cs
├── Delegates.cs
├── Drawings
│ ├── HorizontalLine.cs
│ ├── TrendLine.cs
│ └── VerticalLine.cs
├── Enums.cs
├── Helpers
│ ├── ColorHelper.cs
│ ├── CrossHairHelper.cs
│ ├── IndicatorsHelper.cs
│ └── PlotHelper.cs
├── Models
│ ├── DrawingManager.cs
│ ├── IndicatorItemManager.cs
│ ├── IndicatorsManager.cs
│ └── StockPricesModel.cs
├── OverideSeries
│ ├── OScatterPlot.cs
│ └── OScatterPlotDraggable.cs
└── StockPlot.Charts.csproj
├── StockPlot.Indicators
├── Enums.cs
├── Fill.cs
├── Formulas.cs
├── IndicatorBase.cs
├── IndicatorLevel.cs
├── IndicatorParameter.cs
├── Indicators
│ ├── ATR.cs
│ ├── ATRStop.cs
│ ├── BollingerBands.cs
│ ├── CCI.cs
│ ├── Donchian.cs
│ ├── EMA.cs
│ ├── Ichimoku.cs
│ ├── MACD.cs
│ ├── RagheeWave.cs
│ └── Stochastic.cs
├── IndicatorsList.cs
├── StockPlot.Indicators.csproj
├── XYSerie.cs
└── XYYSerie.cs
├── StockPlot.sln
└── StockPlot
├── App.axaml
├── App.axaml.cs
├── MainWindow.axaml
├── MainWindow.axaml.cs
├── Program.cs
├── StockPlot.csproj
└── app.manifest
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | *.suo
6 | *.user
7 | *.userosscache
8 | *.sln.docstates
9 |
10 | # User-specific files (MonoDevelop/Xamarin Studio)
11 | *.userprefs
12 |
13 | # Build results
14 | [Dd]ebug/
15 | [Dd]ebugPublic/
16 | [Rr]elease/
17 | [Rr]eleases/
18 | x64/
19 | x86/
20 | bld/
21 | [Bb]in/
22 | [Oo]bj/
23 |
24 | # Visual Studio 2015 cache/options directory
25 | .vs/
26 | # Uncomment if you have tasks that create the project's static files in wwwroot
27 | #wwwroot/
28 |
29 | # MSTest test Results
30 | [Tt]est[Rr]esult*/
31 | [Bb]uild[Ll]og.*
32 |
33 | # NUNIT
34 | *.VisualState.xml
35 | TestResult.xml
36 |
37 | # Build Results of an ATL Project
38 | [Dd]ebugPS/
39 | [Rr]eleasePS/
40 | dlldata.c
41 |
42 | # DNX
43 | project.lock.json
44 | artifacts/
45 |
46 | *_i.c
47 | *_p.c
48 | *_i.h
49 | *.ilk
50 | *.meta
51 | *.obj
52 | *.pch
53 | *.pdb
54 | *.pgc
55 | *.pgd
56 | *.rsp
57 | *.sbr
58 | *.tlb
59 | *.tli
60 | *.tlh
61 | *.tmp
62 | *.tmp_proj
63 | *.log
64 | *.vspscc
65 | *.vssscc
66 | .builds
67 | *.pidb
68 | *.svclog
69 | *.scc
70 |
71 | # Chutzpah Test files
72 | _Chutzpah*
73 |
74 | # Visual C++ cache files
75 | ipch/
76 | *.aps
77 | *.ncb
78 | *.opendb
79 | *.opensdf
80 | *.sdf
81 | *.cachefile
82 |
83 | # Visual Studio profiler
84 | *.psess
85 | *.vsp
86 | *.vspx
87 | *.sap
88 |
89 | # TFS 2012 Local Workspace
90 | $tf/
91 |
92 | # Guidance Automation Toolkit
93 | *.gpState
94 |
95 | # ReSharper is a .NET coding add-in
96 | _ReSharper*/
97 | *.[Rr]e[Ss]harper
98 | *.DotSettings.user
99 |
100 | # JustCode is a .NET coding add-in
101 | .JustCode
102 |
103 | # TeamCity is a build add-in
104 | _TeamCity*
105 |
106 | # DotCover is a Code Coverage Tool
107 | *.dotCover
108 |
109 | # NCrunch
110 | _NCrunch_*
111 | .*crunch*.local.xml
112 | nCrunchTemp_*
113 |
114 | # MightyMoose
115 | *.mm.*
116 | AutoTest.Net/
117 |
118 | # Web workbench (sass)
119 | .sass-cache/
120 |
121 | # Installshield output folder
122 | [Ee]xpress/
123 |
124 | # DocProject is a documentation generator add-in
125 | DocProject/buildhelp/
126 | DocProject/Help/*.HxT
127 | DocProject/Help/*.HxC
128 | DocProject/Help/*.hhc
129 | DocProject/Help/*.hhk
130 | DocProject/Help/*.hhp
131 | DocProject/Help/Html2
132 | DocProject/Help/html
133 |
134 | # Click-Once directory
135 | publish/
136 |
137 | # Publish Web Output
138 | *.[Pp]ublish.xml
139 | *.azurePubxml
140 | # TODO: Comment the next line if you want to checkin your web deploy settings
141 | # but database connection strings (with potential passwords) will be unencrypted
142 | *.pubxml
143 | *.publishproj
144 |
145 | # NuGet Packages
146 | *.nupkg
147 | # The packages folder can be ignored because of Package Restore
148 | **/packages/*
149 | # except build/, which is used as an MSBuild target.
150 | !**/packages/build/
151 | # Uncomment if necessary however generally it will be regenerated when needed
152 | #!**/packages/repositories.config
153 | # NuGet v3's project.json files produces more ignoreable files
154 | *.nuget.props
155 | *.nuget.targets
156 |
157 | # Microsoft Azure Build Output
158 | csx/
159 | *.build.csdef
160 |
161 | # Microsoft Azure Emulator
162 | ecf/
163 | rcf/
164 |
165 | # Microsoft Azure ApplicationInsights config file
166 | ApplicationInsights.config
167 |
168 | # Windows Store app package directory
169 | AppPackages/
170 | BundleArtifacts/
171 |
172 | # Visual Studio cache files
173 | # files ending in .cache can be ignored
174 | *.[Cc]ache
175 | # but keep track of directories ending in .cache
176 | !*.[Cc]ache/
177 |
178 | # Others
179 | ClientBin/
180 | ~$*
181 | *~
182 | *.dbmdl
183 | *.dbproj.schemaview
184 | *.pfx
185 | *.publishsettings
186 | node_modules/
187 | orleans.codegen.cs
188 |
189 | # RIA/Silverlight projects
190 | Generated_Code/
191 |
192 | # Backup & report files from converting an old project file
193 | # to a newer Visual Studio version. Backup files are not needed,
194 | # because we have git ;-)
195 | _UpgradeReport_Files/
196 | Backup*/
197 | UpgradeLog*.XML
198 | UpgradeLog*.htm
199 |
200 | # SQL Server files
201 | *.mdf
202 | *.ldf
203 |
204 | # Business Intelligence projects
205 | *.rdl.data
206 | *.bim.layout
207 | *.bim_*.settings
208 |
209 | # Microsoft Fakes
210 | FakesAssemblies/
211 |
212 | # GhostDoc plugin setting file
213 | *.GhostDoc.xml
214 |
215 | # Node.js Tools for Visual Studio
216 | .ntvs_analysis.dat
217 |
218 | # Visual Studio 6 build log
219 | *.plg
220 |
221 | # Visual Studio 6 workspace options file
222 | *.opt
223 |
224 | # Visual Studio LightSwitch build output
225 | **/*.HTMLClient/GeneratedArtifacts
226 | **/*.DesktopClient/GeneratedArtifacts
227 | **/*.DesktopClient/ModelManifest.xml
228 | **/*.Server/GeneratedArtifacts
229 | **/*.Server/ModelManifest.xml
230 | _Pvt_Extensions
231 |
232 | # Paket dependency manager
233 | .paket/paket.exe
234 |
235 | # FAKE - F# Make
236 | .fake/
237 | /desktop.ini
238 | FxcmPriceHistory
239 | Avatars
240 | Tickets
241 | Temp
242 |
--------------------------------------------------------------------------------
/Images/BizUnity.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BizUnity/StockPlot/39fcde4ebbd6eaf646b03a0914902388f98f621c/Images/BizUnity.jpeg
--------------------------------------------------------------------------------
/Images/StockPlot3.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BizUnity/StockPlot/39fcde4ebbd6eaf646b03a0914902388f98f621c/Images/StockPlot3.gif
--------------------------------------------------------------------------------
/Images/StockPlot5.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BizUnity/StockPlot/39fcde4ebbd6eaf646b03a0914902388f98f621c/Images/StockPlot5.gif
--------------------------------------------------------------------------------
/Images/StockPlot6.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BizUnity/StockPlot/39fcde4ebbd6eaf646b03a0914902388f98f621c/Images/StockPlot6.gif
--------------------------------------------------------------------------------
/Images/ouinex.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BizUnity/StockPlot/39fcde4ebbd6eaf646b03a0914902388f98f621c/Images/ouinex.png
--------------------------------------------------------------------------------
/Images/stockplot4.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BizUnity/StockPlot/39fcde4ebbd6eaf646b03a0914902388f98f621c/Images/stockplot4.gif
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | An initiative of [Ouinex Exchange](http://ouinex.com/ "Ouinex Exchange") and [BizUnity](https://www.linkedin.com/company/bizunity/ "BizUnity")
3 |
4 | 
5 |
6 | 
7 |
8 | # StockPlot
9 | 
10 |
11 | A Technical analysis library for [AvaloniaUI](https://avaloniaui.net/ "AvaloniaUI"), based on [ScottPlot](https://scottplot.net/ "ScottPlot") DataVisualization Library (v 4.1.63).
12 |
13 | StockPlot *(will)* allow you to have a full Stock Market Analysis module in your application only by using a single UserControl and a single class.
14 |
15 | StockPlot are in a very early stage (started in April 2023). A lot of features need to be created before deploying a proper and working Nuget Package.
16 | Some refactoring may need to be done a couple of time, **do not use it in a production yet. **
17 | Play around, do not hesitate to contribute <3 .
18 |
19 |
20 |
21 | ## Current features
22 | + Display the price using Candlesticks or Bars (OHLC) with a full live datas update support :
23 | + Update the last bar (OHLC).
24 | + Add a new bar.
25 | + Automaticaly update the indicator(s) on price updating.
26 | + Working using Background dispatcher.
27 | + Display studies.
28 | + Add indicator on price.
29 | + Add sub indicator.
30 | + Auto hide or show the X axis. (Only the last Plot X axis need to be shown).
31 | + Auto link the X axis with sub indicators and main price area.
32 | + Shared cross hair between price area and sub charts.
33 | + Display & modify properties of an indicator.
34 |
35 |
36 | ## Features planned
37 |
38 | + ##### Price displaying
39 | + Allowing the user to choose the price type (candle, OHLC or line).
40 | + Add the line type chart.
41 | + Allowing the user to customize the charts and price colors.
42 | + ##### Indicators :
43 | + Ability to change the parametters of an indicator.
44 | + Ability to change the visual parametter of an indicator (eg : line style, line color, ...).
45 | + For both of those previous point, we need to create and add a PropertyGrid Control.
46 | + Display the list of working indicators on price.
47 | + ##### Drawing tools
48 | + Add an horizontal line on a specified price (Y Axis).
49 | + Add a vertical line on a specified time (X Axis).
50 | + Add a limited line (X1,Y1,X2,Y2).
51 | + Add Fibonacci retracement.
52 | + Add Andrew Pitchfork.
53 | + ...
54 | + #### Presets
55 | + Save and load a preset of indicators.
56 | + Save and load a preset of draws.
57 |
58 | # How to use
59 | 1) From you IDE, add the reference to StockPlot.Charts.
60 | This library contains all the controls and logics.
61 |
62 | 2) In your Window or UserControl add the reference to StockPlot.Charts :
63 | `xmlns:stockPlot="using:StockPlot.Charts.Controls"`
64 |
65 | 3) Add the StockChart control in your axaml code :
66 | ```xml
67 |
73 | ```
74 | 4) In your C# code, find the StockChart control :
75 | ```csharp
76 | StockChart _chart = this.Find("StockChart");
77 | ```
78 | 5) Create a new StockPricesModel and provide the StockChart control with it :
79 | ```csharp
80 | var model = new StockPricesModel();
81 | _chart.PricesModel = model;
82 | ```
83 | 6) FullFill the model with datas. For this exemple we will use Binance API using [Binance.NET](https://github.com/JKorf/Binance.Net "Binance.NET") library.
84 | With this exemple, price is working in live using WebSocket.
85 | ```csharp
86 | var client = new BinanceClient();
87 |
88 | var request = await client.SpotApi.ExchangeData.GetUiKlinesAsync("BTCUSDT", Binance.Net.Enums.KlineInterval.OneMinute, limit: 500);
89 |
90 | if (request.Success)
91 | {
92 | var bars = request.Data.Select(x => new OHLC((double)x.OpenPrice,
93 | (double)x.HighPrice,
94 | (double)x.LowPrice,
95 | (double)x.ClosePrice,
96 | x.OpenTime,
97 | TimeSpan.FromMinutes(1))).ToArray();
98 |
99 | // Append the all bars
100 | model.Append(bars);
101 |
102 | var socket = new BinanceSocketClient();
103 | await socket.SpotStreams.SubscribeToKlineUpdatesAsync("BTCUSDT", Binance.Net.Enums.KlineInterval.OneMinute, async (data) =>
104 | {
105 | await Dispatcher.UIThread.InvokeAsync(() =>
106 | {
107 | var candle = data.Data.Data;
108 |
109 | var toUpdate = model.Prices.FirstOrDefault(x => x.DateTime == candle.OpenTime);
110 |
111 | // Check if the data time are the same as the last. If not, it means we have to add a new bar
112 | if (toUpdate != null)
113 | {
114 | toUpdate.Volume = (double)candle.Volume;
115 | toUpdate.High = (double)candle.HighPrice;
116 | toUpdate.Close = (double)candle.ClosePrice;
117 | toUpdate.Low = (double)candle.LowPrice;
118 |
119 | // Update the last bar
120 | model.UpdateBar(toUpdate);
121 | }
122 | else
123 | {
124 | var newBar = new OHLC((double)candle.OpenPrice,
125 | (double)candle.HighPrice,
126 | (double)candle.LowPrice,
127 | (double)candle.ClosePrice,
128 | candle.OpenTime, TimeSpan.FromMinutes(1));
129 |
130 | // Append the new bar
131 | model.Append(newBar);
132 | }
133 | }, DispatcherPriority.Background);
134 | });
135 | }
136 | ```
137 |
138 | It is very easy to fullfill the chart with datas just by using Append() and UpdateBar().
139 | **
140 |
--------------------------------------------------------------------------------
/StockPlot.Charts/Controls/AvaPlot.axaml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/StockPlot.Charts/Controls/AvaPlot.axaml.cs:
--------------------------------------------------------------------------------
1 | using Avalonia;
2 | using Avalonia.Controls;
3 | using Avalonia.Input;
4 | using Avalonia.Interactivity;
5 | using Avalonia.Markup.Xaml;
6 | using Avalonia.Threading;
7 | using System.ComponentModel;
8 | using Ava = Avalonia;
9 |
10 | #pragma warning disable IDE1006 // lowercase public properties
11 | #pragma warning disable CS0067 // unused events
12 |
13 | namespace ScottPlot.Avalonia
14 | {
15 | ///
16 | /// Interaction logic for AvaPlot.axaml
17 | ///
18 |
19 | [System.ComponentModel.ToolboxItem(true)]
20 | [System.ComponentModel.DesignTimeVisible(true)]
21 | public partial class AvaPlot : UserControl, ScottPlot.Control.IPlotControl
22 | {
23 | public Plot Plot => Backend.Plot;
24 | public ScottPlot.Control.Configuration Configuration { get; }
25 |
26 | ///
27 | /// This event is invoked any time the axis limits are modified.
28 | ///
29 | public event EventHandler AxesChanged;
30 |
31 | ///
32 | /// This event is invoked any time the plot is right-clicked.
33 | ///
34 | public event EventHandler RightClicked;
35 |
36 | ///
37 | /// This event is invoked any time the plot is left-clicked.
38 | /// It is typically used to interact with custom plot types.
39 | ///
40 | public event EventHandler LeftClicked;
41 |
42 | ///
43 | /// This event is invoked when a plottable is left-clicked.
44 | ///
45 | public event EventHandler LeftClickedPlottable;
46 |
47 | ///
48 | /// This event is invoked after the mouse moves while dragging a draggable plottable.
49 | /// The object passed is the plottable being dragged.
50 | ///
51 | public event EventHandler PlottableDragged;
52 |
53 | [Obsolete("use 'PlottableDragged' instead", error: true)]
54 | public event EventHandler MouseDragPlottable;
55 |
56 | ///
57 | /// This event is invoked right after a draggable plottable was dropped.
58 | /// The object passed is the plottable that was just dropped.
59 | ///
60 | public event EventHandler PlottableDropped;
61 |
62 | [Obsolete("use 'PlottableDropped' instead", error: true)]
63 | public event EventHandler MouseDropPlottable;
64 |
65 | private readonly Control.ControlBackEnd Backend;
66 | private readonly Dictionary Cursors;
67 | private readonly Ava.Controls.Image PlotImage = new Ava.Controls.Image();
68 | private float ScaledWidth => (float)(Bounds.Width * Configuration.DpiStretchRatio);
69 | private float ScaledHeight => (float)(Bounds.Height * Configuration.DpiStretchRatio);
70 |
71 | [Obsolete("Reference Plot instead of plt")]
72 | public ScottPlot.Plot plt => Plot;
73 |
74 | static AvaPlot()
75 | {
76 |
77 | }
78 |
79 | public AvaPlot()
80 | {
81 | InitializeComponent();
82 |
83 | Cursors = new Dictionary()
84 | {
85 | [ScottPlot.Cursor.Arrow] = new Ava.Input.Cursor(StandardCursorType.Arrow),
86 | [ScottPlot.Cursor.WE] = new Ava.Input.Cursor(StandardCursorType.SizeWestEast),
87 | [ScottPlot.Cursor.NS] = new Ava.Input.Cursor(StandardCursorType.SizeNorthSouth),
88 | [ScottPlot.Cursor.All] = new Ava.Input.Cursor(StandardCursorType.SizeAll),
89 | [ScottPlot.Cursor.Crosshair] = new Ava.Input.Cursor(StandardCursorType.Cross),
90 | [ScottPlot.Cursor.Hand] = new Ava.Input.Cursor(StandardCursorType.Hand),
91 | [ScottPlot.Cursor.Question] = new Ava.Input.Cursor(StandardCursorType.Help),
92 | };
93 |
94 | Backend = new ScottPlot.Control.ControlBackEnd((float)Bounds.Width, (float)Bounds.Height, GetType().Name);
95 | Backend.BitmapChanged += new EventHandler(OnBitmapChanged);
96 | Backend.BitmapUpdated += new EventHandler(OnBitmapUpdated);
97 | Backend.CursorChanged += new EventHandler(OnCursorChanged);
98 | Backend.RightClicked += new EventHandler(OnRightClicked);
99 | Backend.LeftClicked += new EventHandler(OnLeftClicked);
100 | Backend.LeftClickedPlottable += new EventHandler(OnLeftClickedPlottable);
101 | Backend.AxesChanged += new EventHandler(OnAxesChanged);
102 | Backend.PlottableDragged += new EventHandler(OnPlottableDragged);
103 | Backend.PlottableDropped += new EventHandler(OnPlottableDropped);
104 | Backend.Configuration.ScaleChanged += new EventHandler(OnScaleChanged);
105 | Configuration = Backend.Configuration;
106 |
107 |
108 | InitializeLayout();
109 | Backend.StartProcessingEvents();
110 | }
111 |
112 | public (double x, double y) GetMouseCoordinates(int xAxisIndex = 0, int yAxisIndex = 0) => Backend.GetMouseCoordinates(xAxisIndex, yAxisIndex);
113 |
114 | public (float x, float y) GetMousePixel() => Backend.GetMousePixel();
115 | public void Reset() => Backend.Reset(ScaledWidth, ScaledHeight);
116 | public void Reset(Plot newPlot) => Backend.Reset(ScaledWidth, ScaledHeight, newPlot);
117 | public void Refresh() => Refresh(false);
118 | public void Refresh(bool lowQuality = false)
119 | {
120 | Backend.WasManuallyRendered = true;
121 | Backend.Render(lowQuality);
122 | }
123 | public void RefreshRequest(RenderType renderType = RenderType.LowQualityThenHighQualityDelayed)
124 | {
125 | Backend.WasManuallyRendered = true;
126 | Backend.RenderRequest(renderType);
127 | }
128 |
129 | // TODO: mark this obsolete in ScottPlot 5.0 (favor Refresh)
130 | public void Render(bool lowQuality = false) => Refresh(lowQuality);
131 |
132 | // TODO: mark this obsolete in ScottPlot 5.0 (favor Refresh)
133 | public void RenderRequest(RenderType renderType = RenderType.LowQualityThenHighQualityDelayed) => RefreshRequest(renderType);
134 |
135 | private Task SetImagePlot(Func getBmp)
136 | {
137 | return Task.Run(() =>
138 | {
139 | Dispatcher.UIThread.InvokeAsync(() =>
140 | {
141 | PlotImage.Source = getBmp();
142 | });
143 | });
144 | }
145 |
146 | private void OnBitmapChanged(object sender, EventArgs e) => SetImagePlot(() => BmpImageFromBmp(Backend.GetLatestBitmap()));
147 | private void OnCursorChanged(object sender, EventArgs e) { PlotImage.Cursor = Cursors[Backend.Cursor]; }
148 | private void OnBitmapUpdated(object sender, EventArgs e) => SetImagePlot(() => BmpImageFromBmp(Backend.GetLatestBitmap()));
149 | private void OnRightClicked(object sender, EventArgs e) => RightClicked?.Invoke(this, e);
150 | private void OnLeftClicked(object sender, EventArgs e) => LeftClicked?.Invoke(this, e);
151 | private void OnLeftClickedPlottable(object sender, EventArgs e) => LeftClickedPlottable?.Invoke(sender, e);
152 | private void OnPlottableDragged(object sender, EventArgs e) => PlottableDragged?.Invoke(sender, e);
153 | private void OnPlottableDropped(object sender, EventArgs e) => PlottableDropped?.Invoke(sender, e);
154 | private void OnAxesChanged(object sender, EventArgs e) => AxesChanged?.Invoke(this, e);
155 | private void OnSizeChanged(object sender, EventArgs e) => Backend.Resize(ScaledWidth, ScaledHeight, useDelayedRendering: true);
156 | private void OnScaleChanged(object sender, EventArgs e) { System.Diagnostics.Debug.WriteLine("SCALECHANGED"); OnSizeChanged(null, null); }
157 | private void OnMouseDown(object sender, PointerEventArgs e) { CaptureMouse(e.Pointer); Backend.MouseDown(GetInputState(e)); }
158 | private void OnMouseUp(object sender, PointerEventArgs e) { Backend.MouseUp(GetInputState(e)); UncaptureMouse(e.Pointer); }
159 | private void OnDoubleClick(object sender, RoutedEventArgs e) => Backend.DoubleClick();
160 | private void OnMouseWheel(object sender, PointerWheelEventArgs e) => Backend.MouseWheel(GetInputState(e, e.Delta.Y));
161 | private void OnMouseMove(object sender, PointerEventArgs e) { Backend.MouseMove(GetInputState(e)); base.OnPointerMoved(e); }
162 | private void OnMouseEnter(object sender, PointerEventArgs e) => base.OnPointerEntered(e);
163 | private void OnMouseLeave(object sender, PointerEventArgs e) => base.OnPointerExited(e);
164 | private void CaptureMouse(IPointer pointer) => pointer.Capture(this);
165 | private void UncaptureMouse(IPointer pointer) => pointer.Capture(null);
166 |
167 | private ScottPlot.Control.InputState GetInputState(PointerEventArgs e, double? delta = null) =>
168 | new ScottPlot.Control.InputState()
169 | {
170 | X = (float)e.GetPosition(this).X * Configuration.DpiStretchRatio,
171 | Y = (float)e.GetPosition(this).Y * Configuration.DpiStretchRatio,
172 | LeftWasJustPressed = e.GetCurrentPoint(null).Properties.PointerUpdateKind == PointerUpdateKind.LeftButtonPressed,
173 | RightWasJustPressed = e.GetCurrentPoint(null).Properties.PointerUpdateKind == PointerUpdateKind.RightButtonPressed,
174 | MiddleWasJustPressed = e.GetCurrentPoint(null).Properties.PointerUpdateKind == PointerUpdateKind.MiddleButtonPressed,
175 | ShiftDown = e.KeyModifiers.HasFlag(KeyModifiers.Shift),
176 | CtrlDown = e.KeyModifiers.HasFlag(KeyModifiers.Control),
177 | AltDown = e.KeyModifiers.HasFlag(KeyModifiers.Alt),
178 | WheelScrolledUp = delta.HasValue && delta > 0,
179 | WheelScrolledDown = delta.HasValue && delta < 0,
180 | };
181 |
182 | private void InitializeComponent()
183 | {
184 | AvaloniaXamlLoader.Load(this);
185 | this.Focusable = true;
186 |
187 | PointerPressed += OnMouseDown;
188 | PointerMoved += OnMouseMove;
189 | // Note: PointerReleased is handled in OnPointerReleased override instead
190 | PointerWheelChanged += OnMouseWheel;
191 | PointerEntered += OnMouseEnter;
192 | PointerExited += OnMouseLeave;
193 | DoubleTapped += OnDoubleClick;
194 | PropertyChanged += AvaPlot_PropertyChanged;
195 | }
196 |
197 | private void InitializeLayout()
198 | {
199 | Grid mainGrid = this.Find("MainGrid");
200 |
201 | bool isDesignerMode = Design.IsDesignMode;
202 | if (isDesignerMode)
203 | {
204 | try
205 | {
206 | Plot.Title($"ScottPlot {Plot.Version}");
207 | Plot.Render();
208 | }
209 | catch (Exception e)
210 | {
211 | InitializeComponent();
212 | this.Find("ErrorLabel").Text = "ERROR: ScottPlot failed to render in design mode.\n\n" +
213 | "This may be due to incompatible System.Drawing.Common versions or a 32-bit/64-bit mismatch.\n\n" +
214 | "Although rendering failed at design time, it may still function normally at runtime.\n\n" +
215 | $"Exception details:\n{e}";
216 | return;
217 | }
218 | }
219 |
220 | this.Find("ErrorLabel").IsVisible = false;
221 |
222 | Canvas canvas = new Canvas();
223 | mainGrid.Children.Add(canvas);
224 | canvas.Children.Add(PlotImage);
225 | }
226 |
227 | public static Ava.Media.Imaging.Bitmap BmpImageFromBmp(System.Drawing.Bitmap bmp)
228 | {
229 | using var memory = new System.IO.MemoryStream();
230 | bmp.Save(memory, System.Drawing.Imaging.ImageFormat.Png);
231 | memory.Position = 0;
232 | var bitmapImage = new Ava.Media.Imaging.Bitmap(memory);
233 | return bitmapImage;
234 | }
235 |
236 | private void InhibitContextMenuIfMouseDragged(object sender, CancelEventArgs e)
237 | {
238 | e.Cancel = Backend.MouseDownDragged;
239 | }
240 |
241 | protected override void OnPointerReleased(PointerReleasedEventArgs e)
242 | {
243 | // First, make sure backend sees that we are no longer pressing mouse button.
244 | // Otherwise, after selecting an item from the context menu, the control
245 | // will still think we are right-click-dragging even though the button
246 | // is no longer down.
247 | OnMouseUp(this, e);
248 |
249 | // Then allow Avalonia's own click handling to allow the context menu
250 | // to be displayed if needed.
251 | base.OnPointerReleased(e);
252 | }
253 |
254 | private void AvaPlot_PropertyChanged(object sender, AvaloniaPropertyChangedEventArgs e)
255 | {
256 | if (e.Property.Name == "Bounds")
257 | {
258 | Backend.Resize(ScaledWidth, ScaledHeight, useDelayedRendering: true);
259 | PlotImage.Width = ScaledWidth;
260 | PlotImage.Height = ScaledHeight;
261 | Refresh();
262 | }
263 | }
264 | }
265 | }
266 |
--------------------------------------------------------------------------------
/StockPlot.Charts/Controls/PropertyGrid.axaml:
--------------------------------------------------------------------------------
1 |
8 |
12 |
13 |
14 |
16 |
17 |
18 |
19 |
21 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/StockPlot.Charts/Controls/PropertyGrid.axaml.cs:
--------------------------------------------------------------------------------
1 | using System.ComponentModel;
2 | using System.Reflection;
3 | using Avalonia;
4 | using Avalonia.Controls;
5 | using Avalonia.Controls.Primitives;
6 | using Avalonia.Layout;
7 | using Avalonia.Markup.Xaml;
8 | using StockPlot.Indicators;
9 |
10 | namespace StockPlot.Charts.Controls
11 | {
12 | public partial class PropertyGrid : UserControl
13 | {
14 | public Button OkButton;
15 | public PropertyGrid()
16 | {
17 | InitializeComponent();
18 | OkButton = this.Find