Set events for new UIelements on runtime - wpf

I'm kinda confused with some problem, I'm doing a project where the user should be able to design questions with radio buttons, combo box, etc (kinda like toolbox from VS10 to design your XAML).
So far I can drag and drop an UIElement that I previously created, problem comes when the user creates a new element from my toolbox, I can't find the way to make that new UIElement to get the same events from my previosly created UIElement. Take a look at the code
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Canvas Height="190" HorizontalAlignment="Left" Margin="158,41,0,0" Name="canvas1" VerticalAlignment="Top" Width="322" AllowDrop="True">
<Button Content="PROBANDO" Height="23" Name="button" Width="75" Canvas.Left="113" Canvas.Top="43" PreviewMouseDown="button_PreviewMouseDown" PreviewMouseMove="button_PreviewMouseMove" MouseUp="button_MouseUp" IsEnabled="True" />
<TextBlock Canvas.Left="99" Canvas.Top="147" Height="23" Name="textBlock" Text="" Width="107" />
</Canvas>
<ListBox Height="190" Name="listBox" Width="126" Margin="12,41,365,80" >
<ListBoxItem Content="Radio Button" Selected="radio_Selected" Name="radio" />
<ListBoxItem Content="Text" Selected="text_Selected" Name="text" />
<ListBoxItem Content="Combo Box" Name="combo" Selected="combo_Selected" />
</ListBox>
</Grid>
</Window>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
Point p;
private void button_MouseUp(object sender, MouseButtonEventArgs e)
{
button.ReleaseMouseCapture();
}
private void button_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
button.CaptureMouse();
p = e.GetPosition(canvas1);
}
private void button_PreviewMouseMove(object sender, MouseEventArgs e)
{
Point x = e.GetPosition(canvas1);
if (e.LeftButton == MouseButtonState.Pressed)
{
Canvas.SetLeft(button, Canvas.GetLeft(button) + (x.X - p.X));
Canvas.SetTop(button, Canvas.GetTop(button) + (x.Y - p.Y));
}
p = x;
}
private void generic_PreviewMouseDown(UIElement sender, MouseEventArgs e)
{
Point x = e.GetPosition(canvas1);
if (e.LeftButton == MouseButtonState.Pressed)
{
Canvas.SetLeft(sender, Canvas.GetLeft(sender) + (x.X - p.X));
Canvas.SetTop(sender, Canvas.GetTop(sender) + (x.Y - p.Y));
}
p = x;
}
private void radio_Selected(object sender, RoutedEventArgs e)
{
RadioButton newRadio = new RadioButton();
canvas1.Children.Add(newRadio);
newRadio.PreviewMouseDown += generic_PreviewMouseDown(newRadio,?????);
textBlock.Text = listBox.SelectedIndex.ToString();
}
private void text_Selected(object sender, RoutedEventArgs e)
{
TextBox newText = new TextBox();
canvas1.Children.Add(newText);
textBlock.Text = (String)listBox.SelectedIndex.ToString();
}
private void combo_Selected(object sender, RoutedEventArgs e)
{
Console.Write("Combo");
textBlock.Text = (String)listBox.SelectedIndex.ToString();
}
}
Thanks!

If all you want to do is handle the mouse down on the new RadioButton, change this line:
newRadio.PreviewMouseDown += generic_PreviewMouseDown(newRadio,?????);
To this:
newRadio.PreviewMouseDown += generic_PreviewMouseDown;
Edit
And then you need to change the generic_PreviewMouseDown to the following:
private void generic_PreviewMouseDown(object sender, MouseEventArgs e)
{
UIElement elem = sender as UIElement;
Point x = e.GetPosition(canvas1);
if (e.LeftButton == MouseButtonState.Pressed)
{
Canvas.SetLeft(elem, Canvas.GetLeft(elem) + (x.X - p.X));
Canvas.SetTop(elem, Canvas.GetTop(elem) + (x.Y - p.Y));
}
p = x;
}

Related

WPF - Moving a Canvas

