Canvas zooming in WPF using code behind - c#

Here the scenario is:
I have a canvas with different diagrams drawn on it. Now the requirement is to zoom into the canvas using the code behind either using C# or VB. Moreover I need to place the zoom code in some dll so that i can reuse the same set of code through out my application.
Now my question is how to do this....
I have tried the following code pls have a look..
public MainWindow()
{
InitializeComponent();
canvas.MouseEnter += new MouseEventHandler(canvas_MouseEnter);
canvas.MouseWheel += new MouseWheelEventHandler(canvas_MouseWheel);
}
void canvas_MouseWheel(object sender, MouseWheelEventArgs e)
{
double height = canvas.ActualHeight;
double width = canvas.ActualWidth;
double zoom = e.Delta;
height += 2;
width += 2;
ScaleTransform sc = new ScaleTransform(width, height);
canvas.LayoutTransform = sc;
canvas.UpdateLayout();
}

Try to implement this sample:
var canvas = new Canvas();
var st = new ScaleTransform();
var textBox = new TextBox {Text = "Test"};
canvas.RenderTransform = st;
canvas.Children.Add(textBox);
canvas.MouseWheel += (sender, e) =>
{
if (e.Delta > 0)
{
st.ScaleX *= 2;
st.ScaleY *= 2;
}
else
{
st.ScaleX /= 2;
st.ScaleY /= 2;
}
};

I believe what you are looking for is a zoom behavior. Behaviors are objects that encapsulate some form of interactive behavior. I've seen several examples of "Zoom Behaviors" that you should be able to use for your project. You should be able to use or modify one of the following...
Laurent Bugnion's Zoom Behavior
WPF Extensions - has a zoom control

Related

Draw a connector between shapes in wpf

I am creating some shapes from code behind dynamically and adding them to a Grid and further add the Grid to Canvas.
So when I double click on a shape I should be able to add some text which works fine. Now lets say I have two shapes on the Canvas and when I try to draw a line between these shapes for some reason the first shape gets pulled away to the bottom and the line starts from the middle of first shape.
I want the shape not to change the position and the line should start from the bottom of first shape. Please see the image for my problem.
Please help with your thoughts. Here is my code. Also I tried numerous posts eg: Getting the top left coordinates of a WPF UIElement.
But none of them seem to help.
private void CvsSurface_OnDrop(object sender, DragEventArgs e) //In this event I am creating a shape dynamically and adding to a grid which is then added to a canvas.
{
Shape result = null;
Object droppedData = e.Data; //This part is not important
/*Translate Drop Point in reference to Stack Panel*/
Point dropPoint = e.GetPosition(this.cvsSurface);
//Console.WriteLine(dropPoint);
//Label lbl = new Label();
//lbl.Content = draggedItem.Content;
UIElement element = draggedItem.Content as UIElement;
Shape s = element as Shape;
if (s is Ellipse)
{
Ellipse ellipse = new Ellipse()
{
Height = s.Height,
Width = s.Width,
Fill = s.Fill
};
result = ellipse;
}
else if (s is Rectangle)
{
Rectangle rectangle = new Rectangle()
{
Height = s.Height,
Width = s.Width,
Fill = s.Fill
};
result = rectangle;
}
Grid sp = new Grid();
sp.Children.Add(result);
sp.MouseLeftButtonDown += Sp_MouseLeftButtonDown;
sp.MouseLeftButtonUp += Sp_MouseLeftButtonUp;
//sp.PreviewMouseLeftButtonUp += Sp_PreviewMouseLeftButtonUp;
//sp.MouseLeftButtonUp += Sp_MouseLeftButtonUp;
cvsSurface.Children.Add(sp);
Canvas.SetLeft(sp, dropPoint.X);
Canvas.SetTop(sp, dropPoint.Y);
}
private void Sp_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) // The purpose of this event lets say when some one clicks on a shape and drags the mouse to the other shape and when mouse up I want to draw a line between the shapes.
{
bool mouserelease = System.Windows.Input.Mouse.LeftButton == MouseButtonState.Pressed;
if (!mouserelease)
{
x2 = e.GetPosition(stackpanel).X;
y2 = e.GetPosition(stackpanel).Y;
Line l = new Line();
l.X1 = x1;
l.Y1 = y1;
l.X2 = x2;
l.Y2 = y2;
l.Margin = new Thickness(0, 19, 0, 0);
l.Stroke = new SolidColorBrush(Colors.Black);
l.StrokeThickness = 2;
stackpanel.Children.Add(l);
}
}
private void Sp_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) //This method lets say if user clicks twice then he wants to add some text or if he single clicks then I am assuming he is trying to a drag and draw a line
{
stackpanel = sender as Grid; //Sorry, the stackpanel is a global variable name of type Grid. Its actually Grid stackpanel;
if (e.ClickCount == 2)
{
dialog = new UserDialog()
{
DataContext = this,
Height = 180,
Width = 400,
MaxHeight = 180,
MaxWidth = 400
};
dialog.ShowDialog();
}
else
{
x1 = e.GetPosition(stackpanel).X + 18;
y1 = e.GetPosition(stackpanel).Y + 18;
//x1 = GetPosition(stackpanel, cvsSurface).X;
//y1 = GetPosition(stackpanel, cvsSurface).Y;
}
}

