답변:
연결된 동작 을 만들어 순수한 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 참조를 포함해야합니다).
UpdatePropertySourceWhenEnterPressed
유효한 값에서 다른 유효한 값으로 변경 되면 PreviewKeyDown
불필요하게 이벤트 구독을 취소하고 다시 구독하는 것 입니다. 대신, 당신이 필요로하는 것은 그것이 있는지 아닌지를 확인하는 것 e.NewValue
입니다 null
. 그렇지 않은 경우 null
구독하십시오. 그렇지 않은 경우 null
구독을 취소합니다.
이것이 제가이 문제를 해결 한 방법입니다. 코드 뒤에있는 특수 이벤트 핸들러를 만들었습니다.
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에서 작동해야합니다. 이제이 하나의 이벤트 핸들러를이 기능이 필요한 모든 텍스트 상자에 추가 할 수 있습니다.
KeyBinding
내 UI는 어획량 키 입력하는 "기본"컨트롤을 가지고 있기 때문에이 방법을 발사하기 위해 XAML에서. UI 트리를 "Default"컨트롤로 전파하는 것을 중지하려면이 TextBox 내에서이를 포착해야합니다.
설명하는 작업을 수행하는 "순수한 XAML"방법이 있다고 생각하지 않습니다. 다음 과 같이 UpdateSourceTrigger 속성 을 설정하여 TextBox의 텍스트가 변경 될 때마다 (TextBox가 포커스를 잃을 때가 아니라) 업데이트되도록 바인딩을 설정할 수 있습니다 .
<TextBox Name="itemNameTextBox"
Text="{Binding Path=ItemName, UpdateSourceTrigger=PropertyChanged}" />
UpdateSourceTrigger를 "Explicit"로 설정 한 다음 TextBox의 PreviewKeyDown 이벤트 (Enter 키 찾기)를 처리하면 원하는 것을 얻을 수 있지만 코드 숨김이 필요합니다. 아마도 일종의 연결된 속성 (내 EnterKeyTraversal 속성 과 유사 )이 당신을 위해 작동 할 것입니다.
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
과 같이 보입니다 ( DelegateCommand
VM에서 스타일 구현을 사용하는 경우 ).
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 이벤트를 포착합니다.
이것은 원래 질문에 대한 답변이 아니라 @Samuel Jack 이 수락 한 답변 의 확장입니다 . 나는 내 자신의 응용 프로그램에서 다음을 수행했으며 Samuel의 솔루션의 우아함에 경외심을 느꼈습니다. 그것은 매우 깨끗하고 재사용이 가능합니다.TextBox
. 커뮤니티와 공유해야한다고 생각했습니다.
TextBoxes
Enter시 바인딩 소스를 업데이트하는 데 필요한 천 개의 창이있는 경우 Window
Resources
각 TextBox에 연결하는 대신 아래 XAML을 포함하여이 동작을 모든 항목에 연결할 수 있습니다 . 먼저 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>
필요한 경우 항상 Grid
대상 TextBox를 포함하는 Window 자식 요소 중 하나의 리소스 (예 : a )에 스타일을 넣어 범위를 제한 할 수 있습니다 .
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();
}
}
여기에 첨부 된 동작을 사용하여 매우 우아하게 대답했으며 거의 모든 것에 대해 선호하는 방법입니다.
개인적으로 Markup Extension을 사용하는 것이 더 깔끔한 접근 방식이라고 생각합니다.
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>