I have trouble with moving a canvas in a Window.
<Canvas x:Name="Canvas1" Height="200" Grid.Column="1" Margin="10,15,92,54" Grid.Row="1" Background="#FFECECEC" Grid.RowSpan="2" MouseDown="Canvas_MouseDown" MouseUp="Canvas_MouseUp" MouseMove="Canvas_MouseMove" >
<TextBox Height="100" Width="100" Margin="50,50,327,65" Background="Red"/>
<TextBox Height="100" Width="100" Margin="10,15,327,65" Background="Blue" />
<Canvas.RenderTransform>
<TransformGroup>
<TranslateTransform x:Name="translate" />
</TransformGroup>
</Canvas.RenderTransform>
</Canvas>
This is code behind:
private void Canvas_MouseDown(object sender, MouseButtonEventArgs e)
{
Canvas1.CaptureMouse();
point = Mouse.GetPosition(Grid1);
}
private void Canvas_MouseMove(object sender, MouseEventArgs e)
{
if (Canvas1.IsMouseCaptured)
{
translate.X = e.GetPosition(Grid1).X - point.X;
translate.Y = e.GetPosition(Grid1).Y - point.Y;
}
}
private void Canvas_MouseUp(object sender, MouseButtonEventArgs e)
{
Canvas1.ReleaseMouseCapture();
}
First time, it seem woork fine.
But second time i try to click on my canvas, this move but from initial position.
How can i fix it?
When initializing point on mouse down, subtract the current translation:
private void Canvas1_MouseDown(object sender, MouseButtonEventArgs e)
{
Canvas1.CaptureMouse();
point = Mouse.GetPosition(Grid1);
point.X -= translate.X;
point.Y -= translate.Y;
}
Alternatively, always only add the difference vector:
private void Canvas1_MouseDown(object sender, MouseButtonEventArgs e)
{
Canvas1.CaptureMouse();
point = Mouse.GetPosition(Grid1);
}
private void Canvas1_MouseMove(object sender, MouseEventArgs e)
{
if (Canvas1.IsMouseCaptured)
{
var p = e.GetPosition(Grid1);
var diff = p - point;
point = p;
translate.X += diff.X;
translate.Y += diff.Y;
}
}
Also make sure the Grid doesn't position the Canvas, by setting the Canvas alignment to Top/Left:
<Canvas x:Name="Canvas1" HorizontalAlignment="Left" VerticalAlignment="Top" ...>
And probably don't set a Margin, but instead initialize the TranslateTransform appropriately.

WPF Popup wrapping when dragging vertically

I have a project that uses a System.Windows.Controls.Primatives.Popup to drag a 'tooltip' like control along with a mouse.
Whenever the drag crosses a horizontal line the popup 'wraps' to the bottom of the screen - despite having sane values for the VerticalOffset. The point at which this wrapping occurs appears to be tied to the HEIGHT of the window, but not it's position.
Here's the code from the sandbox project I have created that also exhibits the same behavior:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.MainGrid.MouseDown += Grid_MouseDown;
this.MainGrid.MouseUp += Grid_MouseUp;
this.MainGrid.MouseMove += (s, e) => { if (this.Popup.IsOpen) { Popup_Drag(s, e); } };
this.Popup.MouseMove += Popup_Drag;
}
private void Popup_Drag(object sender, MouseEventArgs e)
{
Popup.HorizontalOffset = e.GetPosition(this.Popup).X;
Popup.VerticalOffset = e.GetPosition(this.Popup).Y;
this.Status_Top.Text = String.Format("Height/Top: {0}/{1} Width/Left: {2}/{3}", this.Height, this.Top, this.Width, this.Left);
this.Status.Text = String.Format("Vertical Offset: {0} Horizontal Offset: {1}", Popup.VerticalOffset, Popup.HorizontalOffset);
}
private void Grid_MouseUp(object sender, MouseButtonEventArgs e)
{
this.Popup.IsOpen = false;
}
private void Grid_MouseDown(object sender, MouseButtonEventArgs e)
{
this.Popup.IsOpen = true;
Popup_Drag(sender, e);
}
}
And the Window XAML:
<Window x:Class="WpfSandbox.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid x:Name="MainGrid" Background="Purple">
<TextBlock x:Name="Status_Top"></TextBlock>
<Popup x:Name="Popup" Cursor="Hand" HorizontalAlignment="Left"
VerticalAlignment="Bottom" IsOpen="True">
<TextBlock Background="Blue" Foreground="White">
<TextBlock x:Name="Status">TEXT</TextBlock></TextBlock>
</Popup>
</Grid>
</Window>
I was able to fix this by adding Placement="RelativePoint" to the Popup attributes. Apparently this is the default in Silverlight, but not WPF.

moving any control in wpf

