Frage

Ich möchte einen Befehl aufrufen, wenn die Eingabe in a gedrückt wird TextBox. Betrachten Sie das folgende XAML:

<UserControl
     ...
     xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
     ...>    
     ...    
     <TextBox>
          <i:Interaction.Triggers>
               <i:EventTrigger EventName="KeyUp">
                    <i:InvokeCommandAction Command="{Binding MyCommand}"
                                           CommandParameter="{Binding Text}" />
               </i:EventTrigger>
          </i:Interaction.Triggers>
     </TextBox>    
     ...    
</UserControl>

Und dass MyCommand wie folgt ist:

public ICommand MyCommand {
     get { return new DelegateCommand<string>(MyCommandExecute); }
}

private void MyCommandExecute(string s) { ... }

Mit dem oben genannten wird mein Befehl für jede Taste aufgerufen. Wie kann ich den Befehl einschränken, um nur dann aufzurufen, wenn die Eingabetaste gedrückt wird?

Ich verstehe, dass ich mit Ausdrucksmischung Bedingungen verwenden kann, aber diese scheinen auf Elemente beschränkt zu sein und können Ereignisargumente nicht berücksichtigen.

Ich bin auch auf mich gestoßen SLEX das bietet seine eigene InvokeCommandAction Implementierung, die auf dem aufgebaut wurde Systems.Windows.Interactivity Implementierung und kann tun, was ich brauche. Eine weitere Überlegung ist, meinen eigenen Auslöser zu schreiben, aber ich hoffe, dass es eine Möglichkeit gibt, dies zu tun, ohne externe Toolkits zu verwenden.

War es hilfreich?

Lösung 2

Ich mag Scottrudys Ansatz (dem ich +1 gegeben habe) mit dem benutzerdefinierten Trigger -Ansatz, da er meinem ersten Ansatz treu bleibt. Ich füge unten eine modifizierte Version davon hinzu, um Abhängigkeitseigenschaften anstelle von Reflexionsinformationen zu verwenden, damit es möglich ist, direkt an den Icommand zu binden. Ich füge auch einen Ansatz ein, der beigefügten Eigenschaften verwendet, um die Verwendung zu vermeiden System.Windows.Interactivity wenn gewünscht. Die Einschränkung des letzteren Ansatzes besteht darin, dass Sie das Merkmal mehrerer Invokationen von einem Ereignis verlieren, sie jedoch allgemeiner anwenden können.


Benutzerdefinierte Trigger -Ansatz

ExecuteCommandaction.cs

public class ExecuteCommandAction : TriggerAction<DependencyObject> {
    #region Properties
    public ICommand Command {
        get { return (ICommand)base.GetValue(CommandProperty); }
        set { base.SetValue(CommandProperty, value); }
    }

    public static ICommand GetCommand(DependencyObject obj) {
        return (ICommand)obj.GetValue(CommandProperty);
    }

    public static void SetCommand(DependencyObject obj, ICommand value) {
        obj.SetValue(CommandProperty, value);
    }

    // We use a DependencyProperty so we can bind commands directly rather
    // than have to use reflection info to find them
    public static readonly DependencyProperty CommandProperty =
        DependencyProperty.Register("Command", typeof(ICommand), typeof(ExecuteCommandAction), null);
    #endregion Properties

    protected override void Invoke(object parameter) {
        ICommand command = Command ?? GetCommand(AssociatedObject);
        if (command != null && command.CanExecute(parameter)) {
            command.Execute(parameter);
        }
    }
}

TextBoxEnterKeyTRigger.cs

public class TextBoxEnterKeyTrigger : TriggerBase<UIElement> {
    protected override void OnAttached() {
        base.OnAttached();
        TextBox textBox = this.AssociatedObject as TextBox;

        if (textBox != null) {
            this.AssociatedObject.KeyUp += new System.Windows.Input.KeyEventHandler(AssociatedObject_KeyUp);
        }
        else {
            throw new InvalidOperationException("This behavior only works with TextBoxes");
        }
    }

    protected override void OnDetaching() {
        base.OnDetaching();
        AssociatedObject.KeyUp -= new KeyEventHandler(AssociatedObject_KeyUp);
    }

    private void AssociatedObject_KeyUp(object sender, KeyEventArgs e) {
        if (e.Key == Key.Enter) {
            TextBox textBox = AssociatedObject as TextBox;

            //This checks for an mvvm style binding and updates the source before invoking the actions.
            BindingExpression expression = textBox.GetBindingExpression(TextBox.TextProperty);
            if (expression != null)
                expression.UpdateSource();

            InvokeActions(textBox.Text);
        }
    }
}

