web-dev-qa-db-fra.com

Comment bloquer le flux de code jusqu'à ce qu'un événement soit déclenché en C #

Ici, nous avons un Grid avec un Button. Lorsque l'utilisateur clique sur le bouton, une méthode dans une classe Utility est exécutée, ce qui force l'application à recevoir un clic sur Grid. Le flux de code doit s'arrêter ici et ne pas continuer tant que l'utilisateur n'a pas cliqué sur Grid.

J'ai déjà eu une question similaire ici:

Attendez que l'utilisateur clique sur C # WPF

Dans cette question, j'ai obtenu une réponse en utilisant async/await qui fonctionne, mais comme je vais l'utiliser dans le cadre d'une API, je ne veux pas utiliser async/await, car les consommateurs devront alors marquer leurs méthodes avec asynchrone dont je ne veux pas.

Comment écrire la méthode Utility.PickPoint(Grid grid) pour atteindre cet objectif?

J'ai vu cela qui peut aider mais je n'ai pas bien compris que cela s'applique ici pour être honnête:

Blocage jusqu'à la fin d'un événement

Considérez-le comme quelque chose comme la méthode Console.ReadKey () dans une application Console. Lorsque nous appelons cette méthode, le flux de code s'arrête jusqu'à ce que nous saisissions une valeur. Le débogueur ne continue pas tant que nous n'avons pas entré quelque chose. Je veux le comportement exact de la méthode PickPoint (). Le flux de code s'arrêtera jusqu'à ce que l'utilisateur clique sur la grille.

<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"
        xmlns:local="clr-namespace:WpfApp1"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="3*"/>
            <RowDefinition Height="1*"/>
        </Grid.RowDefinitions>

        <Grid x:Name="View" Background="Green"/>
        <Button Grid.Row="1" Content="Pick" Click="ButtonBase_OnClick"/>
    </Grid>
</Window>

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
    {
        // do not continue the code flow until the user has clicked on the grid. 
        // so when we debug, the code flow will literally stop here.
        var point = Utility.PickPoint(View);


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

public static class Utility
{
    public static Point PickPoint(Grid grid)
    {

    }
}
11
Vahid

Tout d'abord, le fil de l'interface utilisateur ne peut pas être bloqué comme la réponse que vous avez obtenue à partir de votre première question.
Si vous êtes d'accord avec cela, il est possible d'éviter l'asynchronisation/attente pour que votre client fasse moins de modifications, et même pas besoin de multi-threading.

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
    {
        Utility.PickPoint(View, (x) => MessageBox.Show(x.ToString()));
    }
}

public static class Utility
{
    private static Action<Point> work;

    public static void PickPoint(Grid grid, Action<Point> work)
    {
        if (Utility.work == null)
        {
            grid.PreviewMouseLeftButtonUp += Grid_PreviewMouseLeftButtonUp;
            Utility.work = work;
        }
    }

    private static void Grid_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
    {
        var grid = (Grid)sender;
        work.Invoke(e.GetPosition(grid));
        grid.PreviewMouseLeftButtonUp -= Grid_PreviewMouseLeftButtonUp;
        Utility.work = null;
    }
}   

Mais si vous souhaitez bloquer le thread d'interface utilisateur ou le "flux de code", la réponse sera que c'est impossible. Parce que si le thread de l'interface utilisateur a été bloqué, aucune autre entrée ne peut être reçue.
Puisque vous avez un peu parlé de l'application console, je fais juste quelques explications simples.
Lorsque vous exécutez une application console ou appelez AllocConsole à partir d'un processus qui ne s'est attaché à aucune console (fenêtre), conhost.exe qui peut fournir une console (fenêtre) sera exécuté et console l'application ou le processus de l'appelant sera attaché à la console (fenêtre).
Donc, tout code que vous écrivez qui pourrait bloquer le thread de l'appelant tel que Console.ReadKey ne bloquera pas le thread d'interface utilisateur de la fenêtre de la console, c'est la raison pour laquelle lorsque l'application de la console attend votre entrée mais peut toujours répondre à d'autres entrées comme un clic de souris.

0
Alex.Wei

Je pense que le problème réside dans la conception elle-même. Si votre API fonctionne sur un élément particulier, elle doit être utilisée dans le gestionnaire d'événements de cet élément même, pas sur un autre élément.

Par exemple, ici, nous voulons obtenir la position de l'événement de clic sur la grille, l'API doit être utilisée dans le gestionnaire d'événements associé à l'événement sur l'élément Grid, pas sur l'élément bouton.

Maintenant, si l'exigence est de gérer le clic sur la grille uniquement après avoir cliqué sur le bouton, la responsabilité du bouton sera d'ajouter le gestionnaire d'événements sur la grille, et l'événement de clic sur la grille affichera la boîte de message et supprimera ce gestionnaire d'événements ajouté par le bouton pour qu'il ne se déclenche plus après ce clic ... (pas besoin de bloquer le Thread UI)

Juste pour dire que si vous bloquez le thread de l'interface utilisateur sur le clic du bouton, je ne pense pas que le thread de l'interface utilisateur sera en mesure de déclencher l'événement de clic sur la grille par la suite.

0
jsami