Как соединить TextBhanged событие TextBox и Команду, чтобы использовать шаблон MVVM в Silverlight - PullRequest
16 голосов
/ 16 августа 2010

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

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

Я получил следующий код 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>

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

Спасибо

Ответы [ 10 ]

22 голосов
/ 16 августа 2010

Почему бы просто не связать свойство 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}"/>
20 голосов
/ 16 августа 2010

Вот самый простой способ.Свяжите свое текстовое поле со свойством модели представления, как вы описали выше.Затем просто добавьте событие «Код позади» (да, я говорю о коде сзади с 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();
}

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

9 голосов
/ 05 мая 2011

Вот способ 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>

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

5 голосов
/ 02 декабря 2015

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

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));
  }
}
4 голосов
/ 19 сентября 2015

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

<TextBox Text="{Binding MyText,Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
3 голосов
/ 16 августа 2010

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

Обычно есть две части для привязки команды в 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'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));
    }
}
0 голосов
/ 03 августа 2018

Джереми ответил на это. Однако, если вы хотите уменьшить количество кода, просто сделайте что-то вроде этого. На ваш взгляд модель:

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

Тогда в коде позади:

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

Я знаю, что это дублированный код, но если это то, что вы хотите, то вот оно.

0 голосов
/ 20 сентября 2016

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

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

  1. Создать behavior (behavior получить значение для события changend и команду для changend)
  2. Implement behavior в вашем WPF
  3. Свяжите Behavior вашу команду с ValueChanged
0 голосов
/ 19 марта 2015

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

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

0 голосов
/ 16 августа 2010

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

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 другого элемента.

С уважением, Tamás

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...