Initial Commit

This commit is contained in:
2024-02-23 00:46:06 -05:00
commit 2bbedc0178
470 changed files with 46035 additions and 0 deletions

View File

@@ -0,0 +1,128 @@
<Window x:Class="Microsoft.Research.DynamicDataDisplay.Charts.Navigation.AboutWindow" x:ClassModifier="internal"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:l="clr-namespace:Microsoft.Research.DynamicDataDisplay"
xmlns:p="clr-namespace:Microsoft.Research.DynamicDataDisplay.MarkupExtensions"
Width="400"
ResizeMode="NoResize" WindowStartupLocation="CenterOwner" ShowInTaskbar="False"
Icon="{x:Static l:D3IconHelper.DynamicDataDisplayIcon}"
Title="{p:Resource AboutTitle}"
KeyDown="Window_KeyDown" SizeToContent="Height">
<Grid>
<FlowDocumentScrollViewer VerticalScrollBarVisibility="Disabled">
<FlowDocument FontSize="14">
<FlowDocument.Resources>
<Style TargetType="{x:Type Run}" x:Key="nav">
<Setter Property="Foreground" Value="#FF00A010"/>
</Style>
<Style TargetType="InlineUIContainer" x:Key="{x:Type InlineUIContainer}">
<Setter Property="BaselineAlignment" Value="Center"/>
</Style>
<Style TargetType="ContentControl" x:Key="btn">
<Setter Property="Margin" Value="0,1,0,1"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="FontFamily" Value="Lucida Console"/>
</Style>
<Style TargetType="ContentControl" x:Key="commandBtn">
<Setter Property="Margin" Value="0,1,0,1"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="FontFamily" Value="Lucida Console"/>
</Style>
</FlowDocument.Resources>
<Paragraph>
<Hyperlink Foreground="#FF0A00A0" FontSize="16" Click="Hyperlink_Click" ToolTip="{p:Resource AboutSiteUrl}" NavigateUri="http://dynamicdatadisplay.codeplex.com/">Dynamic Data Display</Hyperlink>
<Run Name="r1" Text="{p:Resource About_1}"/>
<InlineUIContainer>
<RepeatButton Content="Shift" Style="{DynamicResource btn}"/>
</InlineUIContainer>
<Run Name="r2" Text="{p:Resource About_2}"/>
<InlineUIContainer>
<RepeatButton Content="Ctrl" Style="{DynamicResource btn}"/>
</InlineUIContainer>
<Run Name="r3" Text="{p:Resource About_3}"/>
<LineBreak/>
<Span>
<Run Name="r4" Style="{DynamicResource nav}" Text="{p:Resource About_4}"/> ,
</Span>
<Run Name="r6" Text="{p:Resource About_6}"/>
<InlineUIContainer>
<RepeatButton Style="{DynamicResource btn}" Content="+" Command="{x:Static l:ChartCommands.ZoomIn}" CommandTarget="{Binding}"/>
</InlineUIContainer>
<Run Name="r5" Text="{p:Resource About_5}"/>
<InlineUIContainer>
<RepeatButton Style="{DynamicResource btn}" Content="-" Command="{x:Static l:ChartCommands.ZoomOut}" CommandTarget="{Binding}"/>
</InlineUIContainer>
<Run Name="r7" Text="{p:Resource About_7}"/>
<LineBreak/>
<Run Name="r8" Text="{p:Resource About_8}" Style="{DynamicResource nav}"/>
<Run Name="r9" Text="{p:Resource About_2}"/>
<InlineUIContainer>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<RepeatButton Grid.Column="1" Grid.Row="0" Style="{DynamicResource btn}" FontSize="16"
Content="↑" Margin="2" Command="{x:Static l:ChartCommands.ScrollUp}" CommandTarget="{Binding}"/>
<RepeatButton Grid.Column="0" Grid.Row="1" Style="{DynamicResource btn}" FontSize="16"
Content="←" Margin="2" Command="{x:Static l:ChartCommands.ScrollLeft}" CommandTarget="{Binding}"/>
<RepeatButton Grid.Column="1" Grid.Row="1" Style="{DynamicResource btn}" FontSize="16"
Content="↓" Margin="2" Command="{x:Static l:ChartCommands.ScrollDown}" CommandTarget="{Binding}"/>
<RepeatButton Grid.Column="2" Grid.Row="1" Style="{DynamicResource btn}" FontSize="16"
Content="→" Margin="2" Command="{x:Static l:ChartCommands.ScrollRight}" CommandTarget="{Binding}"/>
</Grid>
</InlineUIContainer>
<Run Name="r10" Text="{p:Resource About_9}"/>
<LineBreak/>
<InlineUIContainer>
<RepeatButton Style="{DynamicResource commandBtn}" Content="Ctrl"/>
</InlineUIContainer>
<Run>+</Run>
<Run Name="r11" Style="{DynamicResource nav}" Text="{p:Resource About_10}"/>
<Run Name="r14" Text="{p:Resource About_12}"/>
<LineBreak/>
<InlineUIContainer>
<RepeatButton Style="{DynamicResource commandBtn}" Content="Shift"/>
</InlineUIContainer>
<Run>+</Run>
<Run Name="r12" Style="{DynamicResource nav}" Text="{p:Resource About_10}"/>
<Run Name="r13" Text="{p:Resource About_11}"/>
<LineBreak/>
<InlineUIContainer>
<Button Style="{DynamicResource commandBtn}" Content="Home" Command="{x:Static l:ChartCommands.FitToView}" CommandTarget="{Binding}"/>
</InlineUIContainer>
<Run Name="r15" Text="{p:Resource About_13}"/>
<LineBreak/>
<InlineUIContainer>
<Button Style="{DynamicResource commandBtn}" Content="F11" Command="{x:Static l:ChartCommands.CopyScreenshot}" CommandTarget="{Binding}"/>
</InlineUIContainer>
<Run Name="r16" Text="{p:Resource About_15}"/>
<LineBreak/>
<InlineUIContainer>
<Button Style="{DynamicResource commandBtn}" Content="Ctrl" Command="{x:Static l:ChartCommands.SaveScreenshot}" CommandTarget="{Binding}"/>
</InlineUIContainer>
<Run>+</Run>
<InlineUIContainer>
<Button Style="{DynamicResource commandBtn}" Content="S" Command="{x:Static l:ChartCommands.SaveScreenshot}" CommandTarget="{Binding}"/>
</InlineUIContainer>
<Run Name="r17" Text="{p:Resource About_15}"/>
</Paragraph>
<Paragraph TextAlignment="Center">
<Hyperlink Foreground="#FF0A00A0" NavigateUri="http://dynamicdatadisplay.codeplex.com" ToolTip="{p:Resource AboutSireUrl}" Click="Hyperlink_Click_1">
<TextBlock Text="{p:Resource AboutSiteUrl}"/>
</Hyperlink>
</Paragraph>
</FlowDocument>
</FlowDocumentScrollViewer>
</Grid>
</Window>

View File

@@ -0,0 +1,50 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.Diagnostics;
using System.Reflection;
using Microsoft.Research.DynamicDataDisplay.Common.Auxiliary;
namespace Microsoft.Research.DynamicDataDisplay.Charts.Navigation
{
/// <summary>
/// Interaction logic for AboutWindow.xaml
/// </summary>
internal partial class AboutWindow : Window
{
public AboutWindow()
{
InitializeComponent();
}
private void Hyperlink_Click(object sender, RoutedEventArgs e)
{
Hyperlink source = (Hyperlink)sender;
Process.Start(source.NavigateUri.ToString());
}
private void Window_KeyDown(object sender, KeyEventArgs e)
{
// close on Esc or Enter pressed
if (e.Key == Key.Escape || e.Key == Key.Enter)
{
Close();
}
}
private void Hyperlink_Click_1(object sender, RoutedEventArgs e)
{
Hyperlink source = (Hyperlink)sender;
Process.Start(source.NavigateUri.ToString());
}
}
}

View File

@@ -0,0 +1,222 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Controls;
using System.Windows.Shapes;
using System.Windows.Input;
using System.Windows;
using System.Windows.Media;
using System.Windows.Data;
using Microsoft.Research.DynamicDataDisplay.Common;
namespace Microsoft.Research.DynamicDataDisplay.Charts.Navigation
{
/// <summary>
/// Adds to ChartPlotter two lines upon axes, showing current cursor position.
/// </summary>
public class AxisCursorGraph : DependencyObject, IPlotterElement
{
static AxisCursorGraph()
{
Line.StrokeProperty.AddOwner(typeof(AxisCursorGraph), new FrameworkPropertyMetadata(Brushes.Red));
}
/// <summary>
/// Initializes a new instance of the <see cref="AxisCursorGraph"/> class.
/// </summary>
public AxisCursorGraph() { }
#region ShowVerticalLine property
/// <summary>
/// Gets or sets a value indicating whether to show line upon vertical axis.
/// </summary>
/// <value><c>true</c> if line upon vertical axis is shown; otherwise, <c>false</c>.</value>
public bool ShowVerticalLine
{
get { return (bool)GetValue(ShowVerticalLineProperty); }
set { SetValue(ShowVerticalLineProperty, value); }
}
/// <summary>
/// Identifies ShowVerticalLine dependency property.
/// </summary>
public static readonly DependencyProperty ShowVerticalLineProperty = DependencyProperty.Register(
"ShowVerticalLine",
typeof(bool),
typeof(AxisCursorGraph),
new FrameworkPropertyMetadata(true, OnShowLinePropertyChanged));
private static void OnShowLinePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
AxisCursorGraph graph = (AxisCursorGraph)d;
graph.UpdateUIRepresentation();
}
#endregion
#region ShowHorizontalLine property
/// <summary>
/// Gets or sets a value indicating whether to show line upon horizontal axis.
/// </summary>
/// <value><c>true</c> if lien upon horizontal axis is shown; otherwise, <c>false</c>.</value>
public bool ShowHorizontalLine
{
get { return (bool)GetValue(ShowHorizontalLineProperty); }
set { SetValue(ShowHorizontalLineProperty, value); }
}
public static readonly DependencyProperty ShowHorizontalLineProperty = DependencyProperty.Register(
"ShowHorizontalLine",
typeof(bool),
typeof(AxisCursorGraph),
new FrameworkPropertyMetadata(true, OnShowLinePropertyChanged));
#endregion
#region IPlotterElement Members
private Line leftLine;
private Line bottomLine;
private Canvas leftCanvas;
private Canvas bottomCanvas;
private Plotter2D plotter;
void IPlotterElement.OnPlotterAttached(Plotter plotter)
{
this.plotter = (Plotter2D)plotter;
this.plotter.Viewport.PropertyChanged += Viewport_PropertyChanged;
var parent = plotter.MainGrid;
parent.MouseMove += parent_MouseMove;
parent.MouseEnter += parent_MouseEnter;
parent.MouseLeave += parent_MouseLeave;
Style lineStyle = new Style(typeof(Line));
AddBindingSetter(lineStyle, Line.StrokeProperty);
AddBindingSetter(lineStyle, Line.StrokeThicknessProperty);
leftCanvas = new Canvas();
Grid.SetRow(leftCanvas, 1);
Grid.SetColumn(leftCanvas, 0);
leftLine = new Line { Style = lineStyle, IsHitTestVisible = false };
leftCanvas.Children.Add(leftLine);
parent.Children.Add(leftCanvas);
bottomCanvas = new Canvas();
Grid.SetRow(bottomCanvas, 2);
Grid.SetColumn(bottomCanvas, 1);
bottomLine = new Line { Style = lineStyle, IsHitTestVisible = false };
bottomCanvas.Children.Add(bottomLine);
parent.Children.Add(bottomCanvas);
}
private void AddBindingSetter(Style style, DependencyProperty property)
{
style.Setters.Add(new Setter(property,
new Binding
{
Path = new PropertyPath(property.Name),
Source = this
}));
}
void Viewport_PropertyChanged(object sender, ExtendedPropertyChangedEventArgs e)
{
UpdateUIRepresentation();
}
private void UpdateUIRepresentation()
{
if (plotter == null) return;
var transform = plotter.Viewport.Transform;
DataRect visible = plotter.Viewport.Visible;
Rect output = plotter.Viewport.Output;
Point mousePos = Mouse.GetPosition(plotter.CentralGrid);
if (ShowVerticalLine)
{
double y = mousePos.Y;
if (output.Top <= y && y <= output.Bottom)
{
leftLine.Visibility = Visibility.Visible;
leftLine.X1 = 0;
leftLine.X2 = plotter.LeftPanel.ActualWidth;
leftLine.Y1 = leftLine.Y2 = y;
}
else
{
leftLine.Visibility = Visibility.Collapsed;
}
}
else
{
leftLine.Visibility = Visibility.Collapsed;
}
if (ShowHorizontalLine)
{
double x = mousePos.X;
if (output.Left <= x && x <= output.Right)
{
bottomLine.Visibility = Visibility.Visible;
bottomLine.Y1 = 0;
bottomLine.Y2 = plotter.BottomPanel.ActualHeight;
bottomLine.X1 = bottomLine.X2 = x;
}
else
{
bottomLine.Visibility = Visibility.Collapsed;
}
}
else
{
bottomLine.Visibility = Visibility.Collapsed;
}
}
void parent_MouseLeave(object sender, MouseEventArgs e)
{
UpdateUIRepresentation();
}
void parent_MouseEnter(object sender, MouseEventArgs e)
{
UpdateUIRepresentation();
}
void parent_MouseMove(object sender, MouseEventArgs e)
{
UpdateUIRepresentation();
}
void IPlotterElement.OnPlotterDetaching(Plotter plotter)
{
this.plotter.Viewport.PropertyChanged -= Viewport_PropertyChanged;
var parent = plotter.MainGrid;
parent.MouseMove -= parent_MouseMove;
parent.MouseEnter -= parent_MouseEnter;
parent.MouseLeave -= parent_MouseLeave;
parent.Children.Remove(leftCanvas);
parent.Children.Remove(bottomCanvas);
this.plotter = null;
}
Plotter IPlotterElement.Plotter
{
get { return plotter; }
}
#endregion
}
}

View File

