Enter-key प्रेस पर टेक्स्टबॉक्स को बांधें


108

डिफॉल्ट डेटबाइंडिंग ऑन TextBoxहै TwoWayऔर यह टेक्स्ट को प्रॉपर्टी पर तब TextBoxभेजता है जब उसका फोकस खो जाता है।

क्या कोई आसान XAML तरीका है जब मैं Enterकुंजी को दबाता हूं तो डेटाबाइंडिंग हो सकती है TextBox? मुझे पता है कि यह पीछे के कोड में करना बहुत आसान है, लेकिन कल्पना करें कि यह TextBoxकुछ जटिल के अंदर है DataTemplate

जवाबों:


137

संलग्न व्यवहार बनाकर आप अपने आप को एक शुद्ध 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();
        }
    }
}

फिर अपने एक्सएएमएल में आप उस InputBindingsManager.UpdatePropertySourceWhenEnterPressedPropertyसंपत्ति को सेट करते हैं जिसे आप Enterकुंजी दबाए जाने पर अपडेट करना चाहते हैं । ऐशे ही

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

(आपको अपनी XAML फ़ाइल के मूल तत्व में "b" के लिए xmlns clr-namespace संदर्भ शामिल करने के लिए सुनिश्चित करने की आवश्यकता है जो यह इंगित करता है कि आपने कभी नामस्थान में InputBindingsManager डाला है)।


11
भविष्य के पाठकों को कुछ मिनटों की फिजूलखर्ची से बचाने के प्रयास में, मैंने अभी इसे अपने प्रोजेक्ट में इस्तेमाल किया है, और ऊपर दिया गया नमूना XAML सही काम नहीं करता है। स्रोत को हर चरित्र परिवर्तन पर अपडेट किया गया था। "UpdateSourceTrigger = PropertyChanged" को "UpdateSourceTrigger = Explicit" में बदलने से समस्या ठीक हो गई। अब यह सभी वांछित के रूप में काम करता है।
१।

1
@ आईहाके: मुझे लगता है कि आपका अनुशंसित परिवर्तन भी ध्यान खो जाने पर अद्यतन करने से रोक देगा
वोटकोफी

4
UpdateSourceTrigger = प्रॉपर्टीचेंज वास्तविक संख्या टाइप करते समय असुविधाजनक हो सकता है। उदाहरण के लिए "3." जब एक फ्लोट के लिए बाध्य एक समस्या का कारण बनता है। मैं संलग्न व्यवहार के अलावा UpdateSourceTrigger (या LostFocus के लिए सेटिंग) को निर्दिष्ट नहीं करने की सलाह दूंगा। यह दोनों दुनिया का सर्वश्रेष्ठ देता है।
डेविड हॉलिंसहेड

2
उत्कृष्ट कार्य! टाइनी नाइट-पिक: जब UpdatePropertySourceWhenEnterPressedएक मान्य मूल्य से भिन्न मान्य मूल्य में परिवर्तन होता है, तो आप PreviewKeyDownअनावश्यक रूप से घटना की सदस्यता रद्द कर रहे हैं और पुनः सदस्यता ले रहे हैं । इसके बजाय, आप सभी को यह जांचना चाहिए कि क्या e.NewValueहै nullया नहीं। यदि नहीं null, तो सदस्यता लें; अन्यथा, यदि null, तो सदस्यता समाप्त करें।
निकोलस मिलर

1
मैं इस समाधान से प्यार करता हूँ, इसे पोस्ट करने के लिए धन्यवाद! जिस किसी को भी आपके आवेदन में इस व्यवहार को कई टेक्स्टबॉक्स में संलग्न करने की आवश्यकता है, मैंने इस उत्तर के लिए एक एक्सटेंशन पोस्ट किया है कि आप इसे बहुत आसानी से कैसे प्राप्त कर सकते हैं। यहाँ पोस्ट देखें
निक

50

इस तरह मैंने इस समस्या को हल किया। मैंने एक विशेष ईवेंट हैंडलर बनाया जो पीछे के कोड में गया:

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संदर्भ का उपयोग करता है, जिससे यह अद्यतन होने के लिए स्वयं बाध्यकारी हो। चूंकि ईवेंट हैंडलर स्व-सम्‍मिलित है, तो उसे एक जटिल डेटाटेम्पलेट में काम करना चाहिए। यह एक ईवेंट हैंडलर अब उन सभी टेक्स्टबॉक्स में जोड़ा जा सकता है, जिन्हें इस फीचर की आवश्यकता है।


