Недавно я понял, что шаблон MVVM настолько полезен для приложения Silverlight и изучал, как применить его в моем проекте.
Кстати, Как подключить событие textChanged текстового поля с помощью команды? Для Button есть свойство Command, однако Textbox не имеет свойства commapd. Если у Controls нет свойства команды, как объединить 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>
Я пытаюсь сделать следующее: если пользователь введет какой-то текст в текстовое поле, данные будут отображаться в решетка данных вместо нажатия кнопки. Я знаю, что встроенный элемент управления автозаполнением встроен. Однако я хочу знать, как вызывать свойство Command в классе ViewModel в элементах управления, которые не имеют свойства Command, такого как textbox.
Спасибо
Для разговора допустим, что вам действительно нужно подключить какое-то произвольное событие к команде, а не напрямую связываться со свойством в ViewModel (из-за отсутствия поддержки в контроль или фреймворк, дефект и т. д.) Это можно сделать в программном коде. Вопреки некоторым заблуждениям, MVVM не исключает программной поддержки. Просто важно помнить, что логика в программном коде не должна пересекать уровни - она должна быть связана непосредственно с пользовательским интерфейсом и конкретной используемой технологией пользовательского интерфейса. (Обратите внимание, однако, что помещение 95% вашей работы в файл разметки может сделать несколько неинтуитивно понятным наличие некоторых функций в выделенном коде, поэтому один или два комментария в разметке об этой одноразовой реализации выделенного кода могут сделать вещи легче в будущем для себя или других.)
Обычно привязка команды в программном коде состоит из двух частей. Во-первых, вы должны отреагировать на событие. Во-вторых, вы (возможно) захотите привязать к свойству 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));
}
}
Почему бы просто не привязать свойство 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}"/>
Вот самый простой способ. Привяжите свое текстовое поле к свойству в модели представления, как описано выше. Затем просто добавьте событие кода программной части (да, я говорю о коде программной части с 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();
}
Это вызовет ваше свойство для обновления в любое время, когда текст изменяется. }
Вы должны использовать Behavior для выполнения команды:
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>
Если вам нужно, вы можете расширить это Behavior дополнительными свойствами, например, если вам нужен отправитель или DataContext другого элемента ..
С уважением, Тамаш
(Я нашел это в сообщении в блоге, но не могу вспомнить его адрес)