diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000000..a087785bcf
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,3 @@
+root = true
+[*]
+indent_style = tab
\ No newline at end of file
diff --git a/samples/XamlDesigner/Toolbox.cs b/samples/XamlDesigner/Toolbox.cs
index b4b400e8c6..c25b4f3972 100644
--- a/samples/XamlDesigner/Toolbox.cs
+++ b/samples/XamlDesigner/Toolbox.cs
@@ -70,7 +70,7 @@ namespace ICSharpCode.XamlDesigner
{
AddAssembly(Environment.ExpandEnvironmentVariables(path), false);
}
- catch (Exception ex)
+ catch (Exception)
{ }
}
}
diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/CollapsiblePanel.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/CollapsiblePanel.cs
new file mode 100644
index 0000000000..99c43d1ab9
--- /dev/null
+++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/CollapsiblePanel.cs
@@ -0,0 +1,198 @@
+// Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this
+// software and associated documentation files (the "Software"), to deal in the Software
+// without restriction, including without limitation the rights to use, copy, modify, merge,
+// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
+// to whom the Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all copies or
+// substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+
+using System;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Input;
+using System.Windows.Media.Animation;
+
+namespace ICSharpCode.WpfDesign.Designer.Controls
+{
+ ///
+ /// Allows animated collapsing of the content of this panel.
+ ///
+ public class CollapsiblePanel : ContentControl
+ {
+ static CollapsiblePanel()
+ {
+ DefaultStyleKeyProperty.OverrideMetadata(typeof(CollapsiblePanel),
+ new FrameworkPropertyMetadata(typeof(CollapsiblePanel)));
+ FocusableProperty.OverrideMetadata(typeof(CollapsiblePanel),
+ new FrameworkPropertyMetadata(false));
+ }
+
+ public static readonly DependencyProperty IsCollapsedProperty = DependencyProperty.Register(
+ "IsCollapsed", typeof(bool), typeof(CollapsiblePanel),
+ new UIPropertyMetadata(false, new PropertyChangedCallback(OnIsCollapsedChanged)));
+
+ public bool IsCollapsed {
+ get { return (bool)GetValue(IsCollapsedProperty); }
+ set { SetValue(IsCollapsedProperty, value); }
+ }
+
+ public static readonly DependencyProperty CollapseOrientationProperty =
+ DependencyProperty.Register("CollapseOrientation", typeof(Orientation), typeof(CollapsiblePanel),
+ new FrameworkPropertyMetadata(Orientation.Vertical));
+
+ public Orientation CollapseOrientation {
+ get { return (Orientation)GetValue(CollapseOrientationProperty); }
+ set { SetValue(CollapseOrientationProperty, value); }
+ }
+
+ public static readonly DependencyProperty DurationProperty = DependencyProperty.Register(
+ "Duration", typeof(TimeSpan), typeof(CollapsiblePanel),
+ new UIPropertyMetadata(TimeSpan.FromMilliseconds(250)));
+
+ ///
+ /// The duration in milliseconds of the animation.
+ ///
+ public TimeSpan Duration {
+ get { return (TimeSpan)GetValue(DurationProperty); }
+ set { SetValue(DurationProperty, value); }
+ }
+
+ protected internal static readonly DependencyProperty AnimationProgressProperty = DependencyProperty.Register(
+ "AnimationProgress", typeof(double), typeof(CollapsiblePanel),
+ new FrameworkPropertyMetadata(1.0));
+
+ ///
+ /// Value between 0 and 1 specifying how far the animation currently is.
+ ///
+ protected internal double AnimationProgress {
+ get { return (double)GetValue(AnimationProgressProperty); }
+ set { SetValue(AnimationProgressProperty, value); }
+ }
+
+ protected internal static readonly DependencyProperty AnimationProgressXProperty = DependencyProperty.Register(
+ "AnimationProgressX", typeof(double), typeof(CollapsiblePanel),
+ new FrameworkPropertyMetadata(1.0));
+
+ ///
+ /// Value between 0 and 1 specifying how far the animation currently is.
+ ///
+ protected internal double AnimationProgressX {
+ get { return (double)GetValue(AnimationProgressXProperty); }
+ set { SetValue(AnimationProgressXProperty, value); }
+ }
+
+ protected internal static readonly DependencyProperty AnimationProgressYProperty = DependencyProperty.Register(
+ "AnimationProgressY", typeof(double), typeof(CollapsiblePanel),
+ new FrameworkPropertyMetadata(1.0));
+
+ ///
+ /// Value between 0 and 1 specifying how far the animation currently is.
+ ///
+ protected internal double AnimationProgressY {
+ get { return (double)GetValue(AnimationProgressYProperty); }
+ set { SetValue(AnimationProgressYProperty, value); }
+ }
+
+ static void OnIsCollapsedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ ((CollapsiblePanel)d).SetupAnimation((bool)e.NewValue);
+ }
+
+ void SetupAnimation(bool isCollapsed)
+ {
+ if (this.IsLoaded) {
+ // If the animation is already running, calculate remaining portion of the time
+ double currentProgress = AnimationProgress;
+ if (!isCollapsed) {
+ currentProgress = 1.0 - currentProgress;
+ }
+
+ DoubleAnimation animation = new DoubleAnimation();
+ animation.To = isCollapsed ? 0.0 : 1.0;
+ animation.Duration = TimeSpan.FromSeconds(Duration.TotalSeconds * currentProgress);
+ animation.FillBehavior = FillBehavior.HoldEnd;
+
+ this.BeginAnimation(AnimationProgressProperty, animation);
+ if (CollapseOrientation == Orientation.Horizontal) {
+ this.BeginAnimation(AnimationProgressXProperty, animation);
+ this.AnimationProgressY = 1.0;
+ } else {
+ this.AnimationProgressX = 1.0;
+ this.BeginAnimation(AnimationProgressYProperty, animation);
+ }
+ } else {
+ this.AnimationProgress = isCollapsed ? 0.0 : 1.0;
+ this.AnimationProgressX = (CollapseOrientation == Orientation.Horizontal) ? this.AnimationProgress : 1.0;
+ this.AnimationProgressY = (CollapseOrientation == Orientation.Vertical) ? this.AnimationProgress : 1.0;
+ }
+ }
+ }
+
+ sealed class CollapsiblePanelProgressToVisibilityConverter : IValueConverter
+ {
+ public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
+ {
+ if (value is double)
+ return (double)value > 0 ? Visibility.Visible : Visibility.Collapsed;
+ else
+ return Visibility.Visible;
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
+ {
+ throw new NotImplementedException();
+ }
+ }
+
+ public class SelfCollapsingPanel : CollapsiblePanel
+ {
+ public static readonly DependencyProperty CanCollapseProperty =
+ DependencyProperty.Register("CanCollapse", typeof(bool), typeof(SelfCollapsingPanel),
+ new FrameworkPropertyMetadata(false, new PropertyChangedCallback(OnCanCollapseChanged)));
+
+ public bool CanCollapse {
+ get { return (bool)GetValue(CanCollapseProperty); }
+ set { SetValue(CanCollapseProperty, value); }
+ }
+
+ static void OnCanCollapseChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ SelfCollapsingPanel panel = (SelfCollapsingPanel)d;
+ if ((bool)e.NewValue) {
+ if (!panel.HeldOpenByMouse)
+ panel.IsCollapsed = true;
+ } else {
+ panel.IsCollapsed = false;
+ }
+ }
+
+ bool HeldOpenByMouse {
+ get { return IsMouseOver || IsMouseCaptureWithin; }
+ }
+
+ protected override void OnMouseLeave(MouseEventArgs e)
+ {
+ base.OnMouseLeave(e);
+ if (CanCollapse && !HeldOpenByMouse)
+ IsCollapsed = true;
+ }
+
+ protected override void OnLostMouseCapture(MouseEventArgs e)
+ {
+ base.OnLostMouseCapture(e);
+ if (CanCollapse && !HeldOpenByMouse)
+ IsCollapsed = true;
+ }
+ }
+}
diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/ColorHelper.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/ColorHelper.cs
new file mode 100644
index 0000000000..bbda652b78
--- /dev/null
+++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/ColorHelper.cs
@@ -0,0 +1,110 @@
+// Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this
+// software and associated documentation files (the "Software"), to deal in the Software
+// without restriction, including without limitation the rights to use, copy, modify, merge,
+// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
+// to whom the Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all copies or
+// substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+
+using System;
+using System.Windows.Media;
+
+namespace ICSharpCode.WpfDesign.Designer.Controls
+{
+ public static class ColorHelper
+ {
+ public static Color ColorFromString(string s)
+ {
+ if (string.IsNullOrEmpty(s)) {
+ return Colors.White;
+ }
+ if (s[0] != '#') s = "#" + s;
+ try {
+ return (Color)ColorConverter.ConvertFromString(s);
+ }
+ catch {
+ return Colors.White;
+ }
+ }
+
+ public static string StringFromColor(Color c)
+ {
+ return c.ToString().Substring(1);
+ }
+
+ public static Color ColorFromHsv(double h, double s, double v)
+ {
+ double r, g, b;
+ RgbFromHsv(h, s, v, out r, out g, out b);
+ return Color.FromRgb((byte)(r * 255), (byte)(g * 255), (byte)(b * 255));
+
+ }
+
+ public static void HsvFromColor(Color c, out double h, out double s, out double v)
+ {
+ HsvFromRgb(c.R / 255.0, c.G / 255.0, c.B / 255.0, out h, out s, out v);
+ }
+
+ // http://en.wikipedia.org/wiki/HSV_color_space
+ public static void HsvFromRgb(double r, double g, double b, out double h, out double s, out double v)
+ {
+ var max = Math.Max(r, Math.Max(g, b));
+ var min = Math.Min(r, Math.Min(g, b));
+
+ if (max == min) {
+ h = 0;
+ }
+ else if (max == r) {
+ h = (60 * (g - b) / (max - min)) % 360;
+ }
+ else if (max == g) {
+ h = 60 * (b - r) / (max - min) + 120;
+ }
+ else {
+ h = 60 * (r - g) / (max - min) + 240;
+ }
+
+ if (h < 0) h += 360; // C# '%' can return negative values, use real modulus instead
+
+ if (max == 0) {
+ s = 0;
+ }
+ else {
+ s = 1 - min / max;
+ }
+
+ v = max;
+ }
+
+ // http://en.wikipedia.org/wiki/HSV_color_space
+ public static void RgbFromHsv(double h, double s, double v, out double r, out double g, out double b)
+ {
+ h = h % 360;
+ if (h < 0) h += 360; // C# '%' can return negative values, use real modulus instead
+ int hi = (int)(h / 60) % 6;
+ var f = h / 60 - (int)(h / 60);
+ var p = v * (1 - s);
+ var q = v * (1 - f * s);
+ var t = v * (1 - (1 - f) * s);
+
+ switch (hi) {
+ case 0: r = v; g = t; b = p; break;
+ case 1: r = q; g = v; b = p; break;
+ case 2: r = p; g = v; b = t; break;
+ case 3: r = p; g = q; b = v; break;
+ case 4: r = t; g = p; b = v; break;
+ default: r = v; g = p; b = q; break;
+ }
+ }
+ }
+}
diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/ColorPicker.xaml b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/ColorPicker.xaml
new file mode 100644
index 0000000000..fdc76ccdb3
--- /dev/null
+++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/ColorPicker.xaml
@@ -0,0 +1,232 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/ColorPicker.xaml.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/ColorPicker.xaml.cs
new file mode 100644
index 0000000000..667e3a3c33
--- /dev/null
+++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/ColorPicker.xaml.cs
@@ -0,0 +1,191 @@
+// Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this
+// software and associated documentation files (the "Software"), to deal in the Software
+// without restriction, including without limitation the rights to use, copy, modify, merge,
+// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
+// to whom the Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all copies or
+// substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Input;
+using System.Windows.Media;
+
+namespace ICSharpCode.WpfDesign.Designer.Controls
+{
+ public partial class ColorPicker
+ {
+ public ColorPicker()
+ {
+ InitializeComponent();
+ }
+
+ public static readonly DependencyProperty ColorProperty =
+ DependencyProperty.Register("Color", typeof(Color), typeof(ColorPicker),
+ new FrameworkPropertyMetadata(new Color(), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
+
+ public Color Color {
+ get { return (Color)GetValue(ColorProperty); }
+ set { SetValue(ColorProperty, value); }
+ }
+
+ public static readonly DependencyProperty HProperty =
+ DependencyProperty.Register("H", typeof(int), typeof(ColorPicker));
+
+ public int H {
+ get { return (int)GetValue(HProperty); }
+ set { SetValue(HProperty, value); }
+ }
+
+ public static readonly DependencyProperty SProperty =
+ DependencyProperty.Register("S", typeof(int), typeof(ColorPicker));
+
+ public int S {
+ get { return (int)GetValue(SProperty); }
+ set { SetValue(SProperty, value); }
+ }
+
+ public static readonly DependencyProperty VProperty =
+ DependencyProperty.Register("V", typeof(int), typeof(ColorPicker));
+
+ public int V {
+ get { return (int)GetValue(VProperty); }
+ set { SetValue(VProperty, value); }
+ }
+
+ public static readonly DependencyProperty RProperty =
+ DependencyProperty.Register("R", typeof(byte), typeof(ColorPicker));
+
+ public byte R {
+ get { return (byte)GetValue(RProperty); }
+ set { SetValue(RProperty, value); }
+ }
+
+ public static readonly DependencyProperty GProperty =
+ DependencyProperty.Register("G", typeof(byte), typeof(ColorPicker));
+
+ public byte G {
+ get { return (byte)GetValue(GProperty); }
+ set { SetValue(GProperty, value); }
+ }
+
+ public static readonly DependencyProperty BProperty =
+ DependencyProperty.Register("B", typeof(byte), typeof(ColorPicker));
+
+ public byte B {
+ get { return (byte)GetValue(BProperty); }
+ set { SetValue(BProperty, value); }
+ }
+
+ public static readonly DependencyProperty AProperty =
+ DependencyProperty.Register("A", typeof(byte), typeof(ColorPicker));
+
+ public byte A {
+ get { return (byte)GetValue(AProperty); }
+ set { SetValue(AProperty, value); }
+ }
+
+ public static readonly DependencyProperty HexProperty =
+ DependencyProperty.Register("Hex", typeof(string), typeof(ColorPicker));
+
+ public string Hex {
+ get { return (string)GetValue(HexProperty); }
+ set { SetValue(HexProperty, value); }
+ }
+
+ public static readonly DependencyProperty HueColorProperty =
+ DependencyProperty.Register("HueColor", typeof(Color), typeof(ColorPicker));
+
+ public Color HueColor {
+ get { return (Color)GetValue(HueColorProperty); }
+ set { SetValue(HueColorProperty, value); }
+ }
+
+ bool updating;
+
+ protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
+ {
+ base.OnPropertyChanged(e);
+
+ if (updating) return;
+ updating = true;
+ try {
+ if (e.Property == ColorProperty) {
+ UpdateSource(ColorSource.Hsv);
+ UpdateRest(ColorSource.Hsv);
+ } else if (e.Property == HProperty || e.Property == SProperty || e.Property == VProperty) {
+ var c = ColorHelper.ColorFromHsv(H, S / 100.0, V / 100.0);
+ c.A = A;
+ Color = c;
+ UpdateRest(ColorSource.Hsv);
+ } else if (e.Property == RProperty || e.Property == GProperty || e.Property == BProperty || e.Property == AProperty) {
+ Color = Color.FromArgb(A, R, G, B);
+ UpdateRest(ColorSource.Rgba);
+ } else if (e.Property == HexProperty) {
+ Color = ColorHelper.ColorFromString(Hex);
+ UpdateRest(ColorSource.Hex);
+ }
+ } finally {
+ updating = false;
+ }
+ }
+
+ void UpdateRest(ColorSource source)
+ {
+ HueColor = ColorHelper.ColorFromHsv(H, 1, 1);
+ UpdateSource((ColorSource)(((int)source + 1) % 3));
+ UpdateSource((ColorSource)(((int)source + 2) % 3));
+ }
+
+ void UpdateSource(ColorSource source)
+ {
+ if (source == ColorSource.Hsv) {
+ double h, s, v;
+ ColorHelper.HsvFromColor(Color, out h, out s, out v);
+
+ H = (int)h;
+ S = (int)(s * 100);
+ V = (int)(v * 100);
+ }
+ else if (source == ColorSource.Rgba) {
+ R = Color.R;
+ G = Color.G;
+ B = Color.B;
+ A = Color.A;
+ }
+ else {
+ Hex = ColorHelper.StringFromColor(Color);
+ }
+ }
+
+ enum ColorSource
+ {
+ Hsv, Rgba, Hex
+ }
+ }
+
+ class HexTextBox : TextBox
+ {
+ protected override void OnKeyDown(KeyEventArgs e)
+ {
+ if (e.Key == Key.Enter) {
+ var b = BindingOperations.GetBindingExpressionBase(this, TextProperty);
+ if (b != null) {
+ b.UpdateTarget();
+ }
+ SelectAll();
+ }
+ }
+ }
+}
diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/ControlStyles.xaml b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/ControlStyles.xaml
index 237a66ce9f..bb5d4d52b1 100644
--- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/ControlStyles.xaml
+++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/ControlStyles.xaml
@@ -3,7 +3,7 @@
xmlns:Controls="clr-namespace:ICSharpCode.WpfDesign.Designer.Controls"
xmlns:ControlConvertors="clr-namespace:ICSharpCode.WpfDesign.Designer.Controls.Converters"
xmlns:Converters="clr-namespace:ICSharpCode.WpfDesign.Designer.Converters"
- xmlns:Widgets="http://icsharpcode.net/sharpdevelop/widgets">
+ xmlns:Widgets="clr-namespace:ICSharpCode.WpfDesign.Designer.Controls">
diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/NumericUpDown.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/NumericUpDown.cs
new file mode 100644
index 0000000000..7d6072f321
--- /dev/null
+++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/NumericUpDown.cs
@@ -0,0 +1,323 @@
+// Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this
+// software and associated documentation files (the "Software"), to deal in the Software
+// without restriction, including without limitation the rights to use, copy, modify, merge,
+// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
+// to whom the Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all copies or
+// substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+
+using System;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Input;
+using System.Windows.Controls.Primitives;
+
+namespace ICSharpCode.WpfDesign.Designer.Controls
+{
+ public class NumericUpDown : Control
+ {
+ static NumericUpDown()
+ {
+ DefaultStyleKeyProperty.OverrideMetadata(typeof(NumericUpDown),
+ new FrameworkPropertyMetadata(typeof(NumericUpDown)));
+ }
+
+ TextBox textBox;
+ DragRepeatButton upButton;
+ DragRepeatButton downButton;
+
+ public static readonly DependencyProperty DecimalPlacesProperty =
+ DependencyProperty.Register("DecimalPlaces", typeof(int), typeof(NumericUpDown));
+
+ public int DecimalPlaces {
+ get { return (int)GetValue(DecimalPlacesProperty); }
+ set { SetValue(DecimalPlacesProperty, value); }
+ }
+
+ public static readonly DependencyProperty MinimumProperty =
+ DependencyProperty.Register("Minimum", typeof(double), typeof(NumericUpDown));
+
+ public double Minimum {
+ get { return (double)GetValue(MinimumProperty); }
+ set { SetValue(MinimumProperty, value); }
+ }
+
+ public static readonly DependencyProperty MaximumProperty =
+ DependencyProperty.Register("Maximum", typeof(double), typeof(NumericUpDown),
+ new FrameworkPropertyMetadata(100.0));
+
+ public double Maximum {
+ get { return (double)GetValue(MaximumProperty); }
+ set { SetValue(MaximumProperty, value); }
+ }
+
+ public static readonly DependencyProperty ValueProperty =
+ DependencyProperty.Register("Value", typeof(double), typeof(NumericUpDown),
+ new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
+
+ public double Value {
+ get { return (double)GetValue(ValueProperty); }
+ set { SetValue(ValueProperty, value); }
+ }
+
+ public static readonly DependencyProperty SmallChangeProperty =
+ DependencyProperty.Register("SmallChange", typeof(double), typeof(NumericUpDown),
+ new FrameworkPropertyMetadata(1.0));
+
+ public double SmallChange {
+ get { return (double)GetValue(SmallChangeProperty); }
+ set { SetValue(SmallChangeProperty, value); }
+ }
+
+ public static readonly DependencyProperty LargeChangeProperty =
+ DependencyProperty.Register("LargeChange", typeof(double), typeof(NumericUpDown),
+ new FrameworkPropertyMetadata(10.0));
+
+ public double LargeChange {
+ get { return (double)GetValue(LargeChangeProperty); }
+ set { SetValue(LargeChangeProperty, value); }
+ }
+
+ bool IsDragging {
+ get {
+ return upButton.IsDragging;
+ }
+ set {
+ upButton.IsDragging = value; downButton.IsDragging = value;
+ }
+ }
+
+ public override void OnApplyTemplate()
+ {
+ base.OnApplyTemplate();
+
+ upButton = (DragRepeatButton)Template.FindName("PART_UpButton", this);
+ downButton = (DragRepeatButton)Template.FindName("PART_DownButton", this);
+ textBox = (TextBox)Template.FindName("PART_TextBox", this);
+
+ upButton.Click += upButton_Click;
+ downButton.Click += downButton_Click;
+
+ textBox.LostFocus += (sender, e) => OnLostFocus(e);
+
+ var upDrag = new DragListener(upButton);
+ var downDrag = new DragListener(downButton);
+
+ upDrag.Started += drag_Started;
+ upDrag.Changed += drag_Changed;
+ upDrag.Completed += drag_Completed;
+
+ downDrag.Started += drag_Started;
+ downDrag.Changed += drag_Changed;
+ downDrag.Completed += drag_Completed;
+
+ Print();
+ }
+
+ void drag_Started(DragListener drag)
+ {
+ OnDragStarted();
+ }
+
+ void drag_Changed(DragListener drag)
+ {
+ IsDragging = true;
+ MoveValue(-drag.DeltaDelta.Y * SmallChange);
+ }
+
+ void drag_Completed(DragListener drag)
+ {
+ IsDragging = false;
+ OnDragCompleted();
+ }
+
+ void downButton_Click(object sender, RoutedEventArgs e)
+ {
+ if (!IsDragging) SmallDown();
+ }
+
+ void upButton_Click(object sender, RoutedEventArgs e)
+ {
+ if (!IsDragging) SmallUp();
+ }
+
+ protected virtual void OnDragStarted()
+ {
+ }
+
+ protected virtual void OnDragCompleted()
+ {
+ }
+
+ public void SmallUp()
+ {
+ MoveValue(SmallChange);
+ }
+
+ public void SmallDown()
+ {
+ MoveValue(-SmallChange);
+ }
+
+ public void LargeUp()
+ {
+ MoveValue(LargeChange);
+ }
+
+ public void LargeDown()
+ {
+ MoveValue(-LargeChange);
+ }
+
+ void MoveValue(double delta)
+ {
+ double result;
+ if (double.IsNaN(Value) || double.IsInfinity(Value)) {
+ SetValue(delta);
+ }
+ else if (double.TryParse(textBox.Text, out result)) {
+ SetValue(result + delta);
+ }
+ else {
+ SetValue(Value + delta);
+ }
+ }
+
+ void Print()
+ {
+ if (textBox != null) {
+ textBox.Text = Value.ToString("F" + DecimalPlaces);
+ textBox.CaretIndex = int.MaxValue;
+ }
+ }
+
+ //wpf bug?: Value = -1 updates bindings without coercing, workaround
+ //update: not derived from RangeBase - no problem
+ void SetValue(double newValue)
+ {
+ newValue = CoerceValue(newValue);
+ if (Value != newValue) {
+ Value = newValue;
+ }
+ }
+
+ double CoerceValue(double newValue)
+ {
+ return Math.Max(Minimum, Math.Min(newValue, Maximum));
+ }
+
+ protected override void OnPreviewKeyDown(KeyEventArgs e)
+ {
+ base.OnPreviewKeyDown(e);
+ switch (e.Key) {
+ case Key.Enter:
+ SetInputValue();
+ textBox.SelectAll();
+ e.Handled = true;
+ break;
+ case Key.Up:
+ SmallUp();
+ e.Handled = true;
+ break;
+ case Key.Down:
+ SmallDown();
+ e.Handled = true;
+ break;
+ case Key.PageUp:
+ LargeUp();
+ e.Handled = true;
+ break;
+ case Key.PageDown:
+ LargeDown();
+ e.Handled = true;
+ break;
+// case Key.Home:
+// Maximize();
+// e.Handled = true;
+// break;
+// case Key.End:
+// Minimize();
+// e.Handled = true;
+// break;
+ }
+ }
+
+ void SetInputValue()
+ {
+ double result;
+ if (double.TryParse(textBox.Text, out result)) {
+ SetValue(result);
+ } else {
+ Print();
+ }
+ }
+
+ protected override void OnLostFocus(RoutedEventArgs e)
+ {
+ base.OnLostFocus(e);
+ SetInputValue();
+ }
+
+ //protected override void OnMouseWheel(MouseWheelEventArgs e)
+ //{
+ // if (e.Delta > 0)
+ // {
+ // if (Keyboard.IsKeyDown(Key.LeftShift))
+ // {
+ // LargeUp();
+ // }
+ // else
+ // {
+ // SmallUp();
+ // }
+ // }
+ // else
+ // {
+ // if (Keyboard.IsKeyDown(Key.LeftShift))
+ // {
+ // LargeDown();
+ // }
+ // else
+ // {
+ // SmallDown();
+ // }
+ // }
+ // e.Handled = true;
+ //}
+
+ protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
+ {
+ base.OnPropertyChanged(e);
+
+ if (e.Property == ValueProperty) {
+ Value = CoerceValue((double)e.NewValue);
+ Print();
+ }
+ else if (e.Property == SmallChangeProperty &&
+ ReadLocalValue(LargeChangeProperty) == DependencyProperty.UnsetValue) {
+ LargeChange = SmallChange * 10;
+ }
+ }
+ }
+
+ public class DragRepeatButton : RepeatButton
+ {
+ public static readonly DependencyProperty IsDraggingProperty =
+ DependencyProperty.Register("IsDragging", typeof(bool), typeof(DragRepeatButton));
+
+ public bool IsDragging {
+ get { return (bool)GetValue(IsDraggingProperty); }
+ set { SetValue(IsDraggingProperty, value); }
+ }
+ }
+}
diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/NumericUpDown.xaml b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/NumericUpDown.xaml
new file mode 100644
index 0000000000..a622073f1c
--- /dev/null
+++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/NumericUpDown.xaml
@@ -0,0 +1,140 @@
+
+
+ #DADFEA
+ #E6EBEF
+ #B6BDD3
+ #7F9DB9
+ Black
+ #B6BDD3
+
+
+
+
+
+
+
diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/PanelMoveAdorner.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/PanelMoveAdorner.cs
index 37a186e5fa..215a736290 100644
--- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/PanelMoveAdorner.cs
+++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/PanelMoveAdorner.cs
@@ -16,17 +16,12 @@
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
using System.Windows.Controls;
using System.Windows;
using System.Windows.Input;
using ICSharpCode.WpfDesign.Designer.Services;
using System.Windows.Media;
using ICSharpCode.WpfDesign.Designer.Converters;
-using System.Globalization;
using System.Windows.Data;
using ICSharpCode.WpfDesign.UIExtensions;
diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/Picker.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/Picker.cs
new file mode 100644
index 0000000000..347149fef9
--- /dev/null
+++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/Picker.cs
@@ -0,0 +1,151 @@
+// Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this
+// software and associated documentation files (the "Software"), to deal in the Software
+// without restriction, including without limitation the rights to use, copy, modify, merge,
+// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
+// to whom the Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all copies or
+// substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+
+using System;
+using System.Windows;
+using System.Windows.Input;
+using System.Windows.Controls;
+using System.Windows.Media;
+using System.Windows.Data;
+
+namespace ICSharpCode.WpfDesign.Designer.Controls
+{
+ public class Picker : Grid
+ {
+ public Picker()
+ {
+ SizeChanged += delegate { UpdateValueOffset(); };
+ }
+
+ public static readonly DependencyProperty MarkerProperty =
+ DependencyProperty.Register("Marker", typeof(UIElement), typeof(Picker));
+
+ public UIElement Marker {
+ get { return (UIElement)GetValue(MarkerProperty); }
+ set { SetValue(MarkerProperty, value); }
+ }
+
+ public static readonly DependencyProperty ValueProperty =
+ DependencyProperty.Register("Value", typeof(double), typeof(Picker),
+ new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
+
+ public double Value {
+ get { return (double)GetValue(ValueProperty); }
+ set { SetValue(ValueProperty, value); }
+ }
+
+ public static readonly DependencyProperty ValueOffsetProperty =
+ DependencyProperty.Register("ValueOffset", typeof(double), typeof(Picker));
+
+ public double ValueOffset {
+ get { return (double)GetValue(ValueOffsetProperty); }
+ set { SetValue(ValueOffsetProperty, value); }
+ }
+
+ public static readonly DependencyProperty OrientationProperty =
+ DependencyProperty.Register("Orientation", typeof(Orientation), typeof(Picker));
+
+ public Orientation Orientation {
+ get { return (Orientation)GetValue(OrientationProperty); }
+ set { SetValue(OrientationProperty, value); }
+ }
+
+ public static readonly DependencyProperty MinimumProperty =
+ DependencyProperty.Register("Minimum", typeof(double), typeof(Picker));
+
+ public double Minimum {
+ get { return (double)GetValue(MinimumProperty); }
+ set { SetValue(MinimumProperty, value); }
+ }
+
+ public static readonly DependencyProperty MaximumProperty =
+ DependencyProperty.Register("Maximum", typeof(double), typeof(Picker),
+ new FrameworkPropertyMetadata(100.0));
+
+ public double Maximum {
+ get { return (double)GetValue(MaximumProperty); }
+ set { SetValue(MaximumProperty, value); }
+ }
+
+ protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
+ {
+ base.OnPropertyChanged(e);
+
+ if (e.Property == MarkerProperty) {
+ TranslateTransform t = Marker.RenderTransform as TranslateTransform;
+ if (t == null) {
+ t = new TranslateTransform();
+ Marker.RenderTransform = t;
+ }
+ var property = Orientation == Orientation.Horizontal ? TranslateTransform.XProperty : TranslateTransform.YProperty;
+ BindingOperations.SetBinding(t, property, new Binding("ValueOffset") {
+ Source = this
+ });
+ }
+ else if (e.Property == ValueProperty) {
+ UpdateValueOffset();
+ }
+ }
+
+ bool isMouseDown;
+
+ protected override void OnPreviewMouseDown(MouseButtonEventArgs e)
+ {
+ isMouseDown = true;
+ CaptureMouse();
+ UpdateValue();
+ }
+
+ protected override void OnPreviewMouseMove(MouseEventArgs e)
+ {
+ if (isMouseDown) {
+ UpdateValue();
+ }
+ }
+
+ protected override void OnPreviewMouseUp(MouseButtonEventArgs e)
+ {
+ isMouseDown = false;
+ ReleaseMouseCapture();
+ }
+
+ void UpdateValue()
+ {
+ Point p = Mouse.GetPosition(this);
+ double length = 0, pos = 0;
+
+ if (Orientation == Orientation.Horizontal) {
+ length = ActualWidth;
+ pos = p.X;
+ }
+ else {
+ length = ActualHeight;
+ pos = p.Y;
+ }
+
+ pos = Math.Max(0, Math.Min(length, pos));
+ Value = Minimum + (Maximum - Minimum) * pos / length;
+ }
+
+ void UpdateValueOffset()
+ {
+ var length = Orientation == Orientation.Horizontal ? ActualWidth : ActualHeight;
+ ValueOffset = length * (Value - Minimum) / (Maximum - Minimum);
+ }
+ }
+}
diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/RelayCommand.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/RelayCommand.cs
new file mode 100644
index 0000000000..a9878552e5
--- /dev/null
+++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/RelayCommand.cs
@@ -0,0 +1,118 @@
+// Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this
+// software and associated documentation files (the "Software"), to deal in the Software
+// without restriction, including without limitation the rights to use, copy, modify, merge,
+// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
+// to whom the Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all copies or
+// substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+
+using System;
+using System.Windows.Input;
+
+namespace ICSharpCode.WpfDesign.Designer.Controls
+{
+ ///
+ /// A command that invokes a delegate.
+ /// The command parameter must be of type T.
+ ///
+ public class RelayCommand : ICommand
+ {
+ readonly Predicate canExecute;
+ readonly Action execute;
+
+ public RelayCommand(Action execute)
+ {
+ if (execute == null)
+ throw new ArgumentNullException("execute");
+ this.execute = execute;
+ }
+
+ public RelayCommand(Action execute, Predicate canExecute)
+ {
+ if (execute == null)
+ throw new ArgumentNullException("execute");
+ this.execute = execute;
+ this.canExecute = canExecute;
+ }
+
+ public event EventHandler CanExecuteChanged {
+ add {
+ if (canExecute != null)
+ CommandManager.RequerySuggested += value;
+ }
+ remove {
+ if (canExecute != null)
+ CommandManager.RequerySuggested -= value;
+ }
+ }
+
+ public bool CanExecute(object parameter)
+ {
+ if (parameter != null && !(parameter is T))
+ return false;
+ return canExecute == null ? true : canExecute((T)parameter);
+ }
+
+ public void Execute(object parameter)
+ {
+ execute((T)parameter);
+ }
+ }
+
+ ///
+ /// A command that invokes a delegate.
+ /// This class does not provide the command parameter to the delegate -
+ /// if you need that, use the generic version of this class instead.
+ ///
+ public class RelayCommand : ICommand
+ {
+ readonly Func canExecute;
+ readonly Action execute;
+
+ public RelayCommand(Action execute)
+ {
+ if (execute == null)
+ throw new ArgumentNullException("execute");
+ this.execute = execute;
+ }
+
+ public RelayCommand(Action execute, Func canExecute)
+ {
+ if (execute == null)
+ throw new ArgumentNullException("execute");
+ this.execute = execute;
+ this.canExecute = canExecute;
+ }
+
+ public event EventHandler CanExecuteChanged {
+ add {
+ if (canExecute != null)
+ CommandManager.RequerySuggested += value;
+ }
+ remove {
+ if (canExecute != null)
+ CommandManager.RequerySuggested -= value;
+ }
+ }
+
+ public bool CanExecute(object parameter)
+ {
+ return canExecute == null ? true : canExecute();
+ }
+
+ public void Execute(object parameter)
+ {
+ execute();
+ }
+ }
+}
diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/ZoomButtons.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/ZoomButtons.cs
new file mode 100644
index 0000000000..753d369ef5
--- /dev/null
+++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/ZoomButtons.cs
@@ -0,0 +1,66 @@
+// Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this
+// software and associated documentation files (the "Software"), to deal in the Software
+// without restriction, including without limitation the rights to use, copy, modify, merge,
+// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
+// to whom the Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all copies or
+// substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+
+using System;
+using System.Windows;
+using System.Windows.Controls.Primitives;
+
+namespace ICSharpCode.WpfDesign.Designer.Controls
+{
+ public class ZoomButtons : RangeBase
+ {
+ static ZoomButtons()
+ {
+ DefaultStyleKeyProperty.OverrideMetadata(typeof(ZoomButtons),
+ new FrameworkPropertyMetadata(typeof(ZoomButtons)));
+ }
+
+ public override void OnApplyTemplate()
+ {
+ base.OnApplyTemplate();
+
+ var uxPlus = (ButtonBase)Template.FindName("uxPlus", this);
+ var uxMinus = (ButtonBase)Template.FindName("uxMinus", this);
+ var uxReset = (ButtonBase)Template.FindName("uxReset", this);
+
+ if (uxPlus != null)
+ uxPlus.Click += OnZoomInClick;
+ if (uxMinus != null)
+ uxMinus.Click += OnZoomOutClick;
+ if (uxReset != null)
+ uxReset.Click += OnResetClick;
+ }
+
+ const double ZoomFactor = 1.1;
+
+ void OnZoomInClick(object sender, EventArgs e)
+ {
+ SetCurrentValue(ValueProperty, ZoomScrollViewer.RoundToOneIfClose(this.Value * ZoomFactor));
+ }
+
+ void OnZoomOutClick(object sender, EventArgs e)
+ {
+ SetCurrentValue(ValueProperty, ZoomScrollViewer.RoundToOneIfClose(this.Value / ZoomFactor));
+ }
+
+ void OnResetClick(object sender, EventArgs e)
+ {
+ SetCurrentValue(ValueProperty, 1.0);
+ }
+ }
+}
diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/ZoomControl.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/ZoomControl.cs
index ff7e4d4ce9..6e16859422 100644
--- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/ZoomControl.cs
+++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/ZoomControl.cs
@@ -15,16 +15,13 @@
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
-
-using System;
+
using System.IO;
using System.Reflection;
using System.Resources;
using System.Windows;
using System.Windows.Input;
-using ICSharpCode.SharpDevelop.Widgets;
-
namespace ICSharpCode.WpfDesign.Designer.Controls
{
public class ZoomControl : ZoomScrollViewer
diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/ZoomScrollViewer.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/ZoomScrollViewer.cs
new file mode 100644
index 0000000000..5db1c27b1b
--- /dev/null
+++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/ZoomScrollViewer.cs
@@ -0,0 +1,184 @@
+// Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this
+// software and associated documentation files (the "Software"), to deal in the Software
+// without restriction, including without limitation the rights to use, copy, modify, merge,
+// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
+// to whom the Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all copies or
+// substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+
+using System;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Input;
+
+namespace ICSharpCode.WpfDesign.Designer.Controls
+{
+ public class ZoomScrollViewer : ScrollViewer
+ {
+ static ZoomScrollViewer()
+ {
+ DefaultStyleKeyProperty.OverrideMetadata(typeof(ZoomScrollViewer),
+ new FrameworkPropertyMetadata(typeof(ZoomScrollViewer)));
+ }
+
+ public static readonly DependencyProperty CurrentZoomProperty =
+ DependencyProperty.Register("CurrentZoom", typeof(double), typeof(ZoomScrollViewer),
+ new FrameworkPropertyMetadata(1.0, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, CalculateZoomButtonCollapsed, CoerceZoom));
+
+ public double CurrentZoom {
+ get { return (double)GetValue(CurrentZoomProperty); }
+ set { SetValue(CurrentZoomProperty, value); }
+ }
+
+ static object CoerceZoom(DependencyObject d, object baseValue)
+ {
+ var zoom = (double)baseValue;
+ ZoomScrollViewer sv = (ZoomScrollViewer)d;
+ return Math.Max(sv.MinimumZoom, Math.Min(sv.MaximumZoom, zoom));
+ }
+
+ public static readonly DependencyProperty MinimumZoomProperty =
+ DependencyProperty.Register("MinimumZoom", typeof(double), typeof(ZoomScrollViewer),
+ new FrameworkPropertyMetadata(0.2));
+
+ public double MinimumZoom {
+ get { return (double)GetValue(MinimumZoomProperty); }
+ set { SetValue(MinimumZoomProperty, value); }
+ }
+
+ public static readonly DependencyProperty MaximumZoomProperty =
+ DependencyProperty.Register("MaximumZoom", typeof(double), typeof(ZoomScrollViewer),
+ new FrameworkPropertyMetadata(5.0));
+
+ public double MaximumZoom {
+ get { return (double)GetValue(MaximumZoomProperty); }
+ set { SetValue(MaximumZoomProperty, value); }
+ }
+
+ public static readonly DependencyProperty MouseWheelZoomProperty =
+ DependencyProperty.Register("MouseWheelZoom", typeof(bool), typeof(ZoomScrollViewer),
+ new FrameworkPropertyMetadata(true));
+
+ public bool MouseWheelZoom {
+ get { return (bool)GetValue(MouseWheelZoomProperty); }
+ set { SetValue(MouseWheelZoomProperty, value); }
+ }
+
+ public static readonly DependencyProperty AlwaysShowZoomButtonsProperty =
+ DependencyProperty.Register("AlwaysShowZoomButtons", typeof(bool), typeof(ZoomScrollViewer),
+ new FrameworkPropertyMetadata(false, CalculateZoomButtonCollapsed));
+
+ public bool AlwaysShowZoomButtons {
+ get { return (bool)GetValue(AlwaysShowZoomButtonsProperty); }
+ set { SetValue(AlwaysShowZoomButtonsProperty, value); }
+ }
+
+ static readonly DependencyPropertyKey ComputedZoomButtonCollapsedPropertyKey =
+ DependencyProperty.RegisterReadOnly("ComputedZoomButtonCollapsed", typeof(bool), typeof(ZoomScrollViewer),
+ new FrameworkPropertyMetadata(true));
+
+ public static readonly DependencyProperty ComputedZoomButtonCollapsedProperty = ComputedZoomButtonCollapsedPropertyKey.DependencyProperty;
+
+ public bool ComputedZoomButtonCollapsed {
+ get { return (bool)GetValue(ComputedZoomButtonCollapsedProperty); }
+ private set { SetValue(ComputedZoomButtonCollapsedPropertyKey, value); }
+ }
+
+ static void CalculateZoomButtonCollapsed(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ ZoomScrollViewer z = d as ZoomScrollViewer;
+ if (z != null)
+ z.ComputedZoomButtonCollapsed = (z.AlwaysShowZoomButtons == false) && (z.CurrentZoom == 1.0);
+ }
+
+ protected override void OnMouseWheel(MouseWheelEventArgs e)
+ {
+ if (!e.Handled && Keyboard.Modifiers == ModifierKeys.Control && MouseWheelZoom) {
+ double oldZoom = CurrentZoom;
+ double newZoom = RoundToOneIfClose(CurrentZoom * Math.Pow(1.001, e.Delta));
+ newZoom = Math.Max(this.MinimumZoom, Math.Min(this.MaximumZoom, newZoom));
+
+ // adjust scroll position so that mouse stays over the same virtual coordinate
+ ContentPresenter presenter = Template.FindName("PART_Presenter", this) as ContentPresenter;
+ Vector relMousePos;
+ if (presenter != null) {
+ Point mousePos = e.GetPosition(presenter);
+ relMousePos = new Vector(mousePos.X / presenter.ActualWidth, mousePos.Y / presenter.ActualHeight);
+ } else {
+ relMousePos = new Vector(0.5, 0.5);
+ }
+
+ Point scrollOffset = new Point(this.HorizontalOffset, this.VerticalOffset);
+ Vector oldHalfViewport = new Vector(this.ViewportWidth / 2, this.ViewportHeight / 2);
+ Vector newHalfViewport = oldHalfViewport / newZoom * oldZoom;
+ Point oldCenter = scrollOffset + oldHalfViewport;
+ Point virtualMousePos = scrollOffset + new Vector(relMousePos.X * this.ViewportWidth, relMousePos.Y * this.ViewportHeight);
+
+ // As newCenter, we want to choose a point between oldCenter and virtualMousePos. The more we zoom in, the closer
+ // to virtualMousePos. We'll create the line x = oldCenter + lambda * (virtualMousePos-oldCenter).
+ // On this line, we need to choose lambda between -1 and 1:
+ // -1 = zoomed out completely
+ // 0 = zoom unchanged
+ // +1 = zoomed in completely
+ // But the zoom factor (newZoom/oldZoom) we have is in the range [0,+Infinity].
+
+ // Basically, I just played around until I found a function that maps this to [-1,1] and works well.
+ // "f" is squared because otherwise the mouse simply stays over virtualMousePos, but I wanted virtualMousePos
+ // to move towards the middle -> squaring f causes lambda to be closer to 1, giving virtualMousePos more weight
+ // then oldCenter.
+
+ double f = Math.Min(newZoom, oldZoom) / Math.Max(newZoom, oldZoom);
+ double lambda = 1 - f*f;
+ if (oldZoom > newZoom)
+ lambda = -lambda;
+
+ Point newCenter = oldCenter + lambda * (virtualMousePos - oldCenter);
+ scrollOffset = newCenter - newHalfViewport;
+
+ SetCurrentValue(CurrentZoomProperty, newZoom);
+
+ this.ScrollToHorizontalOffset(scrollOffset.X);
+ this.ScrollToVerticalOffset(scrollOffset.Y);
+
+ e.Handled = true;
+ }
+ base.OnMouseWheel(e);
+ }
+
+ internal static double RoundToOneIfClose(double val)
+ {
+ if (Math.Abs(val - 1.0) < 0.001)
+ return 1.0;
+ else
+ return val;
+ }
+ }
+
+ sealed class IsNormalZoomConverter : IValueConverter
+ {
+ public static readonly IsNormalZoomConverter Instance = new IsNormalZoomConverter();
+
+ public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
+ {
+ if (parameter is bool && (bool)parameter)
+ return true;
+ return ((double)value) == 1.0;
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/ZoomScrollViewer.xaml b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/ZoomScrollViewer.xaml
new file mode 100644
index 0000000000..53cff7a136
--- /dev/null
+++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/ZoomScrollViewer.xaml
@@ -0,0 +1,94 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/DesignSurface.xaml b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/DesignSurface.xaml
index b7e1dee70c..79defd0aef 100644
--- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/DesignSurface.xaml
+++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/DesignSurface.xaml
@@ -1,10 +1,9 @@
+ xmlns:Widgets="clr-namespace:ICSharpCode.WpfDesign.Designer.Controls">