我是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>
一切都运行且没有错误但是当我点击“添加标题”或“添加问题”时,没有显示任何文本框,是否有任何帮助?
答案 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
}