MyUSercontrol.xaml

<UserControl
    ...
    xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
    xmlns:b="clr-namespace:MyNameSpace.Interactivity"
    ...
    <TextBox>
        <i:Interaction.Triggers>
            <b:TextBoxEnterKeyTrigger>
                <b:ExecuteCommandAction Command="{Binding MyCommand}" />
            </b:TextBoxEnterKeyTrigger>
        </i:Interaction.Triggers>
    </TextBox>
    ...
</UserControl>

Angehörige Eigenschaften Ansatz

Enterkeydown.cs

public sealed class EnterKeyDown {

    #region Properties

    #region Command

    public static ICommand GetCommand(DependencyObject obj) {
        return (ICommand)obj.GetValue(CommandProperty);
    }

    public static void SetCommand(DependencyObject obj, ICommand value) {
        obj.SetValue(CommandProperty, value);
    }

    public static readonly DependencyProperty CommandProperty =
        DependencyProperty.RegisterAttached("Command", typeof(ICommand), typeof(EnterKeyDown),
            new PropertyMetadata(null, OnCommandChanged));

    #endregion Command

    #region CommandArgument

    public static object GetCommandArgument(DependencyObject obj) {
        return (object)obj.GetValue(CommandArgumentProperty);
    }

    public static void SetCommandArgument(DependencyObject obj, object value) {
        obj.SetValue(CommandArgumentProperty, value);
    }

    public static readonly DependencyProperty CommandArgumentProperty =
        DependencyProperty.RegisterAttached("CommandArgument", typeof(object), typeof(EnterKeyDown),
            new PropertyMetadata(null, OnCommandArgumentChanged));

    #endregion CommandArgument

    #region HasCommandArgument


    private static bool GetHasCommandArgument(DependencyObject obj) {
        return (bool)obj.GetValue(HasCommandArgumentProperty);
    }

    private static void SetHasCommandArgument(DependencyObject obj, bool value) {
        obj.SetValue(HasCommandArgumentProperty, value);
    }

    private static readonly DependencyProperty HasCommandArgumentProperty =
        DependencyProperty.RegisterAttached("HasCommandArgument", typeof(bool), typeof(EnterKeyDown),
            new PropertyMetadata(false));


    #endregion HasCommandArgument

    #endregion Propreties

    #region Event Handling

    private static void OnCommandArgumentChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) {
        SetHasCommandArgument(o, true);
    }

    private static void OnCommandChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) {
        FrameworkElement element = o as FrameworkElement;
        if (element != null) {
            if (e.NewValue == null) {
                element.KeyDown -= new KeyEventHandler(FrameworkElement_KeyDown);
            }
            else if (e.OldValue == null) {
                element.KeyDown += new KeyEventHandler(FrameworkElement_KeyDown);
            }
        }
    }

    private static void FrameworkElement_KeyDown(object sender, KeyEventArgs e) {
        if (e.Key == Key.Enter) {
            DependencyObject o = sender as DependencyObject;
            ICommand command = GetCommand(sender as DependencyObject);

            FrameworkElement element = e.OriginalSource as FrameworkElement;
            if (element != null) {
                // If the command argument has been explicitly set (even to NULL)
                if (GetHasCommandArgument(o)) {
                    object commandArgument = GetCommandArgument(o);

                    // Execute the command
                    if (command.CanExecute(commandArgument)) {
                        command.Execute(commandArgument);
                    }
                }
                else if (command.CanExecute(element.DataContext)) {
                    command.Execute(element.DataContext);
                }
            }
        }
    }

    #endregion
}

MyUSercontrol.xaml

<UserControl
    ...
    xmlns:b="clr-namespace:MyNameSpace.Interactivity"
    ...
    <TextBox b:EnterKeyDown.Command="{Binding AddNewDetailCommand}"
             b:EnterKeyDown.CommandArgument="{Binding Path=Text,RelativeSource={RelativeSource Self}}" />
    ...
</UserControl>

Andere Tipps

Es gibt Keytrigger in Ausdrucksmischung.

<UserControl
xmlns:i="clr-namespace:System.Windows.Interactivity;
         assembly=System.Windows.Interactivity"
xmlns:iex="clr-namespace:Microsoft.Expression.Interactivity.Input;
           assembly=Microsoft.Expression.Interactions" ...>    
     <TextBox>
         <i:Interaction.Triggers>
            <iex:KeyTrigger Key="Enter">
               <i:InvokeCommandAction Command="{Binding PasswordLoginCommand}" />
            </iex:KeyTrigger>
         </i:Interaction.Triggers>
     </TextBox>    
