2016-12-19 5 views
0

私はCADアプリケーションを設計しており、マウスイベントのイベント処理をきれいに設計する方法がわかりません。マウスイベントの結果として生じるさまざまなアクションを適切にフィルタリングするにはどうすればよいですか?

簡単にするために、UIにCreateSquareとCreateCircleという2つのボタンがあるとします。ユーザーがこれらのいずれかをクリックすると、アプリケーションはキャンバス内のクリックを待ち、ユーザーがクリックした後、特定された位置に四角形または円を作成します。私が理解から

は、それは私のキャンバスでの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. 
    } 
} 

をコマンドとそれはすぐに手を抜く。 CreateSquareとCreateCircleのボタンをコマンドにバインドして、MouseDownイベントについて心配する必要がないので、MVVMも壊れます。

私が考えてきた1つの代替案は、アプリケーションの可能なモードをすべて決定し、上記のネストまたはスイッチケースをもう少しエレガントに複製するステートマシンです。それはまだ正しい解決策ではないように感じますが、それはより良いでしょう。

第二の代替 - MouseDownイベントに耳を傾け、私のCreateSquareコマンド内で、何とかするだろう - 私は正しいものであると感じいずれかになります。

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

しかし、私はしないでくださいそれが可能であるかどうかを知る。

この状況を処理する正しい方法は何ですか?ここに私を助けてくれるデザインパターンがあると確信しています。

+0

MVVMのタグが付いているので、ビューモデル内の各ボタンにICommandを設定する必要があります。これにより、ユーザーが行っていること(シェイプxの作成)が記憶されます。ユーザーがキャンバスを再びクリックすると、再びクリックの座標が送信されるICommandが必要になります。私はこれを管理しているUserControlにキャンバスを持っているので、ビューモデルが何をしているのか心配する必要はありません。次に、ビューモデルは、形状の集合内の所定の位置に正しい形状を追加することによって応答することができる。 – Will

+1

私がやったことはかなり終わりです。良い目@ウィル! – celsound

答えて

0

あなたの問題を解決するにはさまざまな方法がありますが、正しい方法はありませんが、確かに良い方法があります。私はあなたのために考えてみたいですが、僕(または他の人)のデザインは "最高"であるとは決して言えません。

ソリューションでは、最初のボタンを押して「CreatingShapeMode」という名前のプロパティを設定しています。特定の時間に1つのモードのみを設定したいですか?もしそうなら、使用を検討してくださいenum

私の意見では、あなたのMouseDownイベントハンドラはシェイプを描く作業をしてはいけません。このリストはもっと大きくなるだろうと指摘しています。代わりに、基本の "ObjectCreator"タイプから継承したカスタムシェイプ描画クラスを使用してみます。最初のボタンクリックで正しいタイプが設定されます。 circleTypeまたはsquareType;キャンバスの2回目のクリック(マウスダウンイベントを発生させる)は、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"/> 

のViewModel:

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

アプリステートマシンは "CreatingShape" 状態を起動し、ボタンを押すだから仕方、コマンドはこのような何かを行きます"SelectedShapeType"変数がシェイプ(この例では四角形)に設定されています。

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); 
     } 
    } 
:その後

、あなたはまだイベントとしてキャンバスのクリックを処理する必要が(私はMVVMLightからEventToCommandを使用していじるのですが、まだ飛躍を行っていない)イベント処理メソッドは次のようになります

このようにして、選択したマウスの位置でコマンドを使用すると、代わりにViewModelにあるEventHandlerに大量のコードを入れる必要がなくなります。

これを処理する最善の方法はありますか?私は知らないが、それが助けてくれることを願っている。

関連する問題