wake-up-neo.com

So verbinden Sie das TextChanged-Ereignis und den Befehl von TextBox, um das MVVM-Muster in Silverlight zu verwenden

Vor kurzem wurde mir klar, dass das MVVM-Muster für Silverlight-Anwendungen so nützlich ist und untersucht, wie es in mein Projekt übernommen werden kann.

BTW, wie kann man das textChanged-Ereignis der Textbox mit Command verbinden? Es gibt eine Befehlseigenschaft für Button, jedoch hat Textbox keine commapd -Eigenschaft. Wenn Controls keine Befehlseigenschaft haben, wie kombinieren Sie das ICommand- und das Control-Ereignis?

Ich habe folgenden XAML-Code erhalten

<UserControl.Resources>
        <vm:CustomerViewModel x:Key="customerVM"/>    
    </UserControl.Resources>

    <Grid x:Name="LayoutRoot" 
          Background="White" 
          DataContext="{Binding Path=Customers, Source={StaticResource customerVM}, Mode=TwoWay}" >

        <StackPanel>
            <StackPanel Orientation="Horizontal"
                        Width="300"
                        HorizontalAlignment="Center">
                <TextBox x:Name="tbName" 
                         Width="50" 
                         Margin="10"/>
                <Button Width="30" 
                        Margin="10" 
                        Content="Find"
                        Command="{Binding Path=GetCustomersByNameCommand, Source={StaticResource customerVM}}"
                        CommandParameter="{Binding Path=Text, ElementName=tbName}"/>
            </StackPanel>
            <sdk:DataGrid ItemsSource="{Binding Path=DataContext, ElementName=LayoutRoot}"
                          AutoGenerateColumns="True"
                          Width="300"
                          Height="300"/>
        </StackPanel>
    </Grid>

Was ich versuche zu tun ist, dass, wenn der Benutzer einen Text in das Textfeld eingibt, die Daten im Datagrid angezeigt werden, anstatt den Klick mit der Schaltfläche zu verwenden. Ich weiß, dass ein Autocomplete-Box-Steuerelement eingebaut ist. Ich möchte jedoch wissen, wie die Command-Eigenschaft in der ViewModel-Klasse in den Steuerelementen aufgerufen wird, die keine Command-Eigenschaft wie z. B. Textbox besitzt.

Vielen Dank 

15
Ray

Warum binden Sie die Text-Eigenschaft nicht einfach an eine Eigenschaft in Ihrem Ansichtsmodell? Auf diese Weise werden Sie benachrichtigt, wenn sich der Wert geändert hat, und Sie erhalten auch den neuen Wert:

public string MyData
{
    get { return this.myData; }
    set
    {
        if (this.myData != value)
        {
            this.myData = value;
            this.OnPropertyChanged(() => this.MyData);
        }
    }
}

XAML:

<TextBox Text="{Binding MyData}"/>
22
Kent Boogaart

Hier ist der einfachste Weg. Binden Sie Ihr Textfeld wie oben beschrieben an die Eigenschaft im Ansichtsmodell. Fügen Sie dann einfach ein Code-Behind-Ereignis (ja, ich spreche Code-Behind mit MVVM, es ist nicht das Ende der Welt) im Textfeld hinzu. Fügen Sie ein TextChanged-Ereignis hinzu, und aktualisieren Sie einfach die Bindung. 

Insgesamt haben Sie so etwas für ein Ansichtsmodell: 

public class MyViewModel 
{
    private string _myText;

    public string MyText 
    {
        get { return _myText; }
        set 
        {
            _myText = value;
            RaisePropertyChanged("MyText"); // this needs to be implemented
            // now do whatever grid refresh/etc
        }
    }
}

In Ihrer XAML haben Sie Folgendes:

<TextBox Text="{Binding MyText,Mode=TwoWay}" TextChanged="TextBox_TextChanged"/>

Zum Schluss machen Sie einfach im Code dahinter folgendes:

public void TextBox_TextChanged(object sender, TextChangedEventArgs e)
{
   var binding = ((TextBox)sender).GetBindingExpression(TextBox.TextProperty);
   binding.UpdateSource();
}

Dies führt dazu, dass sich Ihre Eigenschaft bei jeder Änderung des Textes aktualisiert. }

20
Jeremy Likness

Hier ist der Weg von MvvmLight! Die Gutschrift geht an GalaSoft Laurent Bugnion. 

