Files
DynamicDataDisplay/Backup/Charts/Markers/MarkerChart.cs
2024-02-23 09:29:44 -05:00

287 lines
6.5 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.Collections.Specialized;
using System.Windows;
using System.Windows.Controls;
using System.ComponentModel;
using System.Diagnostics;
using Microsoft.Research.DynamicDataDisplay.Common;
namespace Microsoft.Research.DynamicDataDisplay.Charts.Markers
{
public class MarkerChart : PlotterElement
{
/// <summary>
/// Initializes a new instance of the <see cref="MarkerChart"/> class.
/// </summary>
public MarkerChart()
{
Viewport2D.SetIsContentBoundsHost(this, true);
}
#region DataSource
private IEnumerable dataSource;
/// <summary>
/// Gets or sets the data source.
/// Value can be null.
/// </summary>
/// <value>The data source.</value>
public IEnumerable DataSource
{
get { return dataSource; }
set
{
DetachDataSource();
dataSource = value;
AttachDataSource();
OnDataSourceChanged();
RebuildMarkers(true);
}
}
protected virtual void OnDataSourceChanged() { }
protected virtual void RebuildMarkers(bool shouldReleaseMarkers)
{
if (markerGenerator == null)
return;
if (shouldReleaseMarkers)
{
foreach (FrameworkElement item in itemsPanel.Children)
{
var enumerator = item.GetLocalValueEnumerator();
while (enumerator.MoveNext())
{
item.ClearValue(enumerator.Current.Property);
}
descriptor.RemoveValueChanged(item, OnMarkerViewportBoundsChanged);
markerGenerator.ReleaseMarker(item);
}
}
itemsPanel.Children.Clear();
if (dataSource == null)
return;
IndividualArrangePanel specialPanel = itemsPanel as IndividualArrangePanel;
if (specialPanel != null)
specialPanel.BeginBatchAdd();
foreach (object item in dataSource)
{
var marker = CreateMarker(item);
itemsPanel.Children.Add(marker);
}
if (specialPanel != null)
specialPanel.EndBatchAdd();
RecalculateViewportBounds();
}
private FrameworkElement CreateMarker(object item)
{
var marker = markerGenerator.CreateMarker(item);
if (marker != null)
{
marker.DataContext = item;
AttachViewportChangedListener(marker);
BindMarkerEventArgs bindArgs = new BindMarkerEventArgs { Data = item, Marker = marker };
OnMarkerBind(bindArgs);
if (markerBindCallback != null)
{
markerBindCallback(bindArgs);
}
}
return marker;
}
protected virtual void OnMarkerBind(BindMarkerEventArgs e) { }
private Action<BindMarkerEventArgs> markerBindCallback = null;
public Action<BindMarkerEventArgs> MarkerBindCallback
{
get { return markerBindCallback; }
set
{
if (markerBindCallback != value)
{
markerBindCallback = value;
RebuildMarkers(false);
}
}
}
private void AttachDataSource()
{
INotifyCollectionChanged notifyingCollection = dataSource as INotifyCollectionChanged;
if (notifyingCollection != null)
{
notifyingCollection.CollectionChanged += OnDataSourceCollectionChanged;
}
}
private void OnDataSourceCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.OldItems != null)
{
int index = e.OldStartingIndex;
foreach (object item in e.OldItems)
{
var oldMarker = itemsPanel.Children[index];
if (markerGenerator != null)
{
FrameworkElement element = (FrameworkElement)oldMarker;
// todo call cleanup callback here
markerGenerator.ReleaseMarker(element);
descriptor.RemoveValueChanged(element, OnMarkerViewportBoundsChanged);
}
itemsPanel.Children.RemoveAt(index);
}
}
if (e.NewItems != null)
{
int index = e.NewStartingIndex;
foreach (object item in e.NewItems)
{
var marker = CreateMarker(item);
itemsPanel.Children.Insert(index, marker);
index++;
}
}
RecalculateViewportBounds();
}
DependencyPropertyDescriptor descriptor = DependencyPropertyDescriptor.FromProperty(ViewportPanel.ActualViewportBoundsProperty, typeof(FrameworkElement));
private void AttachViewportChangedListener(FrameworkElement element)
{
// todo or use this, or remove this
//descriptor.RemoveValueChanged(element, OnMarkerViewportBoundsChanged);
//descriptor.AddValueChanged(element, OnMarkerViewportBoundsChanged);
}
private Rect viewportBounds = Rect.Empty;
private void OnMarkerViewportBoundsChanged(object sender, EventArgs e)
{
RecalculateViewportBounds();
FrameworkElement element = (FrameworkElement)sender;
DataRect elementBounds = ViewportPanel.GetActualViewportBounds(element);
DataRect prevElementBounds = ViewportPanel.GetPrevActualViewportBounds(element);
}
private void RecalculateViewportBounds()
{
DataRect bounds = DataRect.Empty;
foreach (UIElement item in itemsPanel.Children)
{
DataRect elementBounds = ViewportPanel.GetActualViewportBounds(item);
bounds.Union(elementBounds);
}
Viewport2D.SetContentBounds(this, bounds);
}
private void DetachDataSource()
{
INotifyCollectionChanged notifyingCollection = dataSource as INotifyCollectionChanged;
if (notifyingCollection != null)
{
notifyingCollection.CollectionChanged -= OnDataSourceCollectionChanged;
}
}
#endregion
#region Marker
private OldMarkerGenerator markerGenerator;
public OldMarkerGenerator MarkerGenerator
{
get { return markerGenerator; }
set
{
if (value == null)
throw new ArgumentNullException("value");
if (markerGenerator != value)
{
DetachMarkerGenerator();
markerGenerator = value;
AttachMarkerGenerator();
RebuildMarkers(false);
}
}
}
private void AttachMarkerGenerator()
{
markerGenerator.Changed += OnMarkerChanged;
}
private void OnMarkerChanged(object sender, EventArgs e)
{
RebuildMarkers(false);
}
private void DetachMarkerGenerator()
{
if (markerGenerator != null)
{
markerGenerator.Changed -= OnMarkerChanged;
}
}
#endregion
#region Plotter attaching
private ViewportPanel itemsPanel = new ViewportPanel();
protected Panel ItemsPanel
{
get { return itemsPanel; }
}
private Plotter2D plotter;
protected override void OnPlotterAttached(Plotter plotter)
{
base.OnPlotterAttached(plotter);
this.plotter = (Plotter2D)plotter;
((IPlotterElement)itemsPanel).OnPlotterAttached(plotter);
}
protected override void OnPlotterDetaching(Plotter plotter)
{
((IPlotterElement)itemsPanel).OnPlotterDetaching(plotter);
this.plotter = null;
base.OnPlotterDetaching(plotter);
}
#endregion
}
}