@@ -0,0 +1,405 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Input;
using System.Windows.Controls;
using System.Windows.Media;
using Microsoft.Research.DynamicDataDisplay.Charts.Axes;
using Microsoft.Research.DynamicDataDisplay.Common.Auxiliary;
using System.Diagnostics;
namespace Microsoft.Research.DynamicDataDisplay.Charts.Navigation
{
public class AxisNavigation : DependencyObject, IPlotterElement
{
public AxisPlacement Placement
{
get { return (AxisPlacement)GetValue(PlacementProperty); }
set { SetValue(PlacementProperty, value); }
}
public static readonly DependencyProperty PlacementProperty = DependencyProperty.Register(
"Placement",
typeof(AxisPlacement),
typeof(AxisNavigation),
new FrameworkPropertyMetadata(AxisPlacement.Left, OnPlacementChanged));
private static void OnPlacementChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
AxisNavigation navigation = (AxisNavigation)d;
navigation.OnPlacementChanged();
}
private Panel listeningPanel;
protected Panel ListeningPanel
{
get { return listeningPanel; }
}
private void OnPlacementChanged()
{
SetListeningPanel();
}
private void SetListeningPanel()
{
if (plotter == null) return;
AxisPlacement placement = Placement;
switch (placement)
{
case AxisPlacement.Left:
listeningPanel = plotter.LeftPanel;
break;
case AxisPlacement.Right:
listeningPanel = plotter.RightPanel;
break;
case AxisPlacement.Top:
listeningPanel = plotter.TopPanel;
break;
case AxisPlacement.Bottom:
listeningPanel = plotter.BottomPanel;
break;
default:
break;
}
}
private CoordinateTransform Transform
{
get { return plotter.Viewport.Transform; }
}
private Panel hostPanel;
#region IPlotterElement Members
void IPlotterElement.OnPlotterAttached(Plotter plotter)
{
this.plotter = (Plotter2D)plotter;
hostPanel = plotter.MainGrid;
//hostPanel.Background = Brushes.Pink;
SetListeningPanel();
if (hostPanel != null)
{
hostPanel.MouseLeftButtonUp += OnMouseLeftButtonUp;
hostPanel.MouseLeftButtonDown += OnMouseLeftButtonDown;
hostPanel.MouseMove += OnMouseMove;
hostPanel.MouseWheel += OnMouseWheel;
hostPanel.MouseRightButtonDown += OnMouseRightButtonDown;
hostPanel.MouseRightButtonUp += OnMouseRightButtonUp;
hostPanel.LostFocus += OnLostFocus;
}
}
private void OnLostFocus(object sender, RoutedEventArgs e)
{
//Debug.WriteLine("Lost Focus");
RevertChanges();
rmbPressed = false;
lmbPressed = false;
e.Handled = true;
}
#region Right button down
DataRect rmbDragStartRect;
private void OnMouseRightButtonDown(object sender, MouseButtonEventArgs e)
{
OnMouseRightButtonDown(e);
}
protected virtual void OnMouseRightButtonDown(MouseButtonEventArgs e)
{
rmbInitialPosition = e.GetPosition(listeningPanel);
var foundActivePlotter = UpdateActivePlotter(e);
if (foundActivePlotter)
{
rmbPressed = true;
dragStartInViewport = rmbInitialPosition.ScreenToViewport(activePlotter.Transform);
rmbDragStartRect = activePlotter.Visible;
listeningPanel.Background = fillBrush;
listeningPanel.CaptureMouse();
//e.Handled = true;
}
}
#endregion
#region Right button up
private void OnMouseRightButtonUp(object sender, MouseButtonEventArgs e)
{
OnMouseRightButtonUp(e);
}
protected virtual void OnMouseRightButtonUp(MouseButtonEventArgs e)
{
if (rmbPressed)
{
rmbPressed = false;
RevertChanges();
//e.Handled = true;
}
}
#endregion
private void RevertChanges()
{
listeningPanel.ClearValue(Panel.CursorProperty);
listeningPanel.Background = Brushes.Transparent;
listeningPanel.ReleaseMouseCapture();
}
private const double wheelZoomSpeed = 1.2;
private void OnMouseWheel(object sender, MouseWheelEventArgs e)
{
Point mousePos = e.GetPosition(listeningPanel);
Rect listeningPanelBounds = new Rect(listeningPanel.RenderSize);
if (!listeningPanelBounds.Contains(mousePos))
return;
var foundActivePlotter = UpdateActivePlotter(e);
if (!foundActivePlotter) return;
int delta = -e.Delta;
Point zoomTo = mousePos.ScreenToViewport(activePlotter.Transform);
double zoomSpeed = Math.Abs(delta / Mouse.MouseWheelDeltaForOneLine);
zoomSpeed *= wheelZoomSpeed;
if (delta < 0)
{
zoomSpeed = 1 / zoomSpeed;
}
DataRect visible = activePlotter.Viewport.Visible.Zoom(zoomTo, zoomSpeed);
DataRect oldVisible = activePlotter.Viewport.Visible;
if (Placement.IsBottomOrTop())
{
visible.YMin = oldVisible.YMin;
visible.Height = oldVisible.Height;
}
else
{
visible.XMin = oldVisible.XMin;
visible.Width = oldVisible.Width;
}
activePlotter.Viewport.Visible = visible;
e.Handled = true;
}
private const int RmbZoomScale = 200;
private void OnMouseMove(object sender, MouseEventArgs e)
{
if (lmbPressed)
{
// panning:
if (e.LeftButton == MouseButtonState.Released)
{
lmbPressed = false;
RevertChanges();
return;
}
Point screenMousePos = e.GetPosition(listeningPanel);
Point dataMousePos = screenMousePos.ScreenToViewport(activePlotter.Transform);
DataRect visible = activePlotter.Viewport.Visible;
double delta;
if (Placement.IsBottomOrTop())
{
delta = (dataMousePos - dragStartInViewport).X;
visible.XMin -= delta;
}
else
{
delta = (dataMousePos - dragStartInViewport).Y;
visible.YMin -= delta;
}
if (screenMousePos != lmbInitialPosition)
{
listeningPanel.Cursor = Placement.IsBottomOrTop() ? Cursors.ScrollWE : Cursors.ScrollNS;
}
activePlotter.Viewport.Visible = visible;
e.Handled = true;
}
else if (rmbPressed)
{
// one direction zooming:
if (e.RightButton == MouseButtonState.Released)
{
rmbPressed = false;
RevertChanges();
return;
}
Point screenMousePos = e.GetPosition(listeningPanel);
DataRect visible = activePlotter.Viewport.Visible;
double delta;
bool isHorizontal = Placement.IsBottomOrTop();
if (isHorizontal)
{
delta = (screenMousePos - rmbInitialPosition).X;
}
else
{
delta = (screenMousePos - rmbInitialPosition).Y;
}
if (delta < 0)
delta = 1 / Math.Exp(-delta / RmbZoomScale);
else
delta = Math.Exp(delta / RmbZoomScale);
Point center = dragStartInViewport;
if (isHorizontal)
{
visible = rmbDragStartRect.ZoomX(center, delta);
}
else
{
visible = rmbDragStartRect.ZoomY(center, delta);
}
if (screenMousePos != lmbInitialPosition)
{
listeningPanel.Cursor = Placement.IsBottomOrTop() ? Cursors.ScrollWE : Cursors.ScrollNS;
}
activePlotter.Viewport.Visible = visible;
//e.Handled = true;
}
}
private Point lmbInitialPosition;
protected Point LmbInitialPosition
{
get { return lmbInitialPosition; }
}
private Point rmbInitialPosition;
private readonly SolidColorBrush fillBrush = new SolidColorBrush(Color.FromRgb(255, 228, 209)).MakeTransparent(0.2);
private bool lmbPressed;
private bool rmbPressed;
private Point dragStartInViewport;
private Plotter2D activePlotter;
#region Left button down
private void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
OnMouseLeftButtonDown(e);
}
protected virtual void OnMouseLeftButtonDown(MouseButtonEventArgs e)
{
lmbInitialPosition = e.GetPosition(listeningPanel);
var foundActivePlotter = UpdateActivePlotter(e);
if (foundActivePlotter)
{
lmbPressed = true;
dragStartInViewport = lmbInitialPosition.ScreenToViewport(activePlotter.Transform);
listeningPanel.Background = fillBrush;
listeningPanel.CaptureMouse();
e.Handled = true;
}
}
#endregion
private bool UpdateActivePlotter(MouseEventArgs e)
{
var axes = listeningPanel.Children.OfType<GeneralAxis>();
foreach (var axis in axes)
{
var positionInAxis = e.GetPosition(axis);
Rect axisBounds = new Rect(axis.RenderSize);
if (axisBounds.Contains(positionInAxis))
{
activePlotter = axis.Plotter;
return true;
}
}
return false;
}
#region Left button up
private void OnMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
OnMouseLeftButtonUp(e);
}
protected virtual void OnMouseLeftButtonUp(MouseButtonEventArgs e)
{
if (lmbPressed)
{
lmbPressed = false;
RevertChanges();
e.Handled = true;
}
}
#endregion
public void OnPlotterDetaching(Plotter plotter)
{
if (plotter.MainGrid != null)
{
hostPanel.MouseLeftButtonUp -= OnMouseLeftButtonUp;
hostPanel.MouseLeftButtonDown -= OnMouseLeftButtonDown;
hostPanel.MouseMove -= OnMouseMove;
hostPanel.MouseWheel -= OnMouseWheel;
hostPanel.MouseRightButtonDown -= OnMouseRightButtonDown;
hostPanel.MouseRightButtonUp -= OnMouseRightButtonUp;
hostPanel.LostFocus -= OnLostFocus;
}
listeningPanel = null;
this.plotter = null;
}
private Plotter2D plotter;
Plotter IPlotterElement.Plotter
{
get { return plotter; }
}
#endregion
public override string ToString()
{
return "AxisNavigation: " + Placement;
}
}
}

View File

@@ -0,0 +1,170 @@
using System.Windows.Input;
namespace Microsoft.Research.DynamicDataDisplay
{
// TODO: probably optimize memory usage by replacing implicit creation of
// all commands on first usage of this class -
// create each command on first access directly to it.
/// <summary>Common chart plotter commands</summary>
public static class ChartCommands
{
#region Auxiliary code for creation of commands
private static RoutedUICommand CreateCommand(string name)
{
return new RoutedUICommand(name, name, typeof(ChartCommands));
}
private static RoutedUICommand CreateCommand(string name, params Key[] keys)
{
InputGestureCollection gestures = new InputGestureCollection();
foreach (var key in keys)
{
gestures.Add(new KeyGesture(key));
}
return new RoutedUICommand(name, name, typeof(ChartCommands), gestures);
}
private static RoutedUICommand CreateCommand(string name, MouseAction mouseAction) {
return new RoutedUICommand(name, name, typeof(ChartCommands), new InputGestureCollection { new MouseGesture(mouseAction) });
}
private static RoutedUICommand CreateCommand(string name, params InputGesture[] gestures)
{
return new RoutedUICommand(name, name, typeof(ChartCommands), new InputGestureCollection(gestures));
}
#endregion
private static readonly RoutedUICommand zoomOutToMouse = CreateCommand("ZoomOutToMouse", MouseAction.RightDoubleClick);
/// <summary>
/// Gets the value that represents the Zoom Out To Mouse command.
/// </summary>
public static RoutedUICommand ZoomOutToMouse
{
get { return ChartCommands.zoomOutToMouse; }
}
private static readonly RoutedUICommand zoomInToMouse = CreateCommand("ZoomInToMouse", MouseAction.LeftDoubleClick);
/// <summary>
/// Gets the value that represents the zoom in to mouse command.
/// </summary>
/// <value></value>
public static RoutedUICommand ZoomInToMouse
{
get { return ChartCommands.zoomInToMouse; }
}
private static readonly RoutedUICommand zoomWithParam = CreateCommand("ZoomWithParam");
/// <summary>
/// Gets the value that represents the zoom with parameter command.
/// </summary>
/// <value></value>
public static RoutedUICommand ZoomWithParameter
{
get { return zoomWithParam; }
}
private static readonly RoutedUICommand zoomIn = CreateCommand("ZoomIn", Key.OemPlus);
/// <summary>
/// Gets the value that represents the zoom in command.
/// </summary>
/// <value></value>
public static RoutedUICommand ZoomIn
{
get { return zoomIn; }
}
private static readonly RoutedUICommand zoomOut = CreateCommand("ZoomOut", Key.OemMinus);
/// <summary>
/// Gets the value that represents the zoom out command.
/// </summary>
/// <value></value>
public static RoutedUICommand ZoomOut
{
get { return zoomOut; }
}
private static readonly RoutedUICommand fitToView = CreateCommand("FitToView", Key.Home);
/// <summary>
/// Gets the value that represents the fit to view command.
/// </summary>
/// <value></value>
public static RoutedUICommand FitToView
{
get { return ChartCommands.fitToView; }
}
private static readonly RoutedUICommand scrollLeft = CreateCommand("ScrollLeft", Key.Left);
/// <summary>
/// Gets the value that represents the scroll left command.
/// </summary>
/// <value></value>
public static RoutedUICommand ScrollLeft
{
get { return ChartCommands.scrollLeft; }
}
private static readonly RoutedUICommand scrollRight = CreateCommand("ScrollRight", Key.Right);
/// <summary>
/// Gets the value that represents the scroll right command.
/// </summary>
/// <value></value>
public static RoutedUICommand ScrollRight
{
get { return ChartCommands.scrollRight; }
}
private static readonly RoutedUICommand scrollUp = CreateCommand("ScrollUp", Key.Up);
/// <summary>
/// Gets the value that represents the scroll up command.
/// </summary>
/// <value></value>
public static RoutedUICommand ScrollUp
{
get { return ChartCommands.scrollUp; }
}
private static readonly RoutedUICommand scrollDown = CreateCommand("ScrollDown", Key.Down);
/// <summary>
/// Gets the value that represents the scroll down command.
/// </summary>
/// <value></value>
public static RoutedUICommand ScrollDown
{
get { return ChartCommands.scrollDown; }
}
private static readonly RoutedUICommand saveScreenshot = CreateCommand("SaveScreenshot", new KeyGesture(Key.S, ModifierKeys.Control));
/// <summary>
/// Gets the value that represents the save screenshot command.
/// </summary>
/// <value></value>
public static RoutedUICommand SaveScreenshot
{
get { return ChartCommands.saveScreenshot; }
}
private static readonly RoutedUICommand copyScreenshot = CreateCommand("CopyScreenshot", Key.F11);
/// <summary>
/// Gets the value that represents the copy screenshot command.
/// </summary>
/// <value></value>
public static RoutedUICommand CopyScreenshot
{
get { return ChartCommands.copyScreenshot; }
}
private static readonly RoutedUICommand showHelp = CreateCommand("ShowHelp", Key.F1);
/// <summary>
/// Gets the value that represents the show help command.
/// </summary>
/// <value></value>
public static RoutedUICommand ShowHelp
{
get { return ChartCommands.showHelp; }
}
}
}

View File

@@ -0,0 +1,59 @@
<d3:ContentGraph x:Class="Microsoft.Research.DynamicDataDisplay.Charts.Navigation.CursorCoordinateGraph"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:d3="clr-namespace:Microsoft.Research.DynamicDataDisplay.Charts"
xmlns:d3nav="clr-namespace:Microsoft.Research.DynamicDataDisplay.Charts.Navigation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
IsHitTestVisible="False" Panel.ZIndex="1">
<d3:ContentGraph.Resources>
<Style x:Key="outerBorderStyle" TargetType="{x:Type Rectangle}" >
<Setter Property="RadiusX" Value="10"/>
<Setter Property="RadiusY" Value="10"/>
<Setter Property="Stroke" Value="LightGray"/>
<Setter Property="StrokeThickness" Value="1"/>
<Setter Property="Fill" Value="#88FFFFFF"/>
</Style>
<Style x:Key="innerBorderStyle" TargetType="{x:Type Border}">
<Setter Property="CornerRadius" Value="4"/>
<Setter Property="Background" Value="White"/>
<Setter Property="Margin" Value="8,4,8,4"/>
</Style>
<Style x:Key="textStyle" TargetType="{x:Type TextBlock}">
<Setter Property="Margin" Value="2,1,2,1"/>
</Style>
<Style x:Key="lineStyle" TargetType="{x:Type Line}">
<Setter Property="Stroke" Value="{Binding
RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type d3nav:CursorCoordinateGraph}},
Path=LineStroke}"/>
<Setter Property="StrokeThickness" Value="{Binding
RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type d3nav:CursorCoordinateGraph}},
Path=LineStrokeThickness}"/>
<Setter Property="StrokeDashArray" Value="{Binding
RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type d3nav:CursorCoordinateGraph}},
Path=LineStrokeDashArray}"/>
<Setter Property="IsHitTestVisible" Value="False"/>
</Style>
</d3:ContentGraph.Resources>
<Canvas Name="content" Cursor="None" Background="Transparent" IsHitTestVisible="False">
<Line Name="horizLine" Style="{StaticResource lineStyle}"/>
<Line Name="vertLine" Style="{StaticResource lineStyle}"/>
<Grid Name="horizGrid" Canvas.Bottom="5">
<Rectangle Style="{StaticResource outerBorderStyle}"/>
<Border Style="{StaticResource innerBorderStyle}">
<TextBlock Name="horizTextBlock" Style="{StaticResource textStyle}"/>
</Border>
</Grid>
<Grid Name="vertGrid" Canvas.Left="5">
<Rectangle Style="{StaticResource outerBorderStyle}"/>
<Border Style="{StaticResource innerBorderStyle}">
<TextBlock Name="vertTextBlock" Style="{StaticResource textStyle}"/>
</Border>
</Grid>
</Canvas>
</d3:ContentGraph>

View File

