Browse Source
git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/branches/3.0wpf@3384 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61shortcuts
9 changed files with 317 additions and 88 deletions
@ -0,0 +1,47 @@ |
|||||||
|
// <file>
|
||||||
|
// <copyright see="prj:///doc/copyright.txt"/>
|
||||||
|
// <license see="prj:///doc/license.txt"/>
|
||||||
|
// <author name="Daniel Grunwald"/>
|
||||||
|
// <version>$Revision$</version>
|
||||||
|
// </file>
|
||||||
|
|
||||||
|
using System; |
||||||
|
using System.Collections.Generic; |
||||||
|
using System.Windows; |
||||||
|
using System.Windows.Controls; |
||||||
|
using System.Windows.Input; |
||||||
|
|
||||||
|
namespace ICSharpCode.Core.Presentation |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// Provides access to the RoutedUICommand objects for commands defined in the addin-tree.
|
||||||
|
/// </summary>
|
||||||
|
public static class CommandService |
||||||
|
{ |
||||||
|
static Dictionary<string, RoutedUICommand> dict = new Dictionary<string, RoutedUICommand>(); |
||||||
|
|
||||||
|
public static RoutedUICommand GetCommand(string className) |
||||||
|
{ |
||||||
|
if (className == null) |
||||||
|
throw new ArgumentNullException("className"); |
||||||
|
if (className.Length == 0) |
||||||
|
throw new ArgumentException("className must not be the empty string"); |
||||||
|
lock (dict) { |
||||||
|
RoutedUICommand cmd; |
||||||
|
if (!dict.TryGetValue(className, out cmd)) { |
||||||
|
dict[className] = cmd = new RoutedUICommand(className, className, typeof(CommandService)); |
||||||
|
} |
||||||
|
return cmd; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public static RoutedUICommand GetCommand(Type commandClass) |
||||||
|
{ |
||||||
|
if (commandClass == null) |
||||||
|
throw new ArgumentNullException("commandClass"); |
||||||
|
if (!typeof(ICommand).IsAssignableFrom(commandClass)) |
||||||
|
throw new ArgumentException("commandClass must implement ICommand", "commandClass"); |
||||||
|
return GetCommand(commandClass.FullName); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,166 @@ |
|||||||
|
// <file>
|
||||||
|
// <copyright see="prj:///doc/copyright.txt"/>
|
||||||
|
// <license see="prj:///doc/license.txt"/>
|
||||||
|
// <owner name="Daniel Grunwald"/>
|
||||||
|
// <version>$Revision$</version>
|
||||||
|
// </file>
|
||||||
|
using System; |
||||||
|
using System.Windows; |
||||||
|
using System.Windows.Media; |
||||||
|
using System.Windows.Media.Imaging; |
||||||
|
|
||||||
|
namespace ICSharpCode.Core.Presentation |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// A WPF control that aligns its content on pixel boundaries.
|
||||||
|
/// </summary>
|
||||||
|
public class PixelSnapper : UIElement |
||||||
|
{ |
||||||
|
public PixelSnapper() |
||||||
|
{ |
||||||
|
LayoutUpdated += new EventHandler(OnLayoutUpdated); |
||||||
|
} |
||||||
|
|
||||||
|
UIElement _visualChild; |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets the visual child.
|
||||||
|
/// </summary>
|
||||||
|
public UIElement Child { |
||||||
|
get { return _visualChild; } |
||||||
|
set { |
||||||
|
RemoveVisualChild(_visualChild); |
||||||
|
_visualChild = value; |
||||||
|
AddVisualChild(_visualChild); |
||||||
|
InvalidateMeasure(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the visual child.
|
||||||
|
/// </summary>
|
||||||
|
protected override Visual GetVisualChild(int index) |
||||||
|
{ |
||||||
|
if (index == 0 && _visualChild != null) |
||||||
|
return _visualChild; |
||||||
|
else |
||||||
|
throw new ArgumentOutOfRangeException("index"); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the number of visual children.
|
||||||
|
/// </summary>
|
||||||
|
protected override int VisualChildrenCount { |
||||||
|
get { return _visualChild != null ? 1 : 0; } |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Measure the visual child.
|
||||||
|
/// </summary>
|
||||||
|
protected override Size MeasureCore(Size availableSize) |
||||||
|
{ |
||||||
|
if (_visualChild != null) { |
||||||
|
_visualChild.Measure(availableSize); |
||||||
|
return _visualChild.DesiredSize; |
||||||
|
} else { |
||||||
|
return base.MeasureCore(availableSize); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
protected override void ArrangeCore(Rect finalRect) |
||||||
|
{ |
||||||
|
base.ArrangeCore(finalRect); |
||||||
|
if (_visualChild != null) { |
||||||
|
_pixelOffset = GetPixelOffset(); |
||||||
|
//LoggingService.Debug("Arrange, Pixel Offset=" + _pixelOffset);
|
||||||
|
_visualChild.Arrange(new Rect(new Point(_pixelOffset.X, _pixelOffset.Y), finalRect.Size)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void OnLayoutUpdated(object sender, EventArgs e) |
||||||
|
{ |
||||||
|
// This event just means that layout happened somewhere. However, this is
|
||||||
|
// what we need since layout anywhere could affect our pixel positioning.
|
||||||
|
Point pixelOffset = GetPixelOffset(); |
||||||
|
if (!AreClose(pixelOffset, _pixelOffset)) { |
||||||
|
InvalidateArrange(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Gets the matrix that will convert a point from "above" the
|
||||||
|
// coordinate space of a visual into the the coordinate space
|
||||||
|
// "below" the visual.
|
||||||
|
private Matrix GetVisualTransform(Visual v) |
||||||
|
{ |
||||||
|
if (v != null) { |
||||||
|
Matrix m = Matrix.Identity; |
||||||
|
|
||||||
|
Transform transform = VisualTreeHelper.GetTransform(v); |
||||||
|
if (transform != null) { |
||||||
|
m *= transform.Value; |
||||||
|
} |
||||||
|
|
||||||
|
Vector offset = VisualTreeHelper.GetOffset(v); |
||||||
|
m.Translate(offset.X, offset.Y); |
||||||
|
|
||||||
|
return m; |
||||||
|
} |
||||||
|
|
||||||
|
return Matrix.Identity; |
||||||
|
} |
||||||
|
|
||||||
|
private Point ApplyVisualTransform(Point point, Visual v, bool inverse) |
||||||
|
{ |
||||||
|
if (v != null) { |
||||||
|
Matrix visualTransform = GetVisualTransform(v); |
||||||
|
if (inverse) |
||||||
|
visualTransform.Invert(); |
||||||
|
point = visualTransform.Transform(point); |
||||||
|
} |
||||||
|
return point; |
||||||
|
} |
||||||
|
|
||||||
|
private Point GetPixelOffset() |
||||||
|
{ |
||||||
|
Point pixelOffset = new Point(); |
||||||
|
|
||||||
|
PresentationSource ps = PresentationSource.FromVisual(this); |
||||||
|
if (ps != null) { |
||||||
|
Visual rootVisual = ps.RootVisual; |
||||||
|
|
||||||
|
// Transform (0,0) from this element up to pixels.
|
||||||
|
pixelOffset = this.TransformToAncestor(rootVisual).Transform(pixelOffset); |
||||||
|
pixelOffset = ApplyVisualTransform(pixelOffset, rootVisual, false); |
||||||
|
pixelOffset = ps.CompositionTarget.TransformToDevice.Transform(pixelOffset); |
||||||
|
|
||||||
|
// Round the origin to the nearest whole pixel.
|
||||||
|
pixelOffset.X = Math.Round(pixelOffset.X); |
||||||
|
pixelOffset.Y = Math.Round(pixelOffset.Y); |
||||||
|
|
||||||
|
// Transform the whole-pixel back to this element.
|
||||||
|
pixelOffset = ps.CompositionTarget.TransformFromDevice.Transform(pixelOffset); |
||||||
|
pixelOffset = ApplyVisualTransform(pixelOffset, rootVisual, true); |
||||||
|
pixelOffset = rootVisual.TransformToDescendant(this).Transform(pixelOffset); |
||||||
|
} |
||||||
|
|
||||||
|
return pixelOffset; |
||||||
|
} |
||||||
|
|
||||||
|
private bool AreClose(Point point1, Point point2) |
||||||
|
{ |
||||||
|
return AreClose(point1.X, point2.X) && AreClose(point1.Y, point2.Y); |
||||||
|
} |
||||||
|
|
||||||
|
private bool AreClose(double value1, double value2) |
||||||
|
{ |
||||||
|
if (value1 == value2) |
||||||
|
{ |
||||||
|
return true; |
||||||
|
} |
||||||
|
double delta = value1 - value2; |
||||||
|
return ((delta < 1.53E-06) && (delta > -1.53E-06)); |
||||||
|
} |
||||||
|
|
||||||
|
private Point _pixelOffset; |
||||||
|
} |
||||||
|
} |
||||||
Loading…
Reference in new issue