TextBox
のデフォルトのデータバインディングはTwoWay
であり、TextBox
がフォーカスを失ったときにのみテキストをプロパティにコミットします。
を押したときにデータバインディングを実行する簡単なXAML方法はありますか Enter TextBox
?のキー。コードビハインドで行うのは非常に簡単ですが、このTextBox
が複雑なDataTemplate
の中にあると想像してください。
attached behaviour を作成することにより、自分自身を純粋なXAMLアプローチにすることができます。
このようなもの:
public static class InputBindingsManager
{
public static readonly DependencyProperty UpdatePropertySourceWhenEnterPressedProperty = DependencyProperty.RegisterAttached(
"UpdatePropertySourceWhenEnterPressed", typeof(DependencyProperty), typeof(InputBindingsManager), new PropertyMetadata(null, OnUpdatePropertySourceWhenEnterPressedPropertyChanged));
static InputBindingsManager()
{
}
public static void SetUpdatePropertySourceWhenEnterPressed(DependencyObject dp, DependencyProperty value)
{
dp.SetValue(UpdatePropertySourceWhenEnterPressedProperty, value);
}
public static DependencyProperty GetUpdatePropertySourceWhenEnterPressed(DependencyObject dp)
{
return (DependencyProperty)dp.GetValue(UpdatePropertySourceWhenEnterPressedProperty);
}
private static void OnUpdatePropertySourceWhenEnterPressedPropertyChanged(DependencyObject dp, DependencyPropertyChangedEventArgs e)
{
UIElement element = dp as UIElement;
if (element == null)
{
return;
}
if (e.OldValue != null)
{
element.PreviewKeyDown -= HandlePreviewKeyDown;
}
if (e.NewValue != null)
{
element.PreviewKeyDown += new KeyEventHandler(HandlePreviewKeyDown);
}
}
static void HandlePreviewKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter)
{
DoUpdateSource(e.Source);
}
}
static void DoUpdateSource(object source)
{
DependencyProperty property =
GetUpdatePropertySourceWhenEnterPressed(source as DependencyObject);
if (property == null)
{
return;
}
UIElement elt = source as UIElement;
if (elt == null)
{
return;
}
BindingExpression binding = BindingOperations.GetBindingExpression(elt, property);
if (binding != null)
{
binding.UpdateSource();
}
}
}
次に、XAMLでInputBindingsManager.UpdatePropertySourceWhenEnterPressedProperty
プロパティは、更新するときに Enter キーが押されました。このような
<TextBox Name="itemNameTextBox"
Text="{Binding Path=ItemName, UpdateSourceTrigger=PropertyChanged}"
b:InputBindingsManager.UpdatePropertySourceWhenEnterPressed="TextBox.Text"/>
(InputBindingsManagerを配置したネームスペースを指すXAMLファイルのルート要素に、「b」のxmlns clr-namespace参照を含めることを確認する必要があります)。
あなたが説明していることを行う「純粋なXAML」の方法があるとは思わない。次のように pdateSourceTrigger プロパティを設定することにより、TextBox内のテキストが変更されるたびに(TextBoxがフォーカスを失うときではなく)更新するようにバインディングを設定できます。
<TextBox Name="itemNameTextBox"
Text="{Binding Path=ItemName, UpdateSourceTrigger=PropertyChanged}" />
UpdateSourceTriggerを "Explicit"に設定し、TextBoxのPreviewKeyDownイベント(Enterキーを探す)を処理した場合、目的を達成できますが、コードビハインドが必要になります。おそらく、何らかの種類の添付プロパティ(私の EnterKeyTraversal プロパティに似ています)woudldが機能します。
これが私がこの問題を解決した方法です。コードビハインドに入る特別なイベントハンドラーを作成しました。
private void TextBox_KeyEnterUpdate(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter)
{
TextBox tBox = (TextBox)sender;
DependencyProperty prop = TextBox.TextProperty;
BindingExpression binding = BindingOperations.GetBindingExpression(tBox, prop);
if (binding != null) { binding.UpdateSource(); }
}
}
次に、これをXAMLのKeyUpイベントハンドラーとして追加しました。
<TextBox Text="{Binding TextValue1}" KeyUp="TextBox_KeyEnterUpdate" />
<TextBox Text="{Binding TextValue2}" KeyUp="TextBox_KeyEnterUpdate" />
イベントハンドラーはsender
参照を使用して、独自のバインディングを更新させます。イベントハンドラは自己完結型なので、複雑なDataTemplateで動作するはずです。これで、この1つのイベントハンドラーを、この機能を必要とするすべてのテキストボックスに追加できます。
TextBoxから継承する独自のコントロールを簡単に作成し、プロジェクト全体で再利用できます。
これに似た何かが動作するはずです:
public class SubmitTextBox : TextBox
{
public SubmitTextBox()
: base()
{
PreviewKeyDown += new KeyEventHandler(SubmitTextBox_PreviewKeyDown);
}
void SubmitTextBox_PreviewKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter)
{
BindingExpression be = GetBindingExpression(TextBox.TextProperty);
if (be != null)
{
be.UpdateSource();
}
}
}
}
このステップを回避する方法があるかもしれませんが、そうでなければ(Explicitを使用して)このようにバインドする必要があります。
<custom:SubmitTextBox
Text="{Binding Path=BoundProperty, UpdateSourceTrigger=Explicit}" />
Benとausadminの両方のソリューションを組み合わせると、非常にMVVMフレンドリーなソリューションになります。
<TextBox Text="{Binding Txt1, Mode=TwoWay, UpdateSourceTrigger=Explicit}">
<TextBox.InputBindings>
<KeyBinding Gesture="Enter"
Command="{Binding UpdateTextBoxBindingOnEnterCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type TextBox}}}" />
</TextBox.InputBindings>
</TextBox>
...つまり、TextBox
自体をパラメーターとしてCommand
に渡します。
これにより、Command
は次のようになります(VMでDelegateCommand
スタイルの実装を使用している場合):
public bool CanExecuteUpdateTextBoxBindingOnEnterCommand(object parameter)
{
return true;
}
public void ExecuteUpdateTextBoxBindingOnEnterCommand(object parameter)
{
TextBox tBox = parameter as TextBox;
if (tBox != null)
{
DependencyProperty prop = TextBox.TextProperty;
BindingExpression binding = BindingOperations.GetBindingExpression(tBox, prop);
if (binding != null)
binding.UpdateSource();
}
}
このCommand
実装は、任意のTextBox
に使用できますが、コードビハインドにコードがないことをお勧めします。ただし、これを独自のクラスに配置して、System.Windows.Controls
をVMで。コードガイドラインの厳格さに依存します。
これは私にとって非常に簡単で、AttachedBehaviourを追加するよりも簡単なアプローチです(これも有効なソリューションです)。デフォルトのUpdateSourceTrigger(TextBoxの場合はLostFocus)を使用してから、コマンドにバインドされたEnterキーにInputBindingを追加します。
Xamlは次のとおりです
<TextBox Grid.Row="0" Text="{Binding Txt1}" Height="30" Width="150">
<TextBox.InputBindings>
<KeyBinding Gesture="Enter"
Command="{Binding UpdateText1Command}"
CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type TextBox}},Path=Text}" />
</TextBox.InputBindings>
</TextBox>
次に、コマンドメソッドは
Private Function CanExecuteUpdateText1(ByVal param As Object) As Boolean
Return True
End Function
Private Sub ExecuteUpdateText1(ByVal param As Object)
If TypeOf param Is String Then
Txt1 = CType(param, String)
End If
End Sub
TextBoxはPropertyにバインドされています
Public Property Txt1 As String
Get
Return _txt1
End Get
Set(value As String)
_txt1 = value
OnPropertyChanged("Txt1")
End Set
End Property
これまでのところ、これはうまく機能しているようで、TextBoxのEnter Keyイベントをキャッチします。
よりシンプルなのは、コードビハインドに何も追加せずに、UpdateSourceTrigger
のバインディングでPropertyChanged
をTextBox
に設定するだけです。ちょうどこのような:
<TextBox Text="{Binding Path=BoundProperty, UpdateSourceTrigger=PropertyChanged}"/>
わたしにはできる。
これは元の質問に対する答えではなく、@-Samuel Jackによる 受け入れられた答え の拡張です。私は自分のアプリケーションで次のことを行いましたが、サミュエルのソリューションの優雅さにa敬の念を抱いていました。 TextBox
だけでなく、任意のコントロールで使用できるため、非常にクリーンで再利用可能です。これはコミュニティと共有すべきだと思いました。
Enterキーでバインドソースをすべて更新する必要があるTextBoxes
が1,000個あるウィンドウがある場合、以下のXAMLをWindow
Resources
各TextBoxに添付するのではなく。まず、 Samuelの投稿 に従って、添付の動作を実装する必要があります。
<Window.Resources>
<Style TargetType="{x:Type TextBox}" BasedOn="{StaticResource {x:Type TextBox}}">
<Style.Setters>
<Setter Property="b:InputBindingsManager.UpdatePropertySourceWhenEnterPressed" Value="TextBox.Text"/>
</Style.Setters>
</Style>
</Window.Resources>
必要に応じて、ターゲットTextBoxを含むWindowの子要素(つまりGrid
)のいずれかのリソースにStyleを配置することにより、いつでもスコープを制限できます。
TextBoxでMultiBindingを使用している場合は、BindingOperations.GetMultiBindingExpression
の代わりにBindingOperations.GetBindingExpression
メソッドを使用する必要があります。
// Get the correct binding expression based on type of binding
//(simple binding or multi binding.
BindingExpressionBase binding =
BindingOperations.GetBindingExpression(element, prop);
if (binding == null)
{
binding = BindingOperations.GetMultiBindingExpression(element, prop);
}
if (binding != null)
{
object value = element.GetValue(prop);
if (string.IsNullOrEmpty(value.ToString()) == true)
{
binding.UpdateTarget();
}
else
{
binding.UpdateSource();
}
}
これは私のために働く:
<TextBox
Text="{Binding Path=UserInput, UpdateSourceTrigger=PropertyChanged}">
<TextBox.InputBindings>
<KeyBinding Key="Return"
Command="{Binding Ok}"/>
</TextBox.InputBindings>
</TextBox>
添付のビヘイビアを使用して非常にエレガントに回答しました。ほとんどすべての場合に私の好みの方法です。
個人的には、マークアップ拡張機能を使用する方がよりクリーンなアプローチだと思います。
public class UpdatePropertySourceWhenEnterPressedExtension : MarkupExtension
{
public override object ProvideValue(IServiceProvider serviceProvider)
{
return new DelegateCommand<TextBox>(textbox => textbox.GetBindingExpression(TextBox.TextProperty).UpdateSource());
}
}
<TextBox x:Name="TextBox"
Text="{Binding Text}">
<TextBox.InputBindings>
<KeyBinding Key="Enter"
Command="{markupExtensions:UpdatePropertySourceWhenEnterPressed}"
CommandParameter="{Binding ElementName=TextBox}"/>
</TextBox.InputBindings>
</TextBox>