@@ -0,0 +1,470 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Diagnostics;
using Microsoft.Research.DynamicDataDisplay.Common.Auxiliary;
using Microsoft.Research.DynamicDataDisplay;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using Microsoft.Research.DynamicDataDisplay.Common;
namespace Microsoft.Research.DynamicDataDisplay.Charts.Navigation
{
/// <summary>
/// Adds to ChartPlotter two crossed lines, bound to mouse cursor position, and two labels near axes with mouse position in its text.
/// </summary>
public partial class CursorCoordinateGraph : ContentGraph
{
/// <summary>
/// Initializes a new instance of the <see cref="CursorCoordinateGraph"/> class.
/// </summary>
public CursorCoordinateGraph()
{
InitializeComponent();
}
Vector blockShift = new Vector(3, 3);
#region Plotter
protected override void OnPlotterAttached()
{
UIElement parent = (UIElement)Parent;
parent.MouseMove += parent_MouseMove;
parent.MouseEnter += Parent_MouseEnter;
parent.MouseLeave += Parent_MouseLeave;
UpdateVisibility();
UpdateUIRepresentation();
}
protected override void OnPlotterDetaching()
{
UIElement parent = (UIElement)Parent;
parent.MouseMove -= parent_MouseMove;
parent.MouseEnter -= Parent_MouseEnter;
parent.MouseLeave -= Parent_MouseLeave;
}
#endregion
private bool autoHide = true;
/// <summary>
/// Gets or sets a value indicating whether to hide automatically cursor lines when mouse leaves plotter.
/// </summary>
/// <value><c>true</c> if auto hide; otherwise, <c>false</c>.</value>
public bool AutoHide
{
get { return autoHide; }
set { autoHide = value; }
}
private void Parent_MouseEnter(object sender, MouseEventArgs e)
{
if (autoHide)
{
UpdateVisibility();
}
}
private void UpdateVisibility()
{
horizLine.Visibility = vertGrid.Visibility = GetHorizontalVisibility();
vertLine.Visibility = horizGrid.Visibility = GetVerticalVisibility();
}
private Visibility GetHorizontalVisibility()
{
return showHorizontalLine ? Visibility.Visible : Visibility.Hidden;
}
private Visibility GetVerticalVisibility()
{
return showVerticalLine ? Visibility.Visible : Visibility.Hidden;
}
private void Parent_MouseLeave(object sender, MouseEventArgs e)
{
if (autoHide)
{
horizLine.Visibility = Visibility.Hidden;
vertLine.Visibility = Visibility.Hidden;
horizGrid.Visibility = Visibility.Hidden;
vertGrid.Visibility = Visibility.Hidden;
}
}
private bool followMouse = true;
/// <summary>
/// Gets or sets a value indicating whether lines are following mouse cursor position.
/// </summary>
/// <value><c>true</c> if lines are following mouse cursor position; otherwise, <c>false</c>.</value>
public bool FollowMouse
{
get { return followMouse; }
set
{
followMouse = value;
if (!followMouse)
{
AutoHide = false;
}
UpdateUIRepresentation();
}
}
private void parent_MouseMove(object sender, MouseEventArgs e)
{
if (followMouse)
{
UpdateUIRepresentation();
}
}
protected override void OnViewportPropertyChanged(ExtendedPropertyChangedEventArgs e)
{
UpdateUIRepresentation();
}
private string customXFormat = null;
/// <summary>
/// Gets or sets the custom format string of x label.
/// </summary>
/// <value>The custom X format.</value>
public string CustomXFormat
{
get { return customXFormat; }
set
{
if (customXFormat != value)
{
customXFormat = value;
UpdateUIRepresentation();
}
}
}
private string customYFormat = null;
/// <summary>
/// Gets or sets the custom format string of y label.
/// </summary>
/// <value>The custom Y format.</value>
public string CustomYFormat
{
get { return customYFormat; }
set
{
if (customYFormat != value)
{
customYFormat = value;
UpdateUIRepresentation();
}
}
}
private Func<double, string> xTextMapping = null;
/// <summary>
/// Gets or sets the text mapping of x label - function that builds text from x-coordinate of mouse in data.
/// </summary>
/// <value>The X text mapping.</value>
public Func<double, string> XTextMapping
{
get { return xTextMapping; }
set
{
if (xTextMapping != value)
{
xTextMapping = value;
UpdateUIRepresentation();
}
}
}
private Func<double, string> yTextMapping = null;
/// <summary>
/// Gets or sets the text mapping of y label - function that builds text from y-coordinate of mouse in data.
/// </summary>
/// <value>The Y text mapping.</value>
public Func<double, string> YTextMapping
{
get { return yTextMapping; }
set
{
if (yTextMapping != value)
{
yTextMapping = value;
UpdateUIRepresentation();
}
}
}
private bool showHorizontalLine = true;
/// <summary>
/// Gets or sets a value indicating whether to show horizontal line.
/// </summary>
/// <value><c>true</c> if horizontal line is shown; otherwise, <c>false</c>.</value>
public bool ShowHorizontalLine
{
get { return showHorizontalLine; }
set
{
if (showHorizontalLine != value)
{
showHorizontalLine = value;
UpdateVisibility();
}
}
}
private bool showVerticalLine = true;
/// <summary>
/// Gets or sets a value indicating whether to show vertical line.
/// </summary>
/// <value><c>true</c> if vertical line is shown; otherwise, <c>false</c>.</value>
public bool ShowVerticalLine
{
get { return showVerticalLine; }
set
{
if (showVerticalLine != value)
{
showVerticalLine = value;
UpdateVisibility();
}
}
}
private void UpdateUIRepresentation()
{
Point position = followMouse ? Mouse.GetPosition(this) : Position;
UpdateUIRepresentation(position);
}
private void UpdateUIRepresentation(Point mousePos)
{
if (Plotter2D == null) return;
var transform = Plotter2D.Viewport.Transform;
DataRect visible = Plotter2D.Viewport.Visible;
Rect output = Plotter2D.Viewport.Output;
if (!output.Contains(mousePos))
{
if (autoHide)
{
horizGrid.Visibility = horizLine.Visibility = vertGrid.Visibility = vertLine.Visibility = Visibility.Hidden;
}
return;
}
if (!followMouse)
{
mousePos = mousePos.DataToScreen(transform);
}
horizLine.X1 = output.Left;
horizLine.X2 = output.Right;
horizLine.Y1 = mousePos.Y;
horizLine.Y2 = mousePos.Y;
vertLine.X1 = mousePos.X;
vertLine.X2 = mousePos.X;
vertLine.Y1 = output.Top;
vertLine.Y2 = output.Bottom;
if (UseDashOffset)
{
horizLine.StrokeDashOffset = (output.Right - mousePos.X) / 2;
vertLine.StrokeDashOffset = (output.Bottom - mousePos.Y) / 2;
}
Point mousePosInData = mousePos.ScreenToData(transform);
string text = null;
if (showVerticalLine)
{
double xValue = mousePosInData.X;
if (xTextMapping != null)
text = xTextMapping(xValue);
// doesnot have xTextMapping or it returned null
if (text == null)
text = GetRoundedValue(visible.XMin, visible.XMax, xValue);
if (!String.IsNullOrEmpty(customXFormat))
text = String.Format(customXFormat, text);
horizTextBlock.Text = text;
}
double width = horizGrid.ActualWidth;
double x = mousePos.X + blockShift.X;
if (x + width > output.Right)
{
x = mousePos.X - blockShift.X - width;
}
Canvas.SetLeft(horizGrid, x);
if (showHorizontalLine)
{
double yValue = mousePosInData.Y;
text = null;
if (yTextMapping != null)
text = yTextMapping(yValue);
if (text == null)
text = GetRoundedValue(visible.YMin, visible.YMax, yValue);
if (!String.IsNullOrEmpty(customYFormat))
text = String.Format(customYFormat, text);
vertTextBlock.Text = text;
}
// by default vertGrid is positioned on the top of line.
double height = vertGrid.ActualHeight;
double y = mousePos.Y - blockShift.Y - height;
if (y < output.Top)
{
y = mousePos.Y + blockShift.Y;
}
Canvas.SetTop(vertGrid, y);
if (followMouse)
Position = mousePos;
}
/// <summary>
/// Gets or sets the mouse position in screen coordinates.
/// </summary>
/// <value>The position.</value>
public Point Position
{
get { return (Point)GetValue(PositionProperty); }
set { SetValue(PositionProperty, value); }
}
/// <summary>
/// Identifies Position dependency property.
/// </summary>
public static readonly DependencyProperty PositionProperty = DependencyProperty.Register(
"Position",
typeof(Point),
typeof(CursorCoordinateGraph),
new UIPropertyMetadata(new Point(), OnPositionChanged));
private static void OnPositionChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
CursorCoordinateGraph graph = (CursorCoordinateGraph)d;
graph.UpdateUIRepresentation((Point)e.NewValue);
}
private string GetRoundedValue(double min, double max, double value)
{
double roundedValue = value;
var log = RoundingHelper.GetDifferenceLog(min, max);
string format = "G3";
double diff = Math.Abs(max - min);
if (1E3 < diff && diff < 1E6)
{
format = "F0";
}
if (log < 0)
format = "G" + (-log + 2).ToString();
return roundedValue.ToString(format);
}
#region UseDashOffset property
public bool UseDashOffset
{
get { return (bool)GetValue(UseDashOffsetProperty); }
set { SetValue(UseDashOffsetProperty, value); }
}
public static readonly DependencyProperty UseDashOffsetProperty = DependencyProperty.Register(
"UseDashOffset",
typeof(bool),
typeof(CursorCoordinateGraph),
new FrameworkPropertyMetadata(true, UpdateUIRepresentation));
private static void UpdateUIRepresentation(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
CursorCoordinateGraph graph = (CursorCoordinateGraph)d;
if ((bool)e.NewValue)
{
graph.UpdateUIRepresentation();
}
else
{
graph.vertLine.ClearValue(Line.StrokeDashOffsetProperty);
graph.horizLine.ClearValue(Line.StrokeDashOffsetProperty);
}
}
#endregion
#region LineStroke property
public Brush LineStroke
{
get { return (Brush)GetValue(LineStrokeProperty); }
set { SetValue(LineStrokeProperty, value); }
}
public static readonly DependencyProperty LineStrokeProperty = DependencyProperty.Register(
"LineStroke",
typeof(Brush),
typeof(CursorCoordinateGraph),
new PropertyMetadata(new SolidColorBrush(Color.FromArgb(170, 86, 86, 86))));
#endregion
#region LineStrokeThickness property
public double LineStrokeThickness
{
get { return (double)GetValue(LineStrokeThicknessProperty); }
set { SetValue(LineStrokeThicknessProperty, value); }
}
public static readonly DependencyProperty LineStrokeThicknessProperty = DependencyProperty.Register(
"LineStrokeThickness",
typeof(double),
typeof(CursorCoordinateGraph),
new PropertyMetadata(2.0));
#endregion
#region LineStrokeDashArray property
[SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public DoubleCollection LineStrokeDashArray
{
get { return (DoubleCollection)GetValue(LineStrokeDashArrayProperty); }
set { SetValue(LineStrokeDashArrayProperty, value); }
}
public static readonly DependencyProperty LineStrokeDashArrayProperty = DependencyProperty.Register(
"LineStrokeDashArray",
typeof(DoubleCollection),
typeof(CursorCoordinateGraph),
new FrameworkPropertyMetadata(DoubleCollectionHelper.Create(3, 3)));
#endregion
}
}

View File

@@ -0,0 +1,331 @@
using System.Collections.Generic;
using System.Reflection;
using System.Windows.Controls;
using System.Windows.Media.Imaging;
using System.Windows;
using System.Windows.Shapes;
using System.Windows.Media;
using System.Windows.Input;
using System.Linq;
using System.Diagnostics;
using System;
using System.Collections.ObjectModel;
using System.Collections;
using System.ComponentModel;
using System.Windows.Data;
using Microsoft.Research.DynamicDataDisplay.Charts.Navigation;
namespace Microsoft.Research.DynamicDataDisplay.Navigation
{
/// <summary>
/// Is responsible for displaying and populating default context menu of ChartPlotter
/// </summary>
public class DefaultContextMenu : IPlotterElement
{
private static readonly BitmapImage helpIcon;
private static readonly BitmapImage copyScreenshotIcon;
private static readonly BitmapImage saveScreenshotIcon;
private static readonly BitmapImage fitToViewIcon;
static DefaultContextMenu()
{
helpIcon = LoadIcon("HelpIcon");
saveScreenshotIcon = LoadIcon("SaveIcon");
copyScreenshotIcon = LoadIcon("CopyScreenshotIcon");
fitToViewIcon = LoadIcon("FitToViewIcon");
}
private static BitmapImage LoadIcon(string name)
{
Assembly currentAssembly = typeof(DefaultContextMenu).Assembly;
BitmapImage icon = new BitmapImage();
icon.BeginInit();
icon.StreamSource = currentAssembly.GetManifestResourceStream("Microsoft.Research.DynamicDataDisplay.Resources." + name + ".png");
icon.EndInit();
icon.Freeze();
return icon;
}
/// <summary>
/// Initializes a new instance of the <see cref="DefaultContextMenu"/> class.
/// </summary>
public DefaultContextMenu() { }
protected ContextMenu PopulateContextMenu(Plotter target)
{
ContextMenu menu = new ContextMenu();
MenuItem fitToViewMenuItem = new MenuItem
{
Header = Strings.UIResources.ContextMenuFitToView,
ToolTip = Strings.UIResources.ContextMenuFitToViewTooltip,
Icon = new Image { Source = fitToViewIcon },
Command = ChartCommands.FitToView,
CommandTarget = target
};
MenuItem savePictureMenuItem = new MenuItem
{
Header = Strings.UIResources.ContextMenuSaveScreenshot,
ToolTip = Strings.UIResources.ContextMenuSaveScreenshotTooltip,
Icon = new Image { Source = saveScreenshotIcon },
Command = ChartCommands.SaveScreenshot,
CommandTarget = target
};
MenuItem copyPictureMenuItem = new MenuItem
{
Header = Strings.UIResources.ContextMenuCopyScreenshot,
ToolTip = Strings.UIResources.ContextMenuCopyScreenshotTooltip,
Icon = new Image { Source = copyScreenshotIcon },
Command = ChartCommands.CopyScreenshot,
CommandTarget = target
};
MenuItem quickHelpMenuItem = new MenuItem
{
Header = Strings.UIResources.ContextMenuQuickHelp,
ToolTip = Strings.UIResources.ContextMenuQuickHelpTooltip,
Command = ChartCommands.ShowHelp,
Icon = new Image { Source = helpIcon },
CommandTarget = target
};
//MenuItem reportFeedback = new MenuItem
//{
// Header = Strings.UIResources.ContextMenuReportFeedback,
// ToolTip = Strings.UIResources.ContextMenuReportFeedbackTooltip,
// Icon = (Image)plotter.Resources["SendFeedbackIcon"]
//};
//reportFeedback.Click += reportFeedback_Click;
staticMenuItems.Add(fitToViewMenuItem);
staticMenuItems.Add(copyPictureMenuItem);
staticMenuItems.Add(savePictureMenuItem);
staticMenuItems.Add(quickHelpMenuItem);
//staticMenuItems.Add(reportFeedback);
menu.ItemsSource = staticMenuItems;
return menu;
}
//private void reportFeedback_Click(object sender, RoutedEventArgs e)
//{
// try
// {
// using (Process.Start("mailto:" + Strings.UIResources.SendFeedbackEmail + "?Subject=[D3]%20" + typeof(DefaultContextMenu).Assembly.GetName().Version)) { }
// }
// catch (Exception)
// {
// MessageBox.Show(Strings.UIResources.SendFeedbackError + Strings.UIResources.SendFeedbackEmail, "Error while sending feedback", MessageBoxButton.OK, MessageBoxImage.Information);
// }
//}
private readonly ObservableCollection<object> staticMenuItems = new ObservableCollection<object>();
// hidden because default menu items' command target is plotter, and serializing this will
// cause circular reference
/// <summary>
/// Gets the static menu items.
/// </summary>
/// <value>The static menu items.</value>
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public ObservableCollection<object> StaticMenuItems
{
get { return staticMenuItems; }
}
#region IPlotterElement Members
private Plotter2D plotter;
void IPlotterElement.OnPlotterAttached(Plotter plotter)
{
this.plotter = (Plotter2D)plotter;
ContextMenu menu = PopulateContextMenu(plotter);
plotter.ContextMenu = menu;
plotter.PreviewMouseRightButtonDown += plotter_PreviewMouseRightButtonDown;
plotter.PreviewMouseRightButtonUp += plotter_PreviewMouseRightButtonUp;
plotter.PreviewMouseLeftButtonDown += plotter_PreviewMouseLeftButtonDown;
plotter.ContextMenu.Closed += ContextMenu_Closed;
}
private void plotter_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
// this will prevent other tools like PointSelector from
// wrong actuations
if (contextMenuOpen)
{
plotter.Focus();
e.Handled = true;
}
}
void IPlotterElement.OnPlotterDetaching(Plotter plotter)
{
plotter.ContextMenu.Closed -= ContextMenu_Closed;
plotter.ContextMenu = null;
plotter.PreviewMouseRightButtonDown -= plotter_PreviewMouseRightButtonDown;
plotter.PreviewMouseRightButtonUp -= plotter_PreviewMouseRightButtonUp;
this.plotter = null;
}
private void ContextMenu_Closed(object sender, RoutedEventArgs e)
{
contextMenuOpen = false;
foreach (var item in dynamicMenuItems)
{
staticMenuItems.Remove(item);
}
}
private Point mousePos;
/// <summary>
/// Gets the mouse position when right mouse button was clicked.
/// </summary>
/// <value>The mouse position on click.</value>
public Point MousePositionOnClick
{
get { return mousePos; }
}
private void plotter_PreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e)
{
contextMenuOpen = false;
mousePos = e.GetPosition(plotter);
}
private bool contextMenuOpen = false;
private readonly ObservableCollection<object> dynamicMenuItems = new ObservableCollection<object>();
private void plotter_PreviewMouseRightButtonUp(object sender, MouseButtonEventArgs e)
{
Point position = e.GetPosition(plotter);
if (mousePos == position)
{
hitResults.Clear();
VisualTreeHelper.HitTest(plotter, null, CollectAllVisuals_Callback, new PointHitTestParameters(position));
foreach (var item in dynamicMenuItems)
{
staticMenuItems.Remove(item);
}
dynamicMenuItems.Clear();
var dynamicItems = (hitResults.Where(r =>
{
IPlotterContextMenuSource menuSource = r as IPlotterContextMenuSource;
if (menuSource != null)
{
menuSource.BuildMenu();
}
var items = GetPlotterContextMenu(r);
return items != null && items.Count > 0;
}).SelectMany(r =>
{
var menuItems = GetPlotterContextMenu(r);
FrameworkElement chart = r as FrameworkElement;
if (chart != null)
{
foreach (var menuItem in menuItems.OfType<MenuItem>())
{
menuItem.DataContext = chart.DataContext;
}
}
return menuItems;
})).ToList();
foreach (var item in dynamicItems)
{
dynamicMenuItems.Add(item);
//MenuItem menuItem = item as MenuItem;
//if (menuItem != null)
//{
// menuItem.CommandTarget = plotter;
//}
}
staticMenuItems.AddMany(dynamicMenuItems);
plotter.Focus();
contextMenuOpen = true;
// in XBAP applications these lines throws a SecurityException, as (as I think)
// we are opening "new window" here. So in XBAP we are not opening context menu manually, but
// it will be opened by itself as this is right click.
#if !RELEASEXBAP
plotter.ContextMenu.IsOpen = true;
e.Handled = true;
#endif
}
else
{
// this is to prevent showing menu when RMB was pressed, then moved and now is releasing.
e.Handled = true;
}
}
#region PlotterContextMenu property
/// <summary>
/// Gets the plotter context menu.
/// </summary>
/// <param name="obj">The obj.</param>
/// <returns></returns>
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public static ObjectCollection GetPlotterContextMenu(DependencyObject obj)
{
return (ObjectCollection)obj.GetValue(PlotterContextMenuProperty);
}
/// <summary>
/// Sets the plotter context menu.
/// </summary>
/// <param name="obj">The obj.</param>
/// <param name="value">The value.</param>
public static void SetPlotterContextMenu(DependencyObject obj, ObjectCollection value)
{
obj.SetValue(PlotterContextMenuProperty, value);
}
/// <summary>
/// Identifies the PlotterContextMenu attached property.
/// </summary>
public static readonly DependencyProperty PlotterContextMenuProperty = DependencyProperty.RegisterAttached(
"PlotterContextMenu",
typeof(ObjectCollection),
typeof(DefaultContextMenu),
new FrameworkPropertyMetadata(null));
#endregion
private List<DependencyObject> hitResults = new List<DependencyObject>();
private HitTestResultBehavior CollectAllVisuals_Callback(HitTestResult result)
{
if (result == null || result.VisualHit == null)
return HitTestResultBehavior.Stop;
hitResults.Add(result.VisualHit);
return HitTestResultBehavior.Continue;
}
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
Plotter IPlotterElement.Plotter
{
get { return plotter; }
}
#endregion
}
/// <summary>
/// Represents a collection of additional menu items in ChartPlotter's context menu.
/// </summary>
public sealed class ObjectCollection : ObservableCollection<Object> { }
}

View File

@@ -0,0 +1,49 @@
// todo commented because base class is 'Rect'AnimationBase, but animated value is of DataRect type.
//using System;
//using System.Collections.Generic;
//using System.Linq;
//using System.Text;
//using System.Windows.Media.Animation;
//using System.Windows;
//using System.Diagnostics;
//using Microsoft.Research.DynamicDataDisplay.Common;
//namespace Microsoft.Research.DynamicDataDisplay.Charts.Navigation
//{
// internal sealed class EndlessPanningRectAnimation : RectAnimationBase
// {
// private DataRect from;
// public DataRect From
// {
// get { return from; }
// set { from = value; }
// }
// private Vector speed;
// public Vector Speed
// {
// get { return speed; }
// set { speed = value; }
// }
// public EndlessPanningRectAnimation(DataRect from, Vector speed)
// {
// Duration = Duration.Forever;
// this.from = from;
// this.speed = speed;
// }
// protected override Rect GetCurrentValueCore(Rect defaultOriginValue, Rect defaultDestinationValue, AnimationClock animationClock)
// {
// double time = animationClock.CurrentTime.Value.TotalSeconds;
// Rect currentValue = Rect.Offset(from, speed * time);
// return currentValue;
// }
// protected override Freezable CreateInstanceCore()
// {
// return this;
// }
// }
//}

