2016-03-23 12 views
2

私のアプリケーションにアンドゥ/リドゥ機能を実装していて、問題が発生しました。基本的には、アイテムのリストにバインドされたコンボボックスがあります。次に、コンボボックスで選択した項目のDataContextとその項目のプロパティにバインドされたテキストを持つ複数のテキストボックスがあります。ユーザーが元に戻すコマンドを発行し、元に戻す項目がテキストボックスのテキストの変更である場合、コンボボックス内の関連する項目を最初に選択します。次に、元のテキストでテキストボックスを直ちに更新したいと思います。これは、理論的には、選択した項目のデータバインドプロパティを更新する必要があります。ただし、選択範囲が変更されますが、テキストボックスのテキストプロパティを変更する前にデータバインドされたテキストボックスが更新されないため、コンボボックスで前に選択したアイテムのプロパティ値が変更されています。ComboBox.SelectedItemにバインドされたTextbox DataContextを強制的に更新できますか?

これは私のコンボボックスである:

<ComboBox x:Name="myComboBox" 
      ItemsSource="{Binding MyItems, UpdateSourceTrigger=PropertyChanged}" 
      DisplayMemberPath="Name" 
      SelectionChanged="myComboBox_SelectionChanged" /> 

これは私のテキストボックスである(のDataContextは、親グリッドに設定されているのDataContextは、テキストボックス自体に設定されている場合、同じ現象が発生。)

<TextBox x:Name="purposeTxtBox" 
     Text="{Binding Purpose, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" /> 
アンドゥが開始されると、私が使用しています

コード(抜粋)は次のとおりです。

case UndoAction.PURPOSE_CHANGE: 
    SelectComboBoxItem(myComboBox, itemToSelect); 
    purposeTxtBox.Text = itemValue; 
    FocusAndSelect(purposeTxtBox); 

そして、 SelectComboBoxItemメソッド:

private void SelectComboBoxItem(ComboBox box, object item) 
{ 
    Dispatcher.BeginInvoke(DispatcherPriority.Input, 
          new Action(delegate() 
          { 
           box.SelectedItem = item; 
          })); 
} 

私のDataContextの 'Purpose'プロパティを更新することができます。それはうまくいきますが、私がそのようにすれば、それは集中したり選択したりしません。さらに、すべてのバインディングが既に存在し、それらの一部が整数にバインドされているため、テキストを更新して 'マジック'が翻訳を処理できるようにしました。そうすれば、どんなテキストボックスでも一般的な解決策を得ることができます。私は、問題はこれであると信じて

private void FocusAndSelect(TextBox box) 
{ 
    box.SelectAll(); 
    Dispatcher.BeginInvoke(DispatcherPriority.Input, 
          new Action(delegate() 
          { 
           box.Focus();   // Set Logical Focus 
           Keyboard.Focus(box); // Set Keyboard Focus 
          })); 
} 
+0

サイドコメント:デリゲートをデリゲートのリストに追加することで元に戻すことができます。これにより、一部のプロパティ値を簡単に戻すことができます。次に、このデリゲートを単純呼び出ししてリストから削除します。 – Sinatr

+0

@Sinatr、あなたはあなたが私に指し示すことができるものの例を持っていますか? – JLB

答えて

2

:完全期すために、ここで私のFocusAndSelect機能であるあなたは、コンボボックスの値を更新するシステムを言っているが、あなたが使っている

Dispatcher.BeginInvoke(DispatcherPriority.Input, 
         new Action(delegate() 
         { 
          box.SelectedItem = item; 
         })); 

非同期メソッド(私は思う)。これは、UIスレッド上で実行できるときに実行することを意味します(ただし、UIスレッドから呼び出していると思いますが)とにかく - 起こることはありませんです。

次の行では、purposeTxtBox.Text = itemValueを直接設定していますが、チェックボックスでまだ選択された値が更新されていないため、値の変更はまだ実行されていません。