<sdk:DataGrid Name="dataGrid1" Grid.Row="1"
    ItemsSource="{Binding Path=CollectionView}"
    IsEnabled="{Binding Path=CanLoad}"
    IsReadOnly="True">
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="SelectionChanged">
            <cmd:EventToCommand
                Command="{Binding SelectionChangedCommand}"
                CommandParameter="{Binding SelectedItems, ElementName=dataGrid1}" />
        </i:EventTrigger>
    </i:Interaction.Triggers>
</sdk:DataGrid>

Quelle: http://blog.galasoft.ch/archive/2010/05/19/handling-datagrid.selecteditems-in-an-mvvm-friendly-manner.aspx

9
Doguhan Uluca

Im Definitionsabschnitt fügen wir hinzu:

xmlns:i="http://schemas.Microsoft.com/expression/2010/interactivity"

Wenn Sie die Variable TextBox verwenden, fügen Sie dem Ereignis, das wir ermitteln möchten, die Referenz hinzu:

<TextBox Text="{Binding TextPrintersFilter}">
  <i:Interaction.Triggers>
    <i:EventTrigger EventName="TextChanged">          
      <i:InvokeCommandAction Command="{Binding FilterTextChangedCommand}"/>
    </i:EventTrigger>
  </i:Interaction.Triggers>
</TextBox>

Fügen Sie im ViewModel Code für Commad hinzu:

public ICommand FilterTextChangedCommand
{
  get
  {
    if (this._filterTextChangedCommand == null)
    {
      this._filterTextChangedCommand =
        new RelayCommand(param => this.OnRequestFilterTextChanged());
    }
    return this._filterTextChangedCommand;
  }
}

private void OnRequestFilterTextChanged()
{
  // Add code
}

Vergessen Sie nicht, den verbindlichen Text auszuführen:

private string _textPrintersFilter;
public string TextPrintersFilter
{
  get { return _textPrintersFilter; }
  set
  {
    _textPrintersFilter = value;
    this.RaisePropertyChange(nameof(TextPrintersFilter));
  }
}
4
Adel

Einfach verwenden 

<TextBox Text="{Binding MyText,Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
4
Vivek Maskara

Nehmen wir zur Unterhaltung an, dass Sie ein beliebiges Ereignis mit einem Befehl verknüpfen müssen, anstatt direkt an eine Eigenschaft im ViewModel zu binden (mangelnde Unterstützung im Steuerelement oder Framework, Defekt usw.) kann im Codebehind gemacht werden. Im Gegensatz zu einigen falschen Vorstellungen schließt MVVM Codebehind nicht aus. Es ist nur wichtig, sich daran zu erinnern, dass die Logik im Codebind nicht Schichten überschreiten sollte - sie sollte sich direkt auf die Benutzeroberfläche und die jeweils verwendete Benutzeroberflächentechnologie beziehen. (Beachten Sie jedoch, dass das Einfügen von 95% Ihrer Arbeit in die Markup-Datei die Verwendung einiger Funktionen im Codebehind möglicherweise etwas unintutitiv macht. Ein oder zwei Kommentare im Markup zu dieser einmaligen Codebehind-Implementierung können dies jedoch erleichtern die Straße hinunter für sich selbst oder für andere.)

Es gibt normalerweise zwei Teile, um einen Befehl in codebehind zu binden. Zuerst müssen Sie auf die Veranstaltung reagieren. Zweitens möchten Sie möglicherweise an die CanExecute-Eigenschaft des Befehls gebunden werden.

// Execute the command from the codebehind
private void HandleTheEvent(Object sender, EventArgs e)
{
    var viewModel = DataContext as ViewModel;
    if (viewModel != null)
    {
        var command = viewModel.SomeCommand;
        command.Execute(null);
    }
}

// Listen for the command's CanExecuteChanged event
// Remember to call this (and unhook events as well) whenever the ViewModel instance changes
private void ListenToCommandEvent()
{
    var viewModel = DataContext as ViewModel;
    if (viewModel != null)
    {
        var command = viewModel.SomeCommand;
        command.CanExecuteChanged += (o, e) => EnableOrDisableControl(command.CanExecute(null));
    }
}
3
avidgator

Ich habe es gelöst, indem ich eine Bindung zu einer Eigenschaft in meinem Ansichtsmodell binde und den UpdateSourceTrigger der Bindung auf PropertyChanged setzte. Die Eigenschaft unterstützt INotifyPropertyChanged.

In meinem Ansichtsmodell abonniere ich dann das PropertyChanged-Ereignis für die Eigenschaft. Wenn es ausgelöst wird, führe ich die Aufgaben aus, die ich erledigen muss (in meinem Fall das Aktualisieren einer Sammlung), und am Ende rufe ich PropertyChanged für die Eigenschaft auf, die meine anderen Elemente in der Ansicht abhören.