View File

@@ -0,0 +1,59 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using Microsoft.Research.DynamicDataDisplay.Common;
namespace Microsoft.Research.DynamicDataDisplay.Charts.Navigation
{
/// <summary>
/// Represents a horizontal scroll bar on the borrom of <see cref="Plotter"/>.
/// Uses ChartPlotter.Plotter.Viewport.DataDomain property as a source of data about current position and position limits.
/// </summary>
public sealed class HorizontalScrollBar : PlotterScrollBar
{
/// <summary>
/// Initializes a new instance of the <see cref="HorizontalScrollBar"/> class.
/// </summary>
public HorizontalScrollBar()
{
ScrollBar.Orientation = Orientation.Horizontal;
}
protected override void UpdateScrollBar(Viewport2D viewport)
{
if (viewport != null && !viewport.Domain.IsEmpty)
{
var visibleRange = new Range<double>(viewport.Visible.XMin, viewport.Visible.XMax);
double size = visibleRange.Max - visibleRange.Min;
ScrollBar.ViewportSize = size;
var domainRange = new Range<double>(viewport.Domain.XMin, viewport.Domain.XMax);
ScrollBar.Minimum = domainRange.Min;
ScrollBar.Maximum = domainRange.Max - size;
ScrollBar.Value = visibleRange.Min;
ScrollBar.Visibility = Visibility.Visible;
}
else
{
ScrollBar.Visibility = Visibility.Collapsed;
}
}
protected override DataRect CreateVisibleRect(DataRect rect, double value)
{
rect.XMin = value;
return rect;
}
protected override Panel GetHostPanel(Plotter plotter)
{
return plotter.BottomPanel;
}
}
}

View File

@@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Microsoft.Research.DynamicDataDisplay.Charts.Navigation
{
public interface IPlotterContextMenuSource
{
void BuildMenu();
}
}

View File

@@ -0,0 +1,94 @@
// todo look to comment for EndlessAnimation
//using System;
//using System.Collections.Generic;
//using System.Linq;
//using System.Text;
//using Microsoft.Research.DynamicDataDisplay.Navigation;
//using System.Windows.Input;
//using System.Windows;
//using System.Windows.Media.Animation;
//using System.Windows.Media;
//using System.Diagnostics;
//namespace Microsoft.Research.DynamicDataDisplay.Charts.Navigation
//{
// [Obsolete("Unready because Viewport became non-visual and no longer has BeginAnimation method.", true)]
// public class InertialMouseNavigation : MouseNavigation
// {
// public InertialMouseNavigation()
// {
// }
// Point visibleStart;
// Point panningStartPointInScreen;
// int panningStartTime;
// protected override void StartPanning(MouseButtonEventArgs e)
// {
// visibleStart = Viewport.Visible.Location;
// panningStartPointInScreen = e.GetPosition(this);
// base.StartPanning(e);
// panningStartTime = e.Timestamp;
// }
// bool runningAnimation = false;
// protected override void StopPanning(MouseButtonEventArgs e)
// {
// Point panningStopPointInScreen = e.GetPosition(this);
// base.StopPanning(e);
// // mouse click with no panning
// if (panningStartPointInScreen == panningStopPointInScreen)
// {
// // remove all animations on Viewport.Visible
// BeginVisibleAnimation(null);
// return;
// }
// double panningDuration = (e.Timestamp - panningStartTime) / 1000.0;
// double animationStartMinDuration = 0.5; // seconds
// double animationStartMinMouseShift = 50; // pixels
// // mouse moved short enough and for rather long distance.
// if (panningDuration < animationStartMinDuration && (panningStopPointInScreen - panningStartPointInScreen).Length > animationStartMinMouseShift)
// {
// Vector visibleShift = Viewport.Visible.Location - visibleStart;
// EndlessPanningRectAnimation panningAnimation = new EndlessPanningRectAnimation(
// Viewport.Visible,
// visibleShift / panningDuration);
// BeginVisibleAnimation(panningAnimation);
// }
// }
// private void BeginVisibleAnimation(AnimationTimeline animation)
// {
// if (animation != null)
// {
// if (runningAnimation)
// {
// BeginVisibleAnimation(null);
// }
// //Plotter2D.Viewport.BeginAnimation(Viewport2D.VisibleProperty, animation);
// runningAnimation = true;
// }
// else
// {
// if (!runningAnimation)
// return;
// Debug.WriteLine("Removing animation");
// Rect visible = Plotter2D.Viewport.Visible;
// //Plotter2D.Viewport.BeginAnimation(Viewport2D.VisibleProperty, null);
// Plotter2D.Viewport.Visible = visible;
// runningAnimation = false;
// }
// }
// }
//}

View File

@@ -0,0 +1,454 @@
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using Microsoft.Win32;
using System;
using Microsoft.Research.DynamicDataDisplay.Common.UndoSystem;
using System.Collections.Generic;
using Microsoft.Research.DynamicDataDisplay.Charts.Navigation;
namespace Microsoft.Research.DynamicDataDisplay.Navigation
{
/// <summary>Provides keyboard navigation around viewport of ChartPlotter.</summary>
public class KeyboardNavigation : IPlotterElement
{
/// <summary>
/// Initializes a new instance of the <see cref="KeyboardNavigation"/> class.
/// </summary>
public KeyboardNavigation() { }
private bool isReversed = true;
/// <summary>
/// Gets or sets a value indicating whether panning directions are reversed.
/// </summary>
/// <value>
/// <c>true</c> if panning directions are reversed; otherwise, <c>false</c>.
/// </value>
public bool IsReversed
{
get { return isReversed; }
set { isReversed = value; }
}
private readonly List<CommandBinding> addedBindings = new List<CommandBinding>();
private void AddBinding(CommandBinding binding)
{
plotter2D.CommandBindings.Add(binding);
addedBindings.Add(binding);
}
private void InitCommands()
{
if (plotter2D == null)
return;
var zoomOutToMouseCommandBinding = new CommandBinding(
ChartCommands.ZoomOutToMouse,
ZoomOutToMouseExecute,
ZoomOutToMouseCanExecute);
AddBinding(zoomOutToMouseCommandBinding);
var zoomInToMouseCommandBinding = new CommandBinding(
ChartCommands.ZoomInToMouse,
ZoomInToMouseExecute,
ZoomInToMouseCanExecute);
AddBinding(zoomInToMouseCommandBinding);
var zoomWithParamCommandBinding = new CommandBinding(
ChartCommands.ZoomWithParameter,
ZoomWithParamExecute,
ZoomWithParamCanExecute);
AddBinding(zoomWithParamCommandBinding);
var zoomInCommandBinding = new CommandBinding(
ChartCommands.ZoomIn,
ZoomInExecute,
ZoomInCanExecute);
AddBinding(zoomInCommandBinding);
var zoomOutCommandBinding = new CommandBinding(
ChartCommands.ZoomOut,
ZoomOutExecute,
ZoomOutCanExecute);
AddBinding(zoomOutCommandBinding);
var fitToViewCommandBinding = new CommandBinding(
ChartCommands.FitToView,
FitToViewExecute,
FitToViewCanExecute);
AddBinding(fitToViewCommandBinding);
var ScrollLeftCommandBinding = new CommandBinding(
ChartCommands.ScrollLeft,
ScrollLeftExecute,
ScrollLeftCanExecute);
AddBinding(ScrollLeftCommandBinding);
var ScrollRightCommandBinding = new CommandBinding(
ChartCommands.ScrollRight,
ScrollRightExecute,
ScrollRightCanExecute);
AddBinding(ScrollRightCommandBinding);
var ScrollUpCommandBinding = new CommandBinding(
ChartCommands.ScrollUp,
ScrollUpExecute,
ScrollUpCanExecute);
AddBinding(ScrollUpCommandBinding);
var ScrollDownCommandBinding = new CommandBinding(
ChartCommands.ScrollDown,
ScrollDownExecute,
ScrollDownCanExecute);
AddBinding(ScrollDownCommandBinding);
var SaveScreenshotCommandBinding = new CommandBinding(
ChartCommands.SaveScreenshot,
SaveScreenshotExecute,
SaveScreenshotCanExecute);
AddBinding(SaveScreenshotCommandBinding);
var CopyScreenshotCommandBinding = new CommandBinding(
ChartCommands.CopyScreenshot,
CopyScreenshotExecute,
CopyScreenshotCanExecute);
AddBinding(CopyScreenshotCommandBinding);
var ShowHelpCommandBinding = new CommandBinding(
ChartCommands.ShowHelp,
ShowHelpExecute,
ShowHelpCanExecute);
AddBinding(ShowHelpCommandBinding);
var UndoCommandBinding = new CommandBinding(
ApplicationCommands.Undo,
UndoExecute,
UndoCanExecute);
AddBinding(UndoCommandBinding);
var RedoCommandBinding = new CommandBinding(
ApplicationCommands.Redo,
RedoExecute,
RedoCanExecute);
AddBinding(RedoCommandBinding);
}
#region Zoom Out To Mouse
private void ZoomToPoint(double coeff)
{
Point pt = Mouse.GetPosition(plotter2D.CentralGrid);
Point dataPoint = Viewport.Transform.ScreenToData(pt);
DataRect visible = Viewport.Visible;
Viewport.Visible = visible.Zoom(dataPoint, coeff);
}
private void ZoomOutToMouseExecute(object target, ExecutedRoutedEventArgs e)
{
ZoomToPoint(zoomOutCoeff);
e.Handled = true;
}
private void ZoomOutToMouseCanExecute(object target, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}
#endregion
#region Zoom In To Mouse
private void ZoomInToMouseExecute(object target, ExecutedRoutedEventArgs e)
{
ZoomToPoint(zoomInCoeff);
e.Handled = true;
}
private void ZoomInToMouseCanExecute(object target, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}
#endregion
#region Zoom With param
private void ZoomWithParamExecute(object target, ExecutedRoutedEventArgs e)
{
double zoomParam = (double)e.Parameter;
plotter2D.Viewport.Zoom(zoomParam);
e.Handled = true;
}
private void ZoomWithParamCanExecute(object target, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}
#endregion
#region Zoom in
private const double zoomInCoeff = 0.9;
private void ZoomInExecute(object target, ExecutedRoutedEventArgs e)
{
Viewport.Zoom(zoomInCoeff);
e.Handled = true;
}
private void ZoomInCanExecute(object target, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}
#endregion
#region Zoom out
private const double zoomOutCoeff = 1 / zoomInCoeff;
private void ZoomOutExecute(object target, ExecutedRoutedEventArgs e)
{
Viewport.Zoom(zoomOutCoeff);
e.Handled = true;
}
private void ZoomOutCanExecute(object target, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}
#endregion
#region Fit to view
private void FitToViewExecute(object target, ExecutedRoutedEventArgs e)
{
// todo сделать нормально.
(Viewport as Viewport2D).FitToView();
e.Handled = true;
}
private void FitToViewCanExecute(object target, CanExecuteRoutedEventArgs e)
{
// todo add a check if viewport is already fitted to view.
e.CanExecute = true;
}
#endregion
#region Scroll
private double scrollCoeff = 0.05;
private void ScrollVisibleProportionally(double xShiftCoeff, double yShiftCoeff)
{
DataRect visible = Viewport.Visible;
DataRect oldVisible = visible;
double width = visible.Width;
double height = visible.Height;
double reverseCoeff = isReversed ? -1 : 1;
visible.Offset(reverseCoeff * xShiftCoeff * width, reverseCoeff * yShiftCoeff * height);
Viewport.Visible = visible;
plotter2D.UndoProvider.AddAction(new DependencyPropertyChangedUndoAction(Viewport, Viewport2D.VisibleProperty, oldVisible, visible));
}
#region ScrollLeft
private void ScrollLeftExecute(object target, ExecutedRoutedEventArgs e)
{
ScrollVisibleProportionally(scrollCoeff, 0);
e.Handled = true;
}
private void ScrollLeftCanExecute(object target, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}
#endregion
#region ScrollRight
private void ScrollRightExecute(object target, ExecutedRoutedEventArgs e)
{
ScrollVisibleProportionally(-scrollCoeff, 0);
e.Handled = true;
}
private void ScrollRightCanExecute(object target, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}
#endregion
#region ScrollUp
private void ScrollUpExecute(object target, ExecutedRoutedEventArgs e)
{
ScrollVisibleProportionally(0, -scrollCoeff);
e.Handled = true;
}
private void ScrollUpCanExecute(object target, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}
#endregion
#region ScrollDown
private void ScrollDownExecute(object target, ExecutedRoutedEventArgs e)
{
ScrollVisibleProportionally(0, scrollCoeff);
e.Handled = true;
}
private void ScrollDownCanExecute(object target, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}
#endregion
#endregion
#region SaveScreenshot
private void SaveScreenshotExecute(object target, ExecutedRoutedEventArgs e)
{
SaveFileDialog dlg = new SaveFileDialog();
dlg.Filter = "PNG (*.png)|*.png|JPEG (*.jpg)|*.jpg|BMP (*.bmp)|*.bmp|GIF (*.gif)|*.gif";
dlg.FilterIndex = 1;
dlg.AddExtension = true;
if (dlg.ShowDialog().GetValueOrDefault(false))
{
string filePath = dlg.FileName;
plotter2D.SaveScreenshot(filePath);
e.Handled = true;
}
}
private void SaveScreenshotCanExecute(object target, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}
#endregion
#region CopyScreenshot
private void CopyScreenshotExecute(object target, ExecutedRoutedEventArgs e)
{
plotter2D.CopyScreenshotToClipboard();
e.Handled = true;
}
private void CopyScreenshotCanExecute(object target, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}
#endregion
#region ShowHelp
private bool aboutWindowOpened = false;
private void ShowHelpExecute(object target, ExecutedRoutedEventArgs e)
{
if (!aboutWindowOpened)
{
AboutWindow window = new AboutWindow();
window.Closed += new EventHandler(aboutWindow_Closed);
window.DataContext = plotter2D;
window.Owner = Window.GetWindow(plotter2D);
aboutWindowOpened = true;
window.Show();
e.Handled = true;
}
}
void aboutWindow_Closed(object sender, EventArgs e)
{
Window window = (Window)sender;
window.Closed -= aboutWindow_Closed;
aboutWindowOpened = false;
}
private void ShowHelpCanExecute(object target, CanExecuteRoutedEventArgs e)
{
e.CanExecute = !aboutWindowOpened;
}
#endregion
#region Undo
private void UndoExecute(object target, ExecutedRoutedEventArgs e)
{
plotter2D.UndoProvider.Undo();
e.Handled = true;
}
private void UndoCanExecute(object target, CanExecuteRoutedEventArgs e)
{
e.CanExecute = plotter2D.UndoProvider.CanUndo;
}
#endregion
#region Redo
private void RedoExecute(object target, ExecutedRoutedEventArgs e)
{
plotter2D.UndoProvider.Redo();
e.Handled = true;
}
private void RedoCanExecute(object target, CanExecuteRoutedEventArgs e)
{
e.CanExecute = plotter2D.UndoProvider.CanRedo;
}
#endregion
#region IPlotterElement Members
private Viewport2D Viewport
{
get { return plotter2D.Viewport; }
}
private Plotter2D plotter2D;
void IPlotterElement.OnPlotterAttached(Plotter plotter)
{
plotter2D = (Plotter2D)plotter;
InitCommands();
}
void IPlotterElement.OnPlotterDetaching(Plotter plotter)
{
foreach (var commandBinding in addedBindings)
{
plotter.CommandBindings.Remove(commandBinding);
}
addedBindings.Clear();
this.plotter2D = null;
}
Plotter IPlotterElement.Plotter
{
get { return plotter2D; }
}
#endregion
}
}

View File

@@ -0,0 +1,144 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Threading;
namespace Microsoft.Research.DynamicDataDisplay.Charts.Navigation
{
public sealed class LongOperationsIndicator : IPlotterElement
{
public LongOperationsIndicator()
{
timer.Tick += new EventHandler(timer_Tick);
}
private void timer_Tick(object sender, EventArgs e)
{
UpdateWaitIndicator();
timer.Stop();
}
#region IPlotterElement Members
void IPlotterElement.OnPlotterAttached(Plotter plotter)
{
this.plotter = plotter;
}
void IPlotterElement.OnPlotterDetaching(Plotter plotter)
{
this.plotter = null;
}
private Plotter plotter;
Plotter IPlotterElement.Plotter
{
get { return plotter; }
}
#endregion
#region LongOperationRunning
public static void BeginLongOperation(DependencyObject obj)
{
obj.SetValue(LongOperationRunningProperty, true);
}
public static void EndLongOperation(DependencyObject obj)
{
obj.SetValue(LongOperationRunningProperty, false);
}
public static bool GetLongOperationRunning(DependencyObject obj)
{
return (bool)obj.GetValue(LongOperationRunningProperty);
}
public static void SetLongOperationRunning(DependencyObject obj, bool value)
{
obj.SetValue(LongOperationRunningProperty, value);
}
public static readonly DependencyProperty LongOperationRunningProperty = DependencyProperty.RegisterAttached(
"LongOperationRunning",
typeof(bool),
typeof(LongOperationsIndicator),
new FrameworkPropertyMetadata(false, OnLongOperationRunningChanged));
private static void OnLongOperationRunningChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
Plotter plotter = null;
IPlotterElement element = d as IPlotterElement;
if (element == null)
{
plotter = Plotter.GetPlotter(d);
}
else
{
plotter = element.Plotter;
}
if (plotter != null)
{
var indicator = plotter.Children.OfType<LongOperationsIndicator>().FirstOrDefault();
if (indicator != null)
{
indicator.OnLongOperationRunningChanged(element, (bool)e.NewValue);
}
}
}
UIElement indicator = LoadIndicator();
private static UIElement LoadIndicator()
{
var resources = (ResourceDictionary)Application.LoadComponent(new Uri("/DynamicDataDisplay;component/Charts/Navigation/LongOperationsIndicatorResources.xaml", UriKind.Relative));
UIElement indicator = (UIElement)resources["Indicator"];
return indicator;
}
private readonly DispatcherTimer timer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(100) };
private int operationsCounter = 0;
private void OnLongOperationRunningChanged(IPlotterElement element, bool longOperationRunning)
{
int change = longOperationRunning ? +1 : -1;
operationsCounter += change;
if (plotter == null) return;
if (operationsCounter == 1)
{
timer.Start();
}
else if (operationsCounter == 0)
{
timer.Stop();
UpdateWaitIndicator();
}
}
private void UpdateWaitIndicator()
{
if (operationsCounter == 1)
{
if (!plotter.MainCanvas.Children.Contains(indicator))
{
plotter.MainCanvas.Children.Add(indicator);
}
plotter.Cursor = Cursors.Wait;
}
else if (operationsCounter == 0)
{
plotter.MainCanvas.Children.Remove(indicator);
plotter.ClearValue(Plotter.CursorProperty);
}
}
#endregion // end of LongOperationRunning
}
}