8
कुछ मुझे बताता है कि यह एक अंडररेटेड पोस्ट है। बहुत सारे उत्तर लोकप्रिय हैं क्योंकि वे "एक्सएएमएल-वाई" हैं। लेकिन यह ज्यादातर मौकों में जगह बचाने के लिए दिखाई देता है।
j riv

3
+1 यह आपको अकेले अद्यतन करने के लिए भी अनुमति देता है यदि आप पहले से ही अपने टेक्स्टबॉक्स बाइंडिंग को पहले से ही अपने तरीके से व्यवहार करने के लिए (स्टाइल, सत्यापन, ट्वॉय, आदि) के साथ व्यवहार करना चाहते हैं, लेकिन वर्तमान में सिर्फ एंटर दबाने के बाद इनपुट नहीं होगा ।
जैमिन

2
यह सबसे साफ तरीका है जो मैंने पाया है, और मेरी राय में जवाब के रूप में चिह्नित किया जाना चाहिए। स्रोत संपत्ति को अपडेट करने के बाद TextBox से ध्यान हटाने के लिए प्रेषक पर एक अतिरिक्त अशक्त जांच, Key.Return पर एक चेक (मेरे कीबोर्ड रिटर्न की कुंजी दर्ज करें), और Keyboard.ClearFocus () पर जोड़ा। मैंने उस उत्तर का संपादन किया है जो सहकर्मी समीक्षा की प्रतीक्षा कर रहा है।
ओएस्टीन

2
ऊपर टिप्पणी के साथ सहमत हूँ। लेकिन मेरे लिए KeyDown इवेंट अधिक उपयुक्त था
Allender

1
मैंने KeyBindingXAML में इस विधि का उपयोग किया था क्योंकि मेरे UI में एक "डिफ़ॉल्ट" नियंत्रण है जो एंटर कुंजी को पकड़ता है। यूआई ट्री को "डिफ़ॉल्ट" नियंत्रण तक फैलाने से रोकने के लिए आपको इसे इस टेक्स्टबॉक्स के भीतर पकड़ना होगा।
सीएडी

46

मुझे विश्वास नहीं होता कि आप जो भी वर्णन कर रहे हैं, उसका कोई "शुद्ध XAML" तरीका है। आप सेट कर सकते हैं एक तो बाध्यकारी है कि यह अद्यतन करता है जब भी किसी पाठ बॉक्स परिवर्तन में पाठ (बजाय जब पाठ बॉक्स ध्यान केंद्रित खो देता है) निर्धारित कर UpdateSourceTrigger संपत्ति, इस तरह:

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

यदि आप UpdateSourceTrigger को "Explicit" के लिए सेट करते हैं और फिर TextBox की पूर्वावलोकनKeyDown घटना (Enter कुंजी की तलाश में) को संभाला करते हैं, तो आप वह हासिल कर सकते हैं जो आप चाहते हैं, लेकिन इसके लिए कोड-पीछे की आवश्यकता होगी। शायद कुछ प्रकार की संलग्न संपत्ति (मेरे EnterKeyTraversal संपत्ति के समान ) आपके लिए काम करती है।


3
यह उत्तर उत्तर के रूप में चिह्नित एक से अधिक सरल हो सकता है, लेकिन इसकी कुछ सीमाएँ हैं। छवि आप किसी प्रकार की चेक-इन बाउंड प्रॉपर्टी के सेट-फंक्शन में करना चाहते हैं (यह देखना कि इनपुट वैध है)। उपयोगकर्ता द्वारा दबाए गए प्रत्येक कुंजी के बाद आप उस चेक फ़ंक्शन को कॉल नहीं करना चाहते हैं, क्या आप (विशेषकर जब फ़ंक्शन कुछ समय लेता है)?
sth_Weird

25

आप आसानी से 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();
            }
        }
    }
}

इस चरण के आसपास आने का एक तरीका हो सकता है, लेकिन अन्यथा आपको इस तरह से बांधना चाहिए (स्पष्ट का उपयोग करके):

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

