Avoid window opening between two screens - c

I am writing a Photoshop plugin for Windows and want to place the plugin dialog in the center of the main window.
This is my code:
void centre_window(HWND hwnd){
RECT rs, rd;
HWND hw = GetParent(hwnd); // GetDesktopWindow();
if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
MoveWindow(hwnd,(rs.right + rs.left + rd.left - rd.right) / 2,
(rs.bottom + rs.top + rd.top - rd.bottom) / 3,
rd.right - rd.left, rd.bottom - rd.top, TRUE);
}
So far, it works. But there is one flaw: If the main window is spread across two screens, then my window is between both screens.
I looked at other Photoshop plugins and they handle it like this:
Place the window at the main window center
If it would be between two screens, choose one of these two and place it at the border of the screen
How can I do that?

With the helpful hint of #IInspectable , I wrote following code:
void _doMonitorAdjustments(LPRECT rcPlugin) {
RECT rcMonitorWork;
HMONITOR hMonitor;
MONITORINFO grMonitorInfo = { sizeof(grMonitorInfo) };
int nXAdjust = 0, nYAdjust = 0, nPluginWidth, nPluginHeight, nMonitorWorkWidth, nMonitorWorkHeight;
hMonitor = MonitorFromRect(rcPlugin, MONITOR_DEFAULTTONEAREST);
if (hMonitor == NULL) return;
if (!GetMonitorInfoA(hMonitor, &grMonitorInfo)) return;
rcMonitorWork = grMonitorInfo.rcWork;
// Don't let the window exit the left/right borders of the monitor
nPluginWidth = rcPlugin->right - rcPlugin->left;
nMonitorWorkWidth = rcMonitorWork.right - rcMonitorWork.left;
if (nPluginWidth > nMonitorWorkWidth) {
// Window larger than screen width. Decrease the width!
rcPlugin->left = rcMonitorWork.left;
rcPlugin->right = rcMonitorWork.right;
}
else if (rcPlugin->left < rcMonitorWork.left) {
nXAdjust = rcMonitorWork.left - rcPlugin->left;
}
else if (rcPlugin->right > rcMonitorWork.right) {
nXAdjust = rcMonitorWork.right - rcPlugin->right;
}
// Don't let the window exit the top/bottom borders of the monitor
nPluginHeight = rcPlugin->bottom - rcPlugin->top;
nMonitorWorkHeight = rcMonitorWork.bottom - rcMonitorWork.top;
if (nPluginHeight > nMonitorWorkHeight) {
// Window larger than screen height. Decrease the height!
rcPlugin->top = rcMonitorWork.top;
rcPlugin->bottom = rcMonitorWork.bottom;
}
else if (rcPlugin->top < rcMonitorWork.top) {
nYAdjust = rcMonitorWork.top - rcPlugin->top;
}
else if (rcPlugin->bottom > rcMonitorWork.bottom) {
nYAdjust = rcMonitorWork.bottom - rcPlugin->bottom;
}
OffsetRect(rcPlugin, nXAdjust, nYAdjust);
}
/*
* Centers a window to the center of its parent form but avoids
* being spread across two screens.
*/
void centre_window(HWND hwnd) {
RECT rcParent, rcWindowOriginal, rcPlugin;
HWND hParent;
hParent = GetParent(hwnd);
if (hParent == NULL) hParent = GetDesktopWindow();
if (!GetWindowRect(hParent, &rcParent)) return;
if (!GetWindowRect(hwnd, &rcWindowOriginal)) return;
rcPlugin.left =
rcParent.left
+ (rcParent.right - rcParent.left) / 2
- (rcWindowOriginal.right - rcWindowOriginal.left) / 2;
rcPlugin.top =
rcParent.top
+ (rcParent.bottom - rcParent.top) / 2
- (rcWindowOriginal.bottom - rcWindowOriginal.top) / 2;
rcPlugin.right =
rcPlugin.left + rcWindowOriginal.right - rcWindowOriginal.left;
rcPlugin.bottom =
rcPlugin.top + rcWindowOriginal.bottom - rcWindowOriginal.top;
// Avoid that the window is spread across two screens
_doMonitorAdjustments(&rcPlugin);
MoveWindow(hwnd,
rcPlugin.left,
rcPlugin.top,
/*width=*/rcPlugin.right - rcPlugin.left,
/*height=*/rcPlugin.bottom - rcPlugin.top,
TRUE);
}

Related

Check transformation values before applying the transform

