├── .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 |
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 |
--------------------------------------------------------------------------------