web-dev-qa-db-ja.com

C#でイベントが発生するまでコードフローをブロックする方法

ここにGridButtonがあります。ユーザーがボタンをクリックすると、Utilityクラスのメソッドが実行され、アプリケーションがグリッドをクリックするように強制します。コードフローはここで停止し、ユーザーがGridをクリックするまで続行しないでください。

私はここまでに同様の質問をしました:

ユーザーがC#WPFをクリックするまで待機

その質問では、動作するasync/awaitを使用して回答を得ましたが、これをAPIの一部として使用するため、async/awaitは使用しないでください。非同期は必要ありません。

この目標を達成するためのUtility.PickPoint(Grid grid)メソッドの記述方法を教えてください。

私はこれを助けるかもしれませんが、正直に言うとここに適用することは完全には理解していませんでした:

イベントが完了するまでブロックする

コンソールアプリケーションのConsole.ReadKey()メソッドのようなものと考えてください。このメソッドを呼び出すと、値を入力するまでコードフローが停止します。デバッガーは、何かを入力するまで続行されません。 PickPoint()メソッドの正確な動作が必要です。コードフローは、ユーザーがグリッドをクリックするまで停止します。

<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

まず、UIスレッドは、初期の質問から得た答えと同じようにブロックすることはできません。
これに同意できる場合は、非同期/待機を回避して顧客に変更を少なくしてもらうことができ、マルチスレッドも必要ありません。

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;
    }
}   

しかし、UIスレッドまたは「コードフロー」をブロックしたい場合、答えは不可能です。 UIスレッドがブロックされた場合、それ以上の入力を受け取ることができないためです。
コンソールアプリについて説明したので、簡単な説明をします。
コンソールアプリを実行するか、コンソール(ウィンドウ)に接続されていないプロセスからAllocConsoleを呼び出すと、コンソール(ウィンドウ)を提供できるconhost.exeが実行され、コンソールアプリまたは呼び出し元のプロセスは、コンソール(ウィンドウ)にアタッチされます。
そのため、Console.ReadKeyなどの呼び出し元のスレッドをブロックする可能性のあるコードを記述しても、コンソールウィンドウのUIスレッドはブロックされません。これが、コンソールアプリが入力を待機しているにもかかわらず、他の入力に応答できる理由です。マウスクリック。

0
Alex.Wei

問題はデザイン自体にあると思います。 APIが特定の要素で機能する場合は、別の要素ではなく、この要素自体のイベントハンドラーで使用する必要があります。

たとえば、ここではグリッド上のクリックイベントの位置を取得するために、ボタン要素ではなくグリッド要素のイベントに関連付けられたイベントハンドラーでAPIを使用する必要があります。

ここで、ボタンをクリックした後でのみグリッドのクリックを処理することが要件である場合、ボタンの責任はグリッドにイベントハンドラーを追加することであり、グリッドのクリックイベントはメッセージボックスを表示して削除しますこのイベントハンドラーはボタンによって追加されるため、このクリック後にトリガーされなくなります...(UIスレッドをブロックする必要はありません)

ボタンのクリックでUIスレッドをブロックすると、UIスレッドが後でグリッドのクリックイベントをトリガーできないと言うだけです。

0
jsami