Как подключить событие 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