mirror of
https://github.com/microsoft/PowerToys.git
synced 2025-12-16 11:48:06 +01:00
[ColorPicker] Change zoom animation behavior (#11057)
* [ColorPicker] Change zoom animation behavior Makes the main window large enough to accommodate all zoom levels. * [ColorPicker] Change zoom window position logic Use PointFromScreen to calculate mouse position relative to window This requires a "visible" window, so use opacity to fake-hide window Window is still fully hidden when color picker closes * [ColorPicker] Extract and modify resize behavior Allows easier editing of animation easing/duration * Update expect.txt IAnimatable IEasing Co-authored-by: Clint Rutkas <clint@rutkas.com>
This commit is contained in:
2
.github/actions/spell-check/expect.txt
vendored
2
.github/actions/spell-check/expect.txt
vendored
@@ -752,6 +752,7 @@ hxx
|
|||||||
Hyperlink
|
Hyperlink
|
||||||
IAction
|
IAction
|
||||||
IActivated
|
IActivated
|
||||||
|
IAnimatable
|
||||||
IApp
|
IApp
|
||||||
IApplication
|
IApplication
|
||||||
IAppx
|
IAppx
|
||||||
@@ -796,6 +797,7 @@ IDrop
|
|||||||
idx
|
idx
|
||||||
IDXGI
|
IDXGI
|
||||||
IDYES
|
IDYES
|
||||||
|
IEasing
|
||||||
IEnum
|
IEnum
|
||||||
IEnumerable
|
IEnumerable
|
||||||
IEnumerator
|
IEnumerator
|
||||||
|
|||||||
@@ -1,60 +0,0 @@
|
|||||||
// Copyright (c) Microsoft Corporation
|
|
||||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
|
||||||
// See the LICENSE file in the project root for more information.
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Windows;
|
|
||||||
using System.Windows.Media.Animation;
|
|
||||||
using Microsoft.Xaml.Behaviors;
|
|
||||||
|
|
||||||
namespace ColorPicker.Behaviors
|
|
||||||
{
|
|
||||||
public class MoveWindowBehavior : Behavior<Window>
|
|
||||||
{
|
|
||||||
public static readonly DependencyProperty LeftProperty = DependencyProperty.Register("Left", typeof(double), typeof(MoveWindowBehavior), new PropertyMetadata(new PropertyChangedCallback(LeftPropertyChanged)));
|
|
||||||
|
|
||||||
private static void LeftPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
|
||||||
{
|
|
||||||
var sender = ((MoveWindowBehavior)d).AssociatedObject;
|
|
||||||
var move = new DoubleAnimation(sender.Left, (double)e.NewValue, new Duration(TimeSpan.FromMilliseconds(150)), FillBehavior.Stop);
|
|
||||||
move.EasingFunction = new CubicEase() { EasingMode = EasingMode.EaseInOut };
|
|
||||||
sender.BeginAnimation(Window.LeftProperty, move, HandoffBehavior.Compose);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static readonly DependencyProperty TopProperty = DependencyProperty.Register("Top", typeof(double), typeof(MoveWindowBehavior), new PropertyMetadata(new PropertyChangedCallback(TopPropertyChanged)));
|
|
||||||
|
|
||||||
private static void TopPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
|
||||||
{
|
|
||||||
var sender = ((MoveWindowBehavior)d).AssociatedObject;
|
|
||||||
var move = new DoubleAnimation(sender.Top, (double)e.NewValue, new Duration(TimeSpan.FromMilliseconds(150)), FillBehavior.Stop);
|
|
||||||
move.EasingFunction = new CubicEase() { EasingMode = EasingMode.EaseInOut };
|
|
||||||
sender.BeginAnimation(Window.TopProperty, move, HandoffBehavior.Compose);
|
|
||||||
}
|
|
||||||
|
|
||||||
public double Left
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return (double)GetValue(LeftProperty);
|
|
||||||
}
|
|
||||||
|
|
||||||
set
|
|
||||||
{
|
|
||||||
SetValue(LeftProperty, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public double Top
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return (double)GetValue(TopProperty);
|
|
||||||
}
|
|
||||||
|
|
||||||
set
|
|
||||||
{
|
|
||||||
SetValue(TopProperty, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -11,20 +11,55 @@ namespace ColorPicker.Behaviors
|
|||||||
{
|
{
|
||||||
public class ResizeBehavior : Behavior<FrameworkElement>
|
public class ResizeBehavior : Behavior<FrameworkElement>
|
||||||
{
|
{
|
||||||
|
// animation behavior variables
|
||||||
|
// used when size is getting bigger
|
||||||
|
private static readonly TimeSpan _animationTime = TimeSpan.FromMilliseconds(200);
|
||||||
|
private static readonly IEasingFunction _easeFunction = new SineEase() { EasingMode = EasingMode.EaseOut };
|
||||||
|
|
||||||
|
// used when size is getting smaller
|
||||||
|
private static readonly TimeSpan _animationTimeSmaller = _animationTime;
|
||||||
|
private static readonly IEasingFunction _easeFunctionSmaller = new QuadraticEase() { EasingMode = EasingMode.EaseIn };
|
||||||
|
|
||||||
|
private static void CustomAnimation(DependencyProperty prop, IAnimatable sender, double fromValue, double toValue)
|
||||||
|
{
|
||||||
|
// if the animation is to/from a value of 0, it will cancel the current animation
|
||||||
|
DoubleAnimation move = null;
|
||||||
|
if (toValue > 0 && fromValue > 0)
|
||||||
|
{
|
||||||
|
// if getting bigger
|
||||||
|
if (fromValue < toValue)
|
||||||
|
{
|
||||||
|
move = new DoubleAnimation(fromValue, toValue, new Duration(_animationTime), FillBehavior.Stop)
|
||||||
|
{
|
||||||
|
EasingFunction = _easeFunction,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
move = new DoubleAnimation(fromValue, toValue, new Duration(_animationTimeSmaller), FillBehavior.Stop)
|
||||||
|
{
|
||||||
|
EasingFunction = _easeFunctionSmaller,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// HandoffBehavior must be SnapshotAndReplace
|
||||||
|
// Compose does not allow cancellation
|
||||||
|
sender.BeginAnimation(prop, move, HandoffBehavior.SnapshotAndReplace);
|
||||||
|
}
|
||||||
|
|
||||||
public static readonly DependencyProperty WidthProperty = DependencyProperty.Register("Width", typeof(double), typeof(ResizeBehavior), new PropertyMetadata(new PropertyChangedCallback(WidthPropertyChanged)));
|
public static readonly DependencyProperty WidthProperty = DependencyProperty.Register("Width", typeof(double), typeof(ResizeBehavior), new PropertyMetadata(new PropertyChangedCallback(WidthPropertyChanged)));
|
||||||
|
|
||||||
private static void WidthPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
private static void WidthPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||||
{
|
{
|
||||||
var sender = ((ResizeBehavior)d).AssociatedObject;
|
var sender = ((ResizeBehavior)d).AssociatedObject;
|
||||||
var move = new DoubleAnimation(sender.Width, (double)e.NewValue, new Duration(TimeSpan.FromMilliseconds(150)), FillBehavior.Stop);
|
|
||||||
move.Completed += (s, e1) =>
|
|
||||||
{
|
|
||||||
sender.BeginAnimation(FrameworkElement.WidthProperty, null);
|
|
||||||
sender.Width = (double)e.NewValue;
|
|
||||||
};
|
|
||||||
|
|
||||||
move.EasingFunction = new QuadraticEase() { EasingMode = EasingMode.EaseOut };
|
var fromValue = sender.Width;
|
||||||
sender.BeginAnimation(FrameworkElement.WidthProperty, move, HandoffBehavior.Compose);
|
var toValue = (double)e.NewValue;
|
||||||
|
|
||||||
|
// setting Width before animation prevents jumping
|
||||||
|
sender.Width = toValue;
|
||||||
|
CustomAnimation(FrameworkElement.WidthProperty, sender, fromValue, toValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static readonly DependencyProperty HeightProperty = DependencyProperty.Register("Height", typeof(double), typeof(ResizeBehavior), new PropertyMetadata(new PropertyChangedCallback(HeightPropertyChanged)));
|
public static readonly DependencyProperty HeightProperty = DependencyProperty.Register("Height", typeof(double), typeof(ResizeBehavior), new PropertyMetadata(new PropertyChangedCallback(HeightPropertyChanged)));
|
||||||
@@ -32,15 +67,13 @@ namespace ColorPicker.Behaviors
|
|||||||
private static void HeightPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
private static void HeightPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||||
{
|
{
|
||||||
var sender = ((ResizeBehavior)d).AssociatedObject;
|
var sender = ((ResizeBehavior)d).AssociatedObject;
|
||||||
var move = new DoubleAnimation(sender.Height, (double)e.NewValue, new Duration(TimeSpan.FromMilliseconds(150)), FillBehavior.Stop);
|
|
||||||
move.Completed += (s, e1) =>
|
|
||||||
{
|
|
||||||
sender.BeginAnimation(FrameworkElement.HeightProperty, null);
|
|
||||||
sender.Height = (double)e.NewValue;
|
|
||||||
};
|
|
||||||
|
|
||||||
move.EasingFunction = new QuadraticEase() { EasingMode = EasingMode.EaseOut };
|
var fromValue = sender.Height;
|
||||||
sender.BeginAnimation(FrameworkElement.HeightProperty, move, HandoffBehavior.Compose);
|
var toValue = (double)e.NewValue;
|
||||||
|
|
||||||
|
// setting Height before animation prevents jumping
|
||||||
|
sender.Height = toValue;
|
||||||
|
CustomAnimation(FrameworkElement.HeightProperty, sender, fromValue, toValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
public double Width
|
public double Width
|
||||||
|
|||||||
@@ -18,33 +18,27 @@ namespace ColorPicker.Helpers
|
|||||||
[Export(typeof(ZoomWindowHelper))]
|
[Export(typeof(ZoomWindowHelper))]
|
||||||
public class ZoomWindowHelper
|
public class ZoomWindowHelper
|
||||||
{
|
{
|
||||||
private const int ZoomWindowChangeDelayInMS = 50;
|
|
||||||
private const int ZoomFactor = 2;
|
private const int ZoomFactor = 2;
|
||||||
private const int BaseZoomImageSize = 50;
|
private const int BaseZoomImageSize = 50;
|
||||||
private const int MaxZoomLevel = 4;
|
private const int MaxZoomLevel = 4;
|
||||||
private const int MinZoomLevel = 0;
|
private const int MinZoomLevel = 0;
|
||||||
|
|
||||||
|
private static readonly Bitmap _bmp = new Bitmap(BaseZoomImageSize, BaseZoomImageSize, PixelFormat.Format32bppArgb);
|
||||||
|
private static readonly Graphics _graphics = Graphics.FromImage(_bmp);
|
||||||
|
|
||||||
private readonly IZoomViewModel _zoomViewModel;
|
private readonly IZoomViewModel _zoomViewModel;
|
||||||
private readonly AppStateHandler _appStateHandler;
|
private readonly AppStateHandler _appStateHandler;
|
||||||
private readonly IThrottledActionInvoker _throttledActionInvoker;
|
|
||||||
|
|
||||||
private int _currentZoomLevel;
|
private int _currentZoomLevel;
|
||||||
private int _previousZoomLevel;
|
private int _previousZoomLevel;
|
||||||
|
|
||||||
private ZoomWindow _zoomWindow;
|
private ZoomWindow _zoomWindow;
|
||||||
|
|
||||||
private double _lastLeft;
|
|
||||||
private double _lastTop;
|
|
||||||
|
|
||||||
private double _previousScaledX;
|
|
||||||
private double _previousScaledY;
|
|
||||||
|
|
||||||
[ImportingConstructor]
|
[ImportingConstructor]
|
||||||
public ZoomWindowHelper(IZoomViewModel zoomViewModel, AppStateHandler appStateHandler, IThrottledActionInvoker throttledActionInvoker)
|
public ZoomWindowHelper(IZoomViewModel zoomViewModel, AppStateHandler appStateHandler)
|
||||||
{
|
{
|
||||||
_zoomViewModel = zoomViewModel;
|
_zoomViewModel = zoomViewModel;
|
||||||
_appStateHandler = appStateHandler;
|
_appStateHandler = appStateHandler;
|
||||||
_throttledActionInvoker = throttledActionInvoker;
|
|
||||||
_appStateHandler.AppClosed += AppStateHandler_AppClosed;
|
_appStateHandler.AppClosed += AppStateHandler_AppClosed;
|
||||||
_appStateHandler.AppHidden += AppStateHandler_AppClosed;
|
_appStateHandler.AppHidden += AppStateHandler_AppClosed;
|
||||||
}
|
}
|
||||||
@@ -73,7 +67,7 @@ namespace ColorPicker.Helpers
|
|||||||
{
|
{
|
||||||
_currentZoomLevel = 0;
|
_currentZoomLevel = 0;
|
||||||
_previousZoomLevel = 0;
|
_previousZoomLevel = 0;
|
||||||
HideZoomWindow();
|
HideZoomWindow(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetZoomImage(System.Windows.Point point)
|
private void SetZoomImage(System.Windows.Point point)
|
||||||
@@ -89,28 +83,17 @@ namespace ColorPicker.Helpers
|
|||||||
{
|
{
|
||||||
var x = (int)point.X - (BaseZoomImageSize / 2);
|
var x = (int)point.X - (BaseZoomImageSize / 2);
|
||||||
var y = (int)point.Y - (BaseZoomImageSize / 2);
|
var y = (int)point.Y - (BaseZoomImageSize / 2);
|
||||||
var rect = new Rectangle(x, y, BaseZoomImageSize, BaseZoomImageSize);
|
|
||||||
|
|
||||||
using (var bmp = new Bitmap(rect.Width, rect.Height, PixelFormat.Format32bppArgb))
|
_graphics.CopyFromScreen(x, y, 0, 0, _bmp.Size, CopyPixelOperation.SourceCopy);
|
||||||
{
|
|
||||||
var g = Graphics.FromImage(bmp);
|
|
||||||
g.CopyFromScreen(rect.Left, rect.Top, 0, 0, bmp.Size, CopyPixelOperation.SourceCopy);
|
|
||||||
|
|
||||||
var bitmapImage = BitmapToImageSource(bmp);
|
_zoomViewModel.ZoomArea = BitmapToImageSource(_bmp);
|
||||||
|
|
||||||
_zoomViewModel.ZoomArea = bitmapImage;
|
|
||||||
_zoomViewModel.ZoomFactor = 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var enlarge = (_currentZoomLevel - _previousZoomLevel) > 0 ? true : false;
|
_zoomViewModel.ZoomFactor = Math.Pow(ZoomFactor, _currentZoomLevel - 1);
|
||||||
var currentZoomFactor = enlarge ? ZoomFactor : 1.0 / ZoomFactor;
|
|
||||||
|
|
||||||
_zoomViewModel.ZoomFactor *= currentZoomFactor;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ShowZoomWindow((int)point.X, (int)point.Y);
|
ShowZoomWindow(point);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static BitmapSource BitmapToImageSource(Bitmap bitmap)
|
private static BitmapSource BitmapToImageSource(Bitmap bitmap)
|
||||||
@@ -130,84 +113,67 @@ namespace ColorPicker.Helpers
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void HideZoomWindow()
|
private void HideZoomWindow(bool fully = false)
|
||||||
{
|
{
|
||||||
if (_zoomWindow != null)
|
if (_zoomWindow != null)
|
||||||
{
|
{
|
||||||
_zoomWindow.Hide();
|
_zoomWindow.Opacity = 0;
|
||||||
|
_zoomViewModel.DesiredWidth = 0;
|
||||||
|
_zoomViewModel.DesiredHeight = 0;
|
||||||
|
|
||||||
|
if (fully)
|
||||||
|
{
|
||||||
|
_zoomWindow.Hide();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ShowZoomWindow(int x, int y)
|
private void ShowZoomWindow(System.Windows.Point point)
|
||||||
{
|
{
|
||||||
if (_zoomWindow == null)
|
_zoomWindow ??= new ZoomWindow
|
||||||
{
|
{
|
||||||
_zoomWindow = new ZoomWindow();
|
Content = _zoomViewModel,
|
||||||
_zoomWindow.Content = _zoomViewModel;
|
Opacity = 0,
|
||||||
_zoomWindow.Loaded += ZoomWindow_Loaded;
|
};
|
||||||
_zoomWindow.IsVisibleChanged += ZoomWindow_IsVisibleChanged;
|
|
||||||
}
|
|
||||||
|
|
||||||
// we just started zooming, remember where we opened zoom window
|
|
||||||
if (_currentZoomLevel == 1 && _previousZoomLevel == 0)
|
|
||||||
{
|
|
||||||
var dpi = MonitorResolutionHelper.GetCurrentMonitorDpi();
|
|
||||||
_previousScaledX = x / dpi.DpiScaleX;
|
|
||||||
_previousScaledY = y / dpi.DpiScaleY;
|
|
||||||
}
|
|
||||||
|
|
||||||
_lastLeft = Math.Floor(_previousScaledX - (BaseZoomImageSize * Math.Pow(ZoomFactor, _currentZoomLevel - 1) / 2));
|
|
||||||
_lastTop = Math.Floor(_previousScaledY - (BaseZoomImageSize * Math.Pow(ZoomFactor, _currentZoomLevel - 1) / 2));
|
|
||||||
|
|
||||||
var justShown = false;
|
|
||||||
if (!_zoomWindow.IsVisible)
|
if (!_zoomWindow.IsVisible)
|
||||||
{
|
{
|
||||||
_zoomWindow.Left = _lastLeft;
|
|
||||||
_zoomWindow.Top = _lastTop;
|
|
||||||
_zoomViewModel.Height = BaseZoomImageSize;
|
|
||||||
_zoomViewModel.Width = BaseZoomImageSize;
|
|
||||||
_zoomWindow.Show();
|
_zoomWindow.Show();
|
||||||
justShown = true;
|
}
|
||||||
|
|
||||||
|
if (_zoomWindow.Opacity < 0.5)
|
||||||
|
{
|
||||||
|
var halfWidth = _zoomWindow.Width / 2;
|
||||||
|
var halfHeight = _zoomWindow.Height / 2;
|
||||||
|
|
||||||
|
// usually takes 1-3 iterations to converge
|
||||||
|
// 5 is just an arbitrary limit to prevent infinite loops
|
||||||
|
for (var i = 0; i < 5; i++)
|
||||||
|
{
|
||||||
|
// mouse position relative to top left of _zoomWindow
|
||||||
|
var scaledPoint = _zoomWindow.PointFromScreen(point);
|
||||||
|
|
||||||
|
var diffX = scaledPoint.X - halfWidth;
|
||||||
|
var diffY = scaledPoint.Y - halfHeight;
|
||||||
|
|
||||||
|
// minimum difference that is considered important
|
||||||
|
const double minDiff = 0.05;
|
||||||
|
if (Math.Abs(diffX) < minDiff && Math.Abs(diffY) < minDiff)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
_zoomWindow.Left += diffX;
|
||||||
|
_zoomWindow.Top += diffY;
|
||||||
|
}
|
||||||
|
|
||||||
// make sure color picker window is on top of just opened zoom window
|
// make sure color picker window is on top of just opened zoom window
|
||||||
AppStateHandler.SetTopMost();
|
AppStateHandler.SetTopMost();
|
||||||
|
_zoomWindow.Opacity = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// dirty hack - sometimes when we just show a window on a second monitor with different DPI,
|
_zoomViewModel.DesiredHeight = BaseZoomImageSize * _zoomViewModel.ZoomFactor;
|
||||||
// window position is not set correctly on a first time, we need to "ping" it again to make it appear on the proper location
|
_zoomViewModel.DesiredWidth = BaseZoomImageSize * _zoomViewModel.ZoomFactor;
|
||||||
if (justShown)
|
|
||||||
{
|
|
||||||
_zoomWindow.Left = _lastLeft + 1;
|
|
||||||
_zoomWindow.Top = _lastTop + 1;
|
|
||||||
SessionEventHelper.Event.ZoomUsed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
_throttledActionInvoker.ScheduleAction(
|
|
||||||
() =>
|
|
||||||
{
|
|
||||||
_zoomWindow.DesiredLeft = _lastLeft;
|
|
||||||
_zoomWindow.DesiredTop = _lastTop;
|
|
||||||
_zoomViewModel.DesiredHeight = BaseZoomImageSize * _zoomViewModel.ZoomFactor;
|
|
||||||
_zoomViewModel.DesiredWidth = BaseZoomImageSize * _zoomViewModel.ZoomFactor;
|
|
||||||
},
|
|
||||||
ZoomWindowChangeDelayInMS);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ZoomWindow_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
|
|
||||||
{
|
|
||||||
// need to set at this point again, to avoid issues moving between screens with different scaling
|
|
||||||
if ((bool)e.NewValue)
|
|
||||||
{
|
|
||||||
_zoomWindow.Left = _lastLeft;
|
|
||||||
_zoomWindow.Top = _lastTop;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ZoomWindow_Loaded(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
// need to call it again at load time, because it does was not dpi aware at the first time of Show() call
|
|
||||||
_zoomWindow.Left = _lastLeft;
|
|
||||||
_zoomWindow.Top = _lastTop;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AppStateHandler_AppClosed(object sender, EventArgs e)
|
private void AppStateHandler_AppClosed(object sender, EventArgs e)
|
||||||
|
|||||||
@@ -11,6 +11,8 @@
|
|||||||
Focusable="False">
|
Focusable="False">
|
||||||
|
|
||||||
<Border x:Name="WindowBorder"
|
<Border x:Name="WindowBorder"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Center"
|
||||||
BorderBrush="{DynamicResource WindowBorderBrush}"
|
BorderBrush="{DynamicResource WindowBorderBrush}"
|
||||||
Margin="12"
|
Margin="12"
|
||||||
BorderThickness="1"
|
BorderThickness="1"
|
||||||
|
|||||||
@@ -8,7 +8,6 @@
|
|||||||
mc:Ignorable="d"
|
mc:Ignorable="d"
|
||||||
Title="Zoom window"
|
Title="Zoom window"
|
||||||
WindowStyle="None"
|
WindowStyle="None"
|
||||||
SizeToContent="WidthAndHeight"
|
|
||||||
Topmost="True"
|
Topmost="True"
|
||||||
AllowsTransparency="True"
|
AllowsTransparency="True"
|
||||||
Background="Transparent"
|
Background="Transparent"
|
||||||
@@ -17,6 +16,5 @@
|
|||||||
Focusable="False">
|
Focusable="False">
|
||||||
<e:Interaction.Behaviors>
|
<e:Interaction.Behaviors>
|
||||||
<behaviors:CloseZoomWindowBehavior/>
|
<behaviors:CloseZoomWindowBehavior/>
|
||||||
<behaviors:MoveWindowBehavior Left="{Binding DesiredLeft, Mode=TwoWay}" Top="{Binding DesiredTop}"/>
|
|
||||||
</e:Interaction.Behaviors>
|
</e:Interaction.Behaviors>
|
||||||
</Window>
|
</Window>
|
||||||
|
|||||||
@@ -10,50 +10,16 @@ namespace ColorPicker
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Interaction logic for ZoomWindow.xaml
|
/// Interaction logic for ZoomWindow.xaml
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class ZoomWindow : Window, INotifyPropertyChanged
|
public partial class ZoomWindow : Window
|
||||||
{
|
{
|
||||||
private double _left;
|
|
||||||
private double _top;
|
|
||||||
|
|
||||||
public ZoomWindow()
|
public ZoomWindow()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
DataContext = this;
|
DataContext = this;
|
||||||
}
|
|
||||||
|
|
||||||
public double DesiredLeft
|
// must be large enough to fit max zoom
|
||||||
{
|
Width = 500;
|
||||||
get
|
Height = 500;
|
||||||
{
|
|
||||||
return _left;
|
|
||||||
}
|
|
||||||
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_left = value;
|
|
||||||
NotifyPropertyChanged(nameof(DesiredLeft));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public double DesiredTop
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return _top;
|
|
||||||
}
|
|
||||||
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_top = value;
|
|
||||||
NotifyPropertyChanged(nameof(DesiredTop));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public event PropertyChangedEventHandler PropertyChanged;
|
|
||||||
|
|
||||||
private void NotifyPropertyChanged(string propertyName)
|
|
||||||
{
|
|
||||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user