View File

@@ -0,0 +1,11 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:str="clr-namespace:Microsoft.Research.DynamicDataDisplay.Strings"
xmlns:m="clr-namespace:Microsoft.Research.DynamicDataDisplay.MarkupExtensions">
<Grid Canvas.Bottom="10" Canvas.Left="10" Panel.ZIndex="2" x:Key="Indicator">
<Rectangle RadiusX="5" RadiusY="5" Fill="#B0FFFFFF" Stroke="LightGray" StrokeThickness="1"/>
<TextBlock Text="{m:Resource Loading}" Margin="3"/>
</Grid>
</ResourceDictionary>

View File

@@ -0,0 +1,32 @@
using System;
using System.Windows;
namespace Microsoft.Research.DynamicDataDisplay.Navigation
{
/// <summary>Helper class to parse Windows messages</summary>
internal static class MessagesHelper {
internal static int GetXFromLParam(IntPtr lParam) {
return LOWORD(lParam.ToInt32());
}
internal static int GetYFromLParam(IntPtr lParam) {
return HIWORD(lParam.ToInt32());
}
internal static Point GetMousePosFromLParam(IntPtr lParam) {
return new Point(GetXFromLParam(lParam), GetYFromLParam(lParam));
}
internal static int GetWheelDataFromWParam(IntPtr wParam) {
return HIWORD(wParam.ToInt32());
}
internal static short HIWORD(int i) {
return (short)((i & 0xFFFF0000) >> 16);
}
internal static short LOWORD(int i) {
return (short)(i & 0x0000FFFF);
}
}
}

View File

@@ -0,0 +1,406 @@
using System;
using System.Windows;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using Microsoft.Research.DynamicDataDisplay.Common;
using Microsoft.Research.DynamicDataDisplay.Common.Auxiliary;
using System.Diagnostics;
namespace Microsoft.Research.DynamicDataDisplay.Navigation
{
/// <summary>Provides common methods of mouse navigation around viewport</summary>
public class MouseNavigation : NavigationBase
{
/// <summary>
/// Initializes a new instance of the <see cref="MouseNavigation"/> class.
/// </summary>
public MouseNavigation() { }
private AdornerLayer adornerLayer;
protected AdornerLayer AdornerLayer
{
get
{
if (adornerLayer == null)
{
adornerLayer = AdornerLayer.GetAdornerLayer(this);
if (adornerLayer != null)
{
adornerLayer.IsHitTestVisible = false;
}
}
return adornerLayer;
}
}
protected override void OnPlotterAttached(Plotter plotter)
{
base.OnPlotterAttached(plotter);
Mouse.AddMouseDownHandler(Parent, OnMouseDown);
Mouse.AddMouseMoveHandler(Parent, OnMouseMove);
Mouse.AddMouseUpHandler(Parent, OnMouseUp);
Mouse.AddMouseWheelHandler(Parent, OnMouseWheel);
plotter.KeyDown += new KeyEventHandler(OnParentKeyDown);
}
protected override void OnPlotterDetaching(Plotter plotter)
{
plotter.KeyDown -= new KeyEventHandler(OnParentKeyDown);
Mouse.RemoveMouseDownHandler(Parent, OnMouseDown);
Mouse.RemoveMouseMoveHandler(Parent, OnMouseMove);
Mouse.RemoveMouseUpHandler(Parent, OnMouseUp);
Mouse.RemoveMouseWheelHandler(Parent, OnMouseWheel);
base.OnPlotterDetaching(plotter);
}
private void OnParentKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Escape || e.Key == Key.Back)
{
if (isZooming)
{
isZooming = false;
zoomRect = null;
ReleaseMouseCapture();
RemoveSelectionAdorner();
e.Handled = true;
}
}
}
private void OnMouseWheel(object sender, MouseWheelEventArgs e)
{
if (!e.Handled)
{
Point mousePos = e.GetPosition(this);
int delta = -e.Delta;
MouseWheelZoom(mousePos, delta);
e.Handled = true;
}
}
#if DEBUG
public override string ToString()
{
if (!String.IsNullOrEmpty(Name))
{
return Name;
}
return base.ToString();
}
#endif
bool adornerAdded;
RectangleSelectionAdorner selectionAdorner;
private void AddSelectionAdorner()
{
if (!adornerAdded)
{
AdornerLayer layer = AdornerLayer;
if (layer != null)
{
selectionAdorner = new RectangleSelectionAdorner(this) { Border = zoomRect };
layer.Add(selectionAdorner);
adornerAdded = true;
}
}
}
private void RemoveSelectionAdorner()
{
AdornerLayer layer = AdornerLayer;
if (layer != null)
{
layer.Remove(selectionAdorner);
adornerAdded = false;
}
}
private void UpdateSelectionAdorner()
{
selectionAdorner.Border = zoomRect;
selectionAdorner.InvalidateVisual();
}
Rect? zoomRect = null;
private const double wheelZoomSpeed = 1.2;
private bool shouldKeepRatioWhileZooming;
private bool isZooming = false;
protected bool IsZooming
{
get { return isZooming; }
}
private bool isPanning = false;
protected bool IsPanning
{
get { return isPanning; }
}
private Point panningStartPointInViewport;
protected Point PanningStartPointInViewport
{
get { return panningStartPointInViewport; }
}
private Point zoomStartPoint;
private static bool IsShiftOrCtrl
{
get
{
ModifierKeys currKeys = Keyboard.Modifiers;
return (currKeys | ModifierKeys.Shift) == currKeys ||
(currKeys | ModifierKeys.Control) == currKeys;
}
}
protected virtual bool ShouldStartPanning(MouseButtonEventArgs e)
{
return e.ChangedButton == MouseButton.Left && Keyboard.Modifiers == ModifierKeys.None;
}
protected virtual bool ShouldStartZoom(MouseButtonEventArgs e)
{
return e.ChangedButton == MouseButton.Left && IsShiftOrCtrl;
}
Point panningStartPointInScreen;
protected virtual void StartPanning(MouseButtonEventArgs e)
{
panningStartPointInScreen = e.GetPosition(this);
panningStartPointInViewport = panningStartPointInScreen.ScreenToViewport(Viewport.Transform);
Plotter2D.UndoProvider.CaptureOldValue(Viewport, Viewport2D.VisibleProperty, Viewport.Visible);
isPanning = true;
// not capturing mouse because this made some tools like PointSelector not
// receive MouseUp events on markers;
// Mouse will be captured later, in the first MouseMove handler call.
// CaptureMouse();
Viewport.PanningState = Viewport2DPanningState.Panning;
//e.Handled = true;
}
protected virtual void StartZoom(MouseButtonEventArgs e)
{
zoomStartPoint = e.GetPosition(this);
if (Viewport.Output.Contains(zoomStartPoint))
{
isZooming = true;
AddSelectionAdorner();
CaptureMouse();
shouldKeepRatioWhileZooming = Keyboard.Modifiers == ModifierKeys.Shift;
e.Handled = true;
}
}
private void OnMouseDown(object sender, MouseButtonEventArgs e)
{
// dragging
bool shouldStartDrag = ShouldStartPanning(e);
if (shouldStartDrag)
StartPanning(e);
// zooming
bool shouldStartZoom = ShouldStartZoom(e);
if (shouldStartZoom)
StartZoom(e);
if (!Plotter.IsFocused)
{
//var window = Window.GetWindow(Plotter);
//var focusWithinWindow = FocusManager.GetFocusedElement(window) != null;
Plotter.Focus();
//if (!focusWithinWindow)
//{
// this is done to prevent other tools like PointSelector from getting mouse click event when clicking on plotter
// to activate window it's contained within
e.Handled = true;
//}
}
}
private void OnMouseMove(object sender, MouseEventArgs e)
{
if (!isPanning && !isZooming) return;
// dragging
if (isPanning && e.LeftButton == MouseButtonState.Pressed)
{
if (!IsMouseCaptured)
{
CaptureMouse();
}
Point endPoint = e.GetPosition(this).ScreenToViewport(Viewport.Transform);
Point loc = Viewport.Visible.Location;
Vector shift = panningStartPointInViewport - endPoint;
loc += shift;
// preventing unnecessary changes, if actually visible hasn't change.
if (shift.X != 0 || shift.Y != 0)
{
Cursor = Cursors.ScrollAll;
DataRect visible = Viewport.Visible;
visible.Location = loc;
Viewport.Visible = visible;
}
e.Handled = true;
}
// zooming
else if (isZooming && e.LeftButton == MouseButtonState.Pressed)
{
Point zoomEndPoint = e.GetPosition(this);
UpdateZoomRect(zoomEndPoint);
e.Handled = true;
}
}
private static bool IsShiftPressed()
{
return Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift);
}
private void UpdateZoomRect(Point zoomEndPoint)
{
Rect output = Viewport.Output;
Rect tmpZoomRect = new Rect(zoomStartPoint, zoomEndPoint);
tmpZoomRect = Rect.Intersect(tmpZoomRect, output);
shouldKeepRatioWhileZooming = IsShiftPressed();
if (shouldKeepRatioWhileZooming)
{
double currZoomRatio = tmpZoomRect.Width / tmpZoomRect.Height;
double zoomRatio = output.Width / output.Height;
if (currZoomRatio < zoomRatio)
{
double oldHeight = tmpZoomRect.Height;
double height = tmpZoomRect.Width / zoomRatio;
tmpZoomRect.Height = height;
if (!tmpZoomRect.Contains(zoomStartPoint))
{
tmpZoomRect.Offset(0, oldHeight - height);
}
}
else
{
double oldWidth = tmpZoomRect.Width;
double width = tmpZoomRect.Height * zoomRatio;
tmpZoomRect.Width = width;
if (!tmpZoomRect.Contains(zoomStartPoint))
{
tmpZoomRect.Offset(oldWidth - width, 0);
}
}
}
zoomRect = tmpZoomRect;
UpdateSelectionAdorner();
}
private void OnMouseUp(object sender, MouseButtonEventArgs e)
{
OnParentMouseUp(e);
}
protected virtual void OnParentMouseUp(MouseButtonEventArgs e)
{
if (isPanning && e.ChangedButton == MouseButton.Left)
{
isPanning = false;
StopPanning(e);
}
else if (isZooming && e.ChangedButton == MouseButton.Left)
{
isZooming = false;
StopZooming();
}
}
protected virtual void StopZooming()
{
if (zoomRect.HasValue)
{
Rect output = Viewport.Output;
Point p1 = zoomRect.Value.TopLeft.ScreenToViewport(Viewport.Transform);
Point p2 = zoomRect.Value.BottomRight.ScreenToViewport(Viewport.Transform);
DataRect newVisible = new DataRect(p1, p2);
Viewport.Visible = newVisible;
zoomRect = null;
ReleaseMouseCapture();
RemoveSelectionAdorner();
}
}
protected virtual void StopPanning(MouseButtonEventArgs e)
{
Plotter2D.UndoProvider.CaptureNewValue(Plotter2D.Viewport, Viewport2D.VisibleProperty, Viewport.Visible);
if (!Plotter.IsFocused)
{
Plotter2D.Focus();
}
Plotter2D.Viewport.PanningState = Viewport2DPanningState.NotPanning;
ReleaseMouseCapture();
ClearValue(CursorProperty);
}
protected override void OnLostFocus(RoutedEventArgs e)
{
if (isZooming)
{
RemoveSelectionAdorner();
isZooming = false;
}
if (isPanning)
{
Plotter2D.Viewport.PanningState = Viewport2DPanningState.NotPanning;
isPanning = false;
}
ReleaseMouseCapture();
base.OnLostFocus(e);
}
private void MouseWheelZoom(Point mousePos, double wheelRotationDelta)
{
Point zoomTo = mousePos.ScreenToViewport(Viewport.Transform);
double zoomSpeed = Math.Abs(wheelRotationDelta / Mouse.MouseWheelDeltaForOneLine);
zoomSpeed *= wheelZoomSpeed;
if (wheelRotationDelta < 0)
{
zoomSpeed = 1 / zoomSpeed;
}
Viewport.Visible = Viewport.Visible.Zoom(zoomTo, zoomSpeed);
}
}
}

View File

@@ -0,0 +1,63 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Input;
using System.Windows;
namespace Microsoft.Research.DynamicDataDisplay.Navigation
{
// todo проверить, как происходит работа когда мышь не над плоттером, а над его ребенком
// todo если все ОК, то перевести все маус навигейшн контролы на этот класс как базовый
public abstract class MouseNavigationBase : NavigationBase
{
protected override void OnPlotterAttached(Plotter plotter)
{
base.OnPlotterAttached(plotter);
Mouse.AddMouseDownHandler(Parent, OnMouseDown);
Mouse.AddMouseMoveHandler(Parent, OnMouseMove);
Mouse.AddMouseUpHandler(Parent, OnMouseUp);
Mouse.AddMouseWheelHandler(Parent, OnMouseWheel);
}
protected override void OnPlotterDetaching(Plotter plotter)
{
Mouse.RemoveMouseDownHandler(Parent, OnMouseDown);
Mouse.RemoveMouseMoveHandler(Parent, OnMouseMove);
Mouse.RemoveMouseUpHandler(Parent, OnMouseUp);
Mouse.RemoveMouseWheelHandler(Parent, OnMouseWheel);
base.OnPlotterDetaching(plotter);
}
private void OnMouseWheel(object sender, MouseWheelEventArgs e)
{
OnPlotterMouseWheel(e);
}
protected virtual void OnPlotterMouseWheel(MouseWheelEventArgs e) { }
private void OnMouseUp(object sender, MouseButtonEventArgs e)
{
OnPlotterMouseUp(e);
}
protected virtual void OnPlotterMouseUp(MouseButtonEventArgs e) { }
private void OnMouseDown(object sender, MouseButtonEventArgs e)
{
OnPlotterMouseDown(e);
}
protected virtual void OnPlotterMouseDown(MouseButtonEventArgs e) { }
private void OnMouseMove(object sender, MouseEventArgs e)
{
OnPlotterMouseMove(e);
}
protected virtual void OnPlotterMouseMove(MouseEventArgs e) { }
}
}

View File

@@ -0,0 +1,103 @@
using System.Windows.Input;
namespace Microsoft.Research.DynamicDataDisplay
{
// TODO: probably optimize memory usage by replacing implicit creation of
// all commands on first usage of this class -
// create each command on first access directly to it.
/// <summary>This class holds keyboard shortcuts to common viewport navigation commands</summary>
public static class ChartCommands
{
#region Auxiliary code for creation of commands
private static RoutedUICommand CreateCommand(string name)
{
return new RoutedUICommand(name, name, typeof(ChartCommands));
}
private static RoutedUICommand CreateCommand(string name, params Key[] keys)
{
InputGestureCollection gestures = new InputGestureCollection();
foreach (var key in keys)
{
gestures.Add(new KeyGesture(key));
}
return new RoutedUICommand(name, name, typeof(ChartCommands), gestures);
}
private static RoutedUICommand CreateCommand(string name, params InputGesture[] gestures)
{
return new RoutedUICommand(name, name, typeof(ChartCommands), new InputGestureCollection(gestures));
}
#endregion
private static readonly RoutedUICommand zoomWithParam = CreateCommand("ZoomWithParam");
public static RoutedUICommand ZoomWithParameter
{
get { return zoomWithParam; }
}
private static readonly RoutedUICommand zoomIn = CreateCommand("ZoomIn", Key.OemPlus);
public static RoutedUICommand ZoomIn
{
get { return zoomIn; }
}
private static readonly RoutedUICommand zoomOut = CreateCommand("ZoomOut", Key.OemMinus);
public static RoutedUICommand ZoomOut
{
get { return zoomOut; }
}
private static readonly RoutedUICommand fitToView = CreateCommand("FitToView", Key.Home);
public static RoutedUICommand FitToView
{
get { return ChartCommands.fitToView; }
}
private static readonly RoutedUICommand scrollLeft = CreateCommand("ScrollLeft", Key.Left);
public static RoutedUICommand ScrollLeft
{
get { return ChartCommands.scrollLeft; }
}
private static readonly RoutedUICommand scrollRight = CreateCommand("ScrollRight", Key.Right);
public static RoutedUICommand ScrollRight
{
get { return ChartCommands.scrollRight; }
}
private static readonly RoutedUICommand scrollUp = CreateCommand("ScrollUp", Key.Up);
public static RoutedUICommand ScrollUp
{
get { return ChartCommands.scrollUp; }
}
private static readonly RoutedUICommand scrollDown = CreateCommand("ScrollDown", Key.Down);
public static RoutedUICommand ScrollDown
{
get { return ChartCommands.scrollDown; }
}
private static readonly RoutedUICommand saveScreenshot = CreateCommand("SaveScreenshot", new KeyGesture(Key.S, ModifierKeys.Control));
public static RoutedUICommand SaveScreenshot
{
get { return ChartCommands.saveScreenshot; }
}
private static readonly RoutedUICommand copyScreenshot = CreateCommand("CopyScreenshot", Key.F11);
public static RoutedUICommand CopyScreenshot
{
get { return ChartCommands.copyScreenshot; }
}
private static readonly RoutedUICommand showHelp = CreateCommand("ShowHelp", Key.F1);
public static RoutedUICommand ShowHelp
{
get { return ChartCommands.showHelp; }
}
}
}