How to call a method from programmatically created buttons with different parameters?

my problem is how to call a existing method from another object with button specific parameters.
This is the method I need to call ( from MainWindow):
partial class Sidebar : Window
{
[...]
internal void SetPosition(System.Drawing.Rectangle workingarea, bool left)
{
Overlay.Properties.Settings.Default.SidebarSide = left;
Overlay.Properties.Settings.Default.Top = this.Top = workspace.Top;
Overlay.Properties.Settings.Default.Left = this.Left = workspace.Left;
Overlay.Properties.Settings.Default.Save();
this.Height = workspace.Height;
this.Width = workspace.Width;
timeGrid.Style = gridStyle;
Refresh();
}
[...]
}
and following is the method for creating buttons (and more) for each Screen connected to the machine
class SettingsWindow : Window
{
[...]
private void SidebarTab_Initialized(object sender, EventArgs e)
{
Canvas monitorCanvas = new Canvas();
spPosition.Children.Add(monitorCanvas);
System.Windows.Forms.Screen currentScreen = System.Windows.Forms.Screen.FromHandle(
new System.Windows.Interop.WindowInteropHelper(this).Handle);
System.Windows.Forms.Screen[] screens = System.Windows.Forms.Screen.AllScreens;
Point min = new Point(0,0);
Point max = new Point(0,0);
for (int i = 0; i < screens.Length; i++)
{
min.X = min.X < screens[i].Bounds.X ? min.X : screens[i].Bounds.X;
min.Y = min.Y < screens[i].Bounds.Y ? min.Y : screens[i].Bounds.Y;
max.X = max.X > (screens[i].Bounds.X + screens[i].Bounds.Width) ? max.X : (screens[i].Bounds.X + screens[i].Bounds.Width);
max.Y = max.Y > (screens[i].Bounds.Y + screens[i].Bounds.Height) ? max.Y : (screens[i].Bounds.Y + screens[i].Bounds.Height);
}
[...]
for (int i = 0; i < screens.Length; i++)
{
Border monitor = new Border();
monitor.BorderBrush = Brushes.Black;
monitor.BorderThickness = new Thickness(1);
Canvas.SetTop(monitor, (screens[i].Bounds.Top - min.Y) / scale);
Canvas.SetLeft(monitor, (screens[i].Bounds.Left - min.X) / scale);
monitor.Width = screens[i].Bounds.Width / scale;
monitor.Height = screens[i].Bounds.Height / scale;
DockPanel dp = new DockPanel();
Button monLeft = new Button();
monLeft.Width = scale;
DockPanel.SetDock(monLeft, Dock.Left);
Button monRight = new Button();
monRight.Width = scale;
DockPanel.SetDock(monRight, Dock.Right);
[...]
}
}
}
As you see, I need two buttons for every screen on the machine.
monLeft.Click = SetPosition(screens[i].WorkingArea, true); and
monRight.Click = SetPosition(screens[i].WorkingArea, true); is what I need.
Thanks in advance.
Use a lambda to define the event handler. This allows you to close over the local variable(s) that you'll need in your handler.
Note that closures close over variables, not values, so you don't want to close over i (it won't be the value that you want it to be by the time the event fires). You'll need to make a copy of the loop variable inside of the loop so that you can close over that instead. Well, that or use a foreach loop (in C# 5.0+) instead of a for loop.
foreach(var screen in screens)
{
//...
Button monRight = new Button();
monRight.Width = scale;
DockPanel.SetDock(monRight, Dock.Right);
monRight.Click += (s,e) => SetPosition(screen.WorkingArea, true);
}
Well, as you can see in http://msdn.microsoft.com/en-us/library/system.web.ui.webcontrols.button.click(v=vs.110).aspx, Button.Click is an event, so you need to assign an event handler to it:
Either you wrap your SetPosition() method in an event handler and assign the event handler of your Button.Click event to it, or you can do it through lambda construction as suggested in the previous answer.
Another alternative is setting the Button.Command for your buttons, by implementing an instance of ICommand that will call the SetPosition() method.

