2012-06-18 19 views
8

コマンドがトリガされる前にバインディングを更新する方法はありますか?キーボードのショートカットからアクセスできるコマンドを使用して編集および保存できるテキストフィールドがいくつかあります。テキストフィールドがフォーカスを失ったときにのみバインディングが通常更新されるため、キーを押してデータを保存すると、最後の変更は保持されません。その代わりに、まずテキストフィールドからタブアウトして更新してから保存する必要があります。コマンドがトリガされたときに強制的に更新するように強制する

更新を強制的にエレガントにする方法はありますか?私はMVVMを使用していますが(MVVMフレームワークはありません)、UI固有の事項をコマンドコードから守りたいと思います。また、すべての変更でバインドを更新する必要はありません。フォーカスが失われた場合にのみ更新するようにしても問題ありません。 OnPropertyChanged("YourPropertyName")がバウンド値を更新するビューを警告する呼び出す

<TextBox Text="{Binding UserName,Mode=TwoWay,UpdateSourceTrigger=Explicit}"> 
    <i:Interaction.Behaviors> 
     <h:TextBoxUpdatesTextBindingOnPropertyChanged /> 
    </i:Interaction.Behaviors> 
</TextBox> 

public class TextBoxUpdatesTextBindingOnPropertyChanged : Behavior<TextBox> 
{ 
    protected override void OnAttached() 
    { 
     base.OnAttached(); 

     AssociatedObject.TextChanged += TextBox_TextChanged; 
    } 

    protected override void OnDetaching() 
    { 
     base.OnDetaching(); 

     AssociatedObject.TextChanged -= TextBox_TextChanged; 
    } 

    void TextBox_TextChanged(object sender, TextChangedEventArgs e) 
    { 
     var bindingExpression = AssociatedObject.GetBindingExpression(TextBox.TextProperty); 
     bindingExpression.UpdateSource(); 
    } 
} 

XAML:ここ

答えて

2

私は明示的に値を求める前に、他のいくつかの要素にフォーカスを設定することで、今、これを解決しました。これは明らかに、現在フォーカスを持っている要素を失い、バインディングを更新します。

私はフォーカスを設定するために、他の質問の回答に触発された添付プロパティを記述しました。また、my other questionと一緒に、やや自動化しました。

だから、それを使用するために、私は基本的要素に自分の財産を添付し、この場合、タブコントロールに:私の見解モデルで

<TabControl c:Util.ShouldFocus="{Binding ShouldClearFocus}"> 

、私は標準のプロパティ調達でシンプルなブールプロパティShouldClearFocusを持っていますa PropertyChangedEventです。したがって、データバインディングが機能します。フォーカスをリセットしたいときは、ShouldClearFocustrueに設定するだけです。添付プロパティは自動的にフォーカスを設定し、プロパティ値を再度リセットします。そうすれば、ShouldClearFocusを設定しなくても、間にfalseに設定することができます。

添付プロパティは、その変更ハンドラとしてこれを持つ標準的な実装である:

public static void ShouldFocusChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e) 
{ 
    if (!(bool)e.NewValue || !(obj is FrameworkElement)) 
     return; 

    FrameworkElement element = (FrameworkElement)obj; 
    if (element.Focusable) 
     element.Focus(); 

    // reset value 
    BindingExpression bindingExpression = BindingOperations.GetBindingExpression(obj, ShouldFocusProperty); 
    if (bindingExpression != null) 
    { 
     PropertyInfo property = bindingExpression.DataItem.GetType().GetProperty(bindingExpression.ParentBinding.Path.Path); 
     if (property != null) 
      property.SetValue(bindingExpression.DataItem, false, null); 
    } 
    else 
     SetShouldFocus(obj, false); 
} 
+0

これまでのところ、このフォーラムで提案された最も柔軟で洗練されたソリューションです。うまくいきました - ありがとうございます – BCA

2

は、私は似たような状況のために使用されるクラスです。

それ以外の場合は、WPF TextBox DataBind on EnterKey pressをご覧ください。

+0

問題データ→ビューからのバインディングではなく、それは逆の方法です。私はデータ側で更新を強制したい。 – poke

+0

