I want to make an application: the user clicks and clicks the mouse, and when he releases it, the Button is drawn where the upper left corner is the click coordinates ( MouseDown event), and the lower right one is the coordinates of the place where the user released the mouse button ( MouseUp event). Here is the code for MouseDown :

 private void MainWindow_MouseDown(object sender, MouseButtonEventArgs e) { StartPoint = e.GetPosition(this); //Свойство StartPoint объявляется сверху таким образом //private System.Windows.Point StartPoint { get; set; } } 

Here is the code for MouseUp :

 private void MainWindow_MouseUp(object sender, MouseButtonEventArgs e) { EndPoint = e.GetPosition(this); //Свойство EndPoint объявляется сверху таким образом //private System.Windows.Point EndPoint { get; set; } Button temp = new Button(); temp.Margin = new Thickness(StartPoint.X, StartPoint.Y, EndPoint.X, EndPoint.Y); temp.Background = new SolidColorBrush(Colors.Blue); temp.Content = "Do not click me!"; mainGrid.Children.Add(temp); } 

This does not work, the buttons are drawn too large or not drawn at all. What am I doing wrong and how to make it work?

  • Why do you need EndPoint? Take it away. In Margin, leave StartPoint and the rest at 0. Set VerticalAlighnment as Top and HorizontalAlighnment as Left . Also set the desired button size. Width - width, Height - height (in pixels). - D .Stark

3 answers 3

  1. Coordinates must be considered for the control where you put the button. To this case for grid.
  2. Margin on the right and below are not coordinates, it is the distance from the edge of the control to the edge of the button (in your case)

Rewrote your code a bit

 private void MainWindow_MouseDown(object sender, MouseButtonEventArgs e) { StartPoint = e.GetPosition(mainGrid); //Свойство StartPoint объявляется сверху таким образом //private System.Windows.Point StartPoint { get; set; } } private void MainWindow_MouseUp(object sender, MouseButtonEventArgs e) { EndPoint = e.GetPosition(mainGrid); //Свойство EndPoint объявляется сверху таким образом //private System.Windows.Point EndPoint { get; set; } Button temp = new Button(); temp.Margin = new Thickness(StartPoint.X, StartPoint.Y, mainGrid.ActualWidth - EndPoint.X, mainGrid.ActualHeight - EndPoint.Y); temp.Background = new SolidColorBrush(Colors.Blue); temp.Content = "Do not click me!"; mainGrid.Children.Add(temp); } 
  • Thanks, earned! - Philippe

In order to have controls in absolute coordinates, use Canvas correctly; it is specifically designed for this. Therefore, I would write this:

XAML:

 <Grid> <Canvas MouseLeftButtonDown="OnCanvasLeftMouseDown" Background="Transparent"/> </Grid> 

Code-behind:

 void OnCanvasLeftMouseDown(object sender, MouseButtonEventArgs eDown) { var canvas = (Canvas)sender; var startPos = eDown.GetPosition(canvas); canvas.MouseLeftButtonUp += OnUp; void OnUp(object _, MouseButtonEventArgs eUp) // захватим позицию начала { canvas.MouseLeftButtonUp -= OnUp; var endPos = eUp.GetPosition(canvas); var button = new Button() { Content = "Do not click me!", Width = Math.Abs(endPos.X - startPos.X), Height = Math.Abs(endPos.Y - startPos.Y) }; Canvas.SetLeft(button, Math.Min(startPos.X, endPos.X)); Canvas.SetTop(button, Math.Min(startPos.Y, endPos.Y)); canvas.Children.Add(button); } } 

From the feed @ tym32167, a more rigorous option:

 void OnCanvasLeftMouseDown(object sender, MouseButtonEventArgs eDown) { var canvas = (Canvas)sender; var startPos = eDown.GetPosition(canvas); canvas.MouseLeftButtonUp += OnUp; canvas.Unloaded += Unsubscribe; Mouse.Capture(canvas); void OnUp(object _, MouseButtonEventArgs eUp) // захватим позицию начала { Mouse.Capture(null); Unsubscribe(_, eUp); var endPos = eUp.GetPosition(canvas); if (!canvas.IsMouseOver) return; var button = new Button() { Content = "Do not click me!", Width = Math.Abs(endPos.X - startPos.X), Height = Math.Abs(endPos.Y - startPos.Y) }; Canvas.SetLeft(button, Math.Min(startPos.X, endPos.X)); Canvas.SetTop(button, Math.Min(startPos.Y, endPos.Y)); canvas.Children.Add(button); } void Unsubscribe(object _, RoutedEventArgs e) { canvas.MouseLeftButtonUp -= OnUp; canvas.Unloaded -= Unsubscribe; } } 

Added mouse capture to release it in the same context. Also added is a subscription to Canvas ' unloading, in case the UI logic unloads it before the mouse is released.

  • one
    Canvas is good. New syntax is good. Subscribing / unsubscribing to an event in an event is bad. You understand that the mouse can be pressed in our control and pressed in another. To avoid this, in your case, you need to grab the mouse when pressed and release when you release the button ( canvas.CaptureMouse(); / canvas.ReleaseMouseCapture(); ) - tym32167
  • @ tym32167: It would be nice to subscribe to Unloaded , because the control may disappear. - VladD
  • one
    Well, I most disliked the dynamic subscription / unsubscription. I had so many problems with it, that now this code makes me cringe and feel uncomfortable :) - tym32167
  • @ tym32167: By the way, in your version, according to the idea, you also need to check over which control the mouse was pressed and released. For example, mainGrid may not be the size of the entire window. Subscribing to MouseDown at mainGrid , and not at the whole window, looks cleaner. - VladD
  • @ tym32167: It seems that there were no special problems with the dynamic subscription, although, of course, if the UI-element can “escape” between pressing and releasing the mouse, you have to program very carefully. - VladD
 private void Grid_PreviewMouseDown(object sender, MouseButtonEventArgs e) { Mouse.Capture(sender as IInputElement); } private void Grid_PreviewMouseUp(object sender, MouseButtonEventArgs e) { Point upPoint = e.GetPosition(sender as Grid); if (upPoint.X < 0 || upPoint.X > mainGrid.ActualWidth || upPoint.Y < 0 || upPoint.Y > mainGrid.ActualHeight) return; Mouse.Capture(null); Button button = new Button(); button.Margin = new Thickness(upPoint.X, upPoint.Y, 0, 0); button.VerticalAlignment = VerticalAlignment.Top; button.HorizontalAlignment = HorizontalAlignment.Left; button.Background = new SolidColorBrush(Colors.Blue); button.Content = "Do not click me!"; button.Width = 120; button.Height = 20; mainGrid.Children.Add(button); }