View File

@@ -0,0 +1,91 @@
#define _template
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using Microsoft.Win32;
using System.Windows.Media.Imaging;
using System.Windows.Media;
using System.Reflection;
using System;
namespace Microsoft.Research.DynamicDataDisplay.Navigation
{
/// <summary>This class is responsible for displaying and populating default context menu</summary>
public partial class DefaultContextMenu : NavigationBase
{
static BitmapImage helpIcon;
static BitmapImage copyScreenshotIcon;
static BitmapImage saveScreenshotIcon;
static BitmapImage fitToViewIcon;
static DefaultContextMenu()
{
helpIcon = LoadIcon("HelpIcon");
saveScreenshotIcon = LoadIcon("SaveIcon");
copyScreenshotIcon = LoadIcon("CopyScreenshotIcon");
fitToViewIcon = LoadIcon("FitToViewIcon");
}
private static BitmapImage LoadIcon(string name)
{
Assembly currentAssembly = typeof(DefaultContextMenu).Assembly;
BitmapImage icon = new BitmapImage();
icon.BeginInit();
icon.StreamSource = currentAssembly.GetManifestResourceStream("Microsoft.Research.DynamicDataDisplay.Resources." + name + ".png");
icon.EndInit();
return icon;
}
public DefaultContextMenu()
{
InitContextMenu();
}
public void InitContextMenu()
{
ContextMenu menu = new ContextMenu();
MenuItem fitToViewMenuItem = new MenuItem
{
Header = "Fit to view",
ToolTip = "Make visible area fit to display entire contents",
Icon = new Image { Source = fitToViewIcon },
Command = ChartCommands.FitToView
};
MenuItem savePictureMenuItem = new MenuItem
{
Header = "Save picture",
ToolTip = "Specify name of image file to save screenshot to",
Icon = new Image { Source = saveScreenshotIcon },
Command = ChartCommands.SaveScreenshot
};
MenuItem copyPictureMenuItem = new MenuItem
{
Header = "Copy picture",
ToolTip = "Copy screenshot of charts to clipboard",
Icon = new Image { Source = copyScreenshotIcon },
Command = ChartCommands.CopyScreenshot
};
MenuItem quickHelpMenuItem = new MenuItem
{
Header = "Quick Help",
ToolTip = "View brief instructions",
Command = ChartCommands.ShowHelp,
Icon = new Image { Source = helpIcon }
};
List<MenuItem> menuItems = new List<MenuItem> {
fitToViewMenuItem,
copyPictureMenuItem,
savePictureMenuItem,
quickHelpMenuItem,
};
menu.ItemsSource = menuItems;
ContextMenu = menu;
}
}
}

View File

@@ -0,0 +1,317 @@
using System;
using System.Diagnostics;
using System.Windows;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media.Animation;
namespace Microsoft.Research.DynamicDataDisplay.Navigation
{
/// <summary>Adds some inertia to content of 2D viewport to make it more material</summary>
/// <remarks>This is experimental code</remarks>
public class InertialMouseNavigation : NavigationBase {
protected override void OnLoaded(RoutedEventArgs e) {
adornerLayer = AdornerLayer.GetAdornerLayer(this);
adornerLayer.IsHitTestVisible = false;
}
protected override void OnViewportChanged() {
base.OnViewportChanged();
//Mouse.AddPreviewMouseDownHandler(Parent, (MouseButtonEventHandler)OnMouseDown);
//Mouse.AddPreviewMouseMoveHandler(Parent, (MouseEventHandler)OnMouseMove);
//Mouse.AddPreviewMouseUpHandler(Parent, (MouseButtonEventHandler)OnMouseUp);
Mouse.AddMouseDownHandler(Parent, (MouseButtonEventHandler)OnMouseDown);
Mouse.AddMouseMoveHandler(Parent, (MouseEventHandler)OnMouseMove);
Mouse.AddMouseUpHandler(Parent, (MouseButtonEventHandler)OnMouseUp);
Mouse.AddMouseWheelHandler(Parent, (MouseWheelEventHandler)OnMouseWheel);
}
protected Rect VisibleRect {
get { return (Rect)GetValue(VisibleRectProperty); }
set { SetValue(VisibleRectProperty, value); }
}
public static readonly DependencyProperty VisibleRectProperty =
DependencyProperty.Register(
"VisibleRect",
typeof(Rect),
typeof(InertialMouseNavigation),
new FrameworkPropertyMetadata(new Rect(),
OnVisibleRectChanged));
private static void OnVisibleRectChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
InertialMouseNavigation nav = (InertialMouseNavigation)d;
Rect newVisible = (Rect)e.NewValue;
if (nav.shouldApplyAnimation) {
nav.Viewport.Visible = newVisible;
}
}
protected override void OnVisibleChanged(Rect newRect, Rect oldRect) {
// todo was uncommented
// VisibleRect = newRect;
}
private void OnMouseWheel(object sender, MouseWheelEventArgs e) {
if (!e.Handled) {
Point mousePos = e.GetPosition(this);
int delta = -e.Delta;
MouseWheelZoom(mousePos, delta);
}
}
bool adornerAdded;
RectangleSelectionAdorner selectionAdorner;
AdornerLayer adornerLayer;
private void AddSelectionAdorner() {
if (!adornerAdded) {
selectionAdorner = new RectangleSelectionAdorner(this);
selectionAdorner.Border = zoomRect;
adornerLayer.Add(selectionAdorner);
}
adornerAdded = true;
}
private void RemoveSelectionAdorner() {
adornerLayer.Remove(selectionAdorner);
Debug.Assert(adornerAdded);
adornerAdded = false;
}
private void UpdateSelectionAdorner() {
selectionAdorner.Border = zoomRect;
selectionAdorner.InvalidateVisual();
}
Rect? zoomRect = null;
double wheelZoomSpeed = 1.2;
private bool shouldKeepRatioWhileZooming;
private bool isZooming = false;
private bool isDragging = false;
private Point dragStartPointInVisible;
private Point zoomStartPoint;
private Point dragStartPointInOutput;
private static bool IsShiftOrCtrl {
get {
ModifierKeys currKeys = Keyboard.Modifiers;
return (currKeys | ModifierKeys.Shift) == currKeys ||
(currKeys | ModifierKeys.Control) == currKeys;
}
}
private int dragStartTimestamp;
private void OnMouseDown(object sender, MouseButtonEventArgs e) {
// dragging
bool shouldStartDrag = e.ChangedButton == MouseButton.Left && Keyboard.Modifiers == ModifierKeys.None;
if (shouldStartDrag) {
dragStartPointInOutput = e.GetPosition(this);
dragStartPointInVisible = dragStartPointInOutput.Transform(Viewport.Output, Viewport.Visible);
dragStartTimestamp = e.Timestamp;
isDragging = true;
CaptureMouse();
}
bool shouldStartZoom = e.ChangedButton == MouseButton.Left && IsShiftOrCtrl;
if (shouldStartZoom) {
// zooming
zoomStartPoint = e.GetPosition(this);
if (Viewport.Output.Contains(zoomStartPoint)) {
isZooming = true;
AddSelectionAdorner();
CaptureMouse();
shouldKeepRatioWhileZooming = Keyboard.Modifiers == ModifierKeys.Shift;
}
}
(Parent as IInputElement).Focus();
}
private void OnMouseMove(object sender, MouseEventArgs e) {
if (!isDragging && !isZooming) return;
// dragging
if (isDragging && e.LeftButton == MouseButtonState.Pressed) {
Point endPoint = e.GetPosition(this).Transform(Viewport.Output, Viewport.Visible);
Point loc = Viewport.Visible.Location;
Vector shift = dragStartPointInVisible - endPoint;
loc += shift;
//Debug.WriteLine("Shift = " + shift);
if (shift.X != 0 || shift.Y != 0) {
StopDragAnimation();
Rect visible = Viewport.Visible;
visible.Location = loc;
Viewport.Visible = visible;
if (Viewport.Visible != visible) { }
}
else {
}
}
// zooming
else if (isZooming && e.LeftButton == MouseButtonState.Pressed) {
Point zoomEndPoint = e.GetPosition(this);
UpdateZoomRect(zoomEndPoint);
}
}
private bool shouldApplyAnimation = true;
private void StopDragAnimation() {
shouldApplyAnimation = false;
if (dragAnimation != null && HasAnimatedProperties) {
Debug.WriteLine("Stopping animation");
//BeginAnimation(VisibleRectProperty, null, HandoffBehavior.SnapshotAndReplace);
}
}
private static bool IsShiftPressed() {
return Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift);
}
private void UpdateZoomRect(Point zoomEndPoint) {
Rect output = Viewport.Output;
Rect tmpZoomRect = new Rect(zoomStartPoint, zoomEndPoint);
tmpZoomRect = Rect.Intersect(tmpZoomRect, output);
shouldKeepRatioWhileZooming = IsShiftPressed();
if (shouldKeepRatioWhileZooming) {
double currZoomRatio = tmpZoomRect.Width / tmpZoomRect.Height;
double zoomRatio = output.Width / output.Height;
if (currZoomRatio < zoomRatio) {
double oldHeight = tmpZoomRect.Height;
double height = tmpZoomRect.Width / zoomRatio;
tmpZoomRect.Height = height;
if (!tmpZoomRect.Contains(zoomStartPoint)) {
tmpZoomRect.Offset(0, oldHeight - height);
}
}
else {
double oldWidth = tmpZoomRect.Width;
double width = tmpZoomRect.Height * zoomRatio;
tmpZoomRect.Width = width;
if (!tmpZoomRect.Contains(zoomStartPoint)) {
tmpZoomRect.Offset(oldWidth - width, 0);
}
}
}
zoomRect = tmpZoomRect;
UpdateSelectionAdorner();
}
RectAnimation dragAnimation;
private void OnMouseUp(object sender, MouseButtonEventArgs e) {
if (isDragging && e.ChangedButton == MouseButton.Left) {
isDragging = false;
ReleaseMouseCapture();
Point endPointInOutput = e.GetPosition(this);
Point endPointInVisible = endPointInOutput.Transform(Viewport.Output, Viewport.Visible);
Vector mouseShift = -(endPointInVisible - dragStartPointInVisible);
Vector outputShift = endPointInOutput - dragStartPointInOutput;
int time = e.Timestamp;
int deltaTime = time - dragStartTimestamp;
double seconds = deltaTime / 1000.0;
// mouse moved short enough and for rather long distance.
bool shouldStartAnimation = seconds < 1 && outputShift.Length > 20;
if (shouldStartAnimation) {
Rect moveTo = Viewport.Visible;
moveTo.Offset(mouseShift * 2);
//if (dragAnimation == null) {
dragAnimation = new RectAnimation(moveTo,
TimeSpan.FromSeconds(1));
dragAnimation.DecelerationRatio = 0.2;
//}
//else {
// dragAnimation.To = moveTo;
// dragAnimation.Duration = TimeSpan.FromSeconds(1);
// AnimationClock clock = dragAnimation.CreateClock();
// clock.Controller.Begin();
//}
BeginAnimation(VisibleRectProperty, dragAnimation);
shouldApplyAnimation = true;
Debug.WriteLine("Starting animation");
}
else if (dragAnimation != null) {
StopDragAnimation();
}
// focusing on LMB click
if (endPointInVisible == dragStartPointInVisible) {
//Keyboard.Focus(Parent as IInputElement);
}
}
else if (isZooming && e.ChangedButton == MouseButton.Left) {
isZooming = false;
if (zoomRect.HasValue) {
Rect output = Viewport.Output;
Point p1 = zoomRect.Value.TopLeft.Transform(output, Viewport.Visible);
Point p2 = zoomRect.Value.BottomRight.Transform(output, Viewport.Visible);
Rect newVisible = new Rect(p1, p2);
Viewport.Visible = newVisible;
zoomRect = null;
ReleaseMouseCapture();
RemoveSelectionAdorner();
}
}
}
/*
class MyRectAnimation : RectAnimationBase {
public MyRectAnimation() {
Duration = new Duration(TimeSpan.FromSeconds(2));
}
protected override Rect GetCurrentValueCore(Rect defaultOriginValue, Rect defaultDestinationValue, AnimationClock animationClock) {
Debug.WriteLine("In animation");
return new Rect();
}
protected override Freezable CreateInstanceCore() {
return new MyRectAnimation();
}
}
*/
protected override void OnLostFocus(RoutedEventArgs e) {
if (isZooming) {
RemoveSelectionAdorner();
}
ReleaseMouseCapture();
base.OnLostFocus(e);
}
private void MouseWheelZoom(Point mousePos, int wheelRotationDelta) {
Point zoomTo = mousePos.Transform(Viewport.Output, Viewport.Visible);
double zoomSpeed = Math.Abs(wheelRotationDelta / Mouse.MouseWheelDeltaForOneLine);
zoomSpeed *= wheelZoomSpeed;
if (wheelRotationDelta < 0) {
zoomSpeed = 1 / zoomSpeed;
}
Viewport.Visible = Viewport.Visible.Zoom(zoomTo, zoomSpeed);
}
}
}

View File

@@ -0,0 +1,300 @@
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using Microsoft.Win32;
namespace Microsoft.Research.DynamicDataDisplay.Navigation
{
/// <summary>Provides keyboard navigation around viewport</summary>
public class KeyboardNavigation : NavigationBase
{
public KeyboardNavigation()
{
Focusable = true;
Loaded += KeyboardNavigation_Loaded;
}
private void KeyboardNavigation_Loaded(object sender, RoutedEventArgs e)
{
Keyboard.Focus(this);
InitCommands();
}
public void InitCommands()
{
var zoomWithParamCommandBinding = new CommandBinding(
ChartCommands.ZoomWithParameter,
ZoomWithParamExecute,
ZoomWithParamCanExecute);
Plotter2D.CommandBindings.Add(zoomWithParamCommandBinding);
var zoomInCommandBinding = new CommandBinding(
ChartCommands.ZoomIn,
ZoomInExecute,
ZoomInCanExecute);
Plotter2D.CommandBindings.Add(zoomInCommandBinding);
var zoomOutCommandBinding = new CommandBinding(
ChartCommands.ZoomOut,
ZoomOutExecute,
ZoomOutCanExecute);
Plotter2D.CommandBindings.Add(zoomOutCommandBinding);
var fitToViewCommandBinding = new CommandBinding(
ChartCommands.FitToView,
FitToViewExecute,
FitToViewCanExecute);
Plotter2D.CommandBindings.Add(fitToViewCommandBinding);
var ScrollLeftCommandBinding = new CommandBinding(
ChartCommands.ScrollLeft,
ScrollLeftExecute,
ScrollLeftCanExecute);
Plotter2D.CommandBindings.Add(ScrollLeftCommandBinding);
var ScrollRightCommandBinding = new CommandBinding(
ChartCommands.ScrollRight,
ScrollRightExecute,
ScrollRightCanExecute);
Plotter2D.CommandBindings.Add(ScrollRightCommandBinding);
var ScrollUpCommandBinding = new CommandBinding(
ChartCommands.ScrollUp,
ScrollUpExecute,
ScrollUpCanExecute);
Plotter2D.CommandBindings.Add(ScrollUpCommandBinding);
var ScrollDownCommandBinding = new CommandBinding(
ChartCommands.ScrollDown,
ScrollDownExecute,
ScrollDownCanExecute);
Plotter2D.CommandBindings.Add(ScrollDownCommandBinding);
var SaveScreenshotCommandBinding = new CommandBinding(
ChartCommands.SaveScreenshot,
SaveScreenshotExecute,
SaveScreenshotCanExecute);
Plotter2D.CommandBindings.Add(SaveScreenshotCommandBinding);
var CopyScreenshotCommandBinding = new CommandBinding(
ChartCommands.CopyScreenshot,
CopyScreenshotExecute,
CopyScreenshotCanExecute);
Plotter2D.CommandBindings.Add(CopyScreenshotCommandBinding);
var ShowHelpCommandBinding = new CommandBinding(
ChartCommands.ShowHelp,
ShowHelpExecute,
ShowHelpCanExecute);
Plotter2D.CommandBindings.Add(ShowHelpCommandBinding);
}
#region Zoom With param
private void ZoomWithParamExecute(object target, ExecutedRoutedEventArgs e)
{
// TODO: add working code
//double zoomParam = (double)e.Parameter;
//Plotter2d.ZoomVisible(zoomParam);
e.Handled = true;
}
private void ZoomWithParamCanExecute(object target, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}
#endregion
#region Zoom in
private void ZoomInExecute(object target, ExecutedRoutedEventArgs e)
{
Viewport.Zoom(0.9);
e.Handled = true;
}
private void ZoomInCanExecute(object target, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}
#endregion
#region Zoom out
private void ZoomOutExecute(object target, ExecutedRoutedEventArgs e)
{
Viewport.Zoom(1.1);
e.Handled = true;
}
private void ZoomOutCanExecute(object target, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}
#endregion
#region Fit to view
private void FitToViewExecute(object target, ExecutedRoutedEventArgs e)
{
Viewport.FitToView();
e.Handled = true;
}
private void FitToViewCanExecute(object target, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}
#endregion
#region Scroll
private double scrollCoeff = 0.05;
private void ScrollVisibleProportionally(double xShiftCoeff, double yShiftCoeff)
{
Rect visible = Viewport.Visible;
double width = visible.Width;
double height = visible.Height;
visible.Offset(xShiftCoeff * width, yShiftCoeff * height);
Viewport.Visible = visible;
}
#region ScrollLeft
private void ScrollLeftExecute(object target, ExecutedRoutedEventArgs e)
{
ScrollVisibleProportionally(scrollCoeff, 0);
e.Handled = true;
}
private void ScrollLeftCanExecute(object target, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}
#endregion
#region ScrollRight
private void ScrollRightExecute(object target, ExecutedRoutedEventArgs e)
{
ScrollVisibleProportionally(-scrollCoeff, 0);
e.Handled = true;
}
private void ScrollRightCanExecute(object target, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}
#endregion
#region ScrollUp
private void ScrollUpExecute(object target, ExecutedRoutedEventArgs e)
{
ScrollVisibleProportionally(0, -scrollCoeff);
e.Handled = true;
}
private void ScrollUpCanExecute(object target, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}
#endregion
#region ScrollDown
private void ScrollDownExecute(object target, ExecutedRoutedEventArgs e)
{
ScrollVisibleProportionally(0, scrollCoeff);
e.Handled = true;
}
private void ScrollDownCanExecute(object target, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}
#endregion
#endregion
#region SaveScreenshot
private void SaveScreenshotExecute(object target, ExecutedRoutedEventArgs e)
{
SaveFileDialog dlg = new SaveFileDialog();
dlg.Filter = "JPEG(*.jpg)|*.jpg|BMP(*.bmp)|*.bmp|GIF(*.gif)|.gif|All files (*.*)|*.*";
dlg.FilterIndex = 1;
if (dlg.ShowDialog().GetValueOrDefault(false))
{
string filePath = dlg.FileName;
Plotter2D.SaveScreenshot(filePath);
e.Handled = true;
}
}
private void SaveScreenshotCanExecute(object target, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}
#endregion
#region CopyScreenshot
private void CopyScreenshotExecute(object target, ExecutedRoutedEventArgs e)
{
Plotter2D.CopyScreenshotToClipboard();
e.Handled = true;
}
private void CopyScreenshotCanExecute(object target, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}
#endregion
#region ShowHelp
private void ShowHelpExecute(object target, ExecutedRoutedEventArgs e)
{
string helpText =
@"Dynamic Data Display is in ""Auto Fit"" mode by default.
By using the mouse you can manually select an area to display:
Mouse wheel, '+' and '-' — Zoom in/Zoom out
Mouse drag or arrow keys — Pan the display area
Ctrl + Mouse drag — Select an area to zoom in
Shift + Mouse drag — Select an area with fixed aspect ratio
Home — Fit to view
F11 — Copy chart to clipboard";
MessageBox.Show(helpText, "Quick Help", MessageBoxButton.OK, MessageBoxImage.Information);
e.Handled = true;
}
private void ShowHelpCanExecute(object target, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}
#endregion
protected override void OnRenderCore(DrawingContext dc, RenderState state)
{
// do nothing here
}
}
}