Scale and dragging image inside canvas only in windows store app

I have been struggling for the past week in transformation of image within the limits of canvas
void image_ManipulationDelta(object sender, ManipulationDeltaRoutedEventArgs e)
{
var name = (Image)sender;
var translate = (CompositeTransform)name.RenderTransform;
var newPosX = Canvas.GetLeft(name) + translate.TranslateX + e.Delta.Translation.X;
var newPosY = Canvas.GetTop(name) + translate.TranslateY + e.Delta.Translation.Y;
if (!isBoundary(newPosX, DrawCanvas.ActualWidth - name.ActualWidth, 0))
translate.TranslateX += e.Delta.Translation.X;
if (!isBoundary(newPosY, DrawCanvas.ActualHeight - name.ActualHeight, 0))
translate.TranslateY += e.Delta.Translation.Y;
}
bool isBoundary(double value, double max, double min)
{
return value > max ? true : value < min ? true : false;
}
This is my dragging code it works fine and also isBoundary function limits the image inside canvas inly but when I am scaling with this code
{
var translate = (CompositeTransform)name.RenderTransform;
translate.CenterX = name.ActualWidth / 2;
translate.CenterY = name.ActualHeight / 2;
translate.ScaleX += e.Delta.Translation.X*0.001 ;
translate.ScaleY += e.Delta.Translation.Y*0.001;
}
Scale is also done but not limiting inside canvas and I when I scale my image to make big its canvas is also scaling and when I am shriking in size its canvas (drag) area is also shrinking.
Need help in limiting scaling and its canvas problem.

How can i set the Form to be in the Center of the Screen?