私が投稿したリンクは、特定のイベントがコントロールにスローされたときに(データ側の)バインディングを更新するビヘイビアを作成する方法を示しています。 KeyPressedまたはKeyUpイベントまでワイヤリングすると、必要な効果が得られます。 –

+0

その解決策には、 'UpdateSourceTrigger'を' PropertyChanged'に設定することが含まれています。これは既に十分なはずですが、質問で言えば、私が望むものではありません。私はトリガを変更せずにこれを行う方法を探しています。 – poke

0

私は自分のプロジェクトで次のものを使用しています。問題は、ボタンがフォーカスを受け取らなかったことです。私app.xaml.csで

EventManager.RegisterClassHandler(typeof(Button), ButtonBase.ClickEvent, new RoutedEventHandler(ButtonClick)); 

    private void ButtonClick(object sender, RoutedEventArgs e) 
    { 
     if (sender != null && sender is Button) 
     { 
      (sender as Button).Focus(); 
     } 
    } 
+0

その解決策は受け入れられるようですが、私はクリックされたボタンがありません。コマンドは 'KeyBinding'によって呼び出されます。 – poke

9

ではなく、フォーカスを変更する現在の要素がTextBoxであれば、あなたもちょうどバインディングソースを更新することができます。あなたは他のコントロールと似たようなことをすることができますが、私の経験ではこの問題があったのはTextBoxでした。

// if the current focused element is textbox then updates the source. 
var focusedElement = Keyboard.FocusedElement as FrameworkElement; 

if (focusedElement is TextBox) 
{ 
    var expression = focusedElement.GetBindingExpression(TextBox.TextProperty); 
    if (expression != null) expression.UpdateSource(); 
} 
+0

良い考えですが、MVVMの設定でどこに入れていますか? - 実際にバインディング式に 'UpdateSource'メソッドがあることも知りませんでした。ありがとう! – poke

+0

あなたはあなたの付属施設に置いておかなければならないと思います。焦点を前進させることの改善だと思っただけです。この機能を処理するために、Enterprise LibraryのCompositeCommandのようなものを使用することもできますが、実際はアプリケーションの構造によって異なります。 –

0

私はこれを行うための最善の方法は、結合のUpdateSourceTrigger=PropertyChangedを設定し、イベントストリームを絞るReactive Extensionsを使用することだと思います。この方法では、ユーザーが一定の時間(1/2秒が良い量です)入力を停止した後にのみプロパティが更新されます。

プロパティは常に更新されませんが、フォーカスを変更しなくても更新されるため、これは素晴らしいことです。まさにあなたが望むもの。

Here is some really useful information on Rx

バインド可能IObservables hereを持ってMVVMの拡張ライブラリがあります。ここにある例は、私が提案したものを正確に実行しています。

あなたはエレガンスを求めました。ここにあります。そしてそれほど多くの仕事はありません。

3

TextBoxでは、ソースがテキストボックスの値で更新されるタイミングを定義するテキストバインディングにUpdateSourceTriggerを設定する必要があります。デフォルトではTextプロパティの場合はLostFocusです。これはまさに起きているものです。フォーカスを失ったときにのみソースを更新します。 UpdateSourceTriggerの値をPropertyChangedに設定すると、テキストボックスの値が変更されるたびに更新されます。

<TextBox ... Text="{Binding Foo, UpdateSourceTrigger=PropertyChanged}"/> 

パスは、バインディングのコマンドを使用する場合、デフォルトのプロパティであるので、上記のここでの柔軟な/エレガントなものがたくさんありますPath=Foo

+0

質問から:*「すべての変更でバインディングを変更する必要はありません」*。 – poke

0

に等しいです。 私はこの問題に対処するために、コントロールNameとCommandParameterプロパティを使用しています。

私はWPFに新しいので、私は物事を正しい方法をやっていないよなら、私に知らせ;)

XAML:

<dxc:SimpleButton Name="okButton" Content="OK" Command="{Binding OKCommand}" CommandParameter="{Binding ElementName=okButton}" IsDefault="True" Width="100"/> 

のC#:

private void OnOKCommand(object obj) 
    { 
     ((System.Windows.FrameworkElement)obj).Focus(); 

     // other code here 
    } 
関連する問題