最終的には、あなたのロジックを自分のUIに混ぜているのが問題です。 UIに入り、データが更新されるように何かを変更して、選択した値を変更してはいけません。データを直接変更し、必要に応じてUIを更新させる必要があります。

ブール値を表示するチェックボックスを使用すると、UIにチェックボックスを作成してチェックし、ビジネスロジックまたはデータロジックでチェックボックスをオフにする必要はありません。 value - あなたのプログラムは、そうでなければならず、チェックボックスはそれにバインドするだけです。その値をfalseに変更する必要があります。ブール値をfalseにし、チェックボックスに移動せず、チェックボックスをオフにして、バインディングをブール値に更新します。それは物事を逆行させるようなものです。これを持っているあなたがやるべきこと

private Item selectedItem; 
    public Item SelectedItem 
    { 
     get { return selectedItem; } 
     set 
     { 
      if (selectedItem != value) 
      { 
       selectedItem = value; 
       //perform your ItemChange events here, if you have any 
       OnPropertyChanged("SelectedItem"); 
      } 
     } 
    } 

あなたのコンボボックスは、それがそのプロパティへのSelectedItemだバインドする必要があります。テキストボックスは、テキストプロパティをグリッドのデータコンテキストSelectedItem.SomeTextプロパティにバインドする必要があります。あなたが選択を変更すると

、あなたはその後、などなどなど

あなただけの変更イベントをトリガし、戻ってきて、それを処理するために、UIスレッドを待ち、コンポーネントを変更、UIに行っていませんオブジェクト - UIが反応します。

これはずっと優れた方法です。 MVVMとMVCのようなモデルがあり、UIとロジックをこのように分割する理由もこのためです。

直接あなたの問題(私はそれが役に立つことを願っています)が、あなたが代表者で元に戻す整理することができますに関連していない
+0

こんにちは@Joe、詳細な対応をありがとうございます。あなたが言ったように私はやろうとしています。 SelectedItemプロパティを使用して、ComboBoxとGridへのバインディングを更新しました。拘束力が問題視されている限り、それは素晴らしいと思われます。しかし、もし私がそれをし、その後、SelectedItemを行う= item; item.Purpose = "いくつかのテキスト"; UIが応答していません。これらの呼び出しは実際に別のクラスにあることをあなたが持ち出して以来、私は言及する必要があります。だから私はDispatcher.Invokeを使っていたのです...おそらく私はそれが何をしているのか誤解していましたか? – JLB

+0

コンボボックスで選択した項目を変更しないため、UIは更新されませんか?あなたのデータコンテキストにINotifyPropertyChangedを実装しましたか?どのように縛っていますか? Mode = TwoWayを使用していますか? – Joe

+0

別のスレッド(UIスレッドではありません)で直接UIを変更しようとしているときは、Dispatcher.Invokeを使用する必要があります。 プロパティにバインドしている場合は、そのプロパティを任意のスレッドから更新できます。UIはそれ自体を更新します。INotifyPropertyChangedとモードが正しい場合は更新する必要があります。 – Joe

1

:前の値を設定し

readonly Stack<Action> _undo = new Stack<Action>(); 

string _someProperty; 
// property to bind 
public string SomeProperty 
{ 
    get { return _someProperty; } 
    set 
    { 
     var old = _someProperty; // capture old value 
     _undo.Push(() => 
     { 
      _someProperty = old; 
      OnPropertyChanged(); 
     }); 
     _someProperty = value; 
     OnPropertyChanged(); 
    } 
} 

void Undo() 
{ 
    if (_undo.Count > 0) 
     _undo.Pop()(); 
} 

(の結合を介して)プロパティの値を変更します記録デリゲート。 Undo()を呼び出すと、のデリゲートが最後から最初(LIFO)まで再生されます。

デリゲートに選択やフォーカス操作を追加することもできます(これが良い考えであるかどうかは不明です)。

関連する問題