├── .gitignore ├── LICENSE ├── README.md └── WPFCanvasChartSolution ├── WPFCanvasChart ├── IWPFCanvasChartComponent.cs ├── IWPFCanvasChartDrawer.cs ├── Internals │ └── WPFCanvasChartVisualHost.cs ├── Interpolators │ ├── IWPFCanvasChartInterpolator.cs │ └── WPFCanvasChartBasicInterpolators.cs ├── Properties │ └── AssemblyInfo.cs ├── WPFCanvasChart.csproj ├── WPFCanvasChartComponent.cs └── WPFCanvasChartSettings.cs ├── WPFCanvasChartSolution.sln ├── WPFCanvasChartSolution.suo ├── WPFCanvasChartTest ├── App.xaml ├── App.xaml.cs ├── MainWindow.xaml ├── MainWindow.xaml.cs ├── Properties │ ├── AssemblyInfo.cs │ ├── Resources.Designer.cs │ ├── Resources.resx │ ├── Settings.Designer.cs │ └── Settings.settings ├── WPFCanvasChartTest.csproj └── WPFCanvasChartTest.csproj.user ├── WPFChartControl ├── ChartControl.xaml ├── ChartControl.xaml.cs ├── Drawer │ ├── AbstractChartDrawer.cs │ ├── BarChartDrawer.cs │ ├── LineSeriesChartDrawer.cs │ ├── MultiChartDrawer.cs │ └── StackedBarChartDrawer.cs ├── Model │ └── LegendItem.cs ├── Properties │ └── AssemblyInfo.cs ├── ViewModel │ └── ChartViewModel.cs └── WPFChartControl.csproj └── WPFChartControlExample ├── App.xaml ├── App.xaml.cs ├── MainViewModel.cs ├── MainWindow.xaml ├── MainWindow.xaml.cs ├── Properties ├── AssemblyInfo.cs ├── Resources.Designer.cs ├── Resources.resx ├── Settings.Designer.cs └── Settings.settings ├── WPFChartControlExample.csproj └── WPFChartControlExample.csproj.user /.gitignore: -------------------------------------------------------------------------------- 1 | WPFCanvasChartSolution/WPFCanvasChart/bin 2 | WPFCanvasChartSolution/WPFCanvasChart/obj 3 | WPFCanvasChartSolution/WPFCanvasChartTest/bin 4 | WPFCanvasChartSolution/WPFCanvasChartTest/obj 5 | WPFCanvasChartSolution/WPFChartControlExample/bin 6 | WPFCanvasChartSolution/WPFChartControlExample/obj 7 | WPFCanvasChartSolution/WPFChartControl/bin 8 | WPFCanvasChartSolution/WPFChartControl/obj -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2013 Igor Crevar 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WPFCanvasChart 2 | 3 | ### Customizable WPF charting for large number of elements 4 | There are controls for: Line series charts, Bar charts, Stacked bar charts, etc. 5 | You can also use WPFCanvasChartComponent class directly(look at WPFCanvasChartTest example). The class will do for you things like axis drawing, point translation, zooming, scrolling, etc... Actual chart content is drawn by user. 6 | Both WPFCanvasChartComponent and WPFChartControl are fast and customizable. 7 | 8 | ### How to use? 9 | Its easy! Just look at WPFChartControlExample or WPFCanvasChartTest example projects in solution. 10 | Using chart control is easy. In XAML: 11 | ``` xml 12 | ... 13 | xmlns:chart="clr-namespace:IgorCrevar.WPFChartControl;assembly=WPFChartControl" 14 | ... 15 | 16 | ``` 17 | In ViewModel you can do something like: 18 | ``` c# 19 | YourCustomDrawer = new LineSeriesChartDrawer(new List { new Point(10, 20), new Point(20, 40) }); 20 | ``` 21 | or 22 | ``` c# 23 | YourCustomDrawer = new StackedBarChartDrawer(new List 24 | { 25 | new StackedBarItem(0, new double[] { 50, 30, 20 }), 26 | new StackedBarItem(1, new double[] { 100, 0, 100 }), 27 | }) 28 | { 29 | Legend = new LegendItem[] 30 | { 31 | new LegendItem(Colors.Blue, "One"), 32 | new LegendItem(Colors.Red, "Two"), 33 | new LegendItem(Colors.Yellow, "Total"), 34 | }, 35 | LegendWidth = 120.0d, 36 | }; 37 | ``` 38 | or 39 | ``` c# 40 | BarChartDrawer = new BarChartDrawer(new Point[]{ 41 | new Point(1.0d, rnd.Next(100)), 42 | new Point(2.0d, rnd.Next(100)), 43 | new Point(3.0d, rnd.Next(100)), 44 | new Point(4.0d, rnd.Next(100)), 45 | }); 46 | ``` 47 | etc 48 | Check example for more options for every drawer... 49 | 50 | Also you can use WPFCanvasChartComponent directly (from WPFCanvasChart project) 51 | - Before using, instance of WPFCanvasChart must be created 52 | - after that, you must set min/max x/y of your chart values using WPFCanvasChart instance SetMinMax method 53 | - you must implement IWPFCanvasChartDrawer interface 54 | - draw call on WPFCanvasChart instance will draw chart(step above must be executed before drawing!) 55 | - IWPFCanvasChartDrawer method public void Draw(DrawingContext ctx) is used to draw actual graph 56 | - Inside public void Draw(DrawingContext ctx) use Point2ChartPoint to convert your actual value to chart point 57 | 58 | #### Note: 59 | Chart is fast if canvas used for charting is inside Viewbox(like in code snippet bellow). If canvas is inside some other container(for example grid like in second code snippet bellow. This is how chart is created inside chart control), than performances dramatically decrease. Why? I do not know yet. 60 | ``` xml 61 | 62 | 63 | 64 | 65 | 66 | 67 | 69 | 71 | 72 | ``` 73 | -------------------------------------------------------------------------------- /WPFCanvasChartSolution/WPFCanvasChart/IWPFCanvasChartComponent.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Windows.Controls.Primitives; 6 | using System.Windows.Controls; 7 | using IgorCrevar.WPFCanvasChart.Interpolators; 8 | using System.Windows; 9 | 10 | namespace IgorCrevar.WPFCanvasChart 11 | { 12 | public interface IWPFCanvasChartComponent : IDisposable 13 | { 14 | void Init(Canvas canvas, ScrollBar horizScrollBar, ScrollBar vertScrollBar, IWPFCanvasChartDrawer drawer, 15 | WPFCanvasChartSettings settings, IWPFCanvasChartInterpolator xAxisInterpolator, IWPFCanvasChartInterpolator yAxisInterpolator); 16 | void SetMinMax(double minX, double maxX, double minY, double maxY); 17 | void DrawChart(); 18 | Point Point2ChartPoint(Point p); 19 | Point ChartPoint2Point(Point p); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WPFCanvasChartSolution/WPFCanvasChart/IWPFCanvasChartDrawer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Windows.Controls; 6 | using System.Windows.Media; 7 | 8 | namespace IgorCrevar.WPFCanvasChart 9 | { 10 | public interface IWPFCanvasChartDrawer 11 | { 12 | void Draw(DrawingContext ctx); 13 | void OnChartMouseDown(double x, double y); 14 | void OnChartMouseOver(double x, double y); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /WPFCanvasChartSolution/WPFCanvasChart/Internals/WPFCanvasChartVisualHost.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Windows; 6 | using System.Windows.Media; 7 | 8 | namespace IgorCrevar.WPFCanvasChart.Internals 9 | { 10 | internal class WPFCanvasChartVisualHost : FrameworkElement 11 | { 12 | private VisualCollection children; 13 | private DrawingVisual drawingVisual; 14 | 15 | public WPFCanvasChartVisualHost() 16 | { 17 | this.ClipToBounds = true; 18 | drawingVisual = new DrawingVisual(); 19 | children = new VisualCollection(this); 20 | children.Add(drawingVisual); 21 | } 22 | 23 | public DrawingVisual Drawing { get { return drawingVisual; } } 24 | 25 | protected override int VisualChildrenCount 26 | { 27 | get { return children.Count; } 28 | } 29 | 30 | protected override Visual GetVisualChild(int index) 31 | { 32 | if (index < 0 || index >= children.Count) 33 | throw new ArgumentOutOfRangeException(); 34 | 35 | return children[index]; 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /WPFCanvasChartSolution/WPFCanvasChart/Interpolators/IWPFCanvasChartInterpolator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace IgorCrevar.WPFCanvasChart.Interpolators 7 | { 8 | /// 9 | /// Axis values and grids are interpolated with implementators of this interface 10 | /// 11 | public interface IWPFCanvasChartInterpolator 12 | { 13 | IEnumerable GetSteps(double min, double max, int noOfSteps); 14 | string Format(double value); 15 | /// 16 | /// This method is used by DetermineMargins method inside WPFCanvasChart class 17 | /// Just return null if you not sure what to return as result 18 | /// 19 | /// string representation of longest value on axis 20 | string FormatLongestValue(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /WPFCanvasChartSolution/WPFCanvasChart/Interpolators/WPFCanvasChartBasicInterpolators.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace IgorCrevar.WPFCanvasChart.Interpolators 7 | { 8 | public class WPFCanvasChartFloatInterpolator : IWPFCanvasChartInterpolator 9 | { 10 | private string formatString; 11 | 12 | public WPFCanvasChartFloatInterpolator(string formatString = "F2") 13 | { 14 | this.formatString = formatString; 15 | } 16 | 17 | public IEnumerable GetSteps(double min, double max, int noOfSteps) 18 | { 19 | double valueStep = Math.Abs(max - min) / noOfSteps; 20 | double currentValue = min; 21 | for (int i = 0; i <= noOfSteps; ++i) 22 | { 23 | yield return currentValue; 24 | currentValue += valueStep; 25 | } 26 | } 27 | 28 | public virtual string Format(double value) 29 | { 30 | return value.ToString(formatString); 31 | } 32 | 33 | public virtual string FormatLongestValue() 34 | { 35 | return null; 36 | } 37 | } 38 | 39 | public class WPFCanvasChartIntInterpolator : IWPFCanvasChartInterpolator 40 | { 41 | public IEnumerable GetSteps(double min, double max, int noOfSteps) 42 | { 43 | // greater zoom has more steps 44 | double stepDouble = (max - min) / noOfSteps + 0.5d; 45 | int step = Math.Max(1, (int)stepDouble); 46 | for (int i = (int)min; i <= (int)max; i += step) 47 | { 48 | yield return i; 49 | } 50 | } 51 | 52 | public virtual string Format(double value) 53 | { 54 | return ((int)value).ToString(); 55 | } 56 | 57 | public virtual string FormatLongestValue() 58 | { 59 | return null; 60 | } 61 | } 62 | 63 | /// 64 | /// Same as WPFCanvasChartIntInterpolator but outputs all empty strings on axis 65 | /// 66 | public class WPFCanvasChartIntEmptyInterpolator : WPFCanvasChartIntInterpolator 67 | { 68 | public override string Format(double value) 69 | { 70 | return string.Empty; 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /WPFCanvasChartSolution/WPFCanvasChart/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("WPFCustomChart")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("WPFCustomChart")] 13 | [assembly: AssemblyCopyright("Copyright © 2013")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("8258dbb5-26c7-454f-84a2-530acce277b2")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /WPFCanvasChartSolution/WPFCanvasChart/WPFCanvasChart.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | AnyCPU 6 | 8.0.30703 7 | 2.0 8 | {B0C5D614-8F9E-4C31-B47D-EB8CFE7679C1} 9 | Library 10 | Properties 11 | IgorCrevar.WPFCanvasChart 12 | WPFCanvasChart 13 | v4.0 14 | 512 15 | 16 | 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | 25 | 26 | pdbonly 27 | true 28 | bin\Release\ 29 | TRACE 30 | prompt 31 | 4 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 65 | -------------------------------------------------------------------------------- /WPFCanvasChartSolution/WPFCanvasChart/WPFCanvasChartComponent.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Windows.Media; 6 | using System.Globalization; 7 | using System.Windows.Controls; 8 | using System.Windows.Controls.Primitives; 9 | using System.Windows.Input; 10 | using System.Windows; 11 | using IgorCrevar.WPFCanvasChart.Internals; 12 | using IgorCrevar.WPFCanvasChart.Interpolators; 13 | 14 | namespace IgorCrevar.WPFCanvasChart 15 | { 16 | public class WPFCanvasChartComponent : IWPFCanvasChartComponent 17 | { 18 | protected double minX, maxX; 19 | protected double minY, maxY; 20 | 21 | private Canvas canvas; 22 | 23 | private bool isPanning = false; 24 | private Point mousePosition; 25 | private IWPFCanvasChartDrawer drawer; 26 | 27 | private readonly WPFCanvasChartVisualHost chartHost = new WPFCanvasChartVisualHost(); 28 | private readonly WPFCanvasChartVisualHost axisHost = new WPFCanvasChartVisualHost(); 29 | private readonly WPFCanvasChartVisualHost xyAxisValuesHost = new WPFCanvasChartVisualHost(); 30 | 31 | private readonly TranslateTransform chartTransform = new TranslateTransform(); 32 | 33 | private ScrollBar horizScrollBar; 34 | private ScrollBar vertScrollBar; 35 | 36 | private double xScale = 1.0d; 37 | private double yScale = 1.0d; 38 | private double xMarginLeft; 39 | private double xMarginRight; 40 | private double yMargin; 41 | 42 | private double virtualWidth; 43 | private double virtualHeight; 44 | 45 | private bool yZoomEnabled; 46 | private bool xZoomEnabled; 47 | private WPFCanvasChartSettings settings; 48 | private IWPFCanvasChartInterpolator yAxisInterpolator; 49 | private IWPFCanvasChartInterpolator xAxisInterpolator; 50 | private bool isDisposed = true; 51 | 52 | /// 53 | /// Constructor 54 | /// 55 | public WPFCanvasChartComponent() 56 | { 57 | } 58 | 59 | /// 60 | /// Init method 61 | /// 62 | /// Canvas object 63 | /// Horizontal ScrollBar object 64 | /// Verical ScrollBar object. Can be null - that means vertical scroll is disabled 65 | /// ICustomChartCanvasDrawer implementator. This object will implement actual chart drawing(lines, etc) 66 | /// WPFCanvasChartSettings instance - settings for chart 67 | /// value interpolator for x axis 68 | /// value interpolator for y axis 69 | public void Init(Canvas canvas, ScrollBar horizScrollBar, ScrollBar vertScrollBar, IWPFCanvasChartDrawer drawer, 70 | WPFCanvasChartSettings settings, IWPFCanvasChartInterpolator xAxisInterpolator, IWPFCanvasChartInterpolator yAxisInterpolator) 71 | { 72 | this.canvas = canvas; 73 | this.drawer = drawer; 74 | this.settings = settings; 75 | this.horizScrollBar = horizScrollBar; 76 | this.vertScrollBar = vertScrollBar; 77 | this.yZoomEnabled = vertScrollBar != null; 78 | this.xZoomEnabled = horizScrollBar != null; 79 | this.xAxisInterpolator = xAxisInterpolator; 80 | this.yAxisInterpolator = yAxisInterpolator; 81 | 82 | chartHost.Drawing.Transform = chartTransform; 83 | 84 | canvas.Children.Add(axisHost); 85 | canvas.Children.Add(xyAxisValuesHost); 86 | canvas.Children.Add(chartHost); 87 | 88 | UpdateHostsSizes(); 89 | InitCanvasHandlers(); 90 | this.isDisposed = false; 91 | } 92 | 93 | /// 94 | /// Set maximum and minimum for X and Y coordinate. This boundaries determine axis point drawing! 95 | /// 96 | /// minimum for x coord 97 | /// maximum for x coord 98 | /// minimum for y coord 99 | /// maximum for y coord 100 | public void SetMinMax(double minX, double maxX, double minY, double maxY) 101 | { 102 | this.minX = minX; 103 | this.maxX = maxX; 104 | this.minY = minY; 105 | this.maxY = maxY; 106 | UpdateHostsSizes(); 107 | } 108 | 109 | private void InitCanvasHandlers() 110 | { 111 | if (settings.HandleSizeChanged) 112 | { 113 | canvas.SizeChanged += Canvas_SizeChanged; 114 | } 115 | 116 | canvas.MouseWheel += Canvas_MouseWheel; 117 | canvas.MouseMove += Canvas_MouseMove; 118 | canvas.MouseUp += Canvas_MouseUp; 119 | canvas.MouseDown += Canvas_MouseDown; 120 | if (xZoomEnabled) 121 | { 122 | horizScrollBar.ValueChanged += HorizScrollBar_ValueChanged; 123 | } 124 | 125 | if (yZoomEnabled) 126 | { 127 | vertScrollBar.ValueChanged += VertScrollBar_ValueChanged; 128 | } 129 | } 130 | 131 | private void Canvas_SizeChanged(object sender, SizeChangedEventArgs e) 132 | { 133 | UpdateHostsSizes(); 134 | DrawChart(); 135 | } 136 | 137 | private void Canvas_MouseUp(object sender, MouseButtonEventArgs e) 138 | { 139 | if (e.ChangedButton == MouseButton.Middle) 140 | { 141 | isPanning = false; 142 | canvas.Cursor = System.Windows.Input.Cursors.Arrow; 143 | canvas.ReleaseMouseCapture(); 144 | } 145 | } 146 | 147 | private void Canvas_MouseDown(object sender, MouseButtonEventArgs e) 148 | { 149 | if (e.ChangedButton == MouseButton.Middle) 150 | { 151 | mousePosition = e.GetPosition(canvas); 152 | isPanning = true; 153 | canvas.Cursor = System.Windows.Input.Cursors.Hand; 154 | canvas.CaptureMouse(); // mouse up anywhere will be sent to this control 155 | } 156 | else if (e.ChangedButton == MouseButton.Left) 157 | { 158 | var pos = e.GetPosition(chartHost); 159 | if (pos.X >= 0 && pos.Y >= 0 && pos.X < chartHost.Width && pos.Y < chartHost.Height) 160 | { 161 | pos = ChartPoint2Point(pos); 162 | drawer.OnChartMouseDown(pos.X, pos.Y); 163 | } 164 | } 165 | } 166 | 167 | private void VertScrollBar_ValueChanged(object sender, RoutedPropertyChangedEventArgs e) 168 | { 169 | double value = -e.NewValue; 170 | chartTransform.Y = value; 171 | DrawXYAxisValues(); 172 | } 173 | 174 | private void HorizScrollBar_ValueChanged(object sender, RoutedPropertyChangedEventArgs e) 175 | { 176 | double value = -e.NewValue; 177 | chartTransform.X = value; 178 | DrawXYAxisValues(); 179 | } 180 | 181 | private void Canvas_MouseMove(object sender, MouseEventArgs e) 182 | { 183 | Point newPosition = e.GetPosition(canvas); 184 | if (isPanning) 185 | { 186 | if (xZoomEnabled) 187 | { 188 | horizScrollBar.Value -= newPosition.X - mousePosition.X; 189 | } 190 | 191 | if (yZoomEnabled) 192 | { 193 | vertScrollBar.Value -= newPosition.Y - mousePosition.Y; 194 | } 195 | 196 | mousePosition = newPosition; 197 | } 198 | 199 | Point convertedPoint = Point2ChartPoint(newPosition); 200 | drawer.OnChartMouseOver(convertedPoint.X, convertedPoint.Y); 201 | } 202 | 203 | private void Canvas_MouseWheel(object sender, MouseWheelEventArgs e) 204 | { 205 | // fixed zoom step 206 | double zoomStep = e.Delta > 0 ? 1.1d : 0.9d; 207 | if (settings.ZoomXYAtSameTime) 208 | { 209 | ZoomX(zoomStep); 210 | ZoomY(zoomStep); 211 | } 212 | else if ((Keyboard.Modifiers & ModifierKeys.Control) == 0) 213 | { 214 | ZoomX(zoomStep); 215 | } 216 | else 217 | { 218 | ZoomY(zoomStep); 219 | } 220 | 221 | DrawChart(); 222 | } 223 | 224 | private void CheckBoundary(ref double value, double min, double max) 225 | { 226 | if (value < min) 227 | { 228 | value = min; 229 | } 230 | else if (value > max) 231 | { 232 | value = max; 233 | } 234 | } 235 | 236 | private void UpdateHostsSizes() 237 | { 238 | xScale = yScale = 1.0d; 239 | DetermineMargins(); 240 | 241 | axisHost.Width = canvas.Width; 242 | axisHost.Height = canvas.Height; 243 | 244 | chartHost.Width = canvas.Width - xMarginLeft - xMarginRight; 245 | chartHost.Height = canvas.Height - yMargin * 2; 246 | chartHost.Margin = new Thickness(xMarginLeft, yMargin, xMarginRight, yMargin); 247 | 248 | xyAxisValuesHost.Width = canvas.Width; 249 | xyAxisValuesHost.Height = canvas.Height; 250 | 251 | virtualWidth = chartHost.Width; 252 | virtualHeight = chartHost.Height; 253 | 254 | SetHorizScrollBar(); 255 | SetVertScrollBar(); 256 | } 257 | 258 | private void SetHorizScrollBar() 259 | { 260 | if (xZoomEnabled) 261 | { 262 | horizScrollBar.Minimum = 0; 263 | // maximum is full width - what user see 264 | horizScrollBar.Maximum = virtualWidth - chartHost.Width; 265 | horizScrollBar.ViewportSize = chartHost.Width; 266 | } 267 | } 268 | 269 | private void SetVertScrollBar() 270 | { 271 | if (yZoomEnabled) 272 | { 273 | vertScrollBar.Minimum = 0; 274 | vertScrollBar.Maximum = virtualHeight - chartHost.Height; 275 | vertScrollBar.ViewportSize = chartHost.Height; 276 | } 277 | } 278 | 279 | private void ZoomX(double step) 280 | { 281 | if (!xZoomEnabled) 282 | { 283 | return; 284 | } 285 | 286 | double oldX = (horizScrollBar.Value + horizScrollBar.ViewportSize / 2) / xScale; 287 | double width = virtualWidth * step; 288 | CheckBoundary(ref width, chartHost.Width, chartHost.Width * settings.MaxXZoomStep); 289 | xScale = width / chartHost.Width; 290 | virtualWidth = width; 291 | SetHorizScrollBar(); 292 | // fix scroll position 293 | horizScrollBar.Value = oldX * xScale - horizScrollBar.ViewportSize / 2; 294 | } 295 | 296 | private void ZoomY(double step) 297 | { 298 | if (!yZoomEnabled) 299 | { 300 | return; 301 | } 302 | 303 | double oldY = yZoomEnabled ? (vertScrollBar.Value + vertScrollBar.ViewportSize / 2) / yScale : 0.0d; 304 | double height = yZoomEnabled ? virtualHeight * step : virtualHeight; 305 | CheckBoundary(ref height, chartHost.Height, chartHost.Height * settings.MaxYZoomStep); 306 | yScale = height / chartHost.Height; 307 | virtualHeight = height; 308 | SetVertScrollBar(); 309 | vertScrollBar.Value = oldY * yScale - vertScrollBar.ViewportSize / 2; 310 | } 311 | 312 | // this method sets xMargin and yMargin, because every text on x and y axis has its width and height 313 | private void DetermineMargins() 314 | { 315 | FormattedText ft = GetFormattedText(yAxisInterpolator.FormatLongestValue() ?? yAxisInterpolator.Format(minY)); 316 | xMarginLeft = ft.Width; 317 | ft = GetFormattedText(yAxisInterpolator.Format(maxY)); 318 | if (xMarginLeft < ft.Width) 319 | { 320 | xMarginLeft = ft.Width; 321 | } 322 | 323 | ft = GetFormattedText(xAxisInterpolator.FormatLongestValue() ?? xAxisInterpolator.Format(minX)); 324 | yMargin = ft.Height; 325 | xMarginRight = ft.Width / 2; 326 | if (xMarginLeft < ft.Width / 2) 327 | { 328 | xMarginLeft = ft.Width / 2; 329 | } 330 | 331 | ft = GetFormattedText(xAxisInterpolator.Format(maxX)); 332 | if (yMargin < ft.Height) 333 | { 334 | yMargin = ft.Height; 335 | } 336 | 337 | if (xMarginLeft < ft.Width / 2) 338 | { 339 | xMarginLeft = ft.Width / 2; 340 | } 341 | 342 | if (xMarginRight < ft.Width / 2) 343 | { 344 | xMarginRight = ft.Width / 2; 345 | } 346 | 347 | yMargin += 5.0d; 348 | xMarginLeft += 5.0d; 349 | xMarginRight += 5.0d; 350 | } 351 | 352 | /// 353 | /// Draw chart. First draw axis and other stuff and finally call drawer Draw method which will manually draw actuall charting lines, bars etc. 354 | /// drawer Draw method should use CanvasPoint2Point method for converting actual points! 355 | /// 356 | public void DrawChart() 357 | { 358 | DrawAxisLines(); 359 | DrawChartAndValues(); 360 | } 361 | 362 | private void DrawAxisLines() 363 | { 364 | using (var ctx = axisHost.Drawing.RenderOpen()) 365 | { 366 | // fill hole rectangle 367 | ctx.DrawRectangle(settings.BrushBackground, new Pen(settings.BrushBackground, 0.0d), 368 | new Rect(0, 0, axisHost.Width, axisHost.Height)); 369 | // y axis 370 | ctx.DrawLine(settings.PenForAxis, new Point(xMarginLeft, yMargin), new Point(xMarginLeft, axisHost.Height - yMargin)); 371 | // x axis 372 | ctx.DrawLine(settings.PenForAxis, new Point(xMarginLeft, axisHost.Height - yMargin), 373 | new Point(axisHost.Width - xMarginRight, axisHost.Height - yMargin)); 374 | } 375 | } 376 | 377 | private void DrawXYAxisValues() 378 | { 379 | using (var xyAxisCtx = xyAxisValuesHost.Drawing.RenderOpen()) 380 | { 381 | DrawXAxis(xyAxisCtx, null); 382 | DrawYAxis(xyAxisCtx, null); 383 | } 384 | } 385 | 386 | private void DrawChartAndValues() 387 | { 388 | using (var chartCtx = chartHost.Drawing.RenderOpen()) 389 | using (var xyAxisCtx = xyAxisValuesHost.Drawing.RenderOpen()) 390 | { 391 | // clear chart rectangle 392 | chartCtx.DrawRectangle(settings.ChartBackgroundBrush, new Pen(settings.ChartBackgroundBrush, 0.0d), 393 | new Rect(0, 0, virtualWidth, virtualHeight)); 394 | // draw values and grids 395 | DrawXAxis(xyAxisCtx, chartCtx); 396 | DrawYAxis(xyAxisCtx, chartCtx); 397 | // draw actual chart - call user defined drawer 398 | drawer.Draw(chartCtx); 399 | } 400 | } 401 | 402 | #region Helper methods for subclasses 403 | protected void DrawXAxisText(DrawingContext ctx, double value, double desiredXPosition) 404 | { 405 | FormattedText formattedText = GetFormattedText(xAxisInterpolator.Format(value)); 406 | double x = desiredXPosition + chartTransform.X + xMarginLeft - formattedText.Width / 2; 407 | ctx.DrawText(formattedText, new Point(x, 2 + xyAxisValuesHost.Height - yMargin)); 408 | } 409 | 410 | protected void DrawYAxisText(DrawingContext ctx, double value, double desiredYPosition) 411 | { 412 | FormattedText formattedText = GetFormattedText(yAxisInterpolator.Format(value)); 413 | double y = desiredYPosition + chartTransform.Y + yMargin - formattedText.Height / 2; 414 | ctx.DrawText(formattedText, new Point(xMarginLeft - formattedText.Width - 2, y)); 415 | } 416 | 417 | protected FormattedText GetFormattedText(string txt) 418 | { 419 | return new FormattedText(txt, settings.CultureInfo, FlowDirection.LeftToRight, settings.TypeFace, settings.FontSize, 420 | settings.BrushForText); 421 | } 422 | 423 | protected int GetNoOfStepsForXAxis() 424 | { 425 | return (int)(settings.CoordXSteps * xScale); 426 | } 427 | 428 | protected int GetNoOfStepsForYAxis() 429 | { 430 | return (int)(settings.CoordYSteps * yScale); 431 | } 432 | 433 | protected void DrawVerticalGrid(DrawingContext ctx, double x) 434 | { 435 | if (ctx != null && settings.AreGridsEnabled) 436 | { 437 | ctx.DrawLine(settings.PenForGrid, new Point(x, 0), new Point(x, virtualHeight)); 438 | } 439 | } 440 | 441 | protected void DrawHorizontalGrid(DrawingContext ctx, double y) 442 | { 443 | if (ctx != null && settings.AreGridsEnabled) 444 | { 445 | ctx.DrawLine(settings.PenForGrid, new Point(0, y), new Point(virtualWidth, y)); 446 | } 447 | } 448 | 449 | protected bool IsXInsideVisiblePart(double x) 450 | { 451 | return x >= -chartTransform.X && x <= chartHost.Width - chartTransform.X + 1; 452 | } 453 | 454 | protected bool IsYInsideVisiblePart(double y) 455 | { 456 | double relativeY = y + chartTransform.Y; 457 | return relativeY >= 0 && relativeY <= chartHost.Height; 458 | } 459 | #endregion Helper methods for subclasses 460 | 461 | protected virtual void DrawXAxis(DrawingContext ctx, DrawingContext chartCtx) 462 | { 463 | foreach (double currentValue in xAxisInterpolator.GetSteps(minX, maxX, GetNoOfStepsForXAxis())) 464 | { 465 | var currentPosition = Point2ChartPoint(new Point(currentValue, 0.0)); 466 | DrawVerticalGrid(chartCtx, currentPosition.X); 467 | // draw axis value at current step if possible 468 | if (IsXInsideVisiblePart(currentPosition.X)) 469 | { 470 | DrawXAxisText(ctx, currentValue, currentPosition.X); 471 | } 472 | } 473 | } 474 | 475 | protected virtual void DrawYAxis(DrawingContext ctx, DrawingContext chartCtx) 476 | { 477 | foreach (double currentValue in yAxisInterpolator.GetSteps(minY, maxY, GetNoOfStepsForYAxis())) 478 | { 479 | var currentPosition = Point2ChartPoint(new Point(0.0, currentValue)); 480 | DrawHorizontalGrid(chartCtx, currentPosition.Y); 481 | // draw axis value at current step if possible 482 | if (IsYInsideVisiblePart(currentPosition.Y)) 483 | { 484 | // draw axis value at current step 485 | DrawYAxisText(ctx, currentValue, currentPosition.Y); 486 | } 487 | } 488 | } 489 | 490 | /// 491 | /// Convert chart point to point inside chart host 492 | /// 493 | /// Original point 494 | /// new point instance 495 | public Point Point2ChartPoint(Point p) 496 | { 497 | var np = new Point(); 498 | np.X = virtualWidth * Math.Abs(p.X - minX) / Math.Abs(maxX - minX); 499 | np.Y = virtualHeight * Math.Abs(p.Y - minY) / Math.Abs(maxY - minY); 500 | np.Y = virtualHeight - np.Y; 501 | return np; 502 | } 503 | 504 | /// 505 | /// Convert point from chart host(canvas) to original chart point 506 | /// 507 | /// Canvas point 508 | /// new point instance 509 | public Point ChartPoint2Point(Point p) 510 | { 511 | var np = new Point(); 512 | double sX = (p.X - chartTransform.X) / virtualWidth; 513 | double realY = p.Y - chartTransform.Y; 514 | double sY = (virtualHeight - realY) / virtualHeight; 515 | np.X = minX + Math.Abs(maxX - minX) * sX; 516 | np.Y = minY + Math.Abs(maxY - minY) * sY; 517 | return np; 518 | } 519 | 520 | public void Dispose() 521 | { 522 | if (!isDisposed) 523 | { 524 | drawer = null; 525 | if (settings.HandleSizeChanged) 526 | { 527 | canvas.SizeChanged -= Canvas_SizeChanged; 528 | } 529 | 530 | canvas.MouseWheel -= Canvas_MouseWheel; 531 | canvas.MouseMove -= Canvas_MouseMove; 532 | canvas.MouseUp -= Canvas_MouseUp; 533 | canvas.MouseDown -= Canvas_MouseDown; 534 | canvas.Children.Clear(); 535 | if (xZoomEnabled) 536 | { 537 | horizScrollBar.ValueChanged -= HorizScrollBar_ValueChanged; 538 | } 539 | 540 | if (yZoomEnabled) 541 | { 542 | vertScrollBar.ValueChanged -= VertScrollBar_ValueChanged; 543 | } 544 | 545 | isDisposed = true; 546 | } 547 | } 548 | } 549 | } 550 | -------------------------------------------------------------------------------- /WPFCanvasChartSolution/WPFCanvasChart/WPFCanvasChartSettings.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Windows.Media; 6 | using System.Globalization; 7 | 8 | namespace IgorCrevar.WPFCanvasChart 9 | { 10 | public class WPFCanvasChartSettings 11 | { 12 | private Pen penForGrid; 13 | private Pen penForAxis; 14 | private Brush brushBackground; 15 | private Brush brushForText; 16 | 17 | public WPFCanvasChartSettings() 18 | { 19 | CoordXSteps = 10; 20 | CoordYSteps = 5; 21 | FontSize = 9; 22 | Language = "en-us"; 23 | FontName = "Verdana"; 24 | PenForGrid = new Pen((Brush)new BrushConverter().ConvertFromString("#66000000"), 1); 25 | PenForAxis = new Pen((Brush)new BrushConverter().ConvertFromString("#CC000000"), 1); 26 | BrushBackground = (Brush)new BrushConverter().ConvertFrom("#DDDDDDDD"); 27 | BrushForText = (Brush)new BrushConverter().ConvertFrom("#FF000000"); 28 | MaxXZoomStep = 40.0f; 29 | MaxYZoomStep = 40.0f; 30 | ZoomXYAtSameTime = false; 31 | AreGridsEnabled = true; 32 | ChartBackgroundBrush = Brushes.White; 33 | HandleSizeChanged = true; 34 | } 35 | 36 | #region Properties 37 | 38 | public int CoordXSteps { get; set; } 39 | public int CoordYSteps { get; set; } 40 | public int FontSize { get; set; } 41 | public Typeface TypeFace { get; set; } 42 | public CultureInfo CultureInfo { get; set; } 43 | public float MaxXZoomStep { get; set; } 44 | public float MaxYZoomStep { get; set; } 45 | public bool ZoomXYAtSameTime { get; set; } 46 | public bool AreGridsEnabled { get; set; } 47 | public Brush ChartBackgroundBrush { get; set; } 48 | public bool HandleSizeChanged { get; set; } 49 | 50 | public string Language 51 | { 52 | set 53 | { 54 | CultureInfo = CultureInfo.GetCultureInfo(value); 55 | } 56 | } 57 | 58 | public string FontName 59 | { 60 | set 61 | { 62 | TypeFace = new Typeface(value); 63 | } 64 | } 65 | 66 | public Pen PenForGrid 67 | { 68 | get 69 | { 70 | return penForGrid; 71 | } 72 | set 73 | { 74 | penForGrid = value; 75 | penForGrid.Freeze(); 76 | } 77 | } 78 | 79 | public Pen PenForAxis 80 | { 81 | get 82 | { 83 | return penForAxis; 84 | } 85 | set 86 | { 87 | penForAxis = value; 88 | penForAxis.Freeze(); 89 | } 90 | } 91 | 92 | public Brush BrushBackground 93 | { 94 | get 95 | { 96 | return brushBackground; 97 | } 98 | set 99 | { 100 | brushBackground = value; 101 | brushBackground.Freeze(); 102 | } 103 | } 104 | 105 | public Brush BrushForText 106 | { 107 | get 108 | { 109 | return brushForText; 110 | } 111 | 112 | set 113 | { 114 | brushForText = value; 115 | brushForText.Freeze(); 116 | } 117 | } 118 | 119 | #endregion Properties 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /WPFCanvasChartSolution/WPFCanvasChartSolution.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 11.00 3 | # Visual C# Express 2010 4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WPFCanvasChartTest", "WPFCanvasChartTest\WPFCanvasChartTest.csproj", "{ED3CB320-9245-4BE4-8DE6-3917FAA30193}" 5 | EndProject 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WPFCanvasChart", "WPFCanvasChart\WPFCanvasChart.csproj", "{B0C5D614-8F9E-4C31-B47D-EB8CFE7679C1}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WPFChartControl", "WPFChartControl\WPFChartControl.csproj", "{463766C2-C063-4970-96C8-72A39B2A3474}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WPFChartControlExample", "WPFChartControlExample\WPFChartControlExample.csproj", "{CC3CB22D-B1DD-49CD-B057-7FB244327B41}" 11 | EndProject 12 | Global 13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 14 | Debug|Any CPU = Debug|Any CPU 15 | Debug|Mixed Platforms = Debug|Mixed Platforms 16 | Debug|x86 = Debug|x86 17 | Release|Any CPU = Release|Any CPU 18 | Release|Mixed Platforms = Release|Mixed Platforms 19 | Release|x86 = Release|x86 20 | EndGlobalSection 21 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 22 | {ED3CB320-9245-4BE4-8DE6-3917FAA30193}.Debug|Any CPU.ActiveCfg = Debug|x86 23 | {ED3CB320-9245-4BE4-8DE6-3917FAA30193}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 24 | {ED3CB320-9245-4BE4-8DE6-3917FAA30193}.Debug|Mixed Platforms.Build.0 = Debug|x86 25 | {ED3CB320-9245-4BE4-8DE6-3917FAA30193}.Debug|x86.ActiveCfg = Debug|x86 26 | {ED3CB320-9245-4BE4-8DE6-3917FAA30193}.Debug|x86.Build.0 = Debug|x86 27 | {ED3CB320-9245-4BE4-8DE6-3917FAA30193}.Release|Any CPU.ActiveCfg = Release|x86 28 | {ED3CB320-9245-4BE4-8DE6-3917FAA30193}.Release|Mixed Platforms.ActiveCfg = Release|x86 29 | {ED3CB320-9245-4BE4-8DE6-3917FAA30193}.Release|Mixed Platforms.Build.0 = Release|x86 30 | {ED3CB320-9245-4BE4-8DE6-3917FAA30193}.Release|x86.ActiveCfg = Release|x86 31 | {ED3CB320-9245-4BE4-8DE6-3917FAA30193}.Release|x86.Build.0 = Release|x86 32 | {B0C5D614-8F9E-4C31-B47D-EB8CFE7679C1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 33 | {B0C5D614-8F9E-4C31-B47D-EB8CFE7679C1}.Debug|Any CPU.Build.0 = Debug|Any CPU 34 | {B0C5D614-8F9E-4C31-B47D-EB8CFE7679C1}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU 35 | {B0C5D614-8F9E-4C31-B47D-EB8CFE7679C1}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU 36 | {B0C5D614-8F9E-4C31-B47D-EB8CFE7679C1}.Debug|x86.ActiveCfg = Debug|Any CPU 37 | {B0C5D614-8F9E-4C31-B47D-EB8CFE7679C1}.Release|Any CPU.ActiveCfg = Release|Any CPU 38 | {B0C5D614-8F9E-4C31-B47D-EB8CFE7679C1}.Release|Any CPU.Build.0 = Release|Any CPU 39 | {B0C5D614-8F9E-4C31-B47D-EB8CFE7679C1}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU 40 | {B0C5D614-8F9E-4C31-B47D-EB8CFE7679C1}.Release|Mixed Platforms.Build.0 = Release|Any CPU 41 | {B0C5D614-8F9E-4C31-B47D-EB8CFE7679C1}.Release|x86.ActiveCfg = Release|Any CPU 42 | {463766C2-C063-4970-96C8-72A39B2A3474}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 43 | {463766C2-C063-4970-96C8-72A39B2A3474}.Debug|Any CPU.Build.0 = Debug|Any CPU 44 | {463766C2-C063-4970-96C8-72A39B2A3474}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU 45 | {463766C2-C063-4970-96C8-72A39B2A3474}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU 46 | {463766C2-C063-4970-96C8-72A39B2A3474}.Debug|x86.ActiveCfg = Debug|Any CPU 47 | {463766C2-C063-4970-96C8-72A39B2A3474}.Release|Any CPU.ActiveCfg = Release|Any CPU 48 | {463766C2-C063-4970-96C8-72A39B2A3474}.Release|Any CPU.Build.0 = Release|Any CPU 49 | {463766C2-C063-4970-96C8-72A39B2A3474}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU 50 | {463766C2-C063-4970-96C8-72A39B2A3474}.Release|Mixed Platforms.Build.0 = Release|Any CPU 51 | {463766C2-C063-4970-96C8-72A39B2A3474}.Release|x86.ActiveCfg = Release|Any CPU 52 | {CC3CB22D-B1DD-49CD-B057-7FB244327B41}.Debug|Any CPU.ActiveCfg = Debug|x86 53 | {CC3CB22D-B1DD-49CD-B057-7FB244327B41}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 54 | {CC3CB22D-B1DD-49CD-B057-7FB244327B41}.Debug|Mixed Platforms.Build.0 = Debug|x86 55 | {CC3CB22D-B1DD-49CD-B057-7FB244327B41}.Debug|x86.ActiveCfg = Debug|x86 56 | {CC3CB22D-B1DD-49CD-B057-7FB244327B41}.Debug|x86.Build.0 = Debug|x86 57 | {CC3CB22D-B1DD-49CD-B057-7FB244327B41}.Release|Any CPU.ActiveCfg = Release|x86 58 | {CC3CB22D-B1DD-49CD-B057-7FB244327B41}.Release|Mixed Platforms.ActiveCfg = Release|x86 59 | {CC3CB22D-B1DD-49CD-B057-7FB244327B41}.Release|Mixed Platforms.Build.0 = Release|x86 60 | {CC3CB22D-B1DD-49CD-B057-7FB244327B41}.Release|x86.ActiveCfg = Release|x86 61 | {CC3CB22D-B1DD-49CD-B057-7FB244327B41}.Release|x86.Build.0 = Release|x86 62 | EndGlobalSection 63 | GlobalSection(SolutionProperties) = preSolution 64 | HideSolutionNode = FALSE 65 | EndGlobalSection 66 | EndGlobal 67 | -------------------------------------------------------------------------------- /WPFCanvasChartSolution/WPFCanvasChartSolution.suo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/igorcrevar/WPFCanvasChart/ca00bd58df4d552a74f0265a85e90a818d2415ab/WPFCanvasChartSolution/WPFCanvasChartSolution.suo -------------------------------------------------------------------------------- /WPFCanvasChartSolution/WPFCanvasChartTest/App.xaml: -------------------------------------------------------------------------------- 1 |  5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /WPFCanvasChartSolution/WPFCanvasChartTest/App.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Configuration; 4 | using System.Data; 5 | using System.Linq; 6 | using System.Windows; 7 | 8 | namespace WpfApplication1 9 | { 10 | /// 11 | /// Interaction logic for App.xaml 12 | /// 13 | public partial class App : Application 14 | { 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /WPFCanvasChartSolution/WPFCanvasChartTest/MainWindow.xaml: -------------------------------------------------------------------------------- 1 |  5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 41 | 43 | 44 | 45 | 46 | 47 | 48 | 56 | 57 | 58 | 59 | 60 | 62 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /WPFCanvasChartSolution/WPFCanvasChartTest/MainWindow.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Windows; 6 | using System.Windows.Controls; 7 | using System.Windows.Data; 8 | using System.Windows.Documents; 9 | using System.Windows.Input; 10 | using System.Windows.Media; 11 | using System.Windows.Media.Imaging; 12 | using System.Windows.Navigation; 13 | using System.Windows.Shapes; 14 | using System.ComponentModel; 15 | using IgorCrevar.WPFCanvasChart; 16 | using IgorCrevar.WPFCanvasChart.Interpolators; 17 | 18 | namespace WpfApplication1 19 | { 20 | /// 21 | /// Interaction logic for MainWindow.xaml 22 | /// 23 | partial class MainWindow : Window, IWPFCanvasChartDrawer 24 | { 25 | enum ChartType 26 | { 27 | None, Big, Small, Bar 28 | }; 29 | 30 | private IWPFCanvasChartComponent cc = null; 31 | private List pointsList = new List(); 32 | private Pen pen = new Pen(Brushes.Red, 1); 33 | private Brush brush = Brushes.Red; 34 | private WPFCanvasChartSettings settings; 35 | private ChartType chartType = ChartType.None; 36 | private IWPFCanvasChartInterpolator xAxisInterpolator; 37 | private IWPFCanvasChartInterpolator yAxisInterpolator; 38 | 39 | public MainWindow() 40 | { 41 | InitializeComponent(); 42 | this.AreGridEnabledButton.IsChecked = true; 43 | settings = new WPFCanvasChartSettings(); 44 | settings.MaxXZoomStep = 200.0f; 45 | settings.MaxYZoomStep = 200.0f; 46 | pen.Freeze(); 47 | brush.Freeze(); 48 | this.Loaded += (sender, e) => 49 | { 50 | // chart must created after all UI elements are loaded (canvas, scroll bars, etc...) 51 | settings.HandleSizeChanged = false; 52 | settings.FontSize = 4; 53 | settings.PenForGrid = new Pen((Brush)new BrushConverter().ConvertFromString("#66000000"), 0.3); 54 | settings.PenForAxis = new Pen((Brush)new BrushConverter().ConvertFromString("#CC000000"), 0.5); 55 | xAxisInterpolator = new WPFCanvasChartIntInterpolator(); 56 | yAxisInterpolator = new WPFCanvasChartFloatInterpolator(); 57 | cc = new WPFCanvasChartComponent(); 58 | cc.Init(this.Canvas, HorizScroll, VertScroll, this, settings, xAxisInterpolator, yAxisInterpolator); 59 | cc.SetMinMax(-5, 5, 10, 20); 60 | cc.DrawChart(); 61 | }; 62 | 63 | this.Closed += (sender, e) => 64 | { 65 | cc.Dispose(); 66 | }; 67 | } 68 | 69 | public void Draw(DrawingContext ctx) 70 | { 71 | switch (chartType) 72 | { 73 | case ChartType.Small: 74 | DrawSmall(ctx); 75 | break; 76 | case ChartType.Big: 77 | DrawBig(ctx); 78 | break; 79 | case ChartType.Bar: 80 | DrawBar(ctx); 81 | break; 82 | default: 83 | // do nothing 84 | break; 85 | } 86 | } 87 | 88 | private void DrawBar(DrawingContext ctx) 89 | { 90 | var points = pointsList.ConvertAll(i => cc.Point2ChartPoint(i)); 91 | double width = points[1].X - points[0].X; 92 | width = width * 2 / 3; 93 | double yStart = cc.Point2ChartPoint(new Point(0.0, 0.0)).Y; 94 | foreach (var p in points) 95 | { 96 | ctx.DrawRectangle(brush, pen, new Rect(p.X - width / 2, p.Y, width, yStart - p.Y)); 97 | } 98 | } 99 | 100 | private void DrawBig(DrawingContext ctx) 101 | { 102 | Point start = cc.Point2ChartPoint(pointsList[0]); 103 | for (int i = 1; i < pointsList.Count; i++) 104 | { 105 | Point end = cc.Point2ChartPoint(pointsList[i]); 106 | ctx.DrawLine(pen, start, end); 107 | start = end; 108 | } 109 | } 110 | 111 | public void DrawSmall(DrawingContext ctx) 112 | { 113 | var p1 = cc.Point2ChartPoint(new Point(2, -3)); 114 | var p2 = cc.Point2ChartPoint(new Point(4, 10)); 115 | ctx.DrawLine(pen, p1, p2); 116 | 117 | p1 = cc.Point2ChartPoint(new Point(4, 10)); 118 | p2 = cc.Point2ChartPoint(new Point(7, -1.5)); 119 | ctx.DrawLine(pen, p1, p2); 120 | } 121 | 122 | public void OnChartMouseDown(double x, double y) 123 | { 124 | MessageBox.Show(this, string.Format("Position {0} : {1}", xAxisInterpolator.Format(x), yAxisInterpolator.Format(y)), 125 | "WPFCanvasChart Left Mouse Click"); 126 | } 127 | 128 | private void Refresh() 129 | { 130 | cc.DrawChart(); 131 | } 132 | 133 | private void AreGridEnabledButton_Click(object sender, RoutedEventArgs e) 134 | { 135 | settings.AreGridsEnabled = !settings.AreGridsEnabled; 136 | Refresh(); 137 | } 138 | 139 | private void ZoomBothAtSameTime_Click(object sender, RoutedEventArgs e) 140 | { 141 | settings.ZoomXYAtSameTime = !settings.ZoomXYAtSameTime; 142 | } 143 | 144 | private void SmallChartMenuItem_Click(object sender, RoutedEventArgs e) 145 | { 146 | settings.CoordXSteps = 10; 147 | chartType = ChartType.Small; 148 | cc.SetMinMax(2, 10, -5, 11.5); 149 | Refresh(); 150 | } 151 | 152 | private void BigChartMenuItem_Click(object sender, RoutedEventArgs e) 153 | { 154 | settings.CoordXSteps = 10; 155 | chartType = ChartType.Big; 156 | 157 | pointsList.Clear(); 158 | var rnd = new Random(); 159 | int count = rnd.Next(1000) + 5000; 160 | int startX = rnd.Next(100) - 50; 161 | for (int i = 0; i < count; ++i) 162 | { 163 | pointsList.Add(new Point(i + startX, rnd.NextDouble() * 400000 - 100000)); 164 | } 165 | 166 | Point min = pointsList[0]; 167 | Point max = pointsList[0]; 168 | foreach (var point in pointsList) 169 | { 170 | min.X = Math.Min(min.X, point.X); 171 | max.X = Math.Max(max.X, point.X); 172 | min.Y = Math.Min(min.Y, point.Y); 173 | max.Y = Math.Max(max.Y, point.Y); 174 | } 175 | 176 | cc.SetMinMax(min.X, max.X, min.Y, max.Y); 177 | Refresh(); 178 | } 179 | 180 | private void BarChartMenuItem_Click(object sender, RoutedEventArgs e) 181 | { 182 | settings.CoordXSteps = 4 + new Random().Next(4); 183 | chartType = ChartType.Bar; 184 | var rnd = new Random(); 185 | double maxY = double.MinValue; 186 | pointsList.Clear(); 187 | for (int i = 0; i < settings.CoordXSteps; ++i) 188 | { 189 | Point p = new Point(i + 1, rnd.NextDouble() * 100); 190 | maxY = Math.Max(maxY, p.Y); 191 | pointsList.Add(p); 192 | } 193 | 194 | cc.SetMinMax(0, settings.CoordXSteps + 1, 0, maxY * 10 / 9); 195 | 196 | Refresh(); 197 | } 198 | 199 | public void OnChartMouseOver(double x, double y) 200 | { 201 | } 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /WPFCanvasChartSolution/WPFCanvasChartTest/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Resources; 3 | using System.Runtime.CompilerServices; 4 | using System.Runtime.InteropServices; 5 | using System.Windows; 6 | 7 | // General Information about an assembly is controlled through the following 8 | // set of attributes. Change these attribute values to modify the information 9 | // associated with an assembly. 10 | [assembly: AssemblyTitle("WpfApplication2")] 11 | [assembly: AssemblyDescription("")] 12 | [assembly: AssemblyConfiguration("")] 13 | [assembly: AssemblyCompany("")] 14 | [assembly: AssemblyProduct("WpfApplication2")] 15 | [assembly: AssemblyCopyright("Copyright © 2013")] 16 | [assembly: AssemblyTrademark("")] 17 | [assembly: AssemblyCulture("")] 18 | 19 | // Setting ComVisible to false makes the types in this assembly not visible 20 | // to COM components. If you need to access a type in this assembly from 21 | // COM, set the ComVisible attribute to true on that type. 22 | [assembly: ComVisible(false)] 23 | 24 | //In order to begin building localizable applications, set 25 | //CultureYouAreCodingWith in your .csproj file 26 | //inside a . For example, if you are using US english 27 | //in your source files, set the to en-US. Then uncomment 28 | //the NeutralResourceLanguage attribute below. Update the "en-US" in 29 | //the line below to match the UICulture setting in the project file. 30 | 31 | //[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] 32 | 33 | 34 | [assembly: ThemeInfo( 35 | ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located 36 | //(used if a resource is not found in the page, 37 | // or application resource dictionaries) 38 | ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located 39 | //(used if a resource is not found in the page, 40 | // app, or any theme specific resource dictionaries) 41 | )] 42 | 43 | 44 | // Version information for an assembly consists of the following four values: 45 | // 46 | // Major Version 47 | // Minor Version 48 | // Build Number 49 | // Revision 50 | // 51 | // You can specify all the values or you can default the Build and Revision Numbers 52 | // by using the '*' as shown below: 53 | // [assembly: AssemblyVersion("1.0.*")] 54 | [assembly: AssemblyVersion("1.0.0.0")] 55 | [assembly: AssemblyFileVersion("1.0.0.0")] 56 | -------------------------------------------------------------------------------- /WPFCanvasChartSolution/WPFCanvasChartTest/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.18052 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace WpfApplication1.Properties { 12 | using System; 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resources() { 33 | } 34 | 35 | /// 36 | /// Returns the cached ResourceManager instance used by this class. 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | internal static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("WpfApplication1.Properties.Resources", typeof(Resources).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// Overrides the current thread's CurrentUICulture property for all 51 | /// resource lookups using this strongly typed resource class. 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | internal static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /WPFCanvasChartSolution/WPFCanvasChartTest/Properties/Resources.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | text/microsoft-resx 107 | 108 | 109 | 2.0 110 | 111 | 112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 113 | 114 | 115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | -------------------------------------------------------------------------------- /WPFCanvasChartSolution/WPFCanvasChartTest/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.18052 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace WpfApplication1.Properties { 12 | 13 | 14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "10.0.0.0")] 16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { 17 | 18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 19 | 20 | public static Settings Default { 21 | get { 22 | return defaultInstance; 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /WPFCanvasChartSolution/WPFCanvasChartTest/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /WPFCanvasChartSolution/WPFCanvasChartTest/WPFCanvasChartTest.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | x86 6 | 8.0.30703 7 | 2.0 8 | {ED3CB320-9245-4BE4-8DE6-3917FAA30193} 9 | WinExe 10 | Properties 11 | WpfApplication1 12 | WPFCanvasChartTest 13 | v4.0 14 | Client 15 | 512 16 | {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 17 | 4 18 | 19 | 20 | x86 21 | true 22 | full 23 | false 24 | bin\Debug\ 25 | DEBUG;TRACE 26 | prompt 27 | 4 28 | 29 | 30 | x86 31 | pdbonly 32 | true 33 | bin\Release\ 34 | TRACE 35 | prompt 36 | 4 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 4.0 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | MSBuild:Compile 56 | Designer 57 | 58 | 59 | MSBuild:Compile 60 | Designer 61 | 62 | 63 | App.xaml 64 | Code 65 | 66 | 67 | MainWindow.xaml 68 | Code 69 | 70 | 71 | 72 | 73 | Code 74 | 75 | 76 | True 77 | True 78 | Resources.resx 79 | 80 | 81 | True 82 | Settings.settings 83 | True 84 | 85 | 86 | ResXFileCodeGenerator 87 | Resources.Designer.cs 88 | 89 | 90 | SettingsSingleFileGenerator 91 | Settings.Designer.cs 92 | 93 | 94 | 95 | 96 | 97 | {B0C5D614-8F9E-4C31-B47D-EB8CFE7679C1} 98 | WPFCanvasChart 99 | 100 | 101 | 102 | 109 | -------------------------------------------------------------------------------- /WPFCanvasChartSolution/WPFCanvasChartTest/WPFCanvasChartTest.csproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | -------------------------------------------------------------------------------- /WPFCanvasChartSolution/WPFChartControl/ChartControl.xaml: -------------------------------------------------------------------------------- 1 |  8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 37 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 47 | 48 | 49 | 51 | 53 | 54 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /WPFCanvasChartSolution/WPFChartControl/ChartControl.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows; 3 | using System.Windows.Controls; 4 | using IgorCrevar.WPFChartControl.Model; 5 | using IgorCrevar.WPFChartControl.ViewModel; 6 | using IgorCrevar.WPFChartControl.Drawer; 7 | using IgorCrevar.WPFCanvasChart; 8 | using IgorCrevar.WPFCanvasChart.Interpolators; 9 | 10 | namespace IgorCrevar.WPFChartControl 11 | { 12 | /// 13 | /// Interaction logic for BarChart.xaml 14 | /// 15 | public partial class ChartControl : UserControl 16 | { 17 | private ChartViewModel viewModel; 18 | 19 | public ChartControl() 20 | { 21 | InitializeComponent(); 22 | this.viewModel = new ChartViewModel(); 23 | this.MainGrid.DataContext = viewModel; 24 | Dispatcher.ShutdownStarted += (sender, e) => 25 | { 26 | if (Drawer != null && Drawer.Chart != null) 27 | { 28 | Drawer.Chart.Dispose(); 29 | } 30 | }; 31 | } 32 | 33 | // Dependency Property 34 | public static readonly DependencyProperty DrawerProperty = 35 | DependencyProperty.Register("Drawer", typeof(AbstractChartDrawer), 36 | typeof(ChartControl), new FrameworkPropertyMetadata(null, OnPropertyChanged)); 37 | 38 | private static void OnPropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e) 39 | { 40 | ChartControl control = (ChartControl)source; 41 | AbstractChartDrawer drawer = (AbstractChartDrawer)e.NewValue; 42 | control.Update(drawer); 43 | } 44 | 45 | public AbstractChartDrawer Drawer 46 | { 47 | get 48 | { 49 | return (AbstractChartDrawer)GetValue(DrawerProperty); 50 | } 51 | 52 | set 53 | { 54 | SetValue(DrawerProperty, value); 55 | } 56 | } 57 | 58 | private void Update(AbstractChartDrawer drawer) 59 | { 60 | if (drawer == null) 61 | { 62 | return; 63 | } 64 | 65 | if (drawer.Settings == null) 66 | { 67 | drawer.Settings = new WPFCanvasChartSettings(); //default settings 68 | } 69 | 70 | if (drawer.XAxisInterpolator == null) 71 | { 72 | drawer.XAxisInterpolator = new WPFCanvasChartIntInterpolator(); 73 | } 74 | 75 | if (drawer.YAxisInterpolator == null) 76 | { 77 | drawer.YAxisInterpolator = new WPFCanvasChartFloatInterpolator(); 78 | } 79 | 80 | if (drawer.Chart == null) 81 | { 82 | drawer.Chart = new WPFCanvasChartComponent(); 83 | } 84 | 85 | drawer.Chart.Dispose(); 86 | drawer.Chart.Init(Canvas, 87 | drawer.HorizScrollVisibility == System.Windows.Visibility.Visible ? HorizScroll : null, 88 | drawer.VertScrollVisibility == System.Windows.Visibility.Visible ? VertScroll : null, 89 | drawer, 90 | drawer.Settings, 91 | drawer.XAxisInterpolator, 92 | drawer.YAxisInterpolator); 93 | 94 | viewModel.Update(drawer); 95 | drawer.Update(); 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /WPFCanvasChartSolution/WPFChartControl/Drawer/AbstractChartDrawer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using IgorCrevar.WPFCanvasChart; 3 | using System.Windows.Controls; 4 | using System.Windows.Controls.Primitives; 5 | using IgorCrevar.WPFChartControl.Model; 6 | using System.Windows; 7 | using System.Windows.Media; 8 | using IgorCrevar.WPFCanvasChart.Interpolators; 9 | using System.Collections.Generic; 10 | 11 | namespace IgorCrevar.WPFChartControl.Drawer 12 | { 13 | public struct MinMax 14 | { 15 | public MinMax(bool dummy) 16 | { 17 | minX = minY = double.MaxValue; 18 | maxX = maxY = double.MinValue; 19 | } 20 | 21 | public MinMax(double minX, double maxX, double minY, double maxY) 22 | { 23 | this.minX = minX; 24 | this.maxX = maxX; 25 | this.minY = minY; 26 | this.maxY = maxY; 27 | } 28 | 29 | public void Update(Point min, Point max) 30 | { 31 | Update(min.X, max.X, min.Y, max.Y); 32 | } 33 | 34 | public void Update(MinMax minMax) 35 | { 36 | Update(minMax.minX, minMax.maxX, minMax.minY, minMax.maxY); 37 | } 38 | 39 | public void Update(double minX, double maxX, double minY, double maxY) 40 | { 41 | this.minX = Math.Min(this.minX, minX); 42 | this.minY = Math.Min(this.minY, minY); 43 | 44 | this.maxX = Math.Max(this.maxX, maxX); 45 | this.maxY = Math.Max(this.maxY, maxY); 46 | } 47 | 48 | public void UpdateChart(IWPFCanvasChartComponent chart) 49 | { 50 | chart.SetMinMax(minX, maxX, minY, maxY); 51 | } 52 | 53 | public double minX; 54 | public double maxX; 55 | public double minY; 56 | public double maxY; 57 | } 58 | 59 | /// 60 | /// Base class for all charts drawers 61 | /// 62 | public abstract class AbstractChartDrawer : IWPFCanvasChartDrawer 63 | { 64 | public AbstractChartDrawer() 65 | { 66 | Background = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#DDDDDDDD")); 67 | LegendWidth = double.NaN; 68 | DefaultMinMax = new MinMax(1, 12, 0, 10); 69 | FixedYMin = double.NaN; 70 | } 71 | 72 | #region Abstract Methods 73 | public abstract void Draw(System.Windows.Media.DrawingContext ctx); 74 | /// 75 | /// Get min/max for X and Y axis 76 | /// 77 | /// min for X and Y 78 | /// max for X and Y 79 | /// true if min max can be determined 80 | public abstract MinMax GetMinMax(); 81 | #endregion Abstract Methods 82 | 83 | #region Properties 84 | public IWPFCanvasChartComponent Chart { get; set; } 85 | public string XAxisText { get; set; } 86 | public string YAxisText { get; set; } 87 | public Visibility HorizScrollVisibility { get; set; } 88 | public Visibility VertScrollVisibility { get; set; } 89 | public IList Legend { get; set; } 90 | public Brush Background { get; set; } 91 | public WPFCanvasChartSettings Settings { get; set; } 92 | public IWPFCanvasChartInterpolator XAxisInterpolator { get; set; } 93 | public IWPFCanvasChartInterpolator YAxisInterpolator { get; set; } 94 | public MinMax DefaultMinMax { get; set; } 95 | /// 96 | /// Put here 0.0 if you dont want legend, or double.NaN(default) if you want auto width 97 | /// 98 | public double LegendWidth { get; set; } 99 | /// 100 | /// set to NaN if you want minimum depends on points data, otherwise set fixed y min 101 | /// 102 | public double FixedYMin { get; set; } 103 | protected MinMax MinMaxValue { get; set; } 104 | #endregion 105 | 106 | public void Update() 107 | { 108 | OnUpdate(); 109 | var minMax = GetMinMax(); 110 | if (minMax.minX != double.MaxValue) 111 | { 112 | minMax.minY = double.IsNaN(FixedYMin) || FixedYMin > minMax.minY ? minMax.minY : FixedYMin; 113 | MinMaxValue = minMax; 114 | } 115 | else 116 | { 117 | MinMaxValue = DefaultMinMax; 118 | } 119 | 120 | MinMaxValue.UpdateChart(Chart); 121 | Chart.DrawChart(); 122 | } 123 | 124 | protected virtual void OnUpdate() 125 | { 126 | } 127 | 128 | public virtual void OnChartMouseOver(double x, double y) 129 | { 130 | } 131 | 132 | public virtual void OnChartMouseDown(double x, double y) 133 | { 134 | } 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /WPFCanvasChartSolution/WPFChartControl/Drawer/BarChartDrawer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Windows; 4 | using System.Windows.Media; 5 | using IgorCrevar.WPFChartControl.Model; 6 | using System.Collections.Generic; 7 | 8 | namespace IgorCrevar.WPFChartControl.Drawer 9 | { 10 | public class BarChartDrawer : AbstractChartDrawer 11 | { 12 | private IList chartPoints; 13 | 14 | public BarChartDrawer(IList chartPoints) 15 | { 16 | this.chartPoints = chartPoints; 17 | } 18 | 19 | public override void Draw(DrawingContext ctx) 20 | { 21 | if (chartPoints.Count == 0) 22 | { 23 | return; 24 | } 25 | 26 | var points = chartPoints.Select(i => Chart.Point2ChartPoint(i)).ToArray(); 27 | double width = 0.0d; 28 | if (chartPoints.Count == 1) 29 | { 30 | var p1 = Chart.Point2ChartPoint(new Point(points[0].X - 1, 0)); 31 | var p2 = Chart.Point2ChartPoint(new Point(points[0].X + 1, 0)); 32 | width = p2.X - p1.X; 33 | width = width * 1 / 3; 34 | } 35 | else 36 | { 37 | width = points[1].X - points[0].X; 38 | width = width * 2 / 3; 39 | } 40 | 41 | double yStart = Chart.Point2ChartPoint(new Point(0.0, MinMaxValue.minY)).Y; 42 | for (int i = 0; i < points.Length; ++i) 43 | { 44 | Point p = points[i]; 45 | Brush brush = new SolidColorBrush(Legend[i].Color); 46 | Pen pen = new Pen(brush, 1.0d); 47 | ctx.DrawRectangle(brush, pen, new Rect(p.X - width / 2, p.Y, width, yStart - p.Y)); 48 | } 49 | } 50 | 51 | public override MinMax GetMinMax() 52 | { 53 | MinMax minMax = new MinMax(true); 54 | if (chartPoints.Count == 0) 55 | { 56 | return minMax; 57 | } 58 | 59 | foreach (var p in chartPoints) 60 | { 61 | minMax.Update(p, p); 62 | } 63 | 64 | minMax.minX -= 1; 65 | minMax.maxX += 1; 66 | return minMax; 67 | } 68 | 69 | protected override void OnUpdate() 70 | { 71 | if (Legend == null) 72 | { 73 | Legend = new List(); 74 | for (int i = 0; i < chartPoints.Count; ++i) 75 | { 76 | Legend.Add(new LegendItem(Colors.Blue, string.Empty)); 77 | } 78 | } 79 | 80 | if (chartPoints.Count != Legend.Count) 81 | { 82 | throw new ArgumentException(string.Format( 83 | "chartPoints.Count = {0} and Legend.Count = {1}. Lists must contains same number of elements", 84 | chartPoints.Count, Legend.Count)); 85 | } 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /WPFCanvasChartSolution/WPFChartControl/Drawer/LineSeriesChartDrawer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Windows; 6 | using System.Windows.Media; 7 | using IgorCrevar.WPFChartControl.Model; 8 | 9 | namespace IgorCrevar.WPFChartControl.Drawer 10 | { 11 | public class LineSeriesChartDrawer : AbstractChartDrawer 12 | { 13 | public struct DotsSettings 14 | { 15 | public DotsSettings(Brush brush, Pen pen, double size, bool isEnabled = true) 16 | { 17 | DotBrush = brush; 18 | DotBrush.Freeze(); 19 | DotPen = pen; 20 | DotPen.Freeze(); 21 | Size = size; 22 | IsEnabled = isEnabled; 23 | } 24 | 25 | public Brush DotBrush; 26 | public Pen DotPen; 27 | public double Size; 28 | public bool IsEnabled; 29 | } 30 | 31 | private IList> chartPoints; 32 | public double LineTickness { get; set; } 33 | public DotsSettings Dots { get; set; } 34 | 35 | public LineSeriesChartDrawer(IList> chartPoints) 36 | { 37 | this.chartPoints = chartPoints; 38 | Dots = new DotsSettings(new SolidColorBrush(), new Pen(), 1.0d, false); 39 | this.LineTickness = 1.0d; 40 | } 41 | 42 | public LineSeriesChartDrawer(IList chartPoints) 43 | : this(new List>() { chartPoints }) 44 | { 45 | } 46 | 47 | private void DrawDot(Point point, DrawingContext ctx) 48 | { 49 | if (Dots.IsEnabled) 50 | { 51 | ctx.DrawEllipse(Dots.DotBrush, Dots.DotPen, point, Dots.Size, Dots.Size); 52 | } 53 | } 54 | 55 | public override void Draw(DrawingContext ctx) 56 | { 57 | for (int j = 0; j < chartPoints.Count; ++j) 58 | { 59 | var seriePoints = chartPoints[j]; 60 | if (seriePoints.Count < 2) 61 | { 62 | continue; 63 | } 64 | 65 | Pen pen = new Pen(new SolidColorBrush(Legend[j].Color), LineTickness); 66 | pen.Freeze(); 67 | Point prevPoint = Chart.Point2ChartPoint(seriePoints[0]); 68 | DrawDot(prevPoint, ctx); 69 | for (int i = 1; i < seriePoints.Count; ++i) 70 | { 71 | var currPoint = Chart.Point2ChartPoint(seriePoints[i]); 72 | ctx.DrawLine(pen, prevPoint, currPoint); 73 | prevPoint = currPoint; 74 | DrawDot(prevPoint, ctx); 75 | } 76 | } 77 | } 78 | 79 | public override MinMax GetMinMax() 80 | { 81 | MinMax minMax = new MinMax(true); 82 | foreach (var serie in chartPoints) 83 | { 84 | foreach (var p in serie) 85 | { 86 | minMax.Update(p, p); 87 | } 88 | } 89 | 90 | if (minMax.minY == minMax.maxY) 91 | { 92 | minMax.maxY += 1.0d; 93 | minMax.minY -= 1.0d; 94 | } 95 | 96 | return minMax; 97 | } 98 | 99 | protected override void OnUpdate() 100 | { 101 | if (Legend == null) 102 | { 103 | Legend = new List(); 104 | for (int i = 0; i < chartPoints.Count; ++i) 105 | { 106 | Legend.Add(new LegendItem(Colors.Black, string.Empty)); 107 | } 108 | } 109 | 110 | if (chartPoints.Count != Legend.Count) 111 | { 112 | throw new ArgumentException(string.Format( 113 | "chartPoints.Count = {0} and Legend.Count = {1}. Lists must contains same number of elements", 114 | chartPoints.Count, Legend.Count)); 115 | } 116 | } 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /WPFCanvasChartSolution/WPFChartControl/Drawer/MultiChartDrawer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Windows; 6 | 7 | namespace IgorCrevar.WPFChartControl.Drawer 8 | { 9 | public class MultiChartDrawer : AbstractChartDrawer 10 | { 11 | private IEnumerable drawers; 12 | 13 | public MultiChartDrawer(IEnumerable drawers) 14 | { 15 | this.drawers = drawers; 16 | } 17 | 18 | public override void Draw(System.Windows.Media.DrawingContext ctx) 19 | { 20 | foreach (var it in drawers) 21 | { 22 | it.Draw(ctx); 23 | } 24 | } 25 | 26 | public override void OnChartMouseDown(double x, double y) 27 | { 28 | foreach (var it in drawers) 29 | { 30 | it.OnChartMouseDown(x, y); 31 | } 32 | } 33 | 34 | public override MinMax GetMinMax() 35 | { 36 | MinMax minMax = new MinMax(true); 37 | foreach (var it in drawers) 38 | { 39 | minMax.Update(it.GetMinMax()); 40 | } 41 | 42 | return minMax; 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /WPFCanvasChartSolution/WPFChartControl/Drawer/StackedBarChartDrawer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Windows.Media; 6 | using System.Windows; 7 | 8 | namespace IgorCrevar.WPFChartControl.Drawer 9 | { 10 | public class StackedBarItem 11 | { 12 | public StackedBarItem(double xvalue, IEnumerable yvalues) 13 | { 14 | XValue = xvalue; 15 | YValues = yvalues.Select(i => new StackedBarYValue(i)).ToList(); 16 | } 17 | 18 | public double XValue { get; set; } 19 | internal List YValues { get; set; } 20 | } 21 | 22 | internal class StackedBarYValue 23 | { 24 | public StackedBarYValue(double value) 25 | { 26 | Value = value; 27 | } 28 | 29 | public Color Color { get; set; } 30 | public double Value { get; set; } 31 | } 32 | 33 | public class StackedBarChartDrawer : AbstractChartDrawer 34 | { 35 | private IList values; 36 | 37 | public StackedBarChartDrawer(IList values) 38 | { 39 | this.values = values; 40 | } 41 | 42 | protected override void OnUpdate() 43 | { 44 | if (Legend == null) 45 | { 46 | throw new ArgumentNullException("Legend is null. Can not be null for StackedBar"); 47 | } 48 | } 49 | 50 | public override void Draw(System.Windows.Media.DrawingContext ctx) 51 | { 52 | if (values.Count == 0 || double.MinValue == MinMaxValue.maxX || double.MinValue == MinMaxValue.maxY) 53 | { 54 | return; 55 | } 56 | 57 | var xPoints = values.Select(i => Chart.Point2ChartPoint(new Point(i.XValue, 0.0d)).X).ToArray(); 58 | double width = 0.0d; 59 | if (xPoints.Length == 1) 60 | { 61 | var p1 = Chart.Point2ChartPoint(new Point(xPoints[0] - 1, 0)); 62 | var p2 = Chart.Point2ChartPoint(new Point(xPoints[0] + 1, 0)); 63 | width = p2.X - p1.X; 64 | width = width * 1 / 3; 65 | } 66 | else 67 | { 68 | width = xPoints[1] - xPoints[0]; 69 | width = width * 2 / 3; 70 | } 71 | 72 | for (int i = 0; i < values.Count; ++i) 73 | { 74 | var yValues = values[i].YValues; 75 | double xCoord = xPoints[i]; 76 | double yStart = Chart.Point2ChartPoint(new Point(0.0, MinMaxValue.minY)).Y; 77 | foreach (var yVal in yValues) 78 | { 79 | var yCoord = Chart.Point2ChartPoint(new Point(0.0d, yVal.Value)).Y; 80 | if (yStart - yCoord > 0.0d) 81 | { 82 | Brush brush = new SolidColorBrush(yVal.Color); 83 | Pen pen = new Pen(brush, 1.0d); 84 | ctx.DrawRectangle(brush, pen, new Rect(xCoord - width / 2, yCoord, width, yStart - yCoord)); 85 | } 86 | yStart = yCoord; 87 | } 88 | } 89 | } 90 | 91 | public override MinMax GetMinMax() 92 | { 93 | MinMax minMax = new MinMax(true); 94 | foreach (var v in values) 95 | { 96 | minMax.Update(v.XValue, v.XValue, double.MaxValue, double.MinValue); 97 | if (v.YValues.Count < 1 || v.YValues.Count != Legend.Count) 98 | { 99 | throw new ArgumentException(string.Format( 100 | "StackedBarItem.YValues.Count = {0} and Legend.Count = {1}. Lists must contains same number of elements and > 0", 101 | v.YValues.Count, Legend.Count)); 102 | } 103 | 104 | // fix colors 105 | for (int i = 0; i < Legend.Count; ++i) 106 | { 107 | v.YValues[i].Color = Legend[i].Color; 108 | } 109 | 110 | // sort 111 | v.YValues.Sort((a, b) => a.Value.CompareTo(b.Value)); 112 | minMax.Update(double.MaxValue, double.MinValue, v.YValues[0].Value, v.YValues[v.YValues.Count - 1].Value); 113 | } 114 | 115 | if (double.MinValue != minMax.maxX && double.MinValue != minMax.maxY) 116 | { 117 | minMax.minX -= 1; 118 | minMax.maxX += 1; 119 | } 120 | 121 | return minMax; 122 | } 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /WPFCanvasChartSolution/WPFChartControl/Model/LegendItem.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Windows.Media; 6 | 7 | namespace IgorCrevar.WPFChartControl.Model 8 | { 9 | public class LegendItem 10 | { 11 | public LegendItem(Color color, string name) 12 | { 13 | Color = color; 14 | Name = name; 15 | } 16 | 17 | public Color Color { get; set; } 18 | public string Name { get; set; } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /WPFCanvasChartSolution/WPFChartControl/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("BarChartControl")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("BarChartControl")] 13 | [assembly: AssemblyCopyright("Copyright © 2013")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("584283da-c410-4b2b-960e-25078c7a930e")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /WPFCanvasChartSolution/WPFChartControl/ViewModel/ChartViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | using System.Windows; 3 | using System.Collections.ObjectModel; 4 | using System.Windows.Media; 5 | using IgorCrevar.WPFChartControl.Model; 6 | using IgorCrevar.WPFChartControl.Drawer; 7 | 8 | namespace IgorCrevar.WPFChartControl.ViewModel 9 | { 10 | class ChartViewModel : INotifyPropertyChanged 11 | { 12 | private string xAxisText = string.Empty; 13 | private string yAxisText = string.Empty; 14 | private Visibility horizScrollVisibility = Visibility.Collapsed; 15 | private Visibility vertScrollVisibility = Visibility.Collapsed; 16 | private Visibility legendVisibility = Visibility.Collapsed; 17 | private ObservableCollection legend = new ObservableCollection(); 18 | private Brush background; 19 | private double legendWidth; 20 | 21 | public void Update(AbstractChartDrawer drawer) 22 | { 23 | XAxisText = drawer.XAxisText; 24 | YAxisText = drawer.YAxisText; 25 | HorizScrollVisibility = drawer.HorizScrollVisibility; 26 | VertScrollVisibility = drawer.VertScrollVisibility; 27 | LegendVisibility = drawer.Legend != null && drawer.LegendWidth != 0.0d ? Visibility.Visible : Visibility.Collapsed; 28 | Background = drawer.Background; 29 | LegendWidth = drawer.LegendWidth; 30 | legend.Clear(); 31 | if (drawer.Legend != null) 32 | { 33 | foreach (var it in drawer.Legend) 34 | { 35 | legend.Add(it); 36 | } 37 | } 38 | } 39 | 40 | public string XAxisText 41 | { 42 | get 43 | { 44 | return xAxisText; 45 | } 46 | 47 | set 48 | { 49 | if (xAxisText != value) 50 | { 51 | xAxisText = value; 52 | OnPropertyChanged("XAxisText"); 53 | } 54 | } 55 | } 56 | 57 | public string YAxisText 58 | { 59 | get 60 | { 61 | return yAxisText; 62 | } 63 | 64 | set 65 | { 66 | if (yAxisText != value) 67 | { 68 | yAxisText = value; 69 | OnPropertyChanged("YAxisText"); 70 | } 71 | } 72 | } 73 | 74 | public Visibility HorizScrollVisibility 75 | { 76 | get 77 | { 78 | return horizScrollVisibility; 79 | } 80 | 81 | set 82 | { 83 | if (horizScrollVisibility != value) 84 | { 85 | horizScrollVisibility = value; 86 | OnPropertyChanged("HorizScrollVisibility"); 87 | } 88 | } 89 | } 90 | 91 | public Visibility VertScrollVisibility 92 | { 93 | get 94 | { 95 | return vertScrollVisibility; 96 | } 97 | 98 | set 99 | { 100 | if (vertScrollVisibility != value) 101 | { 102 | vertScrollVisibility = value; 103 | OnPropertyChanged("VertScrollVisibility"); 104 | } 105 | } 106 | } 107 | 108 | public Visibility LegendVisibility 109 | { 110 | get 111 | { 112 | return legendVisibility; 113 | } 114 | 115 | set 116 | { 117 | if (legendVisibility != value) 118 | { 119 | legendVisibility = value; 120 | OnPropertyChanged("LegendVisibility"); 121 | } 122 | } 123 | } 124 | 125 | public double LegendWidth 126 | { 127 | get 128 | { 129 | return legendWidth; 130 | } 131 | 132 | set 133 | { 134 | if (legendWidth != value) 135 | { 136 | legendWidth = value; 137 | OnPropertyChanged("LegendWidth"); 138 | } 139 | } 140 | } 141 | 142 | public ObservableCollection Legend 143 | { 144 | get 145 | { 146 | return legend; 147 | } 148 | } 149 | 150 | public Brush Background 151 | { 152 | get 153 | { 154 | return background; 155 | } 156 | 157 | set 158 | { 159 | if (background != value) 160 | { 161 | background = value; 162 | OnPropertyChanged("Background"); 163 | } 164 | } 165 | } 166 | 167 | #region INotifyPropertyChanged part 168 | public event PropertyChangedEventHandler PropertyChanged; 169 | 170 | protected void OnPropertyChanged(string name) 171 | { 172 | PropertyChangedEventHandler handler = PropertyChanged; 173 | if (handler != null) 174 | { 175 | handler(this, new PropertyChangedEventArgs(name)); 176 | } 177 | } 178 | #endregion 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /WPFCanvasChartSolution/WPFChartControl/WPFChartControl.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | AnyCPU 6 | 8.0.30703 7 | 2.0 8 | {463766C2-C063-4970-96C8-72A39B2A3474} 9 | Library 10 | Properties 11 | IgorCrevar.WPFChartControl 12 | WPFChartControl 13 | v4.0 14 | 512 15 | 16 | 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | 25 | 26 | pdbonly 27 | true 28 | bin\Release\ 29 | TRACE 30 | prompt 31 | 4 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | ChartControl.xaml 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | MSBuild:Compile 66 | Designer 67 | 68 | 69 | 70 | 71 | {B0C5D614-8F9E-4C31-B47D-EB8CFE7679C1} 72 | WPFCanvasChart 73 | 74 | 75 | 76 | 83 | -------------------------------------------------------------------------------- /WPFCanvasChartSolution/WPFChartControlExample/App.xaml: -------------------------------------------------------------------------------- 1 |  5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /WPFCanvasChartSolution/WPFChartControlExample/App.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Configuration; 4 | using System.Data; 5 | using System.Linq; 6 | using System.Windows; 7 | 8 | namespace WPFChartControlExample 9 | { 10 | /// 11 | /// Interaction logic for App.xaml 12 | /// 13 | public partial class App : Application 14 | { 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /WPFCanvasChartSolution/WPFChartControlExample/MainViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using IgorCrevar.WPFCanvasChart; 5 | using IgorCrevar.WPFCanvasChart.Interpolators; 6 | using System.Windows; 7 | using System.Windows.Media; 8 | using IgorCrevar.WPFChartControl.Model; 9 | using IgorCrevar.WPFChartControl.Drawer; 10 | 11 | namespace WPFChartControlExample 12 | { 13 | class MainViewModel : INotifyPropertyChanged 14 | { 15 | class CustomInterpolator : WPFCanvasChartIntInterpolator 16 | { 17 | public override string Format(double value) 18 | { 19 | var months = new string[] { 20 | "January", "February", "March", "April", "Maj", "June", "July" 21 | }; 22 | int v = (int)value; 23 | return v >= 0 && v < months.Length ? months[v] : string.Empty; 24 | } 25 | 26 | public override string FormatLongestValue() 27 | { 28 | return "February"; 29 | } 30 | } 31 | 32 | private AbstractChartDrawer barChartDrawer; 33 | private AbstractChartDrawer lineSeriesChartDrawer; 34 | private AbstractChartDrawer stackedBarChartDrawer; 35 | 36 | public MainViewModel() 37 | { 38 | var rnd = new Random(); 39 | BarChartDrawer = new BarChartDrawer(new Point[]{ 40 | new Point(1.0d, rnd.Next(100)), 41 | new Point(2.0d, rnd.Next(100)), 42 | new Point(3.0d, rnd.Next(100)), 43 | new Point(4.0d, rnd.Next(100)), 44 | }) 45 | { 46 | VertScrollVisibility = Visibility.Collapsed, 47 | YAxisInterpolator = new WPFCanvasChartIntInterpolator(), 48 | XAxisInterpolator = new WPFCanvasChartIntEmptyInterpolator(), 49 | YAxisText = "Number of people", 50 | Legend = new LegendItem[] 51 | { 52 | new LegendItem(Colors.Blue, "Programmers"), 53 | new LegendItem(Colors.Red, "Designers"), 54 | new LegendItem(Colors.Yellow, "Admins"), 55 | new LegendItem(Colors.Brown, "Management"), 56 | }, 57 | FixedYMin = 0.0d, 58 | LegendWidth = 120.0d, 59 | }; 60 | 61 | var serie1 = new List(); 62 | var serie2 = new List(); 63 | for (int i = 0; i < rnd.Next(1000) + 1000; ++i) 64 | { 65 | serie1.Add(new Point(i, rnd.NextDouble() * 200 - 100)); 66 | serie2.Add(new Point(i + 1, rnd.NextDouble() * 200 - 100)); 67 | } 68 | 69 | LineSeriesChartDrawer = new LineSeriesChartDrawer(new List>{ 70 | serie1, serie2 71 | }); 72 | 73 | StackedBarChartDrawer = new StackedBarChartDrawer(new List 74 | { 75 | new StackedBarItem(0, new double[] { 50, 30, 20 }), 76 | new StackedBarItem(1, new double[] { 100, 0, 100 }), 77 | new StackedBarItem(2, new double[] { 40, 10, 30 }), 78 | new StackedBarItem(3, new double[] { 100, 50, 50 }), 79 | new StackedBarItem(4, new double[] { 80, 90, -10 }), 80 | new StackedBarItem(5, new double[] { 30, 20, 10 }), 81 | new StackedBarItem(6, new double[] { 20, 5, 15 }), 82 | }) 83 | { 84 | YAxisText = "Power", 85 | XAxisText = "Month", 86 | Settings = new WPFCanvasChartSettings(), 87 | YAxisInterpolator = new WPFCanvasChartFloatInterpolator(), 88 | XAxisInterpolator = new CustomInterpolator(), 89 | Legend = new LegendItem[] 90 | { 91 | new LegendItem(Colors.Blue, "Active"), 92 | new LegendItem(Colors.Red, "Reactive"), 93 | new LegendItem(Colors.Yellow, "Total"), 94 | }, 95 | FixedYMin = 0.0d, 96 | LegendWidth = 120.0d, 97 | }; 98 | } 99 | 100 | public AbstractChartDrawer BarChartDrawer 101 | { 102 | get 103 | { 104 | return barChartDrawer; 105 | } 106 | 107 | set 108 | { 109 | if (barChartDrawer != value) 110 | { 111 | barChartDrawer = value; 112 | OnPropertyChanged("BarChartDrawer"); 113 | } 114 | } 115 | } 116 | 117 | public AbstractChartDrawer LineSeriesChartDrawer 118 | { 119 | get 120 | { 121 | return lineSeriesChartDrawer; 122 | } 123 | 124 | set 125 | { 126 | if (lineSeriesChartDrawer != value) 127 | { 128 | lineSeriesChartDrawer = value; 129 | OnPropertyChanged("LineSeriesChartDrawer"); 130 | } 131 | } 132 | } 133 | 134 | public AbstractChartDrawer StackedBarChartDrawer 135 | { 136 | get 137 | { 138 | return stackedBarChartDrawer; 139 | } 140 | 141 | set 142 | { 143 | if (stackedBarChartDrawer != value) 144 | { 145 | stackedBarChartDrawer = value; 146 | OnPropertyChanged("StackedBarChartDrawer"); 147 | } 148 | } 149 | } 150 | 151 | #region INotifyPropertyChanged part 152 | public event PropertyChangedEventHandler PropertyChanged; 153 | protected void OnPropertyChanged(string name) 154 | { 155 | PropertyChangedEventHandler handler = PropertyChanged; 156 | if (handler != null) 157 | { 158 | handler(this, new PropertyChangedEventArgs(name)); 159 | } 160 | } 161 | #endregion 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /WPFCanvasChartSolution/WPFChartControlExample/MainWindow.xaml: -------------------------------------------------------------------------------- 1 |  6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /WPFCanvasChartSolution/WPFChartControlExample/MainWindow.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Windows; 6 | using System.Windows.Controls; 7 | using System.Windows.Data; 8 | using System.Windows.Documents; 9 | using System.Windows.Input; 10 | using System.Windows.Media; 11 | using System.Windows.Media.Imaging; 12 | using System.Windows.Navigation; 13 | using System.Windows.Shapes; 14 | 15 | namespace WPFChartControlExample 16 | { 17 | /// 18 | /// Interaction logic for MainWindow.xaml 19 | /// 20 | public partial class MainWindow : Window 21 | { 22 | public MainWindow() 23 | { 24 | InitializeComponent(); 25 | this.DataContext = new MainViewModel(); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /WPFCanvasChartSolution/WPFChartControlExample/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Resources; 3 | using System.Runtime.CompilerServices; 4 | using System.Runtime.InteropServices; 5 | using System.Windows; 6 | 7 | // General Information about an assembly is controlled through the following 8 | // set of attributes. Change these attribute values to modify the information 9 | // associated with an assembly. 10 | [assembly: AssemblyTitle("BarChart")] 11 | [assembly: AssemblyDescription("")] 12 | [assembly: AssemblyConfiguration("")] 13 | [assembly: AssemblyCompany("")] 14 | [assembly: AssemblyProduct("BarChart")] 15 | [assembly: AssemblyCopyright("Copyright © 2013")] 16 | [assembly: AssemblyTrademark("")] 17 | [assembly: AssemblyCulture("")] 18 | 19 | // Setting ComVisible to false makes the types in this assembly not visible 20 | // to COM components. If you need to access a type in this assembly from 21 | // COM, set the ComVisible attribute to true on that type. 22 | [assembly: ComVisible(false)] 23 | 24 | //In order to begin building localizable applications, set 25 | //CultureYouAreCodingWith in your .csproj file 26 | //inside a . For example, if you are using US english 27 | //in your source files, set the to en-US. Then uncomment 28 | //the NeutralResourceLanguage attribute below. Update the "en-US" in 29 | //the line below to match the UICulture setting in the project file. 30 | 31 | //[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] 32 | 33 | 34 | [assembly: ThemeInfo( 35 | ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located 36 | //(used if a resource is not found in the page, 37 | // or application resource dictionaries) 38 | ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located 39 | //(used if a resource is not found in the page, 40 | // app, or any theme specific resource dictionaries) 41 | )] 42 | 43 | 44 | // Version information for an assembly consists of the following four values: 45 | // 46 | // Major Version 47 | // Minor Version 48 | // Build Number 49 | // Revision 50 | // 51 | // You can specify all the values or you can default the Build and Revision Numbers 52 | // by using the '*' as shown below: 53 | // [assembly: AssemblyVersion("1.0.*")] 54 | [assembly: AssemblyVersion("1.0.0.0")] 55 | [assembly: AssemblyFileVersion("1.0.0.0")] 56 | -------------------------------------------------------------------------------- /WPFCanvasChartSolution/WPFChartControlExample/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.18052 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace WPFChartControlExample.Properties { 12 | using System; 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resources() { 33 | } 34 | 35 | /// 36 | /// Returns the cached ResourceManager instance used by this class. 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | internal static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("WPFChartControlExample.Properties.Resources", typeof(Resources).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// Overrides the current thread's CurrentUICulture property for all 51 | /// resource lookups using this strongly typed resource class. 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | internal static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /WPFCanvasChartSolution/WPFChartControlExample/Properties/Resources.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | text/microsoft-resx 107 | 108 | 109 | 2.0 110 | 111 | 112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 113 | 114 | 115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | -------------------------------------------------------------------------------- /WPFCanvasChartSolution/WPFChartControlExample/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.18052 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace WPFChartControlExample.Properties { 12 | 13 | 14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "10.0.0.0")] 16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { 17 | 18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 19 | 20 | public static Settings Default { 21 | get { 22 | return defaultInstance; 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /WPFCanvasChartSolution/WPFChartControlExample/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /WPFCanvasChartSolution/WPFChartControlExample/WPFChartControlExample.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | x86 6 | 8.0.30703 7 | 2.0 8 | {CC3CB22D-B1DD-49CD-B057-7FB244327B41} 9 | WinExe 10 | Properties 11 | WPFChartControlExample 12 | WPFChartControlExample 13 | v4.0 14 | Client 15 | 512 16 | {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 17 | 4 18 | 19 | 20 | x86 21 | true 22 | full 23 | false 24 | bin\Debug\ 25 | DEBUG;TRACE 26 | prompt 27 | 4 28 | 29 | 30 | x86 31 | pdbonly 32 | true 33 | bin\Release\ 34 | TRACE 35 | prompt 36 | 4 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 4.0 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | MSBuild:Compile 58 | Designer 59 | 60 | 61 | MSBuild:Compile 62 | Designer 63 | 64 | 65 | App.xaml 66 | Code 67 | 68 | 69 | 70 | MainWindow.xaml 71 | Code 72 | 73 | 74 | 75 | 76 | Code 77 | 78 | 79 | True 80 | True 81 | Resources.resx 82 | 83 | 84 | True 85 | Settings.settings 86 | True 87 | 88 | 89 | ResXFileCodeGenerator 90 | Resources.Designer.cs 91 | 92 | 93 | SettingsSingleFileGenerator 94 | Settings.Designer.cs 95 | 96 | 97 | 98 | 99 | 100 | {B0C5D614-8F9E-4C31-B47D-EB8CFE7679C1} 101 | WPFCanvasChart 102 | 103 | 104 | {463766C2-C063-4970-96C8-72A39B2A3474} 105 | WPFChartControl 106 | 107 | 108 | 109 | 116 | -------------------------------------------------------------------------------- /WPFCanvasChartSolution/WPFChartControlExample/WPFChartControlExample.csproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | --------------------------------------------------------------------------------