0
Mike Cheel

Sie sollten ein Verhalten verwenden, um den Befehl auszuführen:

public class CommandBehavior : TriggerAction<FrameworkElement>
{
    public static readonly DependencyProperty CommandBindingProperty = DependencyProperty.Register(
        "CommandBinding",
        typeof(string),
        typeof(CommandBehavior),
        null);

    public string CommandBinding
    {
        get { return (string)GetValue(CommandBindingProperty); }
        set { SetValue(CommandBindingProperty, value); }
    }

    private ICommand _action;

    protected override void OnAttached()
    {
        DataContextChangedHandler.Bind(AssociatedObject, _ProcessCommand);
    }

    private void _ProcessCommand(FrameworkElement obj)
    {
        if (AssociatedObject != null)
        {

            var dataContext = AssociatedObject.DataContext;

            if (dataContext != null)
            {
                var property = dataContext.GetType().GetProperty(CommandBinding);
                if (property != null)
                {
                    var value = property.GetValue(dataContext, null);
                    if (value != null && value is ICommand)
                    {
                        _action = value as ICommand;
                        if (AssociatedObject is Control)
                        {
                            var associatedControl = AssociatedObject as Control;
                            associatedControl.IsEnabled = _action.CanExecute(null);
                            _action.CanExecuteChanged +=
                                (o, e) => associatedControl.IsEnabled = _action.CanExecute(null);
                        }

                    }
                }
            }
        }
    }

    protected override void Invoke(object parameter)
    {
        if (_action != null && _action.CanExecute(parameter))
        {
            _action.Execute(parameter);
        }
    }
}

public static class DataContextChangedHandler
{
    private const string INTERNAL_CONTEXT = "InternalDataContext";
    private const string CONTEXT_CHANGED = "DataContextChanged";

    public static readonly DependencyProperty InternalDataContextProperty =
        DependencyProperty.Register(INTERNAL_CONTEXT,
                                    typeof(Object),
                                    typeof(FrameworkElement),
                                    new PropertyMetadata(_DataContextChanged));

    public static readonly DependencyProperty DataContextChangedProperty =
        DependencyProperty.Register(CONTEXT_CHANGED,
                                    typeof(Action<FrameworkElement>),
                                    typeof(FrameworkElement),
                                    null);


    private static void _DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
    {
        var control = (FrameworkElement)sender;
        var handler = (Action<FrameworkElement>)control.GetValue(DataContextChangedProperty);
        if (handler != null)
        {
            handler(control);
        }
    }

    public static void Bind(FrameworkElement control, Action<FrameworkElement> dataContextChanged)
    {
        control.SetBinding(InternalDataContextProperty, new Binding());
        control.SetValue(DataContextChangedProperty, dataContextChanged);
    }
}

Jetzt können Sie Ihren Befehl in xaml "binden":

        <TextBox Text="{Binding SearchText, Mode=TwoWay}" >
            <i:Interaction.Triggers>
                <i:EventTrigger EventName="TextChanged">
                    <utils:CommandBehavior CommandBinding="SearchCommand" />
                </i:EventTrigger>
            </i:Interaction.Triggers>
        </TextBox>

Bei Bedarf können Sie dieses Verhalten um zusätzliche Eigenschaften erweitern, z. B. wenn Sie den Sender oder den DataContext eines anderen Elements benötigen. 

Herzliche Grüße, Tamás

(Ich habe dies in einem Blogbeitrag gefunden, kann mich aber nicht an seine Adresse erinnern.)

0
abtamas

Jeremy antwortete darauf. Wenn Sie jedoch Code dahinter reduzieren möchten, tun Sie einfach so etwas. In Ihrem Ansichtsmodell:

public class MyViewModel 
{
    private string _myText;

    public string MyText 
    {
        get { return _myText; }
        set 
        {
            _myText = value;
            RaisePropertyChanged("MyText"); // this needs to be implemented
            // now do whatever grid refresh/etc
        }
    }
    public void TextBox_TextChanged(object sender, TextChangedEventArgs e)
    {
        var binding = ((TextBox)sender).GetBindingExpression(TextBox.TextProperty);
        binding.UpdateSource();
    }
}

Dann im Code hinter:

public void TextBox_TextChanged(object sender, TextChangedEventArgs e)
{
    YourViewModel.TextBox_TextChanged(sender, e);
}

Ich weiß, es ist doppelter Code, aber wenn Sie das wollen, dann hier.

0
Marc2001