Wait till user click C# WPF [duplicate]
Asked Answered
L

2

1

I want to create a button in a WPF window that, when clicked, waits for the user to click on the window and acquires the location of the click. Once the location is acquired, the program will continue to the next line of code and display the location of the click.

<Window x:Class="WpfApp1.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"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <Button Content="Pick Point &amp; Display it!" HorizontalAlignment="Left" Margin="301,172,0,0" VerticalAlignment="Top" Width="168" Click="Button_Click" RenderTransformOrigin="1.479,2.177"/>

    </Grid>
</Window>


    private void Button_Click(object sender, RoutedEventArgs e)
    {
        var point = WaitTillUserClicks();

        MessageBox.Show(point.ToString());
    }

    private Point WaitTillUserClicks()
    {
        // Prompt the user for a mouse click and do not proceed unless he has clicked at least once on the Window
    }
Longicorn answered 8/2, 2020 at 23:15 Comment(2)
async/await and TaskCompletionSource is your friend hereArenas
@SirRufo Thanks, but how can I return a result? I want the code flow to stop at WaitTillUserClicks() while the UI wait for the user to click.Longicorn
A
5

Here a solution with TaskCompletionSource

private TaskCompletionSource<Point> _clickSomeWhere;
private async void Button_Click( object sender, RoutedEventArgs e )
{
    ( sender as UIElement ).IsHitTestVisible = false;
    try
    {
        var point = await ReadPointAsync();
        MessageBox.Show( point.ToString() );
    }
    finally
    {
        ( sender as UIElement ).IsHitTestVisible = true;
    }
}

private async Task<Point> ReadPointAsync()
{
    _clickSomeWhere = new TaskCompletionSource<Point>();

    // here is your prompt
    this.Title = "Please click on the point you like!";

    try
    {
        return await _clickSomeWhere.Task;
    }
    finally
    {
        this.Title = "Thank you!";
    }
}

private void Window_MouseDown( object sender, System.Windows.Input.MouseButtonEventArgs e )
{
    if ( _clickSomeWhere != null )
    {
        _clickSomeWhere.TrySetResult( e.GetPosition( this ) );
        _clickSomeWhere = null;
    }
}
Arenas answered 9/2, 2020 at 13:35 Comment(6)
Thanks, I have managed this far, but my problem is how to encapsulate it in a method as I described in my comment. For example if you check the Console.ReadLine() method, when the code flow reaches it, it forces you to enter some value before proceeding to the next line of code. I want something similar. Something like UI.ReadPoint(). Then whenever the code reaches this line of code, it should force the user to click on the screen before proceeding to the next line. I have seen this kind of thing in AutoCAD and Revit API and want to replicate the same behavior here.Longicorn
The UI.ReadPoint() must prompt the user to click and return the clicked point.Longicorn
@Longicorn I updated my answer and added a kind of prompting.Arenas
Thanks Sir Rufo, but this is still not what I described. How can I have a method like UI.ReadPoint() that I can call and enforce the user to click on the Window. The current solution works only when the Button_Click is raised. Am I missing something obvious here?Longicorn
Sorry, I don't get your point - I take your sample from your question and the request for clicking on the window is started by a click on a button (see your code). I cannot answer what you did not ask in your question.Arenas
You are right. Actually you have answered it correctly and I am accepting it as the right answer. I will try to open another question later for what I have in mind and link here. Thanks.Longicorn
F
1

Will that work?

private void Button_Click(object sender, RoutedEventArgs e)
    {
      this.PreviewMouseLeftButtonDown += MainWindow_PreviewMouseLeftButtonDown;
    }

 private void MainWindow_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
       this.PreviewMouseLeftButtonDown -= MainWindow_PreviewMouseLeftButtonDown;
       Point point = e.GetPosition(this);
       MessageBox.Show(point.ToString());
       e.Handled = true;
    }

You don't need to use "this", I just like to use it, not sure if it is a good practice.

Flocculant answered 9/2, 2020 at 9:25 Comment(7)
Thanks Ivan, it works. But in my original question I want to have this as var point = WaitTillUserClicks(); I want te code to stop at this function until the mouse is clicked.Longicorn
Well, as far as I know, the event handlers such as your Button_Click are executing in the main STA thread, so you cannot wait inside of the event handler method because than user interface will be frozen. I am not sure what exactly you want to achieve, but if you need to wait in the middle of the function, you have to consider doing so in another thread (Using Thread, Task, BackgroundWorker etc functionality). But then you will need to access the main thread variables anyway, so I don't really understand why do you need that. Could you give some more info about your goal?Flocculant
I want to encapsulate all this inside a method like WaitTillUserClicks(), and put it in some class like 'var point = Utility.WaitTillUserClicks()' Then whenever a user calls this method, the code flow is paused and the user is asked to click on the Window.Longicorn
look at it as something like var str = Console.ReadLine(); When the code reaches this line, it forces you to enter some value and press enter before going to the next line of code. I want the same behavior for accepting a mouse click.Longicorn
Seems a similar case; #12746348Longicorn
I think you could show another transparent window on top of your main window using ShowDialog(). But I still don't understand what is the use case for that.Flocculant
If you check the Console.ReadLine() method, when the code flow reaches it, it forces you to enter some value before proceeding to the next line of code. I want something similar. Something like UI.ReadPoint(). Then whenever the code reaches this line of code, it should force the user to click on the screen before proceeding to the next line. I have seen this kind of thing in AutoCAD and Revit API and want to replicate the same behavior here.Longicorn

© 2022 - 2024 — McMap. All rights reserved.