我尽量不发布这样的问题,但我真的很难找到答案或类似的例子。我认为这是一个非常简单的例子,我想设置。 基本上我想使用命令将项目从文本框添加到列表框。我想通过CanExecute确保文本框中有一些内容,我想确保它不在列表中。
我知道这看起来似乎过于复杂,但它会在我一直在努力的某些方面受到打击。
<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),因为这主要是出于教育原因。
也非常感谢任何参考。我已经阅读了很多博客等,但在实施完整的简单示例之前,我总觉得他们偏离了。
答案 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
}