Initial Commit
This commit is contained in:
952
Common/Plotter.cs
Normal file
952
Common/Plotter.cs
Normal file
@@ -0,0 +1,952 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using Microsoft.Research.DynamicDataDisplay.Common;
|
||||
using Microsoft.Research.DynamicDataDisplay.Common.Auxiliary;
|
||||
using System.Windows.Markup;
|
||||
using System.ComponentModel;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Collections.Specialized;
|
||||
using System.Windows.Input;
|
||||
using Microsoft.Research.DynamicDataDisplay.Common.UndoSystem;
|
||||
using Microsoft.Research.DynamicDataDisplay.Navigation;
|
||||
using System.Windows.Data;
|
||||
using Microsoft.Research.DynamicDataDisplay.Charts.Navigation;
|
||||
using System.Windows.Automation.Peers;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Microsoft.Research.DynamicDataDisplay
|
||||
{
|
||||
/// <summary>Plotter is a base control for displaying various graphs. It provides
|
||||
/// means to draw chart itself and side space for axes, annotations, etc</summary>
|
||||
[ContentProperty("Children")]
|
||||
[TemplatePart(Name = "PART_HeaderPanel", Type = typeof(StackPanel))]
|
||||
[TemplatePart(Name = "PART_FooterPanel", Type = typeof(StackPanel))]
|
||||
[TemplatePart(Name = "PART_BottomPanel", Type = typeof(StackPanel))]
|
||||
[TemplatePart(Name = "PART_LeftPanel", Type = typeof(StackPanel))]
|
||||
[TemplatePart(Name = "PART_RightPanel", Type = typeof(StackPanel))]
|
||||
[TemplatePart(Name = "PART_TopPanel", Type = typeof(StackPanel))]
|
||||
[TemplatePart(Name = "PART_MainCanvas", Type = typeof(Canvas))]
|
||||
[TemplatePart(Name = "PART_CentralGrid", Type = typeof(Grid))]
|
||||
[TemplatePart(Name = "PART_MainGrid", Type = typeof(Grid))]
|
||||
[TemplatePart(Name = "PART_ContentsGrid", Type = typeof(Grid))]
|
||||
[TemplatePart(Name = "PART_ParallelCanvas", Type = typeof(Canvas))]
|
||||
public abstract class Plotter : ContentControl
|
||||
{
|
||||
private PlotterLoadMode loadMode = PlotterLoadMode.Normal;
|
||||
protected PlotterLoadMode LoadMode
|
||||
{
|
||||
get { return loadMode; }
|
||||
}
|
||||
|
||||
protected Plotter() : this(PlotterLoadMode.Normal) { }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Plotter"/> class.
|
||||
/// </summary>
|
||||
protected Plotter(PlotterLoadMode loadMode)
|
||||
{
|
||||
this.loadMode = loadMode;
|
||||
|
||||
SetPlotter(this, this);
|
||||
|
||||
if (loadMode == PlotterLoadMode.Normal)
|
||||
{
|
||||
UpdateUIParts();
|
||||
}
|
||||
|
||||
children = new PlotterChildrenCollection(this);
|
||||
children.CollectionChanged += OnChildrenCollectionChanged;
|
||||
Loaded += Plotter_Loaded;
|
||||
Unloaded += Plotter_Unloaded;
|
||||
|
||||
genericResources = (ResourceDictionary)Application.LoadComponent(new Uri("/DynamicDataDisplay;component/Themes/Generic.xaml", UriKind.Relative));
|
||||
|
||||
ContextMenu = null;
|
||||
}
|
||||
|
||||
void Plotter_Unloaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
OnUnloaded();
|
||||
}
|
||||
|
||||
protected virtual void OnUnloaded() { }
|
||||
|
||||
protected override AutomationPeer OnCreateAutomationPeer()
|
||||
{
|
||||
return new PlotterAutomationPeer(this);
|
||||
}
|
||||
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public override bool ShouldSerializeContent()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
protected override bool ShouldSerializeProperty(DependencyProperty dp)
|
||||
{
|
||||
// do not serialize context menu if it was created by DefaultContextMenu, because that context menu items contains references of plotter
|
||||
if (dp == ContextMenuProperty && children.Any(el => el is DefaultContextMenu)) return false;
|
||||
if (dp == TemplateProperty) return false;
|
||||
if (dp == ContentProperty) return false;
|
||||
|
||||
return base.ShouldSerializeProperty(dp);
|
||||
}
|
||||
|
||||
private const string templateKey = "defaultPlotterTemplate";
|
||||
private const string styleKey = "defaultPlotterStyle";
|
||||
private void UpdateUIParts()
|
||||
{
|
||||
ResourceDictionary dict = new ResourceDictionary
|
||||
{
|
||||
Source = new Uri("/DynamicDataDisplay;component/Common/PlotterStyle.xaml", UriKind.Relative)
|
||||
};
|
||||
|
||||
Resources.MergedDictionaries.Add(dict);
|
||||
|
||||
Style = (Style)dict[styleKey];
|
||||
|
||||
ControlTemplate template = (ControlTemplate)dict[templateKey];
|
||||
Template = template;
|
||||
ApplyTemplate();
|
||||
}
|
||||
|
||||
private ResourceDictionary genericResources;
|
||||
protected ResourceDictionary GenericResources
|
||||
{
|
||||
get { return genericResources; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Forces plotter to load.
|
||||
/// </summary>
|
||||
public void PerformLoad()
|
||||
{
|
||||
isLoadedIntensionally = true;
|
||||
ApplyTemplate();
|
||||
|
||||
Plotter_Loaded(null, null);
|
||||
}
|
||||
|
||||
private bool isLoadedIntensionally = false;
|
||||
protected virtual bool IsLoadedInternal
|
||||
{
|
||||
get { return isLoadedIntensionally || IsLoaded; }
|
||||
}
|
||||
|
||||
private void Plotter_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
ExecuteWaitingChildrenAdditions();
|
||||
|
||||
OnLoaded();
|
||||
}
|
||||
|
||||
protected internal void ExecuteWaitingChildrenAdditions()
|
||||
{
|
||||
foreach (var action in waitingForExecute)
|
||||
{
|
||||
action();
|
||||
}
|
||||
|
||||
waitingForExecute.Clear();
|
||||
}
|
||||
|
||||
protected virtual void OnLoaded()
|
||||
{
|
||||
// this is done to enable keyboard shortcuts
|
||||
Focus();
|
||||
|
||||
//foreach (var plotterElement in elementsWaitingForBeingAttached)
|
||||
//{
|
||||
// if (Children.Contains(plotterElement))
|
||||
// {
|
||||
// plotterElement.OnPlotterAttached(this);
|
||||
// }
|
||||
//}
|
||||
//elementsWaitingForBeingAttached.Clear();
|
||||
}
|
||||
|
||||
protected override void OnTemplateChanged(ControlTemplate oldTemplate, ControlTemplate newTemplate)
|
||||
{
|
||||
base.OnTemplateChanged(oldTemplate, newTemplate);
|
||||
}
|
||||
|
||||
private Grid contentsGrid;
|
||||
public override void OnApplyTemplate()
|
||||
{
|
||||
base.OnApplyTemplate();
|
||||
|
||||
addedVisualElements.Clear();
|
||||
|
||||
foreach (var item in GetAllPanels())
|
||||
{
|
||||
INotifyingPanel panel = item as INotifyingPanel;
|
||||
if (panel != null)
|
||||
{
|
||||
panel.ChildrenCreated -= notifyingItem_ChildrenCreated;
|
||||
if (panel.NotifyingChildren != null)
|
||||
{
|
||||
panel.NotifyingChildren.CollectionChanged -= OnVisualCollectionChanged;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var headerPanel = GetPart<StackPanel>("PART_HeaderPanel");
|
||||
MigrateChildren(this.headerPanel, headerPanel);
|
||||
this.headerPanel = headerPanel;
|
||||
|
||||
var footerPanel = GetPart<StackPanel>("PART_FooterPanel");
|
||||
MigrateChildren(this.footerPanel, footerPanel);
|
||||
this.footerPanel = footerPanel;
|
||||
|
||||
var leftPanel = GetPart<StackPanel>("PART_LeftPanel");
|
||||
MigrateChildren(this.leftPanel, leftPanel);
|
||||
this.leftPanel = leftPanel;
|
||||
|
||||
var bottomPanel = GetPart<StackPanel>("PART_BottomPanel");
|
||||
MigrateChildren(this.bottomPanel, bottomPanel);
|
||||
this.bottomPanel = bottomPanel;
|
||||
|
||||
var rightPanel = GetPart<StackPanel>("PART_RightPanel");
|
||||
MigrateChildren(this.rightPanel, rightPanel);
|
||||
this.rightPanel = rightPanel;
|
||||
|
||||
var topPanel = GetPart<StackPanel>("PART_TopPanel");
|
||||
MigrateChildren(this.topPanel, topPanel);
|
||||
this.topPanel = topPanel;
|
||||
|
||||
var mainCanvas = GetPart<Canvas>("PART_MainCanvas");
|
||||
MigrateChildren(this.mainCanvas, mainCanvas);
|
||||
this.mainCanvas = mainCanvas;
|
||||
|
||||
var centralGrid = GetPart<Grid>("PART_CentralGrid");
|
||||
MigrateChildren(this.centralGrid, centralGrid);
|
||||
this.centralGrid = centralGrid;
|
||||
|
||||
var mainGrid = GetPart<Grid>("PART_MainGrid");
|
||||
MigrateChildren(this.mainGrid, mainGrid);
|
||||
this.mainGrid = mainGrid;
|
||||
|
||||
var parallelCanvas = GetPart<Canvas>("PART_ParallelCanvas");
|
||||
MigrateChildren(this.parallelCanvas, parallelCanvas);
|
||||
this.parallelCanvas = parallelCanvas;
|
||||
|
||||
var contentsGrid = GetPart<Grid>("PART_ContentsGrid");
|
||||
MigrateChildren(this.contentsGrid, contentsGrid);
|
||||
this.contentsGrid = contentsGrid;
|
||||
|
||||
Content = contentsGrid;
|
||||
AddLogicalChild(contentsGrid);
|
||||
|
||||
foreach (var notifyingItem in GetAllPanels())
|
||||
{
|
||||
INotifyingPanel panel = notifyingItem as INotifyingPanel;
|
||||
if (panel != null)
|
||||
{
|
||||
if (panel.NotifyingChildren == null)
|
||||
panel.ChildrenCreated += notifyingItem_ChildrenCreated;
|
||||
else
|
||||
panel.NotifyingChildren.CollectionChanged += OnVisualCollectionChanged;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void MigrateChildren(Panel previousParent, Panel currentParent)
|
||||
{
|
||||
if (previousParent != null && currentParent != null)
|
||||
{
|
||||
UIElement[] children = new UIElement[previousParent.Children.Count];
|
||||
previousParent.Children.CopyTo(children, 0);
|
||||
previousParent.Children.Clear();
|
||||
|
||||
foreach (var child in children)
|
||||
{
|
||||
if (!currentParent.Children.Contains(child))
|
||||
{
|
||||
currentParent.Children.Add(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (previousParent != null)
|
||||
{
|
||||
previousParent.Children.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyingItem_ChildrenCreated(object sender, EventArgs e)
|
||||
{
|
||||
INotifyingPanel panel = (INotifyingPanel)sender;
|
||||
|
||||
SubscribePanelEvents(panel);
|
||||
}
|
||||
|
||||
private void SubscribePanelEvents(INotifyingPanel panel)
|
||||
{
|
||||
panel.ChildrenCreated -= notifyingItem_ChildrenCreated;
|
||||
|
||||
panel.NotifyingChildren.CollectionChanged -= OnVisualCollectionChanged;
|
||||
panel.NotifyingChildren.CollectionChanged += OnVisualCollectionChanged;
|
||||
}
|
||||
|
||||
private void OnVisualCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
if (e.NewItems != null)
|
||||
{
|
||||
foreach (var item in e.NewItems)
|
||||
{
|
||||
INotifyingPanel notifyingPanel = item as INotifyingPanel;
|
||||
if (notifyingPanel != null)
|
||||
{
|
||||
if (notifyingPanel.NotifyingChildren != null)
|
||||
{
|
||||
notifyingPanel.NotifyingChildren.CollectionChanged -= OnVisualCollectionChanged;
|
||||
notifyingPanel.NotifyingChildren.CollectionChanged += OnVisualCollectionChanged;
|
||||
}
|
||||
else
|
||||
{
|
||||
notifyingPanel.ChildrenCreated += notifyingItem_ChildrenCreated;
|
||||
}
|
||||
}
|
||||
|
||||
OnVisualChildAdded((UIElement)item, (UIElementCollection)sender);
|
||||
}
|
||||
}
|
||||
if (e.OldItems != null)
|
||||
{
|
||||
foreach (var item in e.OldItems)
|
||||
{
|
||||
INotifyingPanel notifyingPanel = item as INotifyingPanel;
|
||||
if (notifyingPanel != null)
|
||||
{
|
||||
notifyingPanel.ChildrenCreated -= notifyingItem_ChildrenCreated;
|
||||
if (notifyingPanel.NotifyingChildren != null)
|
||||
{
|
||||
notifyingPanel.NotifyingChildren.CollectionChanged -= OnVisualCollectionChanged;
|
||||
}
|
||||
}
|
||||
|
||||
OnVisualChildRemoved((UIElement)item, (UIElementCollection)sender);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private readonly VisualBindingCollection visualBindingCollection = new VisualBindingCollection();
|
||||
public VisualBindingCollection VisualBindings
|
||||
{
|
||||
get { return visualBindingCollection; }
|
||||
}
|
||||
|
||||
protected virtual void OnVisualChildAdded(UIElement target, UIElementCollection uIElementCollection)
|
||||
{
|
||||
IPlotterElement element = null;
|
||||
if (addingElements.Count > 0)
|
||||
{
|
||||
element = addingElements.Peek();
|
||||
|
||||
var dict = visualBindingCollection.Cache;
|
||||
var proxy = dict[element];
|
||||
|
||||
List<UIElement> visualElements;
|
||||
if (!addedVisualElements.ContainsKey(element))
|
||||
{
|
||||
visualElements = new List<UIElement>();
|
||||
addedVisualElements.Add(element, visualElements);
|
||||
}
|
||||
else
|
||||
{
|
||||
visualElements = addedVisualElements[element];
|
||||
}
|
||||
|
||||
visualElements.Add(target);
|
||||
|
||||
SetBindings(proxy, target);
|
||||
}
|
||||
}
|
||||
|
||||
private void SetBindings(UIElement proxy, UIElement target)
|
||||
{
|
||||
if (proxy != target)
|
||||
{
|
||||
foreach (var property in GetPropertiesToSetBindingOn())
|
||||
{
|
||||
BindingOperations.SetBinding(target, property, new Binding { Path = new PropertyPath(property.Name), Source = proxy, Mode = BindingMode.TwoWay });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void RemoveBindings(UIElement proxy, UIElement target)
|
||||
{
|
||||
if (proxy != target)
|
||||
{
|
||||
foreach (var property in GetPropertiesToSetBindingOn())
|
||||
{
|
||||
BindingOperations.ClearBinding(target, property);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerable<DependencyProperty> GetPropertiesToSetBindingOn()
|
||||
{
|
||||
yield return UIElement.OpacityProperty;
|
||||
yield return UIElement.VisibilityProperty;
|
||||
yield return UIElement.IsHitTestVisibleProperty;
|
||||
//yield return FrameworkElement.DataContextProperty;
|
||||
}
|
||||
|
||||
protected virtual void OnVisualChildRemoved(UIElement target, UIElementCollection uiElementCollection)
|
||||
{
|
||||
IPlotterElement element = null;
|
||||
if (removingElements.Count > 0)
|
||||
{
|
||||
element = removingElements.Peek();
|
||||
|
||||
var dict = visualBindingCollection.Cache;
|
||||
var proxy = dict[element];
|
||||
|
||||
if (addedVisualElements.ContainsKey(element))
|
||||
{
|
||||
var list = addedVisualElements[element];
|
||||
list.Remove(target);
|
||||
|
||||
if (list.Count == 0)
|
||||
{
|
||||
dict.Remove(element);
|
||||
}
|
||||
|
||||
addedVisualElements.Remove(element);
|
||||
}
|
||||
|
||||
RemoveBindings(proxy, target);
|
||||
}
|
||||
}
|
||||
|
||||
internal virtual IEnumerable<Panel> GetAllPanels()
|
||||
{
|
||||
yield return headerPanel;
|
||||
yield return footerPanel;
|
||||
|
||||
yield return leftPanel;
|
||||
yield return bottomPanel;
|
||||
yield return rightPanel;
|
||||
yield return topPanel;
|
||||
|
||||
yield return mainCanvas;
|
||||
yield return centralGrid;
|
||||
yield return mainGrid;
|
||||
yield return parallelCanvas;
|
||||
yield return contentsGrid;
|
||||
}
|
||||
|
||||
private T GetPart<T>(string name)
|
||||
{
|
||||
return (T)Template.FindName(name, this);
|
||||
}
|
||||
|
||||
#region Children and add/removed events handling
|
||||
|
||||
private readonly PlotterChildrenCollection children;
|
||||
|
||||
/// <summary>
|
||||
/// Provides access to Plotter's children charts.
|
||||
/// </summary>
|
||||
/// <value>The children.</value>
|
||||
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
|
||||
public PlotterChildrenCollection Children
|
||||
{
|
||||
[DebuggerStepThrough]
|
||||
get { return children; }
|
||||
}
|
||||
|
||||
private List<Action> waitingForExecute = new List<Action>();
|
||||
|
||||
bool executedWaitingChildrenAdding = false;
|
||||
private void OnChildrenCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
if (IsLoadedInternal && !executedWaitingChildrenAdding)
|
||||
{
|
||||
executedWaitingChildrenAdding = true;
|
||||
ExecuteWaitingChildrenAdditions();
|
||||
}
|
||||
|
||||
if (e.NewItems != null)
|
||||
{
|
||||
foreach (IPlotterElement item in e.NewItems)
|
||||
{
|
||||
if (IsLoadedInternal)
|
||||
{
|
||||
OnChildAdded(item);
|
||||
}
|
||||
else
|
||||
{
|
||||
waitingForExecute.Add(() => OnChildAdded(item));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (e.OldItems != null)
|
||||
{
|
||||
foreach (IPlotterElement item in e.OldItems)
|
||||
{
|
||||
if (IsLoadedInternal)
|
||||
{
|
||||
OnChildRemoving(item);
|
||||
}
|
||||
else
|
||||
{
|
||||
waitingForExecute.Add(() => OnChildRemoving(item));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private readonly Stack<IPlotterElement> addingElements = new Stack<IPlotterElement>();
|
||||
|
||||
bool performChildChecks = true;
|
||||
internal bool PerformChildChecks
|
||||
{
|
||||
get { return performChildChecks; }
|
||||
set { performChildChecks = value; }
|
||||
}
|
||||
|
||||
private IPlotterElement currentChild = null;
|
||||
protected IPlotterElement CurrentChild
|
||||
{
|
||||
get { return currentChild; }
|
||||
}
|
||||
|
||||
protected virtual void OnChildAdded(IPlotterElement child)
|
||||
{
|
||||
if (child != null)
|
||||
{
|
||||
addingElements.Push(child);
|
||||
currentChild = child;
|
||||
try
|
||||
{
|
||||
UIElement visualProxy = CreateVisualProxy(child);
|
||||
visualBindingCollection.Cache.Add(child, visualProxy);
|
||||
|
||||
if (performChildChecks && child.Plotter != null)
|
||||
{
|
||||
throw new InvalidOperationException(Strings.Exceptions.PlotterElementAddedToAnotherPlotter);
|
||||
}
|
||||
|
||||
FrameworkElement styleableElement = child as FrameworkElement;
|
||||
if (styleableElement != null)
|
||||
{
|
||||
Type key = styleableElement.GetType();
|
||||
if (genericResources.Contains(key))
|
||||
{
|
||||
Style elementStyle = (Style)genericResources[key];
|
||||
styleableElement.Style = elementStyle;
|
||||
}
|
||||
}
|
||||
|
||||
if (performChildChecks)
|
||||
{
|
||||
child.OnPlotterAttached(this);
|
||||
if (child.Plotter != this)
|
||||
{
|
||||
throw new InvalidOperationException(Strings.Exceptions.InvalidParentPlotterValue);
|
||||
}
|
||||
}
|
||||
|
||||
DependencyObject dependencyObject = child as DependencyObject;
|
||||
if (dependencyObject != null)
|
||||
{
|
||||
SetPlotter(dependencyObject, this);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
addingElements.Pop();
|
||||
currentChild = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private UIElement CreateVisualProxy(IPlotterElement child)
|
||||
{
|
||||
if (visualBindingCollection.Cache.ContainsKey(child))
|
||||
throw new InvalidOperationException(Strings.Exceptions.VisualBindingsWrongState);
|
||||
|
||||
UIElement result = child as UIElement;
|
||||
|
||||
if (result == null)
|
||||
{
|
||||
result = new UIElement();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private readonly Stack<IPlotterElement> removingElements = new Stack<IPlotterElement>();
|
||||
protected virtual void OnChildRemoving(IPlotterElement child)
|
||||
{
|
||||
if (child != null)
|
||||
{
|
||||
currentChild = child;
|
||||
removingElements.Push(child);
|
||||
try
|
||||
{
|
||||
// todo probably here child.Plotter can be null.
|
||||
if (performChildChecks && child.Plotter != this && child.Plotter != null)
|
||||
{
|
||||
throw new InvalidOperationException(Strings.Exceptions.InvalidParentPlotterValueRemoving);
|
||||
}
|
||||
|
||||
if (performChildChecks)
|
||||
{
|
||||
if (child.Plotter != null)
|
||||
child.OnPlotterDetaching(this);
|
||||
|
||||
if (child.Plotter != null)
|
||||
{
|
||||
throw new InvalidOperationException(Strings.Exceptions.ParentPlotterNotNull);
|
||||
}
|
||||
}
|
||||
|
||||
DependencyObject dependencyObject = child as DependencyObject;
|
||||
if (dependencyObject != null)
|
||||
{
|
||||
SetPlotter(dependencyObject, null);
|
||||
}
|
||||
|
||||
visualBindingCollection.Cache.Remove(child);
|
||||
|
||||
if (addedVisualElements.ContainsKey(child) && addedVisualElements[child].Count > 0)
|
||||
{
|
||||
throw new InvalidOperationException(String.Format(Strings.Exceptions.PlotterElementDidnotCleanedAfterItself, child.ToString()));
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
currentChild = null;
|
||||
removingElements.Pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private readonly Dictionary<IPlotterElement, List<UIElement>> addedVisualElements = new Dictionary<IPlotterElement, List<UIElement>>();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Layout zones
|
||||
|
||||
private Panel parallelCanvas;
|
||||
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
|
||||
public Panel ParallelCanvas
|
||||
{
|
||||
get { return parallelCanvas; }
|
||||
protected set { parallelCanvas = value; }
|
||||
}
|
||||
|
||||
private Panel headerPanel;
|
||||
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
|
||||
public Panel HeaderPanel
|
||||
{
|
||||
get { return headerPanel; }
|
||||
protected set { headerPanel = value; }
|
||||
}
|
||||
|
||||
private Panel footerPanel;
|
||||
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
|
||||
public Panel FooterPanel
|
||||
{
|
||||
get { return footerPanel; }
|
||||
protected set { footerPanel = value; }
|
||||
}
|
||||
|
||||
private Panel leftPanel;
|
||||
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
|
||||
public Panel LeftPanel
|
||||
{
|
||||
get { return leftPanel; }
|
||||
protected set { leftPanel = value; }
|
||||
}
|
||||
|
||||
private Panel rightPanel;
|
||||
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
|
||||
public Panel RightPanel
|
||||
{
|
||||
get { return rightPanel; }
|
||||
protected set { rightPanel = value; }
|
||||
}
|
||||
|
||||
private Panel topPanel;
|
||||
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
|
||||
public Panel TopPanel
|
||||
{
|
||||
get { return topPanel; }
|
||||
protected set { topPanel = value; }
|
||||
}
|
||||
|
||||
private Panel bottomPanel;
|
||||
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
|
||||
public Panel BottomPanel
|
||||
{
|
||||
get { return bottomPanel; }
|
||||
protected set { bottomPanel = value; }
|
||||
}
|
||||
|
||||
private Panel mainCanvas;
|
||||
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
|
||||
public Panel MainCanvas
|
||||
{
|
||||
get { return mainCanvas; }
|
||||
protected set { mainCanvas = value; }
|
||||
}
|
||||
|
||||
private Panel centralGrid;
|
||||
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
|
||||
public Panel CentralGrid
|
||||
{
|
||||
get { return centralGrid; }
|
||||
protected set { centralGrid = value; }
|
||||
}
|
||||
|
||||
private Panel mainGrid;
|
||||
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
|
||||
public Panel MainGrid
|
||||
{
|
||||
get { return mainGrid; }
|
||||
protected set { mainGrid = value; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Screenshots & copy to clipboard
|
||||
|
||||
public BitmapSource CreateScreenshot()
|
||||
{
|
||||
|
||||
UIElement parent = (UIElement)Parent;
|
||||
|
||||
Rect renderBounds = new Rect(RenderSize);
|
||||
|
||||
Point p1 = renderBounds.TopLeft;
|
||||
Point p2 = renderBounds.BottomRight;
|
||||
|
||||
if (parent != null)
|
||||
{
|
||||
p1 = TranslatePoint(p1, parent);
|
||||
p2 = TranslatePoint(p2, parent);
|
||||
}
|
||||
|
||||
Int32Rect rect = new Rect(p1, p2).ToInt32Rect();
|
||||
|
||||
return ScreenshotHelper.CreateScreenshot(this, rect);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>Saves screenshot to file.</summary>
|
||||
/// <param name="filePath">File path.</param>
|
||||
public void SaveScreenshot(string filePath)
|
||||
{
|
||||
ScreenshotHelper.SaveBitmapToFile(CreateScreenshot(), filePath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves screenshot to stream.
|
||||
/// </summary>
|
||||
/// <param name="stream">The stream.</param>
|
||||
/// <param name="fileExtension">The file type extension.</param>
|
||||
public void SaveScreenshotToStream(Stream stream, string fileExtension)
|
||||
{
|
||||
ScreenshotHelper.SaveBitmapToStream(CreateScreenshot(), stream, fileExtension);
|
||||
}
|
||||
|
||||
/// <summary>Copies the screenshot to clipboard.</summary>
|
||||
public void CopyScreenshotToClipboard()
|
||||
{
|
||||
Clipboard.Clear();
|
||||
Clipboard.SetImage(CreateScreenshot());
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IsDefaultElement attached property
|
||||
|
||||
protected void SetAllChildrenAsDefault()
|
||||
{
|
||||
foreach (var child in Children.OfType<DependencyObject>())
|
||||
{
|
||||
child.SetValue(IsDefaultElementProperty, true);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Gets a value whether specified graphics object is default to this plotter or not</summary>
|
||||
/// <param name="obj">Graphics object to check</param>
|
||||
/// <returns>True if it is default or false otherwise</returns>
|
||||
public static bool GetIsDefaultElement(DependencyObject obj)
|
||||
{
|
||||
return (bool)obj.GetValue(IsDefaultElementProperty);
|
||||
}
|
||||
|
||||
public static void SetIsDefaultElement(DependencyObject obj, bool value)
|
||||
{
|
||||
obj.SetValue(IsDefaultElementProperty, value);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty IsDefaultElementProperty = DependencyProperty.RegisterAttached(
|
||||
"IsDefaultElement",
|
||||
typeof(bool),
|
||||
typeof(Plotter),
|
||||
new UIPropertyMetadata(false));
|
||||
|
||||
/// <summary>Removes all user graphs from given UIElementCollection,
|
||||
/// leaving only default graphs</summary>
|
||||
protected static void RemoveUserElements(IList<IPlotterElement> elements)
|
||||
{
|
||||
int index = 0;
|
||||
|
||||
while (index < elements.Count)
|
||||
{
|
||||
DependencyObject d = elements[index] as DependencyObject;
|
||||
if (d != null && !GetIsDefaultElement(d))
|
||||
{
|
||||
elements.RemoveAt(index);
|
||||
}
|
||||
else
|
||||
{
|
||||
index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveUserElements()
|
||||
{
|
||||
RemoveUserElements(Children);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IsDefaultAxis
|
||||
|
||||
public static bool GetIsDefaultAxis(DependencyObject obj)
|
||||
{
|
||||
return (bool)obj.GetValue(IsDefaultAxisProperty);
|
||||
}
|
||||
|
||||
public static void SetIsDefaultAxis(DependencyObject obj, bool value)
|
||||
{
|
||||
obj.SetValue(IsDefaultAxisProperty, value);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty IsDefaultAxisProperty = DependencyProperty.RegisterAttached(
|
||||
"IsDefaultAxis",
|
||||
typeof(bool),
|
||||
typeof(Plotter),
|
||||
new UIPropertyMetadata(false, OnIsDefaultAxisChanged));
|
||||
|
||||
private static void OnIsDefaultAxisChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
Plotter parentPlotter = null;
|
||||
IPlotterElement plotterElement = d as IPlotterElement;
|
||||
if (plotterElement != null)
|
||||
{
|
||||
parentPlotter = plotterElement.Plotter;
|
||||
|
||||
if (parentPlotter != null)
|
||||
{
|
||||
parentPlotter.OnIsDefaultAxisChangedCore(d, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void OnIsDefaultAxisChangedCore(DependencyObject d, DependencyPropertyChangedEventArgs e) { }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Undo
|
||||
|
||||
private readonly UndoProvider undoProvider = new UndoProvider();
|
||||
public UndoProvider UndoProvider
|
||||
{
|
||||
get { return undoProvider; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Plotter attached property
|
||||
|
||||
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
|
||||
public static Plotter GetPlotter(DependencyObject obj)
|
||||
{
|
||||
return (Plotter)obj.GetValue(PlotterProperty);
|
||||
}
|
||||
|
||||
public static void SetPlotter(DependencyObject obj, Plotter value)
|
||||
{
|
||||
obj.SetValue(PlotterProperty, value);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty PlotterProperty = DependencyProperty.RegisterAttached(
|
||||
"Plotter",
|
||||
typeof(Plotter),
|
||||
typeof(Plotter),
|
||||
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.Inherits, OnPlotterChanged));
|
||||
|
||||
private static void OnPlotterChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
FrameworkElement element = d as FrameworkElement;
|
||||
Plotter prevPlotter = (Plotter)e.OldValue;
|
||||
Plotter currPlotter = (Plotter)e.NewValue;
|
||||
|
||||
// raise Plotter[*] events, where * is Attached, Detaching, Changed.
|
||||
if (element != null)
|
||||
{
|
||||
PlotterChangedEventArgs args = new PlotterChangedEventArgs(prevPlotter, currPlotter, PlotterDetachingEvent);
|
||||
|
||||
if (currPlotter == null && prevPlotter != null)
|
||||
{
|
||||
RaisePlotterEvent(element, args);
|
||||
}
|
||||
else if (currPlotter != null)
|
||||
{
|
||||
args.RoutedEvent = PlotterAttachedEvent;
|
||||
RaisePlotterEvent(element, args);
|
||||
}
|
||||
|
||||
args.RoutedEvent = PlotterChangedEvent;
|
||||
RaisePlotterEvent(element, args);
|
||||
}
|
||||
}
|
||||
|
||||
private static void RaisePlotterEvent(FrameworkElement element, PlotterChangedEventArgs args)
|
||||
{
|
||||
element.RaiseEvent(args);
|
||||
PlotterEvents.Notify(element, args);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Plotter routed events
|
||||
|
||||
public static readonly RoutedEvent PlotterAttachedEvent = EventManager.RegisterRoutedEvent(
|
||||
"PlotterAttached",
|
||||
RoutingStrategy.Direct,
|
||||
typeof(PlotterChangedEventHandler),
|
||||
typeof(Plotter));
|
||||
|
||||
public static readonly RoutedEvent PlotterDetachingEvent = EventManager.RegisterRoutedEvent(
|
||||
"PlotterDetaching",
|
||||
RoutingStrategy.Direct,
|
||||
typeof(PlotterChangedEventHandler),
|
||||
typeof(Plotter));
|
||||
|
||||
public static readonly RoutedEvent PlotterChangedEvent = EventManager.RegisterRoutedEvent(
|
||||
"PlotterChanged",
|
||||
RoutingStrategy.Direct,
|
||||
typeof(PlotterChangedEventHandler),
|
||||
typeof(Plotter));
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
public delegate void PlotterChangedEventHandler(object sender, PlotterChangedEventArgs e);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user