将ICommand绑定到按钮?

时间:2014-01-07 09:52:24

标签: c# wpf mvvm

我是MVVM模式的新手,事情变得非常缓慢,我希望能够单击表单上的按钮,然后在运行时动态创建文本框。我有一个“添加标题”和“添加问题”,它们都添加了文本框,但在不同的位置,您可以在一个标题下添加任意数量的问题。我在这个类中创建了一个名为Standard的类:

public class Standard
{
    string _title;
    ObservableCollection<string> _questions;
    public event PropertyChangedEventHandler PropertyChanged;

  #region NofiftyPropChnage
  protected void NotifyOfPropertyChanged(string name)
  {
      PropertyChangedEventHandler handler = PropertyChanged;
      if (handler != null)
      {
          handler(this, new PropertyChangedEventArgs(name));
      }
  }

  protected void NotifyOfPropertyChanged<TProperty>(Expression<Func<TProperty>> property)
  {
      NotifyOfPropertyChanged(property.GetMemberInfo().Name);
  }
  #endregion

  #region Properties
  public string Title
    {
        get { return _title; }
        set
        {
            _title = value;
            NotifyOfPropertyChanged(() => Title);
        }
    }

    public ObservableCollection<string> Questions
    {
        get { return _questions; }
        set
        {
            _questions = value;
            NotifyOfPropertyChanged(() => Questions);
        }
    }
  #endregion
}

此类包含Title属性以及Questions属性列表,因为您可以在Title下添加Questions。

我还有一个ViewModel类,其中包含:

class ViewModel :INotifyPropertyChanged
{
    #region NotifyPropertyChange
    public event PropertyChangedEventHandler PropertyChanged;

    protected void NotifyOfPropertyChanged(string name)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(name));
        }
}

protected void NotifyOfPropertyChanged<TProperty>(Expression<Func<TProperty>> property)
{
        NotifyOfPropertyChanged(property.GetMemberInfo().Name);
}
    #endregion

private ObservableCollection<Standard> _standardCollection;
public ObservableCollection<Standard> StandardCollection
{
        get
        {
            return _standardCollection;
        }
        set
        {
            _standardCollection = value;
            NotifyOfPropertyChanged(() => StandardCollection);
        }
}
}

此类包含一个标准列表,一个标准就是当您单击保存时,文本框和文本框中的信息完成。它保存为Standard

最后我的XAML代码:

<Grid>

<button Content="Add Title"/>
<button Content="Add Question"/>

<StackPanel>
        <ItemsControl ItemsSource="{Binding StandardCollection}">
            <ItemsControl.ItemTemplate>
                <DataTemplate DataType="{x:Type local:Standard}">
                    <Grid>
                        <TextBox Text="{Binding Title}"/>
                        <ItemsControl ItemsSource="{Binding Questions}">
                            <ItemsControl.ItemTemplate>
                                <DataTemplate>
                                    <TextBox Text="{Binding Questions}"/>
                                </DataTemplate>
                            </ItemsControl.ItemTemplate>
                        </ItemsControl>
                    </Grid>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
</StackPanel>


</Grid>

一切都运行且没有错误但是当我点击“添加标题”或“添加问题”时,没有显示任何文本框,是否有任何帮助?

5 个答案:

答案 0 :(得分:1)

标准需要实现INotifyPropertyChanged接口。通常,您不应该多次执行此操作,只需声明一个实现该内容的基类并从中继承所有视图模型。此外,如果您使用包管理器将MVVM Lite添加到您的项目中,那么您将获得为您提供的大量此类内容。

答案 1 :(得分:1)

我不知道为什么这些其他人正在讨论INotifyPropertyChanged界面,因为它与ICommand几乎没什么关系,尽管看起来你似乎没有尝试过使用它将其添加到Standard类定义。

无论哪种方式,我觉得你需要使用RelayCommand或类似的东西。这是一个扩展ICommand接口的类......您可以将其视为delegate命令。您可以简单地定义命令逻辑 canExecute内联处理程序,而不是为每个命令定义单独的类。这是一个简化的例子:

public ICommand SaveCommand
{
    get { return new RelayCommand(execute => Save(), canExecute => CanSave()); }
}

...

<Button Content="Save" Command="{Binding SaveCommand}" />

您可以在GitHub上的RelayCommand.cs页面找到它的实现,并在MDSN杂志的Commands, RelayCommands and EventToCommand页面中找到它的描述。

答案 2 :(得分:1)

好的,我会再拍一次。我已经删除了标题部分,只是集中在问题上,以便将此作为一个最小的例子。首先,您需要一个为您的视图模型实现INotifyPropertyChanged的基类:

