2016-12-19 17 views
0

我正在設計一個CAD應用程序,我不確定如何以一種乾淨的方式設計鼠標事件的事件處理。如何正確過濾鼠標事件可能導致的不同操作?

爲簡單起見,假設我在UI中有兩個按鈕: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. 
    } 
} 

然而,似乎臭,特別是因爲我將有許多不同的創建命令,並迅速失控。它也打破了MVVM,因爲我希望將CreateSquare和CreateCircle按鈕綁定到命令,而不必擔心MouseDown事件。

我考慮過的一種替代方法是狀態機,我將確定應用程序的所有可能模式,並以更優雅的方式複製上面的if-nest或switch-case。它仍然覺得它不是正確的解決方案,但它會更好。

第二個選擇 - 這是一個我覺得是正確的 - 會以某種方式,我CreateSquare命令中,聽的MouseDown事件:

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

但我不知道這甚至是可能的。

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

+0

它被標記爲MVVM,因此您應該爲視圖模型中的每個按鈕設置一個ICommand,然後它將記住用戶正在做什麼(創建形狀x)。當用戶再次點擊畫布時,您應該再次擁有發送點擊座標的ICommand。我在用戶控件的畫布中管理這個,所以它不必擔心視圖模型在做什麼。視圖模型然後可以通過在其形狀集合中的給定位置添加正確的形狀來作出響應。 – Will

+1

我最終做了什麼。好眼睛@會! – celsound

回答

0

有很多不同的方法來解決你的問題,所以沒有「正確的」方式,但是肯定有更好的方法。我包括一個給你想想,但我永遠不能說我的(或任何其他人)設計是「最好的」,這取決於你自己決定。

在您的解決方案中,您使用第一個按鈕按下來設置名爲「CreatingShapeMode」的屬性。你只想要在特定的時間設置一種模式嗎?如果是這樣,考慮使用enum

在我看來你的MouseDown事件處理程序不應該做繪製形狀的工作(正如你指出這個列表只會變得更大)。相反,我會考慮使用一些自定義的形狀繪圖類,這些類都是從基礎「ObjectCreator」類型繼承的。您第一次點擊按鈕會設置正確的類型,例如circleType或squareType;第二次點擊畫布(會觸發鼠標按下事件)只會調用createShape方法;它不需要任何if語句,因爲它是知道如何繪製形狀的objectcreator。

這是一個控制檯應用程序,它有最少量的代碼來演示我想說的話。顯然它需要很多適應性來適應你的問題。

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

感謝您的回答,它指出了我的正確方向。請參閱下面的答案,閱讀我最終如何做到這一點。 – celsound

1

對於那些閱讀,這是我如何解決這個問題。上面的答案幫助證實了我的想法。

首先,我創建了一個狀態機(我用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"/> 

視圖模型:

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

這樣一來,當你按應用程序狀態機打響 「CreatingShape」 狀態的按鈕, 「SelectedShapeType」變量設置爲您的形狀(在示例中爲正方形)。

之後,你仍然必須處理單擊畫布作爲一個事件(我使用EventToCommand從MVVMLight玩弄,但沒有取得飛躍呢。)事件處理方法進行類似:

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

通過這種方式,您可以使用具有所選鼠標位置的命令,而無需在ViewModel中的EventHandler中使用大量代碼。

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

相關問題