Xaml 如何连接文本框';s text更改事件和命令,以便在Silverlight中使用MVVM模式

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

最近,我意识到MVVM模式对于Silverlight应用程序非常有用,并研究如何将其应用到我的项目中

顺便说一句,如何用命令连接textbox的textChanged事件?按钮有命令属性,但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>

我试图做的是,如果用户在文本框中输入一些文本,数据将显示在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);
}