View File

@@ -0,0 +1,32 @@
using System;
using System.Windows;
namespace Microsoft.Research.DynamicDataDisplay.Navigation
{
/// <summary>Helper class to parse Windows messages</summary>
internal static class MessagesHelper {
internal static int GetXFromLParam(IntPtr lParam) {
return LOWORD(lParam.ToInt32());
}
internal static int GetYFromLParam(IntPtr lParam) {
return HIWORD(lParam.ToInt32());
}
internal static Point GetMousePosFromLParam(IntPtr lParam) {
return new Point(GetXFromLParam(lParam), GetYFromLParam(lParam));
}
internal static int GetWheelDataFromWParam(IntPtr wParam) {
return HIWORD(wParam.ToInt32());
}
internal static short HIWORD(int i) {
return (short)((i & 0xFFFF0000) >> 16);
}
internal static short LOWORD(int i) {
return (short)(i & 0x0000FFFF);
}
}
}

View File

@@ -0,0 +1,235 @@
using System;
using System.Windows;
using System.Windows.Documents;
using System.Windows.Input;
namespace Microsoft.Research.DynamicDataDisplay.Navigation
{
/// <summary>Provides common methods of mouse navigation around viewport</summary>
public class MouseNavigation : NavigationBase {
private AdornerLayer adornerLayer;
protected AdornerLayer AdornerLayer {
get {
if (adornerLayer == null) {
adornerLayer = AdornerLayer.GetAdornerLayer(this);
if (adornerLayer != null) {
adornerLayer.IsHitTestVisible = false;
}
}
return adornerLayer;
}
}
protected override void OnViewportChanged() {
base.OnViewportChanged();
//Mouse.AddPreviewMouseDownHandler(Parent, (MouseButtonEventHandler)OnMouseDown);
//Mouse.AddPreviewMouseMoveHandler(Parent, (MouseEventHandler)OnMouseMove);
//Mouse.AddPreviewMouseUpHandler(Parent, (MouseButtonEventHandler)OnMouseUp);
Mouse.AddMouseDownHandler(Parent, OnMouseDown);
Mouse.AddMouseMoveHandler(Parent, OnMouseMove);
Mouse.AddMouseUpHandler(Parent, OnMouseUp);
Mouse.AddMouseWheelHandler(Parent, OnMouseWheel);
}
private void OnMouseWheel(object sender, MouseWheelEventArgs e) {
if (!e.Handled) {
Point mousePos = e.GetPosition(this);
int delta = -e.Delta;
MouseWheelZoom(mousePos, delta);
}
}
#if DEBUG
public override string ToString()
{
if (!String.IsNullOrEmpty(Name)) {
return Name;
}
return base.ToString();
}
#endif
bool adornerAdded;
RectangleSelectionAdorner selectionAdorner;
private void AddSelectionAdorner() {
if (!adornerAdded) {
AdornerLayer layer = AdornerLayer;
if (layer != null) {
selectionAdorner = new RectangleSelectionAdorner(this) { Border = zoomRect };
layer.Add(selectionAdorner);
adornerAdded = true;
}
}
}
private void RemoveSelectionAdorner() {
AdornerLayer layer = AdornerLayer;
if (layer != null) {
layer.Remove(selectionAdorner);
adornerAdded = false;
}
}
private void UpdateSelectionAdorner() {
selectionAdorner.Border = zoomRect;
selectionAdorner.InvalidateVisual();
}
Rect? zoomRect = null;
private const double wheelZoomSpeed = 1.2;
private bool shouldKeepRatioWhileZooming;
private bool isZooming;
private bool isDragging;
private Point dragStartPointInVisible;
private Point zoomStartPoint;
private static bool IsShiftOrCtrl {
get {
ModifierKeys currKeys = Keyboard.Modifiers;
return (currKeys | ModifierKeys.Shift) == currKeys ||
(currKeys | ModifierKeys.Control) == currKeys;
}
}
private void OnMouseDown(object sender, MouseButtonEventArgs e) {
// dragging
bool shouldStartDrag = e.ChangedButton == MouseButton.Left && Keyboard.Modifiers == ModifierKeys.None;
if (shouldStartDrag) {
Point dragStartPointInOutput = e.GetPosition(this);
dragStartPointInVisible = dragStartPointInOutput.Transform(Viewport.Output, Viewport.Visible);
isDragging = true;
CaptureMouse();
}
bool shouldStartZoom = e.ChangedButton == MouseButton.Left && IsShiftOrCtrl;
if (shouldStartZoom) {
// zooming
zoomStartPoint = e.GetPosition(this);
if (Viewport.Output.Contains(zoomStartPoint)) {
isZooming = true;
AddSelectionAdorner();
CaptureMouse();
shouldKeepRatioWhileZooming = Keyboard.Modifiers == ModifierKeys.Shift;
}
}
((IInputElement) Parent).Focus();
}
private void OnMouseMove(object sender, MouseEventArgs e) {
if (!isDragging && !isZooming) return;
// dragging
if (isDragging && e.LeftButton == MouseButtonState.Pressed) {
Point endPoint = e.GetPosition(this).Transform(Viewport.Output, Viewport.Visible);
Point loc = Viewport.Visible.Location;
Vector shift = dragStartPointInVisible - endPoint;
loc += shift;
if (shift.X != 0 || shift.Y != 0) {
Rect visible = Viewport.Visible;
visible.Location = loc;
Viewport.Visible = visible;
}
}
// zooming
else if (isZooming && e.LeftButton == MouseButtonState.Pressed) {
Point zoomEndPoint = e.GetPosition(this);
UpdateZoomRect(zoomEndPoint);
}
}
private static bool IsShiftPressed() {
return Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift);
}
private void UpdateZoomRect(Point zoomEndPoint) {
Rect output = Viewport.Output;
Rect tmpZoomRect = new Rect(zoomStartPoint, zoomEndPoint);
tmpZoomRect = Rect.Intersect(tmpZoomRect, output);
shouldKeepRatioWhileZooming = IsShiftPressed();
if (shouldKeepRatioWhileZooming) {
double currZoomRatio = tmpZoomRect.Width / tmpZoomRect.Height;
double zoomRatio = output.Width / output.Height;
if (currZoomRatio < zoomRatio) {
double oldHeight = tmpZoomRect.Height;
double height = tmpZoomRect.Width / zoomRatio;
tmpZoomRect.Height = height;
if (!tmpZoomRect.Contains(zoomStartPoint)) {
tmpZoomRect.Offset(0, oldHeight - height);
}
}
else {
double oldWidth = tmpZoomRect.Width;
double width = tmpZoomRect.Height * zoomRatio;
tmpZoomRect.Width = width;
if (!tmpZoomRect.Contains(zoomStartPoint)) {
tmpZoomRect.Offset(oldWidth - width, 0);
}
}
}
zoomRect = tmpZoomRect;
UpdateSelectionAdorner();
}
private void OnMouseUp(object sender, MouseButtonEventArgs e) {
if (isDragging && e.ChangedButton == MouseButton.Left) {
isDragging = false;
ReleaseMouseCapture();
Point endPoint = e.GetPosition(this).Transform(Viewport.Output, Viewport.Visible);
// focusing on LMB click
if (endPoint == dragStartPointInVisible) {
//Keyboard.Focus(Parent as IInputElement);
}
}
else if (isZooming && e.ChangedButton == MouseButton.Left) {
isZooming = false;
if (zoomRect.HasValue) {
Rect output = Viewport.Output;
Point p1 = zoomRect.Value.TopLeft.Transform(output, Viewport.Visible);
Point p2 = zoomRect.Value.BottomRight.Transform(output, Viewport.Visible);
Rect newVisible = new Rect(p1, p2);
Viewport.Visible = newVisible;
zoomRect = null;
ReleaseMouseCapture();
RemoveSelectionAdorner();
}
}
}
protected override void OnLostFocus(RoutedEventArgs e) {
if (isZooming) {
RemoveSelectionAdorner();
}
ReleaseMouseCapture();
base.OnLostFocus(e);
}
private void MouseWheelZoom(Point mousePos, int wheelRotationDelta) {
Point zoomTo = mousePos.Transform(Viewport.Output, Viewport.Visible);
double zoomSpeed = Math.Abs(wheelRotationDelta / Mouse.MouseWheelDeltaForOneLine);
zoomSpeed *= wheelZoomSpeed;
if (wheelRotationDelta < 0) {
zoomSpeed = 1 / zoomSpeed;
}
Viewport.Visible = Viewport.Visible.Zoom(zoomTo, zoomSpeed);
}
}
}

View File

@@ -0,0 +1,25 @@
using System.Windows;
using System.Windows.Media;
namespace Microsoft.Research.DynamicDataDisplay.Navigation
{
/// <summary>Base class for all navigation providers</summary>
public abstract class NavigationBase : ViewportElement2D {
protected NavigationBase() {
ManualTranslate = true;
ManualClip = true;
Loaded += NavigationBase_Loaded;
}
private void NavigationBase_Loaded(object sender, RoutedEventArgs e) {
OnLoaded(e);
}
protected virtual void OnLoaded(RoutedEventArgs e) { }
protected override void OnRenderCore(DrawingContext dc, RenderState state) {
Color color = Color.FromArgb(0, 0, 0, 0);
dc.DrawRectangle(new SolidColorBrush(color), null, state.Output);
}
}
}

View File

@@ -0,0 +1,48 @@
using System.Windows;
using System.Windows.Documents;
using System.Windows.Media;
namespace Microsoft.Research.DynamicDataDisplay.Navigation
{
/// <summary>Helper class to draw semitransparent rectangle over the
/// selection area</summary>
public sealed class RectangleSelectionAdorner : Adorner {
private Rect? border = null;
public Rect? Border {
get { return border; }
set { border = value; }
}
public Brush Fill {
get { return (Brush)GetValue(FillProperty); }
set { SetValue(FillProperty, value); }
}
public static readonly DependencyProperty FillProperty =
DependencyProperty.Register(
"Fill",
typeof(Brush),
typeof(RectangleSelectionAdorner),
new FrameworkPropertyMetadata(
new SolidColorBrush(Color.FromArgb(60, 100, 100, 100)),
FrameworkPropertyMetadataOptions.AffectsRender));
private Pen pen;
public Pen Pen {
get { return pen; }
set { pen = value; }
}
public RectangleSelectionAdorner(UIElement element)
: base(element) {
pen = new Pen(Brushes.Black, 1.0);
}
protected override void OnRender(DrawingContext dc) {
if (border.HasValue) {
dc.DrawRectangle(Fill, pen, border.Value);
}
}
}
}

View File

@@ -0,0 +1,52 @@
using System;
using System.Windows;
using System.Windows.Input;
using System.Windows.Interop;
namespace Microsoft.Research.DynamicDataDisplay.Navigation
{
/// <summary>This class allows convenient navigation around viewport using touchpad on
/// some notebooks</summary>
public sealed class TouchpadScroll : NavigationBase {
public TouchpadScroll() {
Loaded += OnLoaded;
}
private void OnLoaded(object sender, RoutedEventArgs e) {
WindowInteropHelper helper = new WindowInteropHelper(Window.GetWindow(this));
HwndSource source = HwndSource.FromHwnd(helper.Handle);
source.AddHook(OnMessageAppeared);
}
private IntPtr OnMessageAppeared(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) {
if (msg == WindowsMessages.WM_MOUSEWHEEL) {
Point mousePos = MessagesHelper.GetMousePosFromLParam(lParam);
mousePos = TranslatePoint(mousePos, this);
if (Viewport.Output.Contains(mousePos)) {
MouseWheelZoom(MessagesHelper.GetMousePosFromLParam(lParam), MessagesHelper.GetWheelDataFromWParam(wParam));
handled = true;
}
}
return IntPtr.Zero;
}
double wheelZoomSpeed = 1.2;
public double WheelZoomSpeed {
get { return wheelZoomSpeed; }
set { wheelZoomSpeed = value; }
}
private void MouseWheelZoom(Point mousePos, int wheelRotationDelta) {
Point zoomTo = mousePos.Transform(Viewport.Output, Viewport.Visible);
double zoomSpeed = Math.Abs(wheelRotationDelta / Mouse.MouseWheelDeltaForOneLine);
zoomSpeed *= wheelZoomSpeed;
if (wheelRotationDelta < 0) {
zoomSpeed = 1 / zoomSpeed;
}
Viewport.Visible = Viewport.Visible.Zoom(zoomTo, zoomSpeed);
}
}
}

View File

@@ -0,0 +1,7 @@

namespace Microsoft.Research.DynamicDataDisplay.Navigation
{
internal static class WindowsMessages {
internal static int WM_MOUSEWHEEL = 0x20A;
}
}

View File

@@ -0,0 +1,34 @@
using System.Windows;
using System.Windows.Media;
namespace Microsoft.Research.DynamicDataDisplay.Navigation
{
/// <summary>Base class for all navigation providers</summary>
public abstract class NavigationBase : ViewportElement2D
{
protected NavigationBase()
{
ManualTranslate = true;
ManualClip = true;
Loaded += NavigationBase_Loaded;
}
private void NavigationBase_Loaded(object sender, RoutedEventArgs e)
{
OnLoaded(e);
}
protected virtual void OnLoaded(RoutedEventArgs e)
{
// this call enables contextMenu to be shown after loading and
// before any changes to Viewport - without this call
// context menu was not shown.
InvalidateVisual();
}
protected override void OnRenderCore(DrawingContext dc, RenderState state)
{
dc.DrawRectangle(Brushes.Transparent, null, state.Output);
}
}
}

View File

