332 lines
9.8 KiB
C#
332 lines
9.8 KiB
C#
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> { }
|
|
}
|