Xaml 如何连接文本框';s text更改事件和命令,以便在Silverlight中使用MVVM模式
最近,我意识到MVVM模式对于Silverlight应用程序非常有用,并研究如何将其应用到我的项目中 顺便说一句,如何用命令连接textbox的textChanged事件?按钮有命令属性,但Textbox没有commapd属性。 如果控件没有命令属性,如何组合ICommand和控件的事件 我得到了以下xaml代码Xaml 如何连接文本框';s text更改事件和命令,以便在Silverlight中使用MVVM模式,xaml,silverlight,mvvm,Xaml,Silverlight,Mvvm,最近,我意识到MVVM模式对于Silverlight应用程序非常有用,并研究如何将其应用到我的项目中 顺便说一句,如何用命令连接textbox的textChanged事件?按钮有命令属性,但Textbox没有commapd属性。 如果控件没有命令属性,如何组合ICommand和控件的事件 我得到了以下xaml代码 <UserControl.Resources> <vm:CustomerViewModel x:Key="customerVM"/>
<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中,而不是使用按钮单击。
我知道有自动完成框控制内置。但是,我想知道如何在没有命令属性(如textbox)的控件中调用ViewModel类中的命令属性
谢谢为什么不将
文本
属性绑定到视图模型上的属性?这样,您可以在更改时收到通知,并获得新值:
public string MyData
{
get { return this.myData; }
set
{
if (this.myData != value)
{
this.myData = value;
this.OnPropertyChanged(() => this.MyData);
}
}
}
XAML:
为了便于对话,我们假设您确实需要将一些任意事件连接到命令,而不是直接绑定到ViewModel上的属性(由于控件或框架中缺乏支持、缺陷等),这可以在codebehind中完成。与一些误解相反,MVVM并不排除代码隐藏。记住codebehind中的逻辑不应该跨层,这一点很重要——它应该直接与UI和所使用的特定UI技术相关。(但是,请注意,将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));
}
}
您应该使用行为来执行命令:
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);
}
}
公共类CommandBehavior:TriggerAction
{
公共静态只读DependencyProperty CommandBindingProperty=DependencyProperty.Register(
“命令绑定”,
类型(字符串),
类型(命令行为),
无效);
公共字符串命令绑定
{
get{return(string)GetValue(CommandBindingProperty);}
set{SetValue(CommandBindingProperty,value);}
}
私人ICommand行动;
受保护的覆盖无效附加()
{
Bind(关联对象,_ProcessCommand);
}
私有void\u ProcessCommand(FrameworkElement obj)
{
if(AssociatedObject!=null)
{
var dataContext=AssociatedObject.dataContext;
if(dataContext!=null)
{
var property=dataContext.GetType().GetProperty(CommandBinding);
if(属性!=null)
{
var value=property.GetValue(dataContext,null);
如果(value!=null&&value为ICommand)
{
_动作=作为ICommand的值;
如果(关联对象为控件)
{
var associatedControl=作为控件的AssociatedObject;
associatedControl.IsEnabled=\u action.CanExecute(null);
_action.CanExecuteChanged+=
(o,e)=>associatedControl.IsEnabled=\u action.CanExecute(null);
}
}
}
}
}
}
受保护的覆盖无效调用(对象参数)
{
if(_action!=null&&u action.CanExecute(参数))
{
_action.Execute(参数);
}
}
}
公共静态类DataContextChangedHandler
{
private const string INTERNAL_CONTEXT=“InternalDataContext”;
private const string CONTEXT_CHANGED=“DataContextChanged”;
公共静态只读DependencyProperty InternalDataContextProperty=
DependencyProperty.Register(内部上下文,
类型(对象),
类型(框架元件),
新属性元数据(_DataContextChanged));
公共静态只读从属属性DataContextChangedProperty=
DependencyProperty.Register(上下文\u已更改,
类型(动作),
类型(框架元件),
无效);
私有静态void\u DataContextChanged(对象发送方,DependencyPropertyChangedEventArgs e)
{
var-control=(FrameworkElement)发送方;
var handler=(Action)control.GetValue(DataContextChangedProperty);
if(处理程序!=null)
{
处理者(控制);
}
}
公共静态void绑定(FrameworkElement控件,Action dataContextChanged)
{
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
亲切问候,,
塔马斯
(我在一篇博客文章中发现了这个,但我记不起它的地址了)这是最简单的方法。如上所述,将文本框绑定到视图模型上的属性。然后,只需添加一个代码隐藏(是的,我说的是MV的代码隐藏)
<TextBox Text="{Binding SearchText, Mode=TwoWay}" >
<i:Interaction.Triggers>
<i:EventTrigger EventName="TextChanged">
<utils:CommandBehavior CommandBinding="SearchCommand" />
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
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
}
}
}
<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();
}
<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>
<TextBox Text="{Binding MyText,Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
<TextBox Text="{Binding TextPrintersFilter}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="TextChanged">
<i:InvokeCommandAction Command="{Binding FilterTextChangedCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
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));
}
}
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);
}