public abstract class ObservableObject : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged<T>(Expression<Func<T>> propertyExpresion)
    {
        var property = (MemberExpression)propertyExpresion.Body;
        this.OnPropertyChanged(property.Member.Name);
    }

    protected void OnPropertyChanged(string propertyName)
    {
        if (this.PropertyChanged != null)
        {
            this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

接下来,你需要一个实现ICommand的类来绑定按钮,这会导致在按下这些按钮时调用处理程序:

// by Josh Smith, http://msdn.microsoft.com/en-us/magazine/dd419663.aspx
public class RelayCommand : ICommand
{
    #region Fields

    readonly Action<object> _execute;
    readonly Predicate<object> _canExecute;

    #endregion // Fields

    #region Constructors

    public RelayCommand(Action<object> execute)
        : this(execute, null)
    {
    }

    public RelayCommand(Action<object> execute, Predicate<object> canExecute)
    {
        if (execute == null)
            throw new ArgumentNullException("execute");

        _execute = execute;
        _canExecute = canExecute;
    }
    #endregion // Constructors

    #region ICommand Members

    public bool CanExecute(object parameter)
    {
        return _canExecute == null ? true : _canExecute(parameter);
    }

    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }

    public void Execute(object parameter)
    {
        _execute(parameter);
    }

    #endregion // ICommand Members
}

这两个类是由其他人编写的,如果你将MVVM Lite项目添加到你的项目中,你将为它们提供它们。

接下来,我们需要创建一个带有ObservableCollection of Questions的视图模型和一个在用户按下按钮时调用的处理程序:

public class MyViewModel : ObservableObject
{
    public ICommand AddQuestionCommand {get; private set;}

    ObservableCollection<string> _questions = new ObservableCollection<string>();
    public ObservableCollection<string> Questions
    {
        get { return _questions; }
        set
        {
            _questions = value;
            OnPropertyChanged(() => Questions);
        }
    }

    public MyViewModel()
    {
        this.AddQuestionCommand = new RelayCommand(new Action<object>((o) => OnAddQuestion()));
    }

    private void OnAddQuestion()
    {
        this.Questions.Add("new item");
    }

}

显然你需要创建一个这样的实例并将其设置为窗口的DataContext。当命令被触发时,处理程序被调用,然后它又向集合中添加一个新字符串。 XAML现在需要将一个按钮绑定到该命令,并使用Questions集合创建一个显示所有内容的TextBlock列表:

<StackPanel>
    <Button Content="Add Question" Command="{Binding AddQuestionCommand}" HorizontalAlignment="Left"/>
    <ItemsControl ItemsSource="{Binding Questions}">
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <TextBox Text="{Binding .}" Width="200" HorizontalAlignment="Left"/>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
</StackPanel>

希望这应该给你一个起点。如果我错过了某些内容或者您需要澄清任何内容,请发布后续内容,我将尽我所能。

答案 3 :(得分:1)

您需要大量更改代码才能使其正常工作。执行以下操作:

步骤1.添加Class RelayCommand

public class RelayCommand : ICommand
{
    public Func<bool> CanExecute { get; set; }
    public Action Execute { get; set; }

    public RelayCommand()
    {
    }

    public RelayCommand(Action execute)
    {
        Execute = execute;
    }

    #region ICommand Members

    bool ICommand.CanExecute(object parameter)
    {
        if (this.CanExecute == null)
        {
            return true;
        }
        else
        {
            return this.CanExecute();
        }
    }

    event EventHandler ICommand.CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }

    void ICommand.Execute(object parameter)
    {
        this.Execute();
    }

    #endregion
}

步骤2.在ViewModel中添加命令

public ICommand AddTitle { get; private set; }
public ICommand AddQuestion { get; private set; }

public ViewModel()
{
    _standardCollection = new ObservableCollection<Standard>();

    AddTitle = new RelayCommand(OnAddTitle);
    AddQuestion = new RelayCommand(OnAddQuestion);
}

void OnAddTitle()
{
    _standardCollection.Add(new Standard());
}

void OnAddQuestion()
{
    _standardCollection.Last().Questions.Add(new Question("Some Question"));
}

步骤3.绑定按钮

<Button Content="Add Title"  Command="{Binding AddTitle}"/>
<Button Content="Add Question" Command="{Binding AddQuestion}"/>

您还必须在XAML中解决问题。 由于用户可以更改问题文本,因此您应该创建一个单独的类问题。

答案 4 :(得分:0)

尝试在课程INotifyPropertyChanged上实施Standard

public class Standard : INotifyPropertyChanged
{
    string _title;
    ObservableCollection<string> _questions;
    public event PropertyChangedEventHandler PropertyChanged;

  #region NofiftyPropChnage
  protected void NotifyOfPropertyChanged(string name)
  {
      PropertyChangedEventHandler handler = PropertyChanged;
      if (handler != null)
      {
          handler(this, new PropertyChangedEventArgs(name));
      }
  }

  protected void NotifyOfPropertyChanged<TProperty>(Expression<Func<TProperty>> property)
  {
      NotifyOfPropertyChanged(property.GetMemberInfo().Name);
  }
  #endregion

  #region Properties
  public string Title
    {
        get { return _title; }
        set
        {
            _title = value;
            NotifyOfPropertyChanged(() => Title);
        }
    }

    public ObservableCollection<string> Questions
    {
        get { return _questions; }
        set
        {
            _questions = value;
            NotifyOfPropertyChanged(() => Questions);
        }
    }
  #endregion
}