I'm using this code:
private void notifyIcon1_MouseDoubleClick(object sender, MouseEventArgs e)
{
this.Show();
this.WindowState = FormWindowState.Normal;
//this.Location = new Point(form1_location_on_x, form1_location_on_y);
//this.StartPosition = FormStartPosition.CenterScreen;
Either the line
this.Location = new Point(form1_location_on_x, form1_location_on_y);
or the line
this.StartPosition = FormStartPosition.CenterScreen;
are working when I'm on my original screen resolution 1920x1080, but once I'm changing the resolution to 1024x768, the Form is on the right bottom corner not hidden I see it all but it's not in the center.
form1_location_on_x and on_y are:
form1_location_on_x = this.Location.X;
form1_location_on_y = this.Location.Y;
The question is what should I do to make it work on any other resolution like 1024x768 or any others? I tried many changes but nothing worked so far.
Size screenSize = Screen.PrimaryScreen.WorkingArea.Size;
Location = new Point(screenSize.Width / 2 - Width / 2, screenSize.Height / 2 - Height / 2);
Make sure that you set StartPosition = FormStartPosition.Manual;
Tested and working with 1920x1080 and 1024 x 768
You could calculate the top and left position of your form using this formula:
int formWidth = yourForm.Width;
int formHeight = yourForm.Height;
int screenH = (Screen.PrimaryScreen.WorkingArea.Top +
Screen.PrimaryScreen.WorkingArea.Height) / 2;
int screenW = (Screen.PrimaryScreen.WorkingArea.Left +
Screen.PrimaryScreen.WorkingArea.Width) / 2;
int top = screenH - formWidth / 2;
int left = screenW - formHeight / 2;
yourForm.Location = new Point(top, left);
Of course, these days, you have the problem of dual monitors.
I don't know if you want your form to appear always on the primary screen or you want the form appear in the current screen (the one where the form is currently displayed). In this second case you need to find where your form is displayed
private void CenterForm(Form yuorForm)
{
foreach(var s in Screen.AllScreens)
{
if(s.WorkingArea.Contains(yourForm.Location))
{
int screenH = s.WorkingArea.Height / 2;
int screenW = s.WorkingArea.Width / 2;
int top = (screenH + s.WorkingArea.Top) - formWidth / 2;
int left = (screenW + s.WorkingArea.Left) - formHeight / 2;
yourForm.Location = new Point(top, left);
break;
}
}
}
EDIT: Thanks to #alex I will complete the answer with the information on SystemEvents class
If you want to be notified by the system when the user suddenly change the resolution of your screen you could subscribe to the event SystemEvents.DisplaySettingsChanged (using Microsoft.Win32; needed)
SystemEvents.DisplaySettingsChanged += new EventHandler(SystemEvents_DisplaySettingsChanged);
and then handle the reposition of your form in the event
// This method is called when the display settings change.
void SystemEvents_DisplaySettingsChanged(object sender, EventArgs e)
{
RecenterForm(yourForm);
}
try using one of these after the resize:
this.CenterToScreen();
or
this.CenterToParent();
You can use StartPosition property of Form objects. It determines the position of a form. Set it's value to CenterScreen if you want your form to open in the center of the screen

Selecting part of image on windows form

I'm making instrument to select part of image. I have PictrureBox, and simple way to make it :
void StartPanel(object sender, MouseEventArgs args)
{
xStart = args.X;
yStart = args.Y;
panelStarted = true;
pan.Location = new Point(xStart, yStart);
}
void FinishPanel(object sender, MouseEventArgs args)
{
xFinish = args.X;
yFinish = args.Y;
panelStarted = false;
}
void UpdatePanel(object sender, MouseEventArgs args)
{
if (panelStarted)
{
int x = args.X;
int y = args.Y;
int newxstart = xStart;
int newystart = yStart;
int neww = 0;
int newh = 0;
if (x >= xStart)
neww = x - xStart;
else
{
neww = xStart - x;
newxstart = x;
}
if (y >= yStart)
newh = y - yStart;
else
{
newh = yStart - y;
newystart = y;
}
pan.Size = new Size(neww, newh);
pan.Location = new Point(newxstart, newystart);
}
}
When I move mouse right and down, it is absolutely ok. But when I move it left or up I can see blinks at my area. So I have understood, that it is because when I move mouse left or up, my panel is redrawed, because Panel.Location is changed, and when I move mouse right and down, location is not changed, only size is changed, so it is not redrawed, just some pixels are added to panel. What is standart solution for this?
It's not easy trying to see what you are trying to do, but I guess you are using a panel as a draggable control to drag over the picturebox surface capturing the portion of image below (like a lens) - yes?
If so, then this is not the best way to do it. It is better to just draw a rectangle on the picturebox surface and "drag" that around - this is simple with just using the mouse events to sets the top left corner and use the onpaint to draw the unfilled rectangle over the image. Capturing the image when you are ready is simple too using whatever event you wish, then copy the image giving the same positions to the new bitmap.
Putting one control over another often causes flickers - even with double buffering. It also takes far more code.
Since you are describing a drawing issue when resizing the panel, probably the easiest fix is to replace the panel you are using with one that is double buffered and will invalidate on resize a event:
public class BufferedPanel : Panel {
public BufferedPanel() {
this.DoubleBuffered = true;
this.ResizeRedraw = true;
}
}

Categories

Resources