I'm using a render transform to pan an image. I need the panning to stop at a certain point, so I'm checking the bounds of the image. The problem is that the image can be panned slightly beyond the bounds, because I'm checking the bounding box before the transform has been applied. Is there a way of applying the transform to the bounding box, then checking if this is within the valid range?
Thanks for any help
private void RecalculatePositions(MouseEventArgs e)
{
TranslateTransform tt1 = (TranslateTransform)((TransformGroup)this.image1.RenderTransform).Children.First(tr => tr is TranslateTransform);
TranslateTransform tt2 = (TranslateTransform)((TransformGroup)this.image2.RenderTransform).Children.First(tr => tr is TranslateTransform);
Vector vector = this._startPoint - e.GetPosition(this.grid);
double offsetX = this._originPoint.X - vector.X;
double offsetY = this._originPoint.Y - vector.Y;
Rect image1Bounds = this.image1.TransformToAncestor(this.viewBox1).TransformBounds(new Rect(this.image1.RenderSize));
if (vector.X < 0)
{
//Moving image right
if (offsetX <= 0)
{
tt1.X = offsetX;
tt2.X = offsetX;
}
}
else
{
//Moving image left
if (image1Bounds.Right >= 0)
{
tt1.X = offsetX;
tt2.X = offsetX;
}
}
if (vector.Y < 0)
{
//Moving image down
if (offsetY <= 0)
{
tt1.Y = offsetY;
tt2.Y = offsetY;
}
}
else
{
//Moving image up
if (image1Bounds.Bottom >= this.ActualHeight)
{
tt1.Y = offsetY;
tt2.Y = offsetY;
}
}
}

wpf radgridview How to get vertical offset of GridViewRow?

I have row index of target GridViewRow, and ScrollChanged handler callback of GridViewScrollViewer inside RadGridView. I want to get vertical offset of target row in viewport, while scrolling, if that row leaves viewport from top, return 0; if leaves from bottom, return ActualHeight of RadGridView; else, get a value same as Top offest of target row and viewport.
Here is function to get vertival offset, it will be incorrect after scrollbar moved:
//const int indicator_size = 3;
//RadGridView grid;
Func rowIndicatorOffset = (rowIndex) =>
{
double offset = (rowIndex + 1.5) * this.grid.RowHeight;
if (offset > this.grid.ActualHeight)
{
return this.grid.ActualHeight - indicator_size;
}
return offset;
};
When change vertical scroll bar offset, I want that indicator(red one in the picture above) keep same Y position with target row:
//System.Windows.Controls.ScrollChangedEventArgs offsetValues (from ScrollChanged event)
double fixRatio = (this.grid.ActualHeight - this.offsetValues.VerticalOffset) / this.grid.ActualHeight;
double offset = (rowIndex + 1.5) * this.grid.RowHeight * fixRatio;
the code above works not well as expected apparently, so what's the answer?
I got it wrong about Viewport, the diagram below may figure it out.
diagram
//const int indicator_size = 3;
//RadGridView grid;
Func<int, double> rowIndicatorOffset = (rowIndex) =>
{
double targetOffset = (rowIndex + 1.5) * this.grid.RowHeight;
double viewportOffset = this.offsetValues.VerticalOffset;
double offset = targetOffset - viewportOffset;
if (offset > this.grid.ActualHeight)
{
return this.grid.ActualHeight - indicator_size;
}
else if (offset < 0)
{
return 0;
}
else
{
return offset;
}
};

How can i capture screen region while not focusing wpf app?

