WPF简单指挥示例

时间:2014-07-18 00:44:25

标签: c# wpf mvvm

我尽量不发布这样的问题,但我真的很难找到答案或类似的例子。我认为这是一个非常简单的例子,我想设置。 基本上我想使用命令将项目从文本框添加到列表框。我想通过CanExecute确保文本框中有一些内容,我想确保它不在列表中。

我知道这看起来似乎过于复杂,但它会在我一直在努力的某些方面受到打击。

Simple interface

<Window x:Class="Commanding_Example.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local ="clr-namespace:Commanding_Example"
        Title="MainWindow" Width="200" Height="300">
    <Window.DataContext>
        <local:MainViewModel/>
    </Window.DataContext>

    <StackPanel>
        <TextBlock>Name</TextBlock>
        <TextBox></TextBox>
        <Button>Add</Button>
        <Button>Remove</Button>
        <ListBox ItemsSource="{Binding People}" DisplayMemberPath="Name"></ListBox>
    </StackPanel>
</Window>

我有一个人类

class Person
{
    public string Name { get; set; }
}

我有这个的唯一原因是Add需要创建一个新对象,所以比简单的字符串稍微复杂一些。

然后是一个基本的视图模型

class MainViewModel : INotifyPropertyChanged
{

    public MainViewModel()
    {
        People = new ObservableCollection<Person>();
        People.Add( new Person {Name = "jimmy"});
    }

    public ObservableCollection<Person> People { get; set; }

    #region Default INotifyPropertyChanged implimentation
    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }
    #endregion
}

所以问题是,如果名称已经存在或名称字段为空,我将如何实现命令以便它使用CanExecute禁用“添加”按钮。

然后同样的删除交易,只有在列表中选择了名称时才会启用。

我想尽可能地将其作为MVVM的赞美。

我已经看到你可以使用Button.CommandBindings附加属性来注入你想要为每个方法使用的方法,但这似乎并不完全是MVVM的快乐。

此外,我还想避免使用框架(Prism / Caliburn.Micro),因为这主要是出于教育原因。

也非常感谢任何参考。我已经阅读了很多博客等,但在实施完整的简单示例之前,我总觉得他们偏离了。

2 个答案:

答案 0 :(得分:1)

嗯,MVVM只是一种模式或哲学,所以我认为你避免使用框架的愿望可能有点误导。即使你没有使用其中一个框架,你也可以编写自己的框架来实现MVVM模式。

话虽如此,您可能想要使用的是DelegateCommand或其中一个类似的实现。见:http://www.wpftutorial.net/DelegateCommand.html。我认为您正在寻找的重要部分是WPF按钮绑定的命令必须在视图模型中发生更改时引发CanExecuteChanged事件,这会影响命令是否可以执行

因此,在您的情况下,您可能希望将CanExecuteChanged AddPersonDelegateCommand的{​​{1}}添加到您的OnPropertyChanged方法中(可能会按照该属性的名称进行过滤)被改变了)。这告诉绑定命令的任何东西在命令上调用CanExecute,然后你将在CanExecute中使用你的逻辑来确定具有输入名称的人是否已经存在。

所以要添加一些示例代码,它可能看起来像这样:

class MainViewModel : INotifyPropertyChanged
{
    public MainViewModel()
    {
        People = new ObservableCollection<Person>();
        People.Add( new Person {Name = "jimmy"});
        AddPersonDelegateCommand = new DelegateCommand(AddPerson, CanAddPerson);
    }

  // Your existing code here

  protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
  {
      if(propertyName == "NewNameTextBox") AddPersonDelegateCommand.RaiseCanExecuteChanged();

      PropertyChangedEventHandler handler = PropertyChanged;
      if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
  }

  public DelegateCommand AddPersonDelegateCommand { get; set; }

  public void AddPerson()
  { 
      // Code to add a person to the collection
  }

  public bool CanAddPerson()
  {
      return !People.Any(p=>p.Name == NewNameTextBox);
  }

  public string NewNameTextBox 
  { 
    get { return _newNameTextBox; }
    set 
    { 
       _newNameTextBox = value;
       OnPropertyChanged();
    }
  }
}

*注意:在此示例中,您需要将<TextBox></TextBox>绑定到视图模型上的NewNameTextBox属性。

答案 1 :(得分:1)

  

我将如何实现命令,以便它使用CanExecute   如果名称已存在,则禁用“添加”按钮或“名称”字段   是空的

我将展示如何添加,删除类似,我留下让你弄明白。首先,我将使用AddPerson命令显示xaml更改:

<TextBox Text="{Binding CurrentPerson, 
                Mode=TwoWay, 
                UpdateSourceTrigger=PropertyChanged
               }"/>
<Button Command="{Binding AddPerson}">Add</Button>

我们已将当前已编辑的文本绑定到名为CurrentPerson的视图模型上的新属性。这样做是因为我们想要访问该人输入的内容,但我们还需要在用户输入时绑定 updated 。要完成更新,我们通过将UpdateSourceTrigger属性设置为PropertyChanged来指定绑定更新。否则我们的CurrentPerson字符串和最终命令Can操作只会在编辑文本框失去焦点时触发。

<强>视图模型 viewmodel将订阅AddPerson命令。执行该操作将添加用户,但也检查一个can方法,该方法返回一个布尔值是否启用该按钮。当CurrentPerson属性发生变化时,我们最终会在命令类上调用RaiseCanExecuteChanged以使按钮检查can方法时,将会执行此操作。

此VM缩写为示例并基于您的完整VM

public OperationCommand AddPerson { get; set; }
public string _currentPerson;

public MainViewModel()
{
 People = new ObservableCollection<Person>();
 People.Add(new Person { Name = "jimmy" });

            // First Lamda is where we execute the command to add,
            // The second lamda is the `Can` method to enable the button.
 AddPerson = new OperationCommand((o) => People.Add(new Person { Name = CurrentPerson }),
                                  (o) => (!string.IsNullOrWhiteSpace(CurrentPerson) && 
                                          !People.Any(per => per.Name == CurrentPerson)));

 // When the edit box text changes force a `Can` check.
 this.PropertyChanged += MainViewModel_PropertyChanged ;
}

void MainViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
    if (e.PropertyName == "CurrentPerson")
        AddPerson.RaiseCanExecuteChanged();
}

最后,这是使用的命令类,它基于我的博客文章Xaml: ViewModel Main Page Instantiation and Loading Strategy for Easier Binding.

public class OperationCommand : ICommand
{

#region Variables

Func<object, bool> canExecute;
Action<object> executeAction;

public event EventHandler CanExecuteChanged;

#endregion

#region Properties

#endregion

#region Construction/Initialization

public OperationCommand(Action<object> executeAction)
    : this(executeAction, null)
{
}

public OperationCommand(Action<object> executeAction, Func<object, bool> canExecute)
{
    if (executeAction == null)
    {
        throw new ArgumentNullException("Execute Action was null for ICommanding Operation.");
    }
    this.executeAction = executeAction;
    this.canExecute = canExecute;
}

#endregion

#region Methods

public bool CanExecute(object parameter)
{
    bool result = true;
    Func<object, bool> canExecuteHandler = this.canExecute;
    if (canExecuteHandler != null)
    {
        result = canExecuteHandler(parameter);
    }

    return result;
}

public void RaiseCanExecuteChanged()
{
    EventHandler handler = this.CanExecuteChanged;
    if (handler != null)
    {
        handler(this, new EventArgs());
    }
}

public void Execute(object parameter)
{
    this.executeAction(parameter);
}

#endregion

}