Initial Commit
This commit is contained in:
331
Charts/Navigation/DefaultContextMenu.cs
Normal file
331
Charts/Navigation/DefaultContextMenu.cs
Normal 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> { }
|
||||
}
|
||||
Reference in New Issue
Block a user