Got a custom wpf window (WindowStyle=None, AllowTransparancy=true) and wondering how to get Windows edge resize features to work.. you know when draging window and mouse touches left, right or top edge of screen (even corners in W10).
Tried looking into WM notification but none of them seems to be what im looking for..
To clearify, not ordinary window resizeing.. but draging to screen edge and letting Windows resize it to half/full/quarter (think its called Aero Snap). And I can do it with ordinary resize calls but that doesnt show the transparent preview window or drop animation on mouse when touches edge.
Thanks
Step 1
Create a Style (in <Window1.Resources>) for rectangles as the Grip area around the window:
<Style x:Key="RectBorderStyle" TargetType="Rectangle">
<Setter Property="Focusable" Value="False" />
<Setter Property="Fill" Value="Transparent" />
<Setter Property="Tag" Value="{Binding RelativeSource={RelativeSource AncestorType=Window}}" />
<EventSetter Event="MouseLeftButtonDown" Handler="Resize_Init"/>
<EventSetter Event="MouseLeftButtonUp" Handler="Resize_End"/>
<EventSetter Event="MouseMove" Handler="Resizeing_Form"/>
</Style>
Step 2
Add these styled rectangles to your window. (You can add them inside a simple grid within your window)
<Rectangle x:Name="leftSizeGrip"
Width="7"
HorizontalAlignment="Left"
Cursor="SizeWE"
Style="{StaticResource RectBorderStyle}" />
<Rectangle x:Name="rightSizeGrip"
Width="7"
HorizontalAlignment="Right"
Cursor="SizeWE"
Style="{StaticResource RectBorderStyle}" />
<Rectangle x:Name="topSizeGrip"
Height="7"
VerticalAlignment="Top"
Cursor="SizeNS"
Style="{StaticResource RectBorderStyle}" />
<Rectangle x:Name="bottomSizeGrip"
Height="7"
VerticalAlignment="Bottom"
Cursor="SizeNS"
Style="{StaticResource RectBorderStyle}" />
<!-- Corners -->
<Rectangle Name="topLeftSizeGrip"
Width="7"
Height="7"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Cursor="SizeNWSE"
Style="{StaticResource RectBorderStyle}" />
<Rectangle Name="bottomRightSizeGrip"
Width="7"
Height="7"
HorizontalAlignment="Right"
VerticalAlignment="Bottom"
Cursor="SizeNWSE"
Style="{StaticResource RectBorderStyle}" />
<Rectangle Name="topRightSizeGrip"
Width="7"
Height="7"
HorizontalAlignment="Right"
VerticalAlignment="Top"
Cursor="SizeNESW"
Style="{StaticResource RectBorderStyle}" />
<Rectangle Name="bottomLeftSizeGrip"
Width="7"
Height="7"
HorizontalAlignment="Left"
VerticalAlignment="Bottom"
Cursor="SizeNESW"
Style="{StaticResource RectBorderStyle}" />
Step 3
Add these codes to the code behind of your window (window1.xaml.cs) (or to MyStyle.xaml.cs if you are using a template for window and you have added the 8 rectangles inside the template)
#region ResizeWindows
bool ResizeInProcess = false;
private void Resize_Init(object sender, MouseButtonEventArgs e)
{
Rectangle senderRect = sender as Rectangle;
if (senderRect != null)
{
ResizeInProcess = true;
senderRect.CaptureMouse();
}
}
private void Resize_End(object sender, MouseButtonEventArgs e)
{
Rectangle senderRect = sender as Rectangle;
if (senderRect != null)
{
ResizeInProcess = false; ;
senderRect.ReleaseMouseCapture();
}
}
private void Resizeing_Form(object sender, MouseEventArgs e)
{
if (ResizeInProcess)
{
Rectangle senderRect = sender as Rectangle;
Window mainWindow = senderRect.Tag as Window;
if (senderRect != null)
{
double width = e.GetPosition(mainWindow).X;
double height = e.GetPosition(mainWindow).Y;
senderRect.CaptureMouse();
if (senderRect.Name.ToLower().Contains("right"))
{
width += 5;
if (width > 0)
mainWindow.Width = width;
}
if (senderRect.Name.ToLower().Contains("left"))
{
width -= 5;
mainWindow.Left += width;
width = mainWindow.Width - width;
if (width > 0)
{
mainWindow.Width = width;
}
}
if (senderRect.Name.ToLower().Contains("bottom"))
{
height += 5;
if (height > 0)
mainWindow.Height = height;
}
if (senderRect.Name.ToLower().Contains("top"))
{
height -= 5;
mainWindow.Top += height;
height = mainWindow.Height - height;
if (height > 0)
{
mainWindow.Height = height;
}
}
}
}
}
#endregion
Step 4
Press F5 and enjoy!
note:
these 8 rectangles are transparent. if you can't make it properly work, just change the Fill value of the style to Red to see where are they positioned.
Another note:
When maximized you might want to disable all these rectangles. well the easiest way is to handle the WindowStateChanged event and disable the grid containing them.
Answer of Bizhan is great! But he made a small mistake. It's solution does not take into account the maximum and minimum window sizes. Because of this, instead of changing the window, it shifts. It work:
private void Resizeing_Form(object sender, MouseEventArgs e)
{
if (ResizeInProcess)
{
double temp = 0;
Rectangle senderRect = sender as Rectangle;
Window mainWindow = senderRect.Tag as Window;
if (senderRect != null)
{
double width = e.GetPosition(mainWindow).X;
double height = e.GetPosition(mainWindow).Y;
senderRect.CaptureMouse();
if (senderRect.Name.Contains("right", StringComparison.OrdinalIgnoreCase))
{
width += 5;
if (width > 0)
mainWindow.Width = width;
}
if (senderRect.Name.Contains("left", StringComparison.OrdinalIgnoreCase))
{
width -= 5;
temp = mainWindow.Width - width;
if ((temp > mainWindow.MinWidth) && (temp < mainWindow.MaxWidth))
{
mainWindow.Width = temp;
mainWindow.Left += width;
}
}
if (senderRect.Name.Contains("bottom", StringComparison.OrdinalIgnoreCase))
{
height += 5;
if (height > 0)
mainWindow.Height = height;
}
if (senderRect.Name.ToLower().Contains("top", StringComparison.OrdinalIgnoreCase))
{
height -= 5;
temp = mainWindow.Height - height;
if ((temp > mainWindow.MinHeight) && (temp < mainWindow.MaxHeight))
{
mainWindow.Height = temp;
mainWindow.Top += height;
}
}
}
}
}
And for #GMIKE answer to work you need this String extension method (taken from here) :
public static class StringExtensions
{
public static bool Contains(this string source, string toCheck, StringComparison comp)
{
return source?.IndexOf(toCheck, comp) >= 0;
}
}
Related
I know there are several posts on stack and others websites, but it seems I still make something wrong. When I zoom with MouseWheel event, the zoom is always not centered, but the left side of my canvas always stays on the left on my ViewBox, so when I zoom in, I only can see the left of my canvas.
XAML code :
<Grid x:Name="MainGrid">
<Viewbox x:Name="ViewBoxDessin" Stretch="None" HorizontalAlignment="Center" VerticalAlignment="Center">
<Canvas x:Name="monDessin" Background="WhiteSmoke" MouseWheel="monDessin_MouseWheel">
<Canvas.LayoutTransform>
<ScaleTransform x:Name="st" ScaleX="1" ScaleY="-1" CenterX=".5" CenterY=".5" />
</Canvas.LayoutTransform>
</Canvas>
</Viewbox>
<Viewbox x:Name="ViewBoxDessin2" Stretch="None">
<Canvas x:Name="monDessin2">
<Canvas.LayoutTransform>
<ScaleTransform x:Name="st2" ScaleX="1" ScaleY="1" CenterX=".5" CenterY=".5" />
</Canvas.LayoutTransform>
</Canvas>
</Viewbox>
</Grid>
Code behind
public AfficheGraphiquePiece()
{
InitializeComponent();
MakeMyDrawing();
ViewBoxDessin.Width = System.Windows.SystemParameters.PrimaryScreenWidth;
ViewBoxDessin.Height = System.Windows.SystemParameters.PrimaryScreenHeight;
double ech_x = monDessin.Width / System.Windows.SystemParameters.PrimaryScreenWidth;
double ech_y = monDessin.Height / System.Windows.SystemParameters.PrimaryScreenHeight;
double ech = Math.Min(ech_x, ech_y);
this.ech_full = ech;
st.ScaleX = ech;
st.ScaleY = -ech;
st2.ScaleX = ech;
st2.ScaleY = ech;
}
private void monDessin_MouseWheel(object sender, MouseWheelEventArgs e)
{
double zoom = e.Delta > 0 ? 1.1 : 0.9;
if(st.ScaleX<this.ech_full*1.1 && zoom<1)
{
st.ScaleX = this.ech_full;
st.ScaleY = -this.ech_full;
}
else
{
st.ScaleX *= zoom;
st.ScaleY *= zoom;
double coor_x = Mouse.GetPosition(monDessin).X;
double coor_y = Mouse.GetPosition(monDessin).Y;
st.CenterX = coor_x;
st.CenterY = coor_y;
}
}
Excuse me, didn't remove some code, and it could make confusion, just replaced it by a function MakeMyDrawing()
Well, after Clemens advise, and help of that link for matrix use, I could do the following :
XAML :
<Grid x:Name="MainGrid">
<Canvas x:Name="monDessin" Background="WhiteSmoke" MouseWheel="monDessin_MouseWheel" MouseLeftButtonDown="image_MouseLeftButtonDown" MouseMove="image_MouseMove" MouseLeftButtonUp="image_MouseLeftButtonUp" MouseLeave="image_MouseLeave" >
<Canvas.RenderTransform >
<MatrixTransform/>
</Canvas.RenderTransform>
</Canvas>
<Canvas x:Name="monDessin2">
<Canvas.RenderTransform >
<MatrixTransform/>
</Canvas.RenderTransform>
</Canvas>
</Grid>
Code behind
public AfficheGraphiquePiece(Repere rep)
{
InitializeComponent();
ClassGraphique monGraphe = new ClassGraphique(monDessin);
ClassGraphique monGraphe2 = new ClassGraphique(monDessin2);
MakeMyDrawing();
double screenWidth = System.Windows.SystemParameters.PrimaryScreenWidth;
double screenHeight = System.Windows.SystemParameters.PrimaryScreenHeight;
double ech_x = screenWidth/ monDessin.Width ;
double ech_y = screenHeight/ monDessin.Height;
double ech = Math.Min(ech_x, ech_y)*0.9;
this.ech_full = ech;
this.echelleNow = this.ech_full;
MatrixTransform maTrans =(MatrixTransform)monDessin.RenderTransform;
var mat = maTrans.Matrix;
mat.ScaleAt(ech, -ech, 0.1* screenWidth, (screenHeight-monDessin.Height*ech)/2-0.1*screenHeight);
MatrixTransform maTrans2 = (MatrixTransform)monDessin2.RenderTransform;
var mat2 = maTrans2.Matrix;
mat2.ScaleAt(ech, ech, 0.1 * screenWidth, screenHeight*ech-((screenHeight - monDessin.Height * ech) / 2 - 0.1 * screenHeight));
maTrans.Matrix = mat;
maTrans2.Matrix = mat2;
}
private void monDessin_MouseWheel(object sender, MouseWheelEventArgs e)
{
try
{
var position = e.GetPosition(monDessin);
MatrixTransform transform = (MatrixTransform)monDessin.RenderTransform;
MatrixTransform transform2 = (MatrixTransform)monDessin2.RenderTransform;
var matrix = transform.Matrix;
var matrix2 = transform2.Matrix;
var scale = e.Delta >= 0 ? 1.1 : (1.0 / 1.1);
this.echelleNow *= scale;
matrix.ScaleAtPrepend(scale, scale, position.X, position.Y);
matrix2.ScaleAtPrepend(scale, scale, position.X,monDessin.Height-position.Y);
monDessin.RenderTransform = new MatrixTransform(matrix);
monDessin2.RenderTransform = new MatrixTransform(matrix2);
}
catch { }
}
Here is a very basic example for zooming and panning a Canvas with fixed initial size. The MatrixTransform in the RenderTransform of the inner Canvas provides the necessary transformations, while the outer Canvas handles mouse input and sets an initial scaling.
<Canvas Background="Transparent"
SizeChanged="ViewportSizeChanged"
MouseLeftButtonDown="ViewportMouseLeftButtonDown"
MouseLeftButtonUp="ViewportMouseLeftButtonUp"
MouseMove="ViewportMouseMove"
MouseWheel="ViewportMouseWheel">
<Canvas x:Name="canvas" Width="1000" Height="600">
<Canvas.RenderTransform>
<MatrixTransform x:Name="transform"/>
</Canvas.RenderTransform>
<Ellipse Fill="Red" Width="100" Height="100" Canvas.Left="100" Canvas.Top="100"/>
<Ellipse Fill="Green" Width="100" Height="100" Canvas.Right="100" Canvas.Bottom="100"/>
</Canvas>
</Canvas>
Code behind:
private Point? mousePos;
private void ViewportSizeChanged(object sender, SizeChangedEventArgs e)
{
((MatrixTransform)canvas.RenderTransform).Matrix = new Matrix(
e.NewSize.Width / canvas.ActualWidth,
0, 0,
e.NewSize.Height / canvas.ActualHeight,
0, 0);
}
private void ViewportMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
var viewport = (UIElement)sender;
viewport.CaptureMouse();
mousePos = e.GetPosition(viewport);
}
private void ViewportMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
((UIElement)sender).ReleaseMouseCapture();
mousePos = null;
}
private void ViewportMouseMove(object sender, MouseEventArgs e)
{
if (mousePos.HasValue)
{
var pos = e.GetPosition((UIElement)sender);
var matrix = transform.Matrix;
matrix.Translate(pos.X - mousePos.Value.X, pos.Y - mousePos.Value.Y);
transform.Matrix = matrix;
mousePos = pos;
}
}
private void ViewportMouseWheel(object sender, MouseWheelEventArgs e)
{
var pos = e.GetPosition((UIElement)sender);
var matrix = transform.Matrix;
var scale = e.Delta > 0 ? 1.1 : 1 / 1.1;
matrix.ScaleAt(scale, scale, pos.X, pos.Y);
transform.Matrix = matrix;
}
I'm experimenting with the concept of drawing grid lines over a control and was wondering what adjustments I might need to make to make this actually work. I found some code on another post that enables grid lines to be drawn OnRender over a canvas. Here's what that looks like:
public class MyCanvas : Canvas
{
public bool IsGridVisible = true;
protected override void OnRender(System.Windows.Media.DrawingContext dc)
{
base.OnRender(dc);
if (IsGridVisible)
{
// Draw GridLines
Pen pen = new Pen(Brushes.Black, 1);
pen.DashStyle = DashStyles.Solid;
for (double x = 0; x < this.ActualWidth; x += 2)
{
dc.DrawLine(pen, new Point(x, 0), new Point(x, this.ActualHeight));
}
for (double y = 0; y < this.ActualHeight; y += 2)
{
dc.DrawLine(pen, new Point(0, y), new Point(this.ActualWidth, y));
}
}
}
public MyCanvas()
{
DefaultStyleKey = typeof(MyCanvas);
}
}
This part: y += 2 indicates how many other pixels/points to wait before drawing next line, though I am uncertain of it's correctness.
Here's the xaml:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="30"/>
</Grid.RowDefinitions>
<ScrollViewer>
<local:MyCanvas>
<local:MyCanvas.LayoutTransform>
<ScaleTransform ScaleX="{Binding ElementName=Slider, Path=Value}" ScaleY="{Binding ElementName=Slider, Path=Value}"/>
</local:MyCanvas.LayoutTransform>
<Image Canvas.Top="2" Canvas.Left="2" Source="C:\Users\Me\Pictures\nyan-wallpaper2.jpg" Width="325" RenderOptions.BitmapScalingMode="NearestNeighbor"/>
</local:MyCanvas>
</ScrollViewer>
<Slider x:Name="Slider" Maximum="500" Grid.Row="1" Value="1"/>
</Grid>
Here are screenshots of what the above results in.
As you can see, the grid lines change in size as you zoom and the lines themselves do not snap around each individual pixel. I highlighted an example pixel in red to show how small the lines should be versus how they actually are.
I read that the thickness of the pen should be divided by the scale value, however, I tested this by replacing Pen pen = new Pen(Brushes.Black, 1); with Pen pen = new Pen(Brushes.Black, 1 / 3); and set the ScaleX and ScaleY of MyCanvas to 3. At that point, no lines showed at all.
Any help at all is immensely valued!
Got it working like this for anyone curious:
MainWindow.xaml.cs
namespace Test
{
public class MyCanvas : Canvas
{
public bool IsGridVisible = false;
#region Dependency Properties
public static DependencyProperty ZoomValueProperty = DependencyProperty.Register("ZoomValue", typeof(double), typeof(MyCanvas), new FrameworkPropertyMetadata(1d, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnZoomValueChanged));
public double ZoomValue
{
get
{
return (double)GetValue(ZoomValueProperty);
}
set
{
SetValue(ZoomValueProperty, value);
}
}
private static void OnZoomValueChanged(DependencyObject Object, DependencyPropertyChangedEventArgs e)
{
}
#endregion
protected override void OnRender(System.Windows.Media.DrawingContext dc)
{
base.OnRender(dc);
IsGridVisible = ZoomValue > 4.75 ? true : false;
if (IsGridVisible)
{
// Draw GridLines
Pen pen = new Pen(Brushes.Black, 1 / ZoomValue);
pen.DashStyle = DashStyles.Solid;
for (double x = 0; x < this.ActualWidth; x += 1)
{
dc.DrawLine(pen, new Point(x, 0), new Point(x, this.ActualHeight));
}
for (double y = 0; y < this.ActualHeight; y += 1)
{
dc.DrawLine(pen, new Point(0, y), new Point(this.ActualWidth, y));
}
}
}
public MyCanvas()
{
DefaultStyleKey = typeof(MyCanvas);
}
}
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private WriteableBitmap bitmap = new WriteableBitmap(500, 500, 96d, 96d, PixelFormats.Bgr24, null);
private void Button_Click(object sender, RoutedEventArgs e)
{
int size = 1;
Random rnd = new Random(DateTime.Now.Millisecond);
bitmap.Lock(); // Lock() and Unlock() could be moved to the DrawRectangle() method. Just do some performance tests.
for (int y = 0; y < 500; y++)
{
for (int x = 0; x < 500; x++)
{
byte colR = (byte)rnd.Next(256);
byte colG = (byte)rnd.Next(256);
byte colB = (byte)rnd.Next(256);
DrawRectangle(bitmap, size * x, size * y, size, size, Color.FromRgb(colR, colG, colB));
}
}
bitmap.Unlock(); // Lock() and Unlock() could be moved to the DrawRectangle() method. Just do some performance tests.
Image.Source = bitmap; // This should be done only once
}
public void DrawRectangle(WriteableBitmap writeableBitmap, int left, int top, int width, int height, Color color)
{
// Compute the pixel's color
int colorData = color.R << 16; // R
colorData |= color.G << 8; // G
colorData |= color.B << 0; // B
int bpp = writeableBitmap.Format.BitsPerPixel / 8;
unsafe
{
for (int y = 0; y < height; y++)
{
// Get a pointer to the back buffer
int pBackBuffer = (int)writeableBitmap.BackBuffer;
// Find the address of the pixel to draw
pBackBuffer += (top + y) * writeableBitmap.BackBufferStride;
pBackBuffer += left * bpp;
for (int x = 0; x < width; x++)
{
// Assign the color data to the pixel
*((int*)pBackBuffer) = colorData;
// Increment the address of the pixel to draw
pBackBuffer += bpp;
}
}
}
writeableBitmap.AddDirtyRect(new Int32Rect(left, top, width, height));
}
}
}
MainWindow.xaml
<Window x:Class="Test.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Test"
mc:Ignorable="d"
Title="MainWindow"
Height="Auto"
Width="Auto"
WindowStartupLocation="CenterScreen"
WindowState="Maximized">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="30"/>
</Grid.RowDefinitions>
<ScrollViewer>
<local:MyCanvas ZoomValue="{Binding ElementName=ScaleTransform, Path=ScaleX}">
<local:MyCanvas.LayoutTransform>
<ScaleTransform x:Name="ScaleTransform" ScaleX="{Binding ElementName=Slider, Path=Value}" ScaleY="{Binding ElementName=Slider, Path=Value}"/>
</local:MyCanvas.LayoutTransform>
<Image Canvas.Top="1" Canvas.Left="1" x:Name="Image" RenderOptions.BitmapScalingMode="NearestNeighbor"/>
</local:MyCanvas>
</ScrollViewer>
<StackPanel Grid.Row="1" Orientation="Horizontal">
<Slider x:Name="Slider" Maximum="100" Minimum="0.5" Value="1" Width="200"/>
<Button Click="Button_Click" Content="Click Me!"/>
</StackPanel>
</Grid>
</Window>
We generate a bitmap with random colored pixels and then render the grid lines only if zoomed up close. Performance-wise, this is actually better than expected. I should note, though, that if you attempt to zoom below 50%, the app crashes. Not sure if it's an issue with the grid lines being drawn at a minute size (IsGridVisible = true where ZoomValue < 0.5) or with the bitmap being generated. Either way, cheers!
Update
Didn't realize the grid lines are still behind the contents of the canvas. Haven't worked out a solution for that yet...
Update 2
Replace:
<local:MyCanvas ZoomValue="{Binding ElementName=ScaleTransform, Path=ScaleX}">
<local:MyCanvas.LayoutTransform>
<ScaleTransform x:Name="ScaleTransform" ScaleX="{Binding ElementName=Slider, Path=Value}" ScaleY="{Binding ElementName=Slider, Path=Value}"/>
</local:MyCanvas.LayoutTransform>
<Image Canvas.Top="1" Canvas.Left="1" x:Name="Image" RenderOptions.BitmapScalingMode="NearestNeighbor"/>
</local:MyCanvas>
With:
<Grid>
<Canvas>
<Canvas.LayoutTransform>
<ScaleTransform ScaleX="{Binding ElementName=Slider, Path=Value}" ScaleY="{Binding ElementName=Slider, Path=Value}"/>
</Canvas.LayoutTransform>
<Image Canvas.Top="5" Canvas.Left="5" x:Name="Image" RenderOptions.BitmapScalingMode="NearestNeighbor"/>
</Canvas>
<local:MyGrid ZoomValue="{Binding ElementName=ScaleTransform, Path=ScaleX}">
<local:MyGrid.LayoutTransform>
<ScaleTransform x:Name="ScaleTransform" ScaleX="{Binding ElementName=Slider, Path=Value}" ScaleY="{Binding ElementName=Slider, Path=Value}"/>
</local:MyGrid.LayoutTransform>
</local:MyGrid>
</Grid>
I believe another boost in performance as we are utilizing a simpler control to display the grid lines, plus, the grid lines can be placed either below or above desired controls.
Update 3
I have decided to post my latest solution, which is significantly more efficient and can all be done in XAML:
<Grid>
<Grid.Background>
<DrawingBrush Viewport="0,0,5,5" ViewportUnits="Absolute" TileMode="Tile">
<DrawingBrush.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing Geometry="M-.5,0 L50,0 M0,10 L50,10 M0,20 L50,20 M0,30 L50,30 M0,40 L50,40 M0,0 L0,50 M10,0 L10,50 M20,0 L20,50 M30,0 L30,50 M40,0 L40,50">
<GeometryDrawing.Pen>
<Pen Thickness="1" Brush="Black" />
</GeometryDrawing.Pen>
</GeometryDrawing>
</DrawingGroup.Children>
</DrawingGroup>
</DrawingBrush.Drawing>
</DrawingBrush>
</Grid.Background>
</Grid>
I have this peculiar problem. I am having a user control . I am making an app for Windows 8.1 where I would choose an image from my Picture gallery. The image would open in my app with Stretch is Uniform and Horizontal And vertical alignment to center.
My user control will appear where I tap on the image. Now the problem is , when the image Stretch was none , I was able to magnify the correct region (around my click) , but now when I make it Stretch to Uniform and Set the horizontal and vertical Alignment to Center , I am getting other pixel information in my user control.
I want to know how to fix it.Any how , the images can be of 2*Full HD also or they can be HD or even less.
Secondly , I want to know the boundaries of the image . With boundaries I want to say that , my user control shouldnt go above the boundaries of the image .
How to implement that. If my code is needed , I would paste it , If required.
Have this video for reference . This is what I have to develop ! I have the user control ready and I am getting exact pixels for Stretch=NONE , and no Horizontal And Vertical Alignment set.
This is my code for my app
I believe the issue is with how you use the control, rather than the image. If you avoid doing the bitmap cropping and replacing, it would speed up dramatically and likely work for all stretch types.
I've modified the source to show this - removing the Cropping completely. If you need cropping for other reasons, you should consider using the unsafe keyword (and property setting to allow) in order to dramatically speed up its use.
Also, to avoid the lagging/jumping upward, I added IsHitTestVisible="False" so that your delta wouldn't be interrupted by hovering over your image.
I see you have the 45-degree code already - since it wasn't in your source, I only added an example of 90 degree rotation when you get to the sides - so you can see how to set a RenderTransformOrigin point.
MainPage.xaml:
<Page x:Name="page1"
x:Class="controlMagnifier.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:controlMagnifier"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid x:Name="ParentGrid" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" PointerReleased="ParentGrid_OnPointerReleased" >
<Canvas x:Name="InkPresenter" Height="auto" Width="auto">
<Image Stretch="Uniform" x:Name="image2" >
<Image.Source >
<BitmapImage UriSource="/Assets/wallpaper.jpg" />
</Image.Source>
</Image>
</Canvas>
<local:MagnifierUsercontrol x:Name="MagnifyTip" Visibility="Collapsed" ManipulationMode="All"
IsHitTestVisible="False" Height="227" Width="171"
VerticalContentAlignment="Bottom" HorizontalContentAlignment="Center">
</local:MagnifierUsercontrol>
</Grid>
</Page>
MainPage.xaml.cs:
using System;
using Windows.Foundation;
using Windows.Storage;
using Windows.UI.Input;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Media.Imaging;
namespace controlMagnifier
{
public sealed partial class MainPage : Page
{
public const int XAxis = 200;
public const int YAxis = 435;
private readonly RotateTransform myRotateTransform = new RotateTransform {CenterX = 0.5, CenterY = 1};
private readonly ScaleTransform myScaleTransform = new ScaleTransform {ScaleX = 1, ScaleY = 1};
private readonly TransformGroup myTransformGroup = new TransformGroup();
private readonly TranslateTransform myTranslateTransform = new TranslateTransform();
public WriteableBitmap CurrentBitmapObj, CurrentCroppedImage = null;
public Point currentContactPt, GridPoint;
public Thickness margin;
public PointerPoint pt;
public double xValue, yValue;
public MainPage()
{
InitializeComponent();
ParentGrid.Holding += Grid_Holding;
image2.PointerMoved += InkCanvas_PointerMoved;
image2.PointerReleased += ParentGrid_OnPointerReleased;
margin = MagnifyTip.Margin;
image2.CacheMode = new BitmapCache();
myTransformGroup.Children.Add(myScaleTransform);
myTransformGroup.Children.Add(myRotateTransform);
myTransformGroup.Children.Add(myTranslateTransform);
MagnifyTip.RenderTransformOrigin = new Point(0.5, 1);
MagnifyTip.RenderTransform = myTransformGroup;
}
private void Grid_Holding(object sender, HoldingRoutedEventArgs e)
{
try
{
GridPoint = e.GetPosition(image2);
myTranslateTransform.X = xValue - XAxis;
myTranslateTransform.Y = yValue - YAxis;
MagnifyTip.RenderTransform = myTransformGroup;
MagnifyTip.Visibility = Visibility.Visible;
}
catch (Exception)
{
throw;
}
}
private void InkCanvas_PointerMoved(object sender, PointerRoutedEventArgs e)
{
try
{
pt = e.GetCurrentPoint(image2);
currentContactPt = pt.Position;
xValue = currentContactPt.X;
yValue = currentContactPt.Y;
if (xValue > 300)
{
myRotateTransform.Angle = -90;
}
else if (xValue < 100)
{
myRotateTransform.Angle = 90;
}
else
{
myRotateTransform.Angle = 0;
}
MagnifyTip.RenderTransform = myRotateTransform;
myTranslateTransform.X = xValue - XAxis;
myTranslateTransform.Y = yValue - YAxis;
MagnifyTip.RenderTransform = myTransformGroup;
}
catch (Exception)
{
throw;
}
finally
{
e.Handled = true;
}
}
private async void StoreCrrentImage()
{
try
{
var storageFile =
await
StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///Assets/wallpaper.jpg",
UriKind.RelativeOrAbsolute));
using (
var fileStream =
await storageFile.OpenAsync(FileAccessMode.Read))
{
var bitmapImage = new BitmapImage();
await bitmapImage.SetSourceAsync(fileStream);
var writeableBitmap =
new WriteableBitmap(bitmapImage.PixelWidth, bitmapImage.PixelHeight);
fileStream.Seek(0);
await writeableBitmap.SetSourceAsync(fileStream);
CurrentBitmapObj = writeableBitmap;
writeableBitmap.Invalidate();
}
}
catch (Exception)
{
// Graphics g=new Graphics();
throw;
}
finally
{
}
}
private void ParentGrid_OnPointerReleased(object sender, PointerRoutedEventArgs e)
{
MagnifyTip.Visibility = Visibility.Collapsed;
}
}
}
MagnifierUsercontrol.xaml:
<UserControl
x:Class="controlMagnifier.MagnifierUsercontrol"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:controlMagnifier"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" Height="227" Width="171">
<Canvas x:Name="controlCanvas" x:FieldModifier="public" Height="Auto" Width="Auto" >
<Grid Height="227" Width="171" HorizontalAlignment="Center" Canvas.Left="0" Canvas.Top="0">
<Border x:FieldModifier="public" x:Name="imgBorder" Width="150" CornerRadius="50,50,50,50" Margin="13,25,13,97">
<Border.Background>
<ImageBrush x:FieldModifier="public" x:Name="image1" />
</Border.Background>
</Border>
<TextBlock x:Name="txtreading" Height="30" Width="80" Margin="0,-145,0,0" FontWeight="Bold" Foreground="Red" FontSize="20" Text="ABC" TextAlignment="Center" />
<!--<Image Height="120" Width="150" Margin="0,-50,0,0" Source="Assets/SmallLogo.scale-100.png" ></Image>-->
<Path x:Name="MagnifyTip" Data="M25.533,0C15.457,0,7.262,8.199,7.262,18.271c0,9.461,13.676,19.698,17.63,32.338 c0.085,0.273,0.34,0.459,0.626,0.457c0.287-0.004,0.538-0.192,0.619-0.467c3.836-12.951,17.666-22.856,17.667-32.33 C43.803,8.199,35.607,0,25.533,0z M25.533,32.131c-7.9,0-14.328-6.429-14.328-14.328c0-7.9,6.428-14.328,14.328-14.328 c7.898,0,14.327,6.428,14.327,14.328C39.86,25.702,33.431,32.131,25.533,32.131z" Fill="#FFF4F4F5" Stretch="Fill" Stroke="Black" UseLayoutRounding="False" Height="227" Width="171" />
</Grid>
</Canvas>
</UserControl>
Let me know if this helps or if there is further toward your specific question.
I'm fairly new to C# and programming in general.
I'm building a dummy project to understand how drag and drop works. At first, I begun moving around rectangles, limiting their movements inside the canvas, and they moved just fine. After that, I tried to make it a bit more complex by adding PointerEntered/PointerExited and replacing the rectangles with images. Re-sizing the images when the events PointerEntered/PointerExited occur works, but when I try to drag my images nothing happens. I tried several things I've seen around SO and msdn but it still fails.
Here is my code:
MainPage.xaml
<Canvas x:Name="MyCanvas" Width="800" Height="600">
<Canvas.Background>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="Black" Offset="0"/>
<GradientStop Color="#FF5E8B83" Offset="1"/>
</LinearGradientBrush>
</Canvas.Background>
<TextBlock x:Name="someTB" FontSize="30" Canvas.Left="262" Canvas.Top="25" Height="28" Width="171"/>
<Image x:Name="cauldron" Source="Assets/cauldron-md.png" AllowDrop="True" PointerExited="draggableItem_PointerExited" PointerEntered="draggableItem_PointerEntered" Width="210" Height="184" Canvas.Left="455" Canvas.Top="159"/>
<Image x:Name="antidote" Source="Assets/alchemy-green-potion-no-label.png" ManipulationDelta="dragableItem_ManipulationDelta" PointerExited="draggableItem_PointerExited" PointerEntered="draggableItem_PointerEntered" Width="45" Height="89" Canvas.Left="72" Canvas.Top="263"/>
<Image x:Name="poison" Source="Assets/alchemy-tree-poison-md-standing.png" ManipulationDelta="dragableItem_ManipulationDelta" PointerExited="draggableItem_PointerExited" PointerEntered="draggableItem_PointerEntered" Width="45" Height="89" Canvas.Left="202" Canvas.Top="159"/>
</Canvas>
MainPage.xaml.cs
public sealed partial class MainPage : Page
{
double originalWidth;
double originalHeight;
public MainPage()
{
this.InitializeComponent();
// Add clipping area to the canvas
MyCanvas.Clip = new RectangleGeometry();
MyCanvas.Clip.Rect = new Rect(0, 0, MyCanvas.ActualWidth, MyCanvas.ActualHeight);
}
private Boolean DetectCollisions(FrameworkElement rect1, FrameworkElement rect2)
{
var r1 = new Rect(Canvas.GetLeft(rect1), Canvas.GetTop(rect1), rect1.ActualWidth, rect1.ActualHeight);
var r2 = new Rect(Canvas.GetLeft(rect2), Canvas.GetTop(rect2), rect2.ActualWidth, rect2.ActualHeight);
r1.Intersect(r2);
if (r1 != Rect.Empty)
{
return true;
}
return false;
}
private void dragableItem_ManipulationDelta(object sender, ManipulationDeltaRoutedEventArgs e)
{
Image dragableItem = sender as Image;
dragableItem.ManipulationMode = ManipulationModes.TranslateX | ManipulationModes.TranslateY;
dragableItem.RenderTransform = new CompositeTransform();
var transform = (CompositeTransform)dragableItem.RenderTransform;
var newPosX = Canvas.GetLeft(dragableItem) + transform.TranslateX + e.Delta.Translation.X;
var newPosY = Canvas.GetTop(dragableItem) + transform.TranslateY + e.Delta.Translation.Y;
if (!isBoundary(newPosX, MyCanvas.ActualWidth - dragableItem.ActualWidth, 0))
Canvas.SetLeft(dragableItem, newPosX);
if (!isBoundary(newPosY, MyCanvas.ActualHeight - dragableItem.ActualHeight, 0))
Canvas.SetTop(dragableItem, newPosY);
if (DetectCollisions(dragableItem, cauldron) == true)
{
cauldron.Source = new BitmapImage(new Uri(#"Assets/cauldron-md-autoTone.png"));
}
}
bool isBoundary(double value, double max, double min)
{
return value > max ? true : value < min ? true : false;
}
private void draggableItem_PointerEntered(object sender, PointerRoutedEventArgs e)
{
Image thatPic = sender as Image;
if (thatPic != null)
{
originalWidth = thatPic.ActualWidth;
originalHeight = thatPic.ActualHeight;
thatPic.Width = thatPic.ActualWidth + 10;
thatPic.Height = thatPic.ActualHeight + 10;
}
}
private void draggableItem_PointerExited(object sender, PointerRoutedEventArgs e)
{
Image thatPic = sender as Image;
if (thatPic != null)
{
thatPic.Width = originalWidth;
thatPic.Height = originalHeight;
}
}
}
1) You need to add ManipulationMode="All" to the Image controls.
2) The Image "cauldron" is missing the ManipulationDelta event handler assignment.
I have a custom pushpin which contains an Ellipse, and I would like to change its size programtically in the C# code behind. I am able to change the pushpin size, however, this affects its location on the map.
Is there a way for me to address the ellipse directly through a render transform or by directly changing its height and width?
Here is the xaml:
<map:Pushpin Location="51.4,-0.2" >
<map:Pushpin.Template>
<ControlTemplate>
<Ellipse Width="15" Height="15" Fill="Red" Opacity="0.7" Stroke="Black" StrokeThickness="2.5">
<Ellipse.RenderTransform>
<TranslateTransform X="0" Y="18" />
</Ellipse.RenderTransform>
</Ellipse>
</ControlTemplate>
</map:Pushpin.Template>
</map:Pushpin>
Here is the C# I am using at present which changes the Pushpin sizes (currently to a random number):
private void MapItemsSizeChange()
{
Random rnd = new Random();
ScaleTransform pin_st = new ScaleTransform();
if (mainMap != null)
{
foreach (UIElement UI in mainMap.Children)
{
if (UI is Pushpin)
{
var pin = UI as Pushpin;
if (pin != null)
{
double x = rnd.Next(5, 20);
x = x / 10;
pin_st.ScaleX = x;
pin_st.ScaleY = x;
UI.RenderTransform = pin_st;
}
}
}
}
}
Thanks All!
http://pietschsoft.com/post/2010/06/04/Resizing-and-Auto-Scaling-Pushpin-in-Bing-Maps-Silverlight.aspx