@@ -0,0 +1,205 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Input;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using Microsoft.Research.DynamicDataDisplay;
using Microsoft.Research.DynamicDataDisplay.Common;
namespace Microsoft.Research.DynamicDataDisplay.Charts.Navigation
{
/// <summary>
/// Represents a navigation methods upon one axis - mouse panning and zooming.
/// </summary>
[Obsolete("Will be removed soon. Use AxisNavigation instead.")]
public class OldAxisNavigation : ContentGraph
{
/// <summary>
/// Initializes a new instance of the <see cref="AxisNavigation"/> class.
/// </summary>
public OldAxisNavigation()
{
Focusable = false;
SetHorizontalOrientation();
Content = content;
}
/// <summary>
/// Initializes a new instance of the <see cref="AxisNavigation"/> class.
/// </summary>
/// <param name="orientation">The orientation.</param>
public OldAxisNavigation(Orientation orientation)
{
Orientation = orientation;
Content = content;
}
private void SetHorizontalOrientation()
{
Grid.SetColumn(this, 1);
Grid.SetRow(this, 2);
}
private void SetVerticalOrientation()
{
// todo should automatically search for location of axes as they can be
// not only from the left or bottom.
Grid.SetColumn(this, 0);
Grid.SetRow(this, 1);
}
private Orientation orientation = Orientation.Horizontal;
/// <summary>
/// Gets or sets the orientation of AxisNavigation.
/// </summary>
/// <value>The orientation.</value>
public Orientation Orientation
{
get { return orientation; }
set
{
if (orientation != value)
{
orientation = value;
OnOrientationChanged();
}
}
}
private void OnOrientationChanged()
{
switch (orientation)
{
case Orientation.Horizontal:
SetHorizontalOrientation();
break;
case Orientation.Vertical:
SetVerticalOrientation();
break;
default:
break;
}
}
private bool lmbPressed = false;
private Point dragStart;
private CoordinateTransform Transform
{
get { return Plotter2D.Viewport.Transform; }
}
protected override Panel HostPanel
{
get { return Plotter2D.MainGrid; }
}
private readonly Panel content = new Canvas { Background = Brushes.Transparent };
private readonly SolidColorBrush fillBrush = new SolidColorBrush(Color.FromRgb(255, 228, 209)).MakeTransparent(0.2);
private Point lmbInitialPosition;
protected Point LmbInitialPosition
{
get { return lmbInitialPosition; }
}
protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
{
base.OnMouseLeftButtonDown(e);
lmbInitialPosition = e.GetPosition(this);
dragStart = lmbInitialPosition.ScreenToViewport(Transform);
lmbPressed = true;
content.Background = fillBrush;
CaptureMouse();
}
protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)
{
base.OnMouseLeftButtonUp(e);
lmbPressed = false;
ClearValue(CursorProperty);
content.Background = Brushes.Transparent;
ReleaseMouseCapture();
}
protected override void OnMouseMove(MouseEventArgs e)
{
if (lmbPressed)
{
Point mousePos = e.GetPosition(this).ScreenToViewport(Transform);
DataRect visible = Plotter2D.Viewport.Visible;
double delta;
if (orientation == Orientation.Horizontal)
{
delta = (mousePos - dragStart).X;
visible.XMin -= delta;
}
else
{
delta = (mousePos - dragStart).Y;
visible.YMin -= delta;
}
if (e.GetPosition(this) != lmbInitialPosition)
{
Cursor = orientation == Orientation.Horizontal ? Cursors.ScrollWE : Cursors.ScrollNS;
}
Plotter2D.Viewport.Visible = visible;
}
}
private const double wheelZoomSpeed = 1.2;
protected override void OnMouseWheel(MouseWheelEventArgs e)
{
Point mousePos = e.GetPosition(this);
int delta = -e.Delta;
Point zoomTo = mousePos.ScreenToViewport(Transform);
double zoomSpeed = Math.Abs(delta / Mouse.MouseWheelDeltaForOneLine);
zoomSpeed *= wheelZoomSpeed;
if (delta < 0)
{
zoomSpeed = 1 / zoomSpeed;
}
DataRect visible = Plotter2D.Viewport.Visible.Zoom(zoomTo, zoomSpeed);
DataRect oldVisible = Plotter2D.Viewport.Visible;
if (orientation == Orientation.Horizontal)
{
visible.YMin = oldVisible.YMin;
visible.Height = oldVisible.Height;
}
else
{
visible.XMin = oldVisible.XMin;
visible.Width = oldVisible.Width;
}
Plotter2D.Viewport.Visible = visible;
e.Handled = true;
}
protected override void OnLostFocus(RoutedEventArgs e)
{
base.OnLostFocus(e);
ClearValue(CursorProperty);
content.Background = Brushes.Transparent;
ReleaseMouseCapture();
}
}
}

View File

@@ -0,0 +1,120 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Research.DynamicDataDisplay.Navigation;
using System.Windows.Input;
using System.Diagnostics;
using System.Windows;
using System.Windows.Media.Animation;
using System.Windows.Threading;
namespace Microsoft.Research.DynamicDataDisplay.Charts.Navigation
{
public class PhysicalNavigation : IPlotterElement
{
private readonly DispatcherTimer timer;
public PhysicalNavigation()
{
timer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(30) };
timer.Tick += timer_Tick;
}
DateTime startTime;
private void timer_Tick(object sender, EventArgs e)
{
TimeSpan time = DateTime.Now - startTime;
animation.UseMouse = isMouseDown;
if (!isMouseDown)
{
animation.LiquidFrictionQuadraticCoeff = 1;
}
plotter.Viewport.Visible = animation.GetValue(time);
if (animation.IsFinished && !isMouseDown)
{
timer.Stop();
}
}
private bool isMouseDown = false;
private PhysicalRectAnimation animation;
private Point clickPos;
private void OnMouseDown(object sender, MouseButtonEventArgs e)
{
if (e.ChangedButton == MouseButton.Left)
{
clickPos = e.GetPosition(plotter.CentralGrid);
isMouseDown = true;
startTime = DateTime.Now;
animation = new PhysicalRectAnimation(
plotter.Viewport,
e.GetPosition(plotter.ViewportPanel));
timer.Start();
}
}
private void OnMouseUp(object sender, MouseButtonEventArgs e)
{
if (e.ChangedButton == MouseButton.Left)
{
isMouseDown = false;
if (clickPos == e.GetPosition(plotter.CentralGrid))
{
timer.Stop();
animation = null;
}
else
{
if (animation.IsFinished)
{
timer.Stop();
animation = null;
}
else
{
animation.UseMouse = false;
animation.LiquidFrictionQuadraticCoeff = 1;
}
}
}
}
#region IPlotterElement Members
private Plotter2D plotter;
void IPlotterElement.OnPlotterAttached(Plotter plotter)
{
this.plotter = (Plotter2D)plotter;
Mouse.AddPreviewMouseDownHandler(plotter.CentralGrid, OnMouseDown);
Mouse.AddPreviewMouseUpHandler(plotter.CentralGrid, OnMouseUp);
plotter.CentralGrid.MouseLeave += CentralGrid_MouseLeave;
}
private void CentralGrid_MouseLeave(object sender, MouseEventArgs e)
{
isMouseDown = false;
}
void IPlotterElement.OnPlotterDetaching(Plotter plotter)
{
plotter.CentralGrid.MouseLeave -= CentralGrid_MouseLeave;
Mouse.RemovePreviewMouseDownHandler(plotter.CentralGrid, OnMouseDown);
Mouse.RemovePreviewMouseUpHandler(plotter.CentralGrid, OnMouseUp);
this.plotter = null;
}
Plotter IPlotterElement.Plotter
{
get { return plotter; }
}
#endregion
}
}

View File

@@ -0,0 +1,173 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Media.Animation;
using System.Windows;
using System.Windows.Input;
using System.Diagnostics;
using Microsoft.Research.DynamicDataDisplay.Common.Auxiliary;
using System.Windows.Media;
using Microsoft.Research.DynamicDataDisplay;
using Microsoft.Research.DynamicDataDisplay.Common;
namespace Microsoft.Research.DynamicDataDisplay.Charts.Navigation
{
internal sealed class PhysicalRectAnimation
{
Vector position = new Vector();
Vector velocity = new Vector();
public Vector Velocity
{
get { return velocity; }
set { velocity = value; }
}
Vector acceleration = new Vector();
private double mass = 1; // kilogramms
public double Mass
{
get { return mass; }
set { mass = value; }
}
private double frictionCalmCoeff = 0.0;
public double FrictionCalmCoeff
{
get { return frictionCalmCoeff; }
set { frictionCalmCoeff = value; }
}
double frictionMovementCoeff = 0.1;
public double FrictionMovementCoeff
{
get { return frictionMovementCoeff; }
set { frictionMovementCoeff = value; }
}
double springCoeff = 50;
public double SpringCoeff
{
get { return springCoeff; }
set { springCoeff = value; }
}
double liquidFrictionCoeff = 1;
public double LiquidFrictionCoeff
{
get { return liquidFrictionCoeff; }
set { liquidFrictionCoeff = value; }
}
double liquidFrictionQuadraticCoeff = 10;
public double LiquidFrictionQuadraticCoeff
{
get { return liquidFrictionQuadraticCoeff; }
set { liquidFrictionQuadraticCoeff = value; }
}
const double G = 9.81;
DataRect from;
Viewport2D viewport;
Point initialMousePos;
CoordinateTransform initialTransform;
public PhysicalRectAnimation(Viewport2D viewport, Point initialMousePos)
{
this.from = viewport.Visible;
this.viewport = viewport;
this.initialMousePos = initialMousePos;
initialTransform = viewport.Transform;
position = from.Location.ToVector();
}
double prevTime;
private bool isFinished = false;
public bool IsFinished
{
get { return isFinished; }
}
private bool useMouse = true;
public bool UseMouse
{
get { return useMouse; }
set { useMouse = value; }
}
public DataRect GetValue(TimeSpan timeSpan)
{
double time = timeSpan.TotalSeconds;
double dtime = time - prevTime;
acceleration = GetForces() / mass;
velocity += acceleration * dtime;
var shift = velocity * dtime;
double viewportSize = Math.Sqrt(from.Width * from.Width + from.Height * from.Height);
if (!(shift.Length < viewportSize * 0.002 && time > 0.5))
{
position += shift;
}
else
{
isFinished = true;
}
prevTime = time;
Point pos = new Point(position.X, position.Y);
DataRect bounds = new DataRect(pos, from.Size);
return bounds;
}
private Vector GetForces()
{
Vector springForce = new Vector();
if (useMouse)
{
Point mousePos = GetMousePosition();
if (!mousePos.IsFinite()) { }
Point p1 = initialMousePos.ScreenToData(initialTransform);
Point p2 = mousePos.ScreenToData(viewport.Transform);
var transform = viewport.Transform;
Vector diff = p2 - p1;
springForce = -diff * springCoeff;
}
Vector frictionForce = GetFrictionForce(springForce);
Vector liquidFriction = -liquidFrictionCoeff * velocity - liquidFrictionQuadraticCoeff * velocity * velocity.Length;
Vector result = springForce + frictionForce + liquidFriction;
return result;
}
private Vector GetFrictionForce(Vector springForce)
{
double maxCalmFriction = frictionCalmCoeff * mass * G;
if (maxCalmFriction >= springForce.Length)
return -springForce;
if (velocity.Length == 0)
return new Vector();
return -velocity / velocity.Length * frictionMovementCoeff * mass * G;
}
private Point GetMousePosition()
{
return Mouse.GetPosition(viewport.Plotter.ViewportPanel);
}
}
}

View File

@@ -0,0 +1,96 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Controls.Primitives;
using System.Windows.Controls;
using System.Windows;
using Microsoft.Research.DynamicDataDisplay.Charts;
using Microsoft.Research.DynamicDataDisplay.Common;
namespace Microsoft.Research.DynamicDataDisplay
{
public abstract class PlotterScrollBar : IPlotterElement
{
protected PlotterScrollBar()
{
scrollBar.Scroll += OnScroll;
}
private void OnScroll(object sender, ScrollEventArgs e)
{
DataRect visible = plotter.Viewport.Visible;
visible = CreateVisibleRect(visible, scrollBar.Value);
plotter.Viewport.Visible = visible;
}
private readonly ScrollBar scrollBar = new ScrollBar();
protected ScrollBar ScrollBar
{
get { return scrollBar; }
}
private Plotter2D plotter;
protected Plotter2D Plotter
{
get { return plotter; }
}
private void OnViewportPropertyChanged(object sender, ExtendedPropertyChangedEventArgs e)
{
if (e.PropertyName == "Visible" || e.PropertyName == "Output")
{
UpdateScrollBar((Viewport2D)sender);
}
}
#region IPlotterElement Members
void IPlotterElement.OnPlotterAttached(Plotter plotter)
{
GetHostPanel(plotter).Children.Add(scrollBar);
Plotter2D plotter2d = (Plotter2D)plotter;
this.plotter = plotter2d;
var viewport = plotter2d.Viewport;
viewport.PropertyChanged += OnViewportPropertyChanged;
viewport.DomainChanged += OnViewportDomainChanged;
UpdateScrollBar(viewport);
}
protected abstract void UpdateScrollBar(Viewport2D viewport);
protected virtual void SetValue(Range<double> visibleRange, Range<double> domainRange)
{
scrollBar.Value = visibleRange.Min;
}
private void OnViewportDomainChanged(object sender, EventArgs e)
{
UpdateScrollBar((Viewport2D)sender);
}
protected abstract DataRect CreateVisibleRect(DataRect rect, double value);
protected abstract Panel GetHostPanel(Plotter plotter);
void IPlotterElement.OnPlotterDetaching(Plotter plotter)
{
this.plotter.Viewport.PropertyChanged -= OnViewportPropertyChanged;
this.plotter.Viewport.DomainChanged -= OnViewportDomainChanged;
GetHostPanel(plotter).Children.Remove(scrollBar);
UpdateScrollBar(null);
this.plotter = null;
}
Plotter IPlotterElement.Plotter
{
get { return plotter; }
}
#endregion
}
}

View File

@@ -0,0 +1,48 @@
using System.Windows;
using System.Windows.Documents;
using System.Windows.Media;
namespace Microsoft.Research.DynamicDataDisplay.Navigation
{
/// <summary>Helper class to draw semitransparent rectangle over the
/// selection area</summary>
public sealed class RectangleSelectionAdorner : Adorner {
private Rect? border = null;
public Rect? Border {
get { return border; }
set { border = value; }
}
public Brush Fill {
get { return (Brush)GetValue(FillProperty); }
set { SetValue(FillProperty, value); }
}
public static readonly DependencyProperty FillProperty =
DependencyProperty.Register(
"Fill",
typeof(Brush),
typeof(RectangleSelectionAdorner),
new FrameworkPropertyMetadata(
new SolidColorBrush(Color.FromArgb(60, 100, 100, 100)),
FrameworkPropertyMetadataOptions.AffectsRender));
private Pen pen;
public Pen Pen {
get { return pen; }
set { pen = value; }
}
public RectangleSelectionAdorner(UIElement element)
: base(element) {
pen = new Pen(Brushes.Black, 1.0);
}
protected override void OnRender(DrawingContext dc) {
if (border.HasValue) {
dc.DrawRectangle(Fill, pen, border.Value);
}
}
}
}

View File

@@ -0,0 +1,21 @@
<UserControl x:Class="Microsoft.Research.DynamicDataDisplay.Charts.Navigation.SimpleNavigationBar"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="300" Width="300">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<RepeatButton Name="leftButton" Grid.Row="1" Grid.Column="0"/>
<RepeatButton Name="upButton" Grid.Row="0" Grid.Column="1"/>
<RepeatButton Name="rightButton" Grid.Row="1" Grid.Column="2"/>
<RepeatButton Name="downButton" Grid.Column="1" Grid.Row="2"/>
</Grid>
</UserControl>

View File

@@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace Microsoft.Research.DynamicDataDisplay.Charts.Navigation
{
/// <summary>
/// Interaction logic for SimpleNavigationBar.xaml
/// </summary>
public partial class SimpleNavigationBar : UserControl
{
public SimpleNavigationBar()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,52 @@
using System;
using System.Windows;
using System.Windows.Input;
using System.Windows.Interop;
namespace Microsoft.Research.DynamicDataDisplay.Navigation
{
/// <summary>This class allows convenient navigation around viewport using touchpad on
/// some notebooks</summary>
public sealed class TouchpadScroll : NavigationBase {
public TouchpadScroll() {
Loaded += OnLoaded;
}
private void OnLoaded(object sender, RoutedEventArgs e) {
WindowInteropHelper helper = new WindowInteropHelper(Window.GetWindow(this));
HwndSource source = HwndSource.FromHwnd(helper.Handle);
source.AddHook(OnMessageAppeared);
}
private IntPtr OnMessageAppeared(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) {
if (msg == WindowsMessages.WM_MOUSEWHEEL) {
Point mousePos = MessagesHelper.GetMousePosFromLParam(lParam);
mousePos = TranslatePoint(mousePos, this);
if (Viewport.Output.Contains(mousePos)) {
MouseWheelZoom(MessagesHelper.GetMousePosFromLParam(lParam), MessagesHelper.GetWheelDataFromWParam(wParam));
handled = true;
}
}
return IntPtr.Zero;
}
double wheelZoomSpeed = 1.2;
public double WheelZoomSpeed {
get { return wheelZoomSpeed; }
set { wheelZoomSpeed = value; }
}
private void MouseWheelZoom(Point mousePos, int wheelRotationDelta) {
Point zoomTo = mousePos.ScreenToData(Viewport.Transform);
double zoomSpeed = Math.Abs(wheelRotationDelta / Mouse.MouseWheelDeltaForOneLine);
zoomSpeed *= wheelZoomSpeed;
if (wheelRotationDelta < 0) {
zoomSpeed = 1 / zoomSpeed;
}
Viewport.Visible = Viewport.Visible.Zoom(zoomTo, zoomSpeed);
}
}
}

View File

@@ -0,0 +1,68 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using Microsoft.Research.DynamicDataDisplay.Common;
namespace Microsoft.Research.DynamicDataDisplay.Charts.Navigation
{
[Obsolete("Working wrongly.", true)]
public sealed class VerticalScrollBar : PlotterScrollBar
{
/// <summary>
/// Initializes a new instance of the <see cref="VerticalScrollBar"/> class.
/// </summary>
public VerticalScrollBar()
{
ScrollBar.Orientation = Orientation.Vertical;
}
private Range<double> GetRange(Rect domain)
{
return new Range<double>(domain.Top, domain.Bottom);
}
protected override DataRect CreateVisibleRect(DataRect rect, double scrollValue)
{
rect.YMin = scrollValue;
return rect;
}
protected override Panel GetHostPanel(Plotter plotter)
{
return plotter.LeftPanel;
}
protected override void UpdateScrollBar(Viewport2D viewport)
{
if (viewport != null && !viewport.Domain.IsEmpty)
{
if (ScrollBar.Track != null)
{
//ScrollBar.Track.IsDirectionReversed = true;
}
visibleRange = new Range<double>(viewport.Visible.YMin, viewport.Visible.YMax);
domainRange = new Range<double>(viewport.Domain.YMin, viewport.Domain.YMax);
double size = visibleRange.Max - visibleRange.Min;
ScrollBar.ViewportSize = size;
ScrollBar.Minimum = domainRange.Min + size;
ScrollBar.Maximum = domainRange.Max;
ScrollBar.Value = visibleRange.Min;
ScrollBar.Visibility = Visibility.Visible;
}
else
{
ScrollBar.Visibility = Visibility.Collapsed;
}
}
private Range<double> visibleRange;
private Range<double> domainRange;
}
}

View File

@@ -0,0 +1,7 @@

namespace Microsoft.Research.DynamicDataDisplay.Navigation
{
internal static class WindowsMessages {
internal static int WM_MOUSEWHEEL = 0x20A;
}
}