9
WPF / सिल्वरलाइट में आपको कभी भी वंशानुक्रम का उपयोग नहीं करना चाहिए - यह शैलियों को गड़बड़ करता है और संलग्न व्यवहार के रूप में लचीला नहीं है। संलग्न व्यवहार के साथ उदाहरण के लिए, आपके पास एक ही टेक्स्टबॉक्स पर वॉटरमार्क और अपडेटऑन्टर दोनों हो सकते हैं।
मिखाइल

14

यदि आप बेन और औसादमिन दोनों के समाधानों को जोड़ते हैं, तो आप एक बहुत 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अपने वीएम में एक -स्टाइल कार्यान्वयन का उपयोग कर रहे हैं ):

    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आपके वीएम पर कोई निर्भरता नहीं है । यह इस बात पर निर्भर करता है कि आपके कोड दिशानिर्देश कितने सख्त हैं।


अच्छा लगा। बहुत साफ। यदि कोई मल्टीबाइंडिंग शामिल है, तो आपको BindingOperations.GetBindingExpressionBase (tBox, prop) का उपयोग करने की आवश्यकता हो सकती है; और फिर बाइंडिंग। UpdateTarget ();
वोट कॉफ़ी

4

यहाँ एक दृष्टिकोण है जो मुझे काफी सीधा लगता है, और आसान है कि एक अटैच्ड बिहेवियर को जोड़ना (जो कि एक मान्य संस्करण भी है)। हम डिफ़ॉल्ट UpdateSourceTrigger (TextBox के लिए लॉस्टफोकस) का उपयोग करते हैं, और फिर एक कमांड में बंधे Enter Key में एक इनपुटबाइंडिंग जोड़ते हैं।

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 संपत्ति के लिए बाध्य है

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

अब तक यह अच्छी तरह से काम करता है और पाठ बॉक्स में Enter कुंजी ईवेंट को पकड़ता है।


4

यह मूल प्रश्न का उत्तर नहीं है, बल्कि @ सैमुअल जैक द्वारा स्वीकृत उत्तर का विस्तार है । मैंने अपने आवेदन में निम्नलिखित किया है, और शमूएल के समाधान की लालित्य पर आश्चर्य था। यह बहुत साफ है, और बहुत ही पुन: प्रयोज्य है, क्योंकि इसका उपयोग किसी भी नियंत्रण पर किया जा सकता है, न कि केवल TextBox। मुझे लगा कि इसे समुदाय के साथ साझा किया जाना चाहिए।

यदि आपके पास एक हज़ार के साथ एक विंडो है जिसमें TextBoxesसभी को दर्ज करने पर बाइंडिंग स्रोत को अपडेट करने की आवश्यकता होती है, तो आप इस व्यवहार को उन सभी को संलग्न कर सकते हैं जिनमें Window Resourcesसे प्रत्येक एक्सबॉक्स को संलग्न करने के बजाय नीचे XAML को शामिल किया जा सकता है। पहले आपको शमूएल के पद के अनुसार संलग्न व्यवहार को लागू करना होगा , निश्चित रूप से।

<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) के संसाधन में स्टाइल डालकर, यदि आवश्यक हो, तो आप हमेशा गुंजाइश को सीमित कर सकते हैं, जिसमें लक्ष्य टेक्स्टबॉक्स शामिल हैं।


2

यदि आप अपने टेक्स्टबॉक्स के साथ मल्टीबाइंडिंग का उपयोग कर रहे हैं तो आपको 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();
     }
}

1

यह मेरे लिए काम करता है:

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

1

संलग्न व्यवहारों का उपयोग करते हुए, यहाँ काफी आकर्षक ढंग से उत्तर दिया, लगभग किसी भी चीज़ के लिए मेरी पसंदीदा विधि।

WPF एंट्री मारने के बाद टेक्स्टबॉक्स कैसे बनाते हैं, इस पर ध्यान केंद्रित करें


0

मुझे व्यक्तिगत रूप से लगता है कि मार्कअप एक्सटेंशन एक क्लीनर दृष्टिकोण है।

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

एक अलग समाधान (xaml का उपयोग नहीं कर रहा है, लेकिन अभी भी मुझे लगता है कि बहुत साफ है)।

class ReturnKeyTextBox : TextBox
{
    protected override void OnKeyUp(KeyEventArgs e)
    {
        base.OnKeyUp(e);
        if (e.Key == Key.Return)
            GetBindingExpression(TextProperty).UpdateSource();
    }
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.