/Main/src/DynamicDataDisplay/ChartPlotter.cs
C# | 526 lines | 364 code | 74 blank | 88 comment | 55 complexity | ca590ef526836dbaf92113a00c203eb6 MD5 | raw file
Possible License(s): CC-BY-SA-3.0
1using System; 2using System.Linq; 3using System.ComponentModel; 4using System.Windows; 5using System.Windows.Controls; 6using Microsoft.Research.DynamicDataDisplay.Charts; 7using Microsoft.Research.DynamicDataDisplay.Charts.Navigation; 8using Microsoft.Research.DynamicDataDisplay.Navigation; 9using Microsoft.Research.DynamicDataDisplay.Common; 10using Microsoft.Research.DynamicDataDisplay.Charts.Axes; 11 12namespace Microsoft.Research.DynamicDataDisplay 13{ 14 /// <summary> 15 /// Chart plotter is a plotter that renders axis and grid 16 /// </summary> 17 public class ChartPlotter : Plotter2D 18 { 19 private GeneralAxis horizontalAxis = new HorizontalAxis(); 20 private GeneralAxis verticalAxis = new VerticalAxis(); 21 private AxisGrid axisGrid = new AxisGrid(); 22 23 private Legend legend = new Legend(); 24 25 public ItemsPanelTemplate LegendPanelTemplate 26 { 27 get { return legend.ItemsPanel; } 28 set 29 { 30 if (legend == null) 31 throw new ArgumentNullException("LegendPanelTemplate"); 32 33 legend.ItemsPanel = value; 34 } 35 } 36 37 public Style LegendStyle 38 { 39 get { return legend.Style; } 40 set { legend.Style = value; } 41 } 42 43 /// <summary> 44 /// Sets a value indicating whether to enable smooth axes panning for numeric axes. 45 /// </summary> 46 /// <value> 47 /// <c>true</c> if enable smooth axes panning for numeric axes; otherwise, <c>false</c>. 48 /// </value> 49 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] 50 public bool EnableSmoothPanningForNumericAxes 51 { 52 // todo improve returned value 53 get { return false; }// throw new NotImplementedException(); } 54 set 55 { 56 foreach (var axis in Children.OfType<NumericAxis>()) 57 { 58 axis.UseSmoothPanning = value; 59 } 60 } 61 } 62 63 /// <summary> 64 /// Initializes a new instance of the <see cref="ChartPlotter"/> class. 65 /// </summary> 66 public ChartPlotter() 67 : base() 68 { 69 horizontalAxis.TicksChanged += OnHorizontalAxisTicksChanged; 70 verticalAxis.TicksChanged += OnVerticalAxisTicksChanged; 71 72 SetIsDefaultAxis(horizontalAxis as DependencyObject, true); 73 SetIsDefaultAxis(verticalAxis as DependencyObject, true); 74 75 mouseNavigation = new MouseNavigation(); 76 keyboardNavigation = new KeyboardNavigation(); 77 defaultContextMenu = new DefaultContextMenu(); 78 horizontalAxisNavigation = new AxisNavigation { Placement = AxisPlacement.Bottom }; 79 verticalAxisNavigation = new AxisNavigation { Placement = AxisPlacement.Left }; 80 81 Children.AddMany( 82 horizontalAxis, 83 verticalAxis, 84 axisGrid, 85 mouseNavigation, 86 keyboardNavigation, 87 defaultContextMenu, 88 horizontalAxisNavigation, 89 verticalAxisNavigation, 90 new LongOperationsIndicator(), 91 legend 92 ); 93 94#if DEBUG 95 Children.Add(new DebugMenu()); 96#endif 97 98 SetAllChildrenAsDefault(); 99 } 100 101 /// <summary> 102 /// Creates generic plotter from this ChartPlotter. 103 /// </summary> 104 /// <returns></returns> 105 public GenericChartPlotter<double, double> GetGenericPlotter() 106 { 107 return new GenericChartPlotter<double, double>(this); 108 } 109 110 /// <summary> 111 /// Creates generic plotter from this ChartPlotter. 112 /// Horizontal and Vertical types of GenericPlotter should correspond to ChartPlotter's actual main axes types. 113 /// </summary> 114 /// <typeparam name="THorizontal">The type of horizontal values.</typeparam> 115 /// <typeparam name="TVertical">The type of vertical values.</typeparam> 116 /// <returns>GenericChartPlotter, associated to this ChartPlotter.</returns> 117 public GenericChartPlotter<THorizontal, TVertical> GetGenericPlotter<THorizontal, TVertical>() 118 { 119 return new GenericChartPlotter<THorizontal, TVertical>(this); 120 } 121 122 /// <summary> 123 /// Creates generic plotter from this ChartPlotter. 124 /// </summary> 125 /// <typeparam name="THorizontal">The type of the horizontal axis.</typeparam> 126 /// <typeparam name="TVertical">The type of the vertical axis.</typeparam> 127 /// <param name="horizontalAxis">The horizontal axis to use as data conversion source.</param> 128 /// <param name="verticalAxis">The vertical axis to use as data conversion source.</param> 129 /// <returns>GenericChartPlotter, associated to this ChartPlotter</returns> 130 public GenericChartPlotter<THorizontal, TVertical> GetGenericPlotter<THorizontal, TVertical>(AxisBase<THorizontal> horizontalAxis, AxisBase<TVertical> verticalAxis) 131 { 132 return new GenericChartPlotter<THorizontal, TVertical>(this, horizontalAxis, verticalAxis); 133 } 134 135 protected ChartPlotter(PlotterLoadMode loadMode) : base(loadMode) { } 136 137 /// <summary> 138 /// Creates empty plotter without any axes, navigation, etc. 139 /// </summary> 140 /// <returns>Empty plotter without any axes, navigation, etc.</returns> 141 public static ChartPlotter CreateEmpty() 142 { 143 return new ChartPlotter(PlotterLoadMode.OnlyViewport); 144 } 145 146 public void BeginLongOperation() 147 { 148 LongOperationsIndicator.BeginLongOperation(this); 149 } 150 151 public void EndLongOperation() 152 { 153 LongOperationsIndicator.EndLongOperation(this); 154 } 155 156 #region Default charts 157 158 private MouseNavigation mouseNavigation; 159 /// <summary> 160 /// Gets the default mouse navigation of ChartPlotter. 161 /// </summary> 162 /// <value>The mouse navigation.</value> 163 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] 164 public MouseNavigation MouseNavigation 165 { 166 get { return mouseNavigation; } 167 } 168 169 private KeyboardNavigation keyboardNavigation; 170 /// <summary> 171 /// Gets the default keyboard navigation of ChartPlotter. 172 /// </summary> 173 /// <value>The keyboard navigation.</value> 174 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] 175 public KeyboardNavigation KeyboardNavigation 176 { 177 get { return keyboardNavigation; } 178 } 179 180 private DefaultContextMenu defaultContextMenu; 181 /// <summary> 182 /// Gets the default context menu of ChartPlotter. 183 /// </summary> 184 /// <value>The default context menu.</value> 185 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] 186 public DefaultContextMenu DefaultContextMenu 187 { 188 get { return defaultContextMenu; } 189 } 190 191 private AxisNavigation horizontalAxisNavigation; 192 /// <summary> 193 /// Gets the default horizontal axis navigation of ChartPlotter. 194 /// </summary> 195 /// <value>The horizontal axis navigation.</value> 196 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] 197 public AxisNavigation HorizontalAxisNavigation 198 { 199 get { return horizontalAxisNavigation; } 200 } 201 202 private AxisNavigation verticalAxisNavigation; 203 /// <summary> 204 /// Gets the default vertical axis navigation of ChartPlotter. 205 /// </summary> 206 /// <value>The vertical axis navigation.</value> 207 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] 208 public AxisNavigation VerticalAxisNavigation 209 { 210 get { return verticalAxisNavigation; } 211 } 212 213 /// <summary> 214 /// Gets the default axis grid of ChartPlotter. 215 /// </summary> 216 /// <value>The axis grid.</value> 217 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] 218 public AxisGrid AxisGrid 219 { 220 get { return axisGrid; } 221 } 222 223 #endregion 224 225 private void OnHorizontalAxisTicksChanged(object sender, EventArgs e) 226 { 227 GeneralAxis axis = (GeneralAxis)sender; 228 UpdateHorizontalTicks(axis); 229 } 230 231 private void UpdateHorizontalTicks(GeneralAxis axis) 232 { 233 axisGrid.BeginTicksUpdate(); 234 235 if (axis != null) 236 { 237 axisGrid.HorizontalTicks = axis.ScreenTicks; 238 axisGrid.MinorHorizontalTicks = axis.MinorScreenTicks; 239 } 240 else 241 { 242 axisGrid.HorizontalTicks = null; 243 axisGrid.MinorHorizontalTicks = null; 244 } 245 246 axisGrid.EndTicksUpdate(); 247 } 248 249 private void OnVerticalAxisTicksChanged(object sender, EventArgs e) 250 { 251 GeneralAxis axis = (GeneralAxis)sender; 252 UpdateVerticalTicks(axis); 253 } 254 255 private void UpdateVerticalTicks(GeneralAxis axis) 256 { 257 axisGrid.BeginTicksUpdate(); 258 259 if (axis != null) 260 { 261 axisGrid.VerticalTicks = axis.ScreenTicks; 262 axisGrid.MinorVerticalTicks = axis.MinorScreenTicks; 263 } 264 else 265 { 266 axisGrid.VerticalTicks = null; 267 axisGrid.MinorVerticalTicks = null; 268 } 269 270 axisGrid.EndTicksUpdate(); 271 } 272 273 bool keepOldAxis = false; 274 bool updatingAxis = false; 275 276 /// <summary> 277 /// Gets or sets the main vertical axis of ChartPlotter. 278 /// Main vertical axis of ChartPlotter is axis which ticks are used to draw horizontal lines on AxisGrid. 279 /// Value can be set to null to completely remove main vertical axis. 280 /// </summary> 281 /// <value>The main vertical axis.</value> 282 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] 283 public GeneralAxis MainVerticalAxis 284 { 285 get { return verticalAxis; } 286 set 287 { 288 if (updatingAxis) 289 return; 290 291 if (value == null && verticalAxis != null) 292 { 293 if (!keepOldAxis) 294 { 295 Children.Remove(verticalAxis); 296 } 297 verticalAxis.TicksChanged -= OnVerticalAxisTicksChanged; 298 verticalAxis = null; 299 300 UpdateVerticalTicks(verticalAxis); 301 302 return; 303 } 304 305 VerifyAxisType(value.Placement, AxisType.Vertical); 306 307 if (value != verticalAxis) 308 { 309 ValidateVerticalAxis(value); 310 311 updatingAxis = true; 312 313 if (verticalAxis != null) 314 { 315 verticalAxis.TicksChanged -= OnVerticalAxisTicksChanged; 316 SetIsDefaultAxis(verticalAxis, false); 317 if (!keepOldAxis) 318 { 319 Children.Remove(verticalAxis); 320 } 321 value.Visibility = verticalAxis.Visibility; 322 } 323 SetIsDefaultAxis(value, true); 324 325 verticalAxis = value; 326 verticalAxis.TicksChanged += OnVerticalAxisTicksChanged; 327 328 if (!Children.Contains(value)) 329 { 330 Children.Add(value); 331 } 332 333 UpdateVerticalTicks(value); 334 OnVerticalAxisChanged(); 335 336 updatingAxis = false; 337 } 338 } 339 } 340 341 protected virtual void OnVerticalAxisChanged() { } 342 protected virtual void ValidateVerticalAxis(GeneralAxis axis) { } 343 344 /// <summary> 345 /// Gets or sets the main horizontal axis visibility. 346 /// </summary> 347 /// <value>The main horizontal axis visibility.</value> 348 public Visibility MainHorizontalAxisVisibility 349 { 350 get { return MainHorizontalAxis != null ? ((UIElement)MainHorizontalAxis).Visibility : Visibility.Hidden; } 351 set 352 { 353 if (MainHorizontalAxis != null) 354 { 355 ((UIElement)MainHorizontalAxis).Visibility = value; 356 } 357 } 358 } 359 360 /// <summary> 361 /// Gets or sets the main vertical axis visibility. 362 /// </summary> 363 /// <value>The main vertical axis visibility.</value> 364 public Visibility MainVerticalAxisVisibility 365 { 366 get { return MainVerticalAxis != null ? ((UIElement)MainVerticalAxis).Visibility : Visibility.Hidden; } 367 set 368 { 369 if (MainVerticalAxis != null) 370 { 371 ((UIElement)MainVerticalAxis).Visibility = value; 372 } 373 } 374 } 375 376 /// <summary> 377 /// Gets or sets the main horizontal axis of ChartPlotter. 378 /// Main horizontal axis of ChartPlotter is axis which ticks are used to draw vertical lines on AxisGrid. 379 /// Value can be set to null to completely remove main horizontal axis. 380 /// </summary> 381 /// <value>The main horizontal axis.</value> 382 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] 383 public GeneralAxis MainHorizontalAxis 384 { 385 get { return horizontalAxis; } 386 set 387 { 388 if (updatingAxis) 389 return; 390 391 if (value == null && horizontalAxis != null) 392 { 393 Children.Remove(horizontalAxis); 394 horizontalAxis.TicksChanged -= OnHorizontalAxisTicksChanged; 395 horizontalAxis = null; 396 397 UpdateHorizontalTicks(horizontalAxis); 398 399 return; 400 } 401 402 VerifyAxisType(value.Placement, AxisType.Horizontal); 403 404 if (value != horizontalAxis) 405 { 406 ValidateHorizontalAxis(value); 407 408 updatingAxis = true; 409 410 if (horizontalAxis != null) 411 { 412 horizontalAxis.TicksChanged -= OnHorizontalAxisTicksChanged; 413 SetIsDefaultAxis(horizontalAxis, false); 414 if (!keepOldAxis) 415 { 416 Children.Remove(horizontalAxis); 417 } 418 value.Visibility = horizontalAxis.Visibility; 419 } 420 SetIsDefaultAxis(value, true); 421 422 horizontalAxis = value; 423 horizontalAxis.TicksChanged += OnHorizontalAxisTicksChanged; 424 425 if (!Children.Contains(value)) 426 { 427 Children.Add(value); 428 } 429 430 UpdateHorizontalTicks(value); 431 OnHorizontalAxisChanged(); 432 433 updatingAxis = false; 434 } 435 } 436 } 437 438 protected virtual void OnHorizontalAxisChanged() { } 439 protected virtual void ValidateHorizontalAxis(GeneralAxis axis) { } 440 441 private static void VerifyAxisType(AxisPlacement axisPlacement, AxisType axisType) 442 { 443 bool result = false; 444 switch (axisPlacement) 445 { 446 case AxisPlacement.Left: 447 case AxisPlacement.Right: 448 result = axisType == AxisType.Vertical; 449 break; 450 case AxisPlacement.Top: 451 case AxisPlacement.Bottom: 452 result = axisType == AxisType.Horizontal; 453 break; 454 default: 455 break; 456 } 457 458 if (!result) 459 throw new ArgumentException(Strings.Exceptions.InvalidAxisPlacement); 460 } 461 462 protected override void OnIsDefaultAxisChangedCore(DependencyObject d, DependencyPropertyChangedEventArgs e) 463 { 464 GeneralAxis axis = d as GeneralAxis; 465 if (axis != null) 466 { 467 bool value = (bool)e.NewValue; 468 bool oldKeepOldAxis = keepOldAxis; 469 470 bool horizontal = axis.Placement == AxisPlacement.Bottom || axis.Placement == AxisPlacement.Top; 471 keepOldAxis = true; 472 473 if (value && horizontal) 474 { 475 MainHorizontalAxis = axis; 476 } 477 else if (value && !horizontal) 478 { 479 MainVerticalAxis = axis; 480 } 481 else if (!value && horizontal) 482 { 483 MainHorizontalAxis = null; 484 } 485 else if (!value && !horizontal) 486 { 487 MainVerticalAxis = null; 488 } 489 490 keepOldAxis = oldKeepOldAxis; 491 } 492 } 493 494 /// <summary> 495 /// Gets the default legend of ChartPlotter. 496 /// </summary> 497 /// <value>The legend.</value> 498 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] 499 public Legend Legend 500 { 501 get { return legend; } 502 } 503 504 /// <summary> 505 /// Gets or sets the visibility of legend. 506 /// </summary> 507 /// <value>The legend visibility.</value> 508 public Visibility LegendVisibility 509 { 510 get { return legend.Visibility; } 511 set { legend.Visibility = value; } 512 } 513 514 public bool LegendVisible 515 { 516 get { return legend.LegendVisible; } 517 set { legend.LegendVisible = value; } 518 } 519 520 private enum AxisType 521 { 522 Horizontal, 523 Vertical 524 } 525 } 526}