Enter tuşuna basıldığında TextBox'ı bağlayın


108

Varsayılan veri bağlama TextBoxaçıktır TwoWayve metni mülke yalnızcaTextBox odağı kaybolduğunda .

? EnterÜzerindeki tuşa bastığımda veri bağlamanın gerçekleşmesini sağlamanın kolay XAML yolu var mı TextBox? Bunun arkasındaki kodda yapılmasının oldukça kolay olduğunu biliyorum, ancak bunun TextBoxbir karmaşıklığın içinde olup olmadığını hayal edin DataTemplate.

Yanıtlar:


138

Ekli bir davranış oluşturarak kendinize saf bir XAML yaklaşımı yapabilirsiniz .

Bunun gibi bir şey:

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();
        }
    }
}

Ardından XAML'nizde InputBindingsManager.UpdatePropertySourceWhenEnterPressedPropertyözelliği, Entertuşa basıldığında güncellenmesini istediğiniz bir özelliğe ayarlarsınız . Bunun gibi

<TextBox Name="itemNameTextBox"
         Text="{Binding Path=ItemName, UpdateSourceTrigger=PropertyChanged}"
         b:InputBindingsManager.UpdatePropertySourceWhenEnterPressed="TextBox.Text"/>

(Yalnızca, InputBindingsManager'ı yerleştirdiğiniz her ad alanına işaret eden XAML dosyanızın kök öğesine "b" için bir xmlns clr-ad alanı başvurusu eklediğinizden emin olmanız gerekir).


11
Gelecekteki okuyucuları birkaç dakika uğraşmadan kurtarmak için bunu projemde kullandım ve yukarıda sağlanan örnek XAML doğru çalışmadı. Kaynak, her karakter değişikliğinde güncellenmiştir. "UpdateSourceTrigger = PropertyChanged" ifadesinin "UpdateSourceTrigger = Explicit" olarak değiştirilmesi sorunu çözdü. Şimdi hepsi istendiği gibi çalışıyor.
ihake

1
@ihake: Önerdiğin değişikliğin aynı zamanda odak kaybolmasına ilişkin güncellemeyi de engelleyeceğini düşünüyorum
VoteCoffee

4
UpdateSourceTrigger = PropertyChanged, gerçek bir sayı yazarken uygun olmayabilir. Örneğin "3." bir şamandıraya bağlandığında soruna neden olur. Eklenen davranışa ek olarak UpdateSourceTrigger'ı (veya LostFocus'a ayarlamayı) belirtmemenizi tavsiye ederim. Bu, her iki dünyanın da en iyisini verir.
David Hollinshead

2
Harika iş! Minik nit-pick: UpdatePropertySourceWhenEnterPressedGeçerli bir değerden farklı bir geçerli değere değiştiğinde, PreviewKeyDowngereksiz yere etkinliğe aboneliğinizi iptal ediyorsunuz ve yeniden abone oluyorsunuz . Bunun yerine, ihtiyacınız olan tek şey, olup olmadığını kontrol e.NewValueetmektir null. Değilse nullabone olun; aksi takdirde, eğer null, sonra abonelikten çıkın.
Nicholas Miller

1
Bu çözümü seviyorum, gönderdiğiniz için teşekkürler! Bu davranışı uygulamanızdaki çok sayıda TextBox'a eklemesi gereken herkes için, bunu çok kolay bir şekilde nasıl başarabileceğinize dair bu cevaba bir uzantı gönderdim. Buradaki
Nik

50

Bu sorunu bu şekilde çözdüm. Arkasındaki koda giren özel bir olay işleyicisi oluşturdum:

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(); }
    }
}

Sonra bunu XAML'de bir KeyUp olay işleyicisi olarak ekledim:

<TextBox Text="{Binding TextValue1}" KeyUp="TextBox_KeyEnterUpdate" />
<TextBox Text="{Binding TextValue2}" KeyUp="TextBox_KeyEnterUpdate" />

Olay işleyicisi, senderkendi bağlantısının güncellenmesini sağlamak için başvurusunu kullanır . Olay işleyicisi kendi kendine yeten olduğundan, karmaşık bir DataTemplate içinde çalışması gerekir. Bu tek olay işleyici artık bu özelliğe ihtiyaç duyan tüm metin kutularına eklenebilir.


8
Bir şey bana bunun küçümsenmemiş bir gönderi olduğunu söylüyor. Birçok yanıt popülerdir çünkü bunlar "XAML-y" dir. Ancak bu, çoğu durumda yerden tasarruf sağlıyor gibi görünüyor.
j riv

3
+1 Bu ayrıca, TextBox bağlamalarınızı zaten istediğiniz gibi davranacak şekilde (stiller, doğrulama, iki yönlü vb. İle) titizlikle elde ettiyseniz, ancak şu anda Enter tuşuna bastıktan sonra giriş alamıyorsanız, UpdateSourceTrigger'ı yalnız bırakmanıza izin verir. .
Jamin

2
Bu bulduğum en temiz yaklaşım ve bence yanıt olarak işaretlenmeli. Kaynak özelliği güncelledikten sonra odağı TextBox'tan kaldırmak için gönderene ek bir boş kontrol, Key.Return (klavyemdeki Enter tuşu Key.Return döndürür) ve Keyboard.ClearFocus () üzerinde bir kontrol eklendi. Akran incelemesini bekleyen yanıtta düzenlemeler yaptım.
Oystein

2
Yukarıdaki yorumları kabul edin. Ama benim için KeyDown etkinliği daha uygun oldu
Allender

1
KeyBindingBu yöntemi çalıştırmak için XAML'de kullandım çünkü kullanıcı arayüzümde enter anahtarını yakalayan "Varsayılan" bir denetim var. UI ağacını "Varsayılan" kontrole yaymasını durdurmak için bu TextBox içinde yakalamanız gerekir.
CAD bloke

46

Tanımladığınız şeyi yapmanın "saf XAML" yolu olduğuna inanmıyorum. UpdateSourceTrigger özelliğini ayarlayarak bir TextBox içindeki metin her değiştiğinde (TextBox odağı kaybettiğinde değil) güncellenecek şekilde bir bağlama ayarlayabilir , aşağıdaki gibi UpdateSourceTrigger özelliğini ayarlayabilirsiniz :

<TextBox Name="itemNameTextBox"
    Text="{Binding Path=ItemName, UpdateSourceTrigger=PropertyChanged}" />

UpdateSourceTrigger'ı "Açık" olarak ayarlarsanız ve ardından TextBox'ın PreviewKeyDown olayını işlediyseniz (Enter tuşunu ararsanız), istediğinizi elde edebilirsiniz, ancak arka planda kodlama gerektirir. Belki bir tür ekli mülk ( EnterKeyTraversal mülküme benzer ) sizin için çalışacaktır.


3
Bu yanıt, yanıt olarak işaretlenenden daha basit olabilir, ancak bazı sınırlamaları vardır. Bağlanan özelliğin set işlevinde bir tür kontrol yapmak istediğiniz görüntü (girdinin geçerli olup olmadığını kontrol edin). Kullanıcının bastığı her tuştan sonra bu kontrol işlevini çağırmak istemiyorsunuz, değil mi (özellikle işlev biraz zaman aldığında değil).
sth_Weird

25

TextBox'tan devralarak kendi kontrolünüzü kolayca oluşturabilir ve projeniz boyunca yeniden kullanabilirsiniz.

Buna benzer bir şey çalışmalıdır:

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();
            }
        }
    }
}

Bu adımı atlamanın bir yolu olabilir, ancak aksi halde şu şekilde bağlanmalısınız (Açık kullanarak):

<custom:SubmitTextBox
    Text="{Binding Path=BoundProperty, UpdateSourceTrigger=Explicit}" />

9
WPF / Silverlight'ta kalıtımı asla kullanmamalısınız - bu, stilleri karıştırır ve Ekli Davranışlar kadar esnek değildir. Örneğin, Ekli Davranışlar ile aynı metin kutusunda hem Filigran hem de UpdateOnEnter olabilir.
Mikhail

14

Hem Ben hem de ausadmin çözümlerini birleştirirseniz, MVVM dostu bir çözüm elde edersiniz:

<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>

... bu, TextBoxkendisini parametre olarak parametresine geçirdiğiniz anlamına gelir Command.

Bu, Commandböyle görünmenize yol açar ( DelegateCommandVM'nizde bir -style uygulaması kullanıyorsanız ):

    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();
        }
    }

Bu Commanduygulama, TextBoxarka planda kod içermeyen herhangi bir kod için kullanılabilir, ancak bunu kendi sınıfına koymak isteyebilirsiniz, böylece System.Windows.Controlssanal makinenizde hiçbir bağımlılık kalmaz. Kod kurallarınızın ne kadar katı olduğuna bağlıdır.


Güzel. Çok temiz. Bir çoklu cilt söz konusuysa, BindingOperations.GetBindingExpressionBase (tBox, prop) kullanmanız gerekebilir; ve sonra binding.UpdateTarget ();
VoteCoffee

4

İşte bana oldukça basit görünen bir yaklaşım ve AttachedBehaviour eklemekten daha kolay (ki bu da geçerli bir çözümdür). Varsayılan UpdateSourceTrigger (TextBox için LostFocus) kullanıyoruz ve ardından bir komuta bağlı olarak Enter Tuşuna bir InputBinding ekliyoruz.

Xaml aşağıdaki gibidir

       <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>

Sonra Komut yöntemleri

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

Ve TextBox Özelliğe bağlıdır

 Public Property Txt1 As String
    Get
        Return _txt1
    End Get
    Set(value As String)
        _txt1 = value
        OnPropertyChanged("Txt1")
    End Set
End Property

Şimdiye kadar bu iyi çalışıyor gibi görünüyor ve Metin Kutusunda Enter Key olayını yakalıyor.


4

Bu, orijinal sorunun cevabı değil, daha ziyade @ Samuel Jack tarafından kabul edilen cevabın bir uzantısıdır . Aşağıdakileri kendi başvurumda yaptım ve Samuel'in çözümünün zarafeti karşısında hayran kaldım. Çok temiz ve çok tekrar kullanılabilir, çünkü sadece TextBox. Bunun toplulukla paylaşılması gerektiğini düşündüm.

TextBoxesTümünün Enter'da Bağlama Kaynağını güncellemenizi gerektiren bin içeren bir Pencereniz varsa, bu davranışı Window Resourcesher bir TextBox'a eklemek yerine aşağıdaki XAML'yi ekleyerek bunların tümüne ekleyebilirsiniz . Öncelikle , elbette Samuel'in gönderisine göre ekli davranışı uygulamalısınız .

<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>

GridHedef TextBox'ları içeren Pencerenin alt öğelerinden birinin (yani a ) Kaynaklarına Stili yerleştirerek, gerekirse kapsamı her zaman sınırlayabilirsiniz .


2

TextBox'ınızla MultiBinding kullanıyorsanız, BindingOperations.GetMultiBindingExpressionbunun yerine method kullanmanız gerekir 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();
     }
}

1

Bu benim için çalışıyor:

        <TextBox                 
            Text="{Binding Path=UserInput, UpdateSourceTrigger=PropertyChanged}">
            <TextBox.InputBindings>
                <KeyBinding Key="Return" 
                            Command="{Binding Ok}"/>
            </TextBox.InputBindings>
        </TextBox>


0

Şahsen bir İşaretleme Uzantısına sahip olmanın daha temiz bir yaklaşım olduğunu düşünüyorum.

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>

0

Farklı bir çözüm (xaml kullanmıyorum ama yine de oldukça temiz düşünüyorum).

class ReturnKeyTextBox : TextBox
{
    protected override void OnKeyUp(KeyEventArgs e)
    {
        base.OnKeyUp(e);
        if (e.Key == Key.Return)
            GetBindingExpression(TextProperty).UpdateSource();
    }
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.