簡體   English   中英

如何正確過濾可能由鼠標事件引起的不同操作?

[英]How to properly filter the different actions that may result from a mouse event?

我正在設計CAD應用程序,不確定如何以干凈的方式設計鼠標事件的事件處理。

為簡單起見,假設我在用戶界面中有兩個按鈕:CreateSquare和CreateCircle。 當用戶單擊其中之一時,我希望應用程序等待畫布中的單擊,然后在用戶單擊后在所標識的位置創建正方形或圓形。

據我了解,有必要在畫布上監聽MouseDown事件,並在處理程序中編寫類似以下內容的內容:

bool CreatingSquaresMode;
bool CreatingCirclesMode;

private void ModelViewport_MouseDown(object sender, MouseButtonEventArgs e)
{
    if (CreatingSquaresMode) { 
        // create square at selected position. 
    }
    if (CreatingCirclesMode) { 
        // create circle at selected position. 
    }
}

但是,這似乎很臭,特別是因為我將有許多不同的create命令,但很快就會失去控制。 由於我希望將CreateSquare和CreateCircle按鈕綁定到命令,並且也不必擔心MouseDown事件,因此它也破壞了MVVM。

我考慮過的另一種方法是狀態機,在該狀態機中,我將確定應用程序的所有可能模式,並以更優雅的方式復制上述if-nest或switch-case。 仍然感覺這不是正確的解決方案,但是會更好。

第二種選擇-我認為這是正確的選擇-將在我的CreateSquare命令中以某種方式偵聽MouseDown事件:

   private void CreateSquare() {
       //do something here
       //wait for user mouse input
       //create square
   }

但是我不知道那是否有可能。

處理這種情況的正確方法是什么? 我確定這里有一種設計模式應該可以為我提供幫助。

對於那些閱讀者來說,這就是我解決這個問題的方式。 上面的答案有助於確認我的想法。

首先,我創建了一個具有以下狀態和觸發器的狀態機(我使用Stateless ):

public enum States
{
    Idle,
    CreatingShape
}

public enum Triggers
{
    CreateShape,
    CancelCurrentAction
}

public class AppStateMachine : ObservableObject
{
    public StateMachine<States, Triggers> _stateMachine = new StateMachine<States, Triggers>(States.Idle);

    public AppStateMachine()
    {
        _stateMachine.Configure(States.Idle)
            .Permit(Triggers.CreateShape, States.CreatingJunction)
            .Ignore(Triggers.CancelCurrentAction);

        _stateMachine.Configure(States.CreatingShape)
            .Permit(Triggers.CancelCurrentAction, States.Idle)
            .Ignore(Triggers.CreateShape);
    }

然后,為了盡最大可能保留MVVM,我將UI按鈕綁定到“ SetCreationMode”命令,命令參數是要創建的形狀類型。 該命令如下所示:

XAML:

<RibbonButton x:Name="CreateSquare" Command="{Binding SetShapeCreationCommand}" CommandParameter="{Binding SquareShape}" Label="Create Square"/>

ViewModel:

    private void SetShapeCreationMode(ShapeTypeEnum ShapeType)
    {
        SelectedShapeType = ShapeType;
        _mainViewModel.AppState._stateMachine.Fire(Triggers.CreateShape);
    }

這樣,當您按下按鈕時,App State Machine將觸發“ CreatingShape”狀態,並將“ SelectedShapeType”變量設置為您的形狀(在示例中為正方形)。

之后,您仍然必須將畫布單擊作為事件進行處理(我正在玩MVVMLight中的EventToCommand ,但是還沒有取得飛躍。)事件處理方法類似於:

private void ModelViewport_MouseDown(object sender, MouseButtonEventArgs e)
    {
        if (_mainViewModel.AppState.IsInState(States.CreatingShape))
        {
            // retrieve mouse position

            // Execute command with retrieved position
            _CreateShapeViewModel.CreateShapeGraphicallyCommand.Execute(Position);
        }
    }

這樣,您就可以在選定的鼠標位置上使用命令,而不必在ViewHandler中的EventHandler中擁有大量代碼。

這是處理它的最好方法嗎? 我不知道,但我希望能有所幫助。

解決問題的方法有很多,因此沒有“正確”的方法,但是肯定有更好的方法。 我提供了一個供您考慮的選項,但是我永遠不能說我(或其他任何人)的設計是“最佳”的,這取決於您自己決定。

在您的解決方案中,您將使用第一個按鈕來設置名為“ CreatingShapeMode”的屬性。 您是否只想在任何特定時間設置一種模式? 如果是這樣,請考慮使用一個枚舉

我認為您的MouseDown事件處理程序不應執行繪制形狀的工作(因為您指出此列表只會變得更大)。 取而代之的是,我將使用一些自定義形狀繪制類,這些類都從基本“ ObjectCreator”類型繼承。 第一次單擊將設置正確的類型,例如circleType或squareType; 在畫布上的第二次單擊(將觸發鼠標按下事件)將僅調用createShape方法; 它不需要任何if語句,因為它是知道如何繪制形狀的對象創建者。

這是一個控制台應用程序,具有最少的代碼量來演示我要說的內容。 顯然,它需要大量的調整才能適合您的問題。

using System;

namespace ConsoleApplication27
{
    class Program
    {
        private static ObjectCreator creator;
        static void Main(string[] args)
        {
            creator = new CircleCreator();//change this to change the "shape" you get

            Console.ReadLine();
        }

        private static void YourMouseDownMethod()
        {
            creator.CreateObject();//no if statements required, the createObject method does the work
        }
    }



    abstract class ObjectCreator
    {
        public abstract void CreateObject();
    }

    class SquareCreator:ObjectCreator
    {
        public override void CreateObject()
        {
            Console.WriteLine("Create Square");
        }
    }

    class CircleCreator:ObjectCreator
    {
        public override void CreateObject()
        {
            Console.WriteLine("Create Circle");
        }
    }
}

您提到您希望將按鈕綁定到命令,您可以使用命令對象來設置正確的ObjectCreator類型。

    class SetObjectCreatorCommand:ICommand 
    {
        private readonly ObjectCreator _creator;

        public SetObjectCreatorCommand(ObjectCreator creator)
        {
            _creator = creator;//for each shape type you would create an instance of this class
            //e.g. pass in a circleCreator for circle shapes etc...
        }

        public bool CanExecute(object parameter)
        {
            return true;
        }

        public void Execute(object parameter)
        {
            //Set the creator class here, you can bind a button's command property to a Command object
        }

        public event EventHandler CanExecuteChanged;
    }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM