Как подключить событие TextBox TextChanged и команду для использования шаблона MVVM в Silverlight

Недавно я понял, что шаблон MVVM настолько полезен для приложения Silverlight и изучает, как его использовать в моем проекте.

Кстати, как подключить текстовое поле textChanged событие с помощью команды? Для Button есть свойство Command, но Textbox не имеет свойства commapd. Если элементы управления не имеют свойства команды, как объединить ICommand и событие Control?

Я получил следующий код xaml

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

Что я пытаюсь сделать, так это то, что если пользователь вводит какой-либо текст в текстовое поле, данные будут отображаться в datagrid, а не при нажатии кнопки. Я знаю, что есть встроенный блок управления автозаполнением. Однако я хочу знать, как вызвать свойство Command в классе ViewModel в элементах управления, которые не имеют свойства Command, такого как текстовое поле.

Спасибо

Ответ 1

Здесь самый простой способ. Привяжите свое текстовое поле к свойству модели представления, как описано выше. Затем просто добавьте в текстовое поле код-конец (да, я говорю с кодом MVVM, это не конец света). Добавьте событие TextChanged, а затем просто обновите привязку.

В общем, у вас будет что-то подобное для модели просмотра:

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

В вашем XAML у вас будет следующее:

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

Наконец, в коде позади просто сделайте следующее:

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

Это приведет к тому, что ваше свойство будет обновляться в любое время при изменении текста. }

Ответ 2

Почему бы просто не привязать свойство Text к свойству в вашей модели представлений? Таким образом, вы получите уведомление, когда оно изменилось, а также получите новое значение:

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

XAML:

<TextBox Text="{Binding MyData}"/>

Ответ 3

Вот способ MvvmLight сделать это! Кредит отправляется в 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>

Источник: http://blog.galasoft.ch/archive/2010/05/19/handling-datagrid.selecteditems-in-an-mvvm-friendly-manner.aspx

Ответ 4

Для разговора давайте скажем, что вам нужно подключить какое-то произвольное событие к команде, а не напрямую привязываться к свойству ViewModel (из-за отсутствия поддержки в элементе управления или фреймворке, дефекта и т.д.)..) Это можно сделать в коде. Вопреки некоторым заблуждениям, MVVM не исключает кодов. Просто важно помнить, что логика в кодебе не должна пересекать слои - она ​​должна относиться непосредственно к пользовательскому интерфейсу и к конкретной используемой технологии пользовательского интерфейса. (Обратите внимание, однако, что 95% вашей работы в файле разметки может сделать его немного неинтуитивным, чтобы иметь некоторые функциональные возможности в коде, поэтому комментарий или два в разметке об этой одноразовой реализации кода могут облегчить работу дороги для себя или других.)

Как правило, 2 части связывают команду в codebehind. Во-первых, вы должны ответить на это событие. Во-вторых, вы можете (захотите) привязать к команде свойство CanExecute.

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

Ответ 5

В разделе определения добавим:

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

Если вы используете TextBox, добавьте ссылку на событие, которое мы хотим обнаружить:

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

В ViewModel добавьте код для Commad:

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

private void OnRequestFilterTextChanged()
{
  // Add code
}

Не забудьте выполнить текст привязки:

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

Ответ 6

Просто используйте

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

Ответ 7

Для выполнения команды вы должны использовать Поведение:

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

Теперь вы можете "Связывать" свою команду в xaml:

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

Если вам нужно, вы можете расширить это поведение дополнительными свойствами, например, если вам нужен отправитель или DataContext другого элемента.

С уважением, Тамаш

(Я нашел это в сообщении в блоге, но я не могу вспомнить его адрес)

Ответ 8

Я решил его, привязав свойство к моей модели представления и установив привязку UpdateSourceTrigger к PropertyChanged. Свойство поддерживает INotifyPropertyChanged.

В моей модели просмотра я затем подписываюсь на событие PropertyChanged для свойства. Когда он запускает, я выполняю задачи, которые мне нужно выполнить (в моем случае обновление коллекции), и в конце я вызываю PropertyChanged в свойстве, которое слушает мой другой материал в представлении.

Ответ 9

У меня был тот же вопрос. Затем я нахожу эту статью.

http://deanchalk.com/wpf-mvvm-property-changed-command-behavior/

  • Создать behavior (behavior вернуть значение для изменения события и команды для изменения)
  • Внедрите behavior в свой WPF
  • Привяжите behavior вашу команду к ValueChanged