I am trying to move control in wpf using Canvas
This is the XAML
<Canvas Grid.Column="1" Grid.Row="0" x:Name="DropCanvas" AllowDrop="True" DragOver="DropCanvas_DragOver"
Drop="Canvas_Drop" DragEnter="Canvas_DragEnter" Background="#12000000" >
<TextBox Canvas.Left="162" Canvas.Top="188" Height="23" Name="textBox1" Width="120"
PreviewMouseMove="textBox1_PreviewMouseMove"
PreviewMouseLeftButtonDown="textBox1_PreviewMouseLeftButtonDown"
PreviewMouseUp="textBox1_PreviewMouseUp" />
</Canvas>
and this is the Code
Point p = new Point();
private void textBox1_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
Control control = sender as Control;
control.CaptureMouse();
p = e.GetPosition(control);
}
private void textBox1_PreviewMouseMove(object sender, MouseEventArgs e)
{
Control control = sender as Control;
Point x = e.GetPosition(control);
if (e.LeftButton == MouseButtonState.Pressed)
{
Canvas.SetLeft(control, Canvas.GetLeft(control) + (x.X - p.X));
Canvas.SetTop(control, Canvas.GetTop(control) + (x.Y - p.Y));
}
p = x;
}
private void textBox1_PreviewMouseUp(object sender, MouseButtonEventArgs e)
{
Control control = sender as Control;
control.ReleaseMouseCapture();
activated = false;
}
The code is working, but when it moves, the control shakes.
What is the proplem
When you call GetPosition you should use DropCanvas as the parameter instead of the control. You're seeing vibrations because the TextBox keeps moving and you need something fixed.
Alternatively, you can use the MouseDragElementBehavior available in Expression Blend SDK to move objects in a container.
Also, this project can be useful to you: http://www.codeproject.com/Articles/24681/WPF-Diagram-Designer-Part-4
public void dragme(object sender, MouseButtonEventArgs e)
{
if (_Move.IsChecked == true)
db.Attach((DependencyObject)sender);
}
//// MouseDragElementBehavior db;
private void canvass_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
if (_Move.IsChecked == true && filmgrid.Visibility == Visibility.Visible)// == true)
{
filmgrid.PreviewMouseDown += new MouseButtonEventHandler(dragme);
}

Can't get simple WPF drag and drop to work

For a simple test I want to drag a Button to a TextBox. I can start dragging the Button, but the Drop event is not raised. What am I missing?
Xaml:
<Window x:Class="DayPlanner.View.DnDTest"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="DnDTest" Height="200" Width="200">
<StackPanel>
<Button Name="button"
Content="OK"
PreviewMouseLeftButtonDown="button_PreviewMouseLeftButtonDown"
PreviewMouseMove="button_PreviewMouseMove"/>
<TextBox Name="textBox"
AllowDrop="True"
DragEnter="textBox_DragEnter"
Drop="textBox_Drop"/>
</StackPanel>
</Window>
Code:
public partial class DnDTest : Window
{
public DnDTest()
{
InitializeComponent();
}
private Point dragStartPoint;
private void button_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
dragStartPoint = e.GetPosition(null);
}
private static bool IsDragging(Point dragStartPoint, MouseEventArgs e)
{
var diff = e.GetPosition(null) - dragStartPoint;
return
e.LeftButton == MouseButtonState.Pressed &&
(Math.Abs(diff.X) > SystemParameters.MinimumHorizontalDragDistance ||
Math.Abs(diff.Y) > SystemParameters.MinimumVerticalDragDistance);
}
private void button_PreviewMouseMove(object sender, MouseEventArgs e)
{
if (IsDragging(dragStartPoint, e))
{
DragDrop.DoDragDrop(button, new DataObject("Button", button), DragDropEffects.Move);
e.Handled = true;
}
}
private void textBox_DragEnter(object sender, DragEventArgs e)
{
e.Handled = true;
}
private void textBox_Drop(object sender, DragEventArgs e)
{
var button = (Button)e.Data.GetData("Button");
textBox.Text = string.Format("[0]", button.Content.ToString());
e.Handled = true;
}
}
This might be some strange case, but to fix it, I needed to handle or dragging events, including the Preview versions.
Here's how to make it work.
Xaml:
<Window x:Class="DayPlanner.View.DnDTestBasic"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="DnDTestBasic" Height="200" Width="200">
<StackPanel>
<Button Name="button"
Content="OK"
PreviewMouseLeftButtonDown="button_PreviewMouseLeftButtonDown"
PreviewMouseMove="button_PreviewMouseMove"/>
<TextBox Name="textBox"
AllowDrop="True"
PreviewDragEnter="textBox_Dragging"
DragEnter="textBox_Dragging"
PreviewDragOver="textBox_Dragging"
DragOver="textBox_Dragging"
Drop="textBox_Drop"/>
<TextBlock Name="status"
Text="No dragging"/>
</StackPanel>
</Window>
Code:
public partial class DnDTestBasic : Window
{
public DnDTestBasic()
{
InitializeComponent();
}
private Point dragStartPoint;
private void button_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
dragStartPoint = e.GetPosition(null);
status.Text = "New drag start position";
}
private static bool IsDragging(Point dragStartPoint, MouseEventArgs e)
{
var diff = e.GetPosition(null) - dragStartPoint;
return
e.LeftButton == MouseButtonState.Pressed &&
(Math.Abs(diff.X) > SystemParameters.MinimumHorizontalDragDistance ||
Math.Abs(diff.Y) > SystemParameters.MinimumVerticalDragDistance);
}
private void button_PreviewMouseMove(object sender, MouseEventArgs e)
{
if (IsDragging(dragStartPoint, e))
{
status.Text = "Starting drag...";
DragDrop.DoDragDrop(button, new DataObject("Button", button), DragDropEffects.Copy);
status.Text = "Dragging done.";
e.Handled = true;
}
}
private void textBox_Dragging(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent("Button"))
e.Effects = DragDropEffects.Copy;
else
e.Effects = DragDropEffects.None;
e.Handled = true;
}
private void textBox_Drop(object sender, DragEventArgs e)
{
var button = (Button)e.Data.GetData("Button");
textBox.Text = string.Format("[{0}]", button.Content.ToString());
e.Handled = true;
}
}
I believe it has to do with the fact that when you start the drag event, the button control is capturing mouse input. Any mouse movements you do after that are registered to the button instead of to the application
I actually had a similar problem and ended up using MouseEnter/Leave events instead of the built in WPF drag/drop framework.