</UserControl>

System.Windows.Interactivity und Microsoft.Expression.Interactions Versammlungen sind für WPF im Beamten erhältlich Nuget -Paket.

Ich bin gestern auf dieselbe Ausgabe gestoßen und habe es mit benutzerdefinierten Triggern gelöst. Es mag zunächst ein bisschen viel erscheinen, aber ich fand, dass dieses allgemeine Muster verwendet werden kann, um viele Dinge zu tun, mit denen ich mit Ereignishandlern direkt in einer Ansicht verwendet habe (wie Doppelklick -Ereignisse). Der erste Schritt besteht darin, eine Triggeraktion zu erstellen, die einen Parameter akzeptieren kann, da wir ihn später benötigen.

public class ExecuteCommandAction : TriggerAction<FrameworkElement>
{
    public string Command { get; set; }

    protected override void Invoke(object o)
    {
        if (Command != null)
        {
            object ctx = AssociatedObject.DataContext;
            if (ctx != null)
            {
                var cmd = ctx.GetType().GetProperty(Command)
                    .GetValue(ctx, null) as ICommand;
                if (cmd != null && cmd.CanExecute(o))
                {
                    cmd.Execute(o);
                }
            }
        }
    }
}

Der nächste Schritt besteht darin, den Auslöser zu erstellen. Sie könnten einige interessante Dinge mit Basisklassen machen, um es generischer für die Erfassung verschiedener Arten von Schlüsselpressen zu machen, aber wir werden es einfach halten.

public class TextBoxEnterKeyTrigger: TriggerBase<UIElement>
{
    protected override void OnAttached()
    {
        base.OnAttached();
        AssociatedObject.KeyUp += AssociatedObject_KeyUp;
    }

    protected override void OnDetaching()
    {
        base.OnDetaching();
        AssociatedObject.KeyUp -= AssociatedObject_KeyUp;
    }

    void AssociatedObject_KeyUp(object sender, System.Windows.Input.KeyEventArgs e)
    {
        if (e.Key == Key.Enter)
        {
            TextBox textBox = AssociatedObject as TextBox;
            object o = textBox == null ? null : textBox.Text;
            if (o != null)
            {
                InvokeActions(o);
            }
        }
    }
}

Denken Sie daran, dass die geänderte Eigenschaft, obwohl Sie möglicherweise eine Datenbindung an Ihren Textbox -Wert haben, nicht ausfeuert, da Ihr Textfeld keinen Fokus verloren hat. Aus diesem Grund überspreche ich den Wert der textBox.text -Eigenschaft an den Befehl. Der letzte Schritt besteht darin, diese Funktion in Ihrem XAML zu verwenden. Sie müssen sicher sein, dass Sie den Interaktivitäts -Namespace sowie den Namespace, der Ihren Code von oben enthält, einbeziehen.

<UserControl
...
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:common="clr-namespace:My.UI;assembly=My.UI">
    <TextBox Text="{Binding Path=MyText, Mode=TwoWay}" IsEnabled="{Binding CanMyCommand}">
        <i:Interaction.Triggers>
            <common:TextBoxEnterKeyTrigger>
                <common:ExecuteCommandAction Command=MyCommand" />
            </common:TextBoxEnterKeyTrigger>
        </i:Interaction.Triggers>
    </TextBox>
</UserControl>

Ich habe den Code von ScotTrudy in meiner Anwendung verwendet. Mein Textbox -Text ist jedoch an eine Eigenschaft in der ViewModel -Klasse gebunden, und diese Eigenschaft wird nicht aktualisiert, bis der Befehl zeitlich aufgerufen wird, nachdem Pressiong eingebracht wird, da mein Textfeld noch nicht den Fokus verloren hat. Um dies zu beheben, habe ich die folgenden Code -Snippets, die direkt oben aufgerufen werden, in der assoziierten Methode assoziiertObject_keyup hinzugefügt und die aktualisierte Texteigenschaft wird in der ViewModel -Klasse aktualisiert.

                    BindingExpression bindingExpression = (textBox).GetBindingExpression(TextBox.TextProperty);
                bindingExpression.UpdateSource();

Zusätzlich zu meinem Kopf. Sie können Ereignisse an den Befehl übergeben und als in ViewModel prüfen, ob e.keypress = keys.inter .. Dies ist nicht wirklich Code :) Ich habe meine Vs nicht auf diesem Computer. Idee :)

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top