I have a WPf application that monitors a certain region on screen keeping track of the count of pixels with certain colors.
What i want is the actual capturing of the region and reading of values to happen in another thread, but i cant seem to wrap my head around how to actuale grab the region while i focus another window.
Here is some code:
// Colors to keep track of
var fooColor = (System.Windows.Media.Color)System.Windows.Media.ColorConverter.ConvertFromString("#FF6ebfe2");
var barColor = (System.Windows.Media.Color)System.Windows.Media.ColorConverter.ConvertFromString("#FFe67e6e");
// previewWindow : Rectangle Control
var X = (int)previewWindow.PointToScreen(new System.Windows.Point(0, 0)).X;
var Y = (int)previewWindow.PointToScreen(new System.Windows.Point(0, 0)).Y;
Rectangle rect = new Rectangle(X, Y, (int)previewWindow.ActualWidth, (int)previewWindow.ActualHeight);
Bitmap bmp = new Bitmap(rect.Width, rect.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
using (var screenBmp = new Bitmap(
rect.Width,
rect.Height,
System.Drawing.Imaging.PixelFormat.Format32bppArgb)
)
{
using (var bmpGraphics = Graphics.FromImage(screenBmp))
{
bmpGraphics.CopyFromScreen(X, Y, 0, 0, screenBmp.Size);
BitmapSource bmpSource = Imaging.CreateBitmapSourceFromHBitmap(
screenBmp.GetHbitmap(),
IntPtr.Zero,
Int32Rect.Empty,
BitmapSizeOptions.FromEmptyOptions());
var red = 0;
var blue = 0;
for (int i = 0; i < rect.Width; i++)
{
for (int x = 0; x < rect.Height; x++)
{
System.Windows.Media.Color color = GetPixelColor(bmpSource, i, x);
if (color == fooColor)
{
blue++;
}
else if (color == barColor)
{
red++;
}
}
}
txtbar.Text = red.ToString();
txtfoo.Text = blue.ToString();
if (red < 150)
{
ProcessBar();
}
if (blue < 150)
{
ProcessFoo();
}
}
}

WPF Resize UserControl with aspect ratio

I have a UserControl and that UserControl has to be resized with aspect ratio.
That means: width:height = 2:1.
Currently I am using this code:
protected override Size ArrangeOverride(Size arrangeBounds)
{
if (ActualWidth == 0 || ActualHeight == 0) return arrangeBounds;
base.ArrangeOverride(arrangeBounds);
double ratio = 2;
if (Parent != null)
{
var size = new Size(arrangeBounds.Height * ratio, arrangeBounds.Height);
double containerWidth = ((FrameworkElement)Parent).ActualWidth;
if (containerWidth < size.Width)
{
double newHeight = arrangeBounds.Height * (containerWidth / size.Width);
canvas.Width = newHeight * ratio;
canvas.Height = newHeight;
}
else
{
canvas.Width = size.Height * ratio;
canvas.Height = size.Height;
}
}
return arrangeBounds;
}
But it is not really working. That means it works but not every time. If I max. the window it sometimes does not get resized, so its a bit "random" if the control gets resized. So if someone would have a better solution if would be very nice.
The most straightforward solution would be to Bind the Height directly to the Width, through a value converter.
It's a bit late, but I recently came across the same problem and since I did not find a good solution I decided to write my own layout control/decorator and wrote a blog post about it here:
http://coding4life.wordpress.com/2012/10/15/wpf-resize-maintain-aspect-ratio/
Basically my solution was to overwrite both MeasureOverride and ArrangeOverride. So far it works very nicely in all of the common containers and I didn't encounter any problems like you described.
I recommend reading the post, where you find a working decorator control, but the most important methods are these:
protected override Size MeasureOverride(Size constraint)
{
if (Child != null)
{
constraint = SizeToRatio(constraint, false);
Child.Measure(constraint);
if(double.IsInfinity(constraint.Width)
|| double.IsInfinity(constraint.Height))
{
return SizeToRatio(Child.DesiredSize, true);
}
return constraint;
}
// we don't have a child, so we don't need any space
return new Size(0, 0);
}
protected override Size ArrangeOverride(Size arrangeSize)
{
if (Child != null)
{
var newSize = SizeToRatio(arrangeSize, false);
double widthDelta = arrangeSize.Width - newSize.Width;
double heightDelta = arrangeSize.Height - newSize.Height;
double top = 0;
double left = 0;
if (!double.IsNaN(widthDelta)
&& !double.IsInfinity(widthDelta))
{
left = widthDelta/2;
}
if (!double.IsNaN(heightDelta)
&& !double.IsInfinity(heightDelta))
{
top = heightDelta/2;
}
var finalRect = new Rect(new Point(left, top), newSize);
Child.Arrange(finalRect);
}
return arrangeSize;
}
public Size SizeToRatio(Size size, bool expand)
{
double ratio = AspectRatio;
double height = size.Width / ratio;
double width = size.Height * ratio;
if (expand)
{
width = Math.Max(width, size.Width);
height = Math.Max(height, size.Height);
}
else
{
width = Math.Min(width, size.Width);
height = Math.Min(height, size.Height);
}
return new Size(width, height);
}
I hope it helps someone!
Wrap your Control with a ViewBox and set the ViewBox.Stretch to Uniform. You can also restrict on MaxWidth and MaxHeight that way.
More Info on the Stretch-Enumeration # MSDN
How to apply Stretch # MSDN

How to calculate translate while zooming in WPF/Surface

I'm doing a surface app where I need to scale my scene (zoom) while the user scales with their fingers (i.e pinching)
Currently I have it working ok, but the issue is that I need to zoom in on the center point between the users fingers.
I have the point, but the maths behind the translation is hard to grasp.
When I apply a ScaleTransform to my Canvas scene, it zooms in on the top left of the canvas, i need it to zoom in on the center point of my pinch gesture (which, again, I do have).
How would the maths for the translation work to keep the zoom to appear to zoom in on the center point of the gesture?
Edit:
This is basically what I have:
void Affine2DManipulationDelta(object sender, Affine2DOperationDeltaEventArgs e)
{
//apply zoom and translate
if (e.ScaleDelta != 1)
ApplyZoom(e);
else if (e.Delta.Length != 0)
ApplyTranslation(e);
}
private void ApplyZoom(Affine2DOperationDeltaEventArgs e)
{
//scale
var newScale = _zoomTransform.ScaleX * e.ScaleDelta;
newScale = GetCappedZoomLevel(newScale);
_zoomTransform.ScaleX = newScale;
_zoomTransform.ScaleY = newScale;
}
private void ApplyTranslation(Affine2DOperationDeltaEventArgs e)
{
var xDiff = e.ManipulationOrigin.X - _screenStartPoint.X;
var yDiff = e.ManipulationOrigin.Y - _screenStartPoint.Y;
var translateX = xDiff + _startOffset.X;
var translateY = yDiff + _startOffset.Y;
//bounds testing to limit translation
var rect = new Rect(0.0, 0.0, ZoomCanvas.RenderSize.Width, ZoomCanvas.RenderSize.Height);
Rect bounds = ZoomCanvas.TransformToAncestor(MainViewportCanvas).TransformBounds(rect);
if (CanTranslateX(translateX, bounds))
_translateTransform.X = translateX;
if (CanTranslateY(translateY, bounds))
_translateTransform.Y = translateY;
}
Pretty basic really, but it works to a point...
_zoomTransform is a ScaleTransform, and _translateTransform is a TranslateTransform
MainViewport is a canvas that contains ZoomCanvas which is the canvas that I apply the transforms to.
Here is my implementation that ended up working just fine
private void ApplyZoom(Affine2DOperationDeltaEventArgs e)
{
//scale
var newScale = _zoomTransform.ScaleX * e.ScaleDelta;
newScale = GetCappedZoomLevel(newScale);
var pinchPoint = e.ManipulationOrigin;
DoZoom(newScale, _transformGroup.Inverse.Transform(pinchPoint), pinchPoint);
}
private void DoZoom(double newScale, Point pinchPosition, Point physicalPosition)
{
_translateTransform.X = -1 * (pinchPosition.X * newScale - physicalPosition.X);
_translateTransform.Y = -1 * (pinchPosition.Y * newScale - physicalPosition.Y);
_zoomTransform.ScaleX = newScale;
_zoomTransform.ScaleY = newScale;
}
I had to do exactly this myself. Basically, a Surface control that can host arbitrary content and allow the user to pan and zoom (using gestures). The handler for my maniplation processor's Affine2DManipulationDelta is shown below. Hopefully it's fairly self-explanatory and gets you where you need to be.
private void OnManipulationDelta(object sender, Affine2DOperationDeltaEventArgs e)
{
Debug.Assert(_scrollViewer != null);
if (CanScale && _scrollViewer != null && e.ScaleDelta != 1)
{
var scrollViewerContent = _scrollViewer.Content as UIElement;
//can't do anything if the content isn't present and isn't a UIElement
if (scrollViewerContent != null)
{
var newScale = Scale * e.ScaleDelta;
newScale = Math.Max(MinScale, newScale);
newScale = Math.Min(MaxScale, newScale);
var origin = e.ManipulationOrigin;
var pointInContent = _scrollViewer.TranslatePoint(origin, scrollViewerContent);
var deltaScale = newScale - Scale;
//width and height changes across the whole image
var deltaWidth = deltaScale * _scrollViewer.ExtentWidth;
var deltaHeight = deltaScale * _scrollViewer.ExtentHeight;
//width and height changes relative to the point in the scroll viewer's content
deltaWidth = (pointInContent.X / _scrollViewer.ExtentWidth) * deltaWidth;
deltaHeight = (pointInContent.Y / _scrollViewer.ExtentHeight) * deltaHeight;
_offset = Vector.Add(_offset, new Vector(deltaWidth, deltaHeight));
var centerPoint = new Point(origin.X + deltaWidth, origin.Y + deltaHeight);
centerPoint.Offset(_offset.X, _offset.Y);
Scale = newScale;
HorizontalOffset = _scrollViewer.HorizontalOffset + deltaWidth;
VerticalOffset = _scrollViewer.VerticalOffset + deltaHeight;
}
}
else if (CanPan && e.Delta.Length != 0)
{
var newHorizontalOffset = _scrollViewer.HorizontalOffset + (e.Delta.X * -1);
var newVerticalOffset = _scrollViewer.VerticalOffset + (e.Delta.Y * -1);
newHorizontalOffset = Math.Max(0, newHorizontalOffset);
newVerticalOffset = Math.Max(0, newVerticalOffset);
HorizontalOffset = newHorizontalOffset;
VerticalOffset = newVerticalOffset;
}
}

Resources