WPF Not sending MouseMove events after CaptureMouse();

I'm trying to have a WPF canvas with rounded rectangles on that I can drag round using the mouse. However once I try and capture the mouse on the canvas I don't get the move events any more.
This is a "mycanvas" user control and the rectangles are "foo" user controls. The XAML for these (minus the preamble) are:
mycanvas.xaml:
<Canvas MouseDown="CanvasMouseDown" MouseMove="CanvasMouseMove" MouseUp="CanvasMouseUp" Background="White">
<my:Foo HorizontalAlignment="Left" Canvas.Left="97" Canvas.Top="30" x:Name="m_foo" VerticalAlignment="Top" Height="87" Width="128" />
</Canvas>
foo.xaml:
<Border BorderThickness="2" BorderBrush="Black" CornerRadius="15" Background="Plum">
<Grid>
<Label Content="Foo" Height="28" HorizontalAlignment="Left" Margin="6,6,0,0" Name="label1" VerticalAlignment="Top" />
</Grid>
</Border>
And then the handlers are:
mycanvas.xaml.cs:
private void CanvasMouseDown(object sender, MouseButtonEventArgs e)
{
if (e.Source is Foo)
{
m_moving = e.Source as Foo;
CaptureMouse();
e.Handled = true;
}
}
private void CanvasMouseMove(object sender, MouseEventArgs e)
{
if (m_moving != null)
{
Canvas.SetLeft(m_moving, e.GetPosition(this).X);
Canvas.SetTop(m_moving, e.GetPosition(this).Y);
}
}
private void CanvasMouseUp(object sender, MouseButtonEventArgs e)
{
ReleaseMouseCapture();
m_moving = null;
}
The MouseDown fires and so the CaptureMouse gets called (and works because I can no longer close the app or click anything else in it!) but the MouseMove never gets called anymore - so where do the MouseMove events get sent now???
If I alt-tab to another application and then go back now suddendly the MouseMove is called and the Foo moves with the mouse.
Try either:
Mouse.Capture(this, CaptureMode.SubTree);
or
m_moving.CaptureMouse();
...
if (m_moving != null)
{
m_moving.ReleaseMouseCapture();
m_moving = null;
}
The mouse events were being raised by the Foo, not by the Canvas, so when you capture the mouse with the Canvas you prevent them from being raised.
You can directly use the MouseMove event on the Window:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.MouseMove += MouseEventHandler;
}
private void MouseEventHandler(Object sender, MouseEventArgs e)
{
System.Windows.Point position = e.GetPosition(this);
Canvas.SetLeft(ElipseElement, position.X-5);
Canvas.SetTop(ElipseElement, position.Y-5);
}
}

Resources