我刚开始使用Silverlight中的mvvm模型。 在步骤1中,我得到了一个绑定到我的viewmodel的列表框,但现在我想在按钮中传播一个单击,并将列表框的selecteditemchanged传回到viewmodel。 我想我必须以某种方式将按钮的click事件和listbox的selecteditemchanged绑定到我的viewmodel中的2个方法?
对于列表框的selecteditemchanged,我认为当viewmodel尝试将selecteditem设置为另一个值时,还必须有“返回调用”吗?
我来自asp.net(mvc)背景,但无法弄清楚如何在silverlight中做到这一点。
答案 0 :(得分:2)
Roboblob为Silverlight 4提供excellent step-by-step solution。它严格遵循MVVM范例。
答案 1 :(得分:1)
我不会以任何方式将VM绑定或绑定到View中的控件事件。相反,有一个单独的事件由View引发以响应按钮单击。
[免责声明:此代码全部直接来自我的头脑,而不是复制&从VS粘贴 - 以此为例!!]
因此在伪代码中,View将如下所示:
private void MyView_Loaded(...)
{
MyButton.Click += new EventHandler(MyButton_Click);
}
private void MyButton_Click(...)
{
//Raise my event:
OnUserPressedGo();
}
private void OnUserPressedGo()
{
if (UserPressedTheGoButton != null)
this.UserPressedTheGoButton(this, EventArgs.Empty);
}
public EventHandler UserPressedTheGoButton;
并且VM会有这样一行:
MyView.UserPressedTheGoButton += new EventHandler(myHandler);
这可能看起来有点啰嗦,为什么不直接这样做呢?主要原因是您不希望将VM绑定得太紧(如果有的话)到View的内容,否则很难更改View。像这样的一个UI不可知事件意味着按钮可以随时改变而不影响VM - 您可以将其从按钮更改为超链接,或者您雇用的kool kat设计师可能将其更改为完全奇怪和时髦的东西,它不会无所谓。
现在,我们来谈谈列表框的 SelectedItemChanged 事件。您可能希望拦截此事件,以便您可以修改绑定到视图中另一个控件的数据。如果这是一个正确的假设,那么请继续阅读 - 如果我错了,那么请停止阅读并重复使用上面的例子:)
可能的情况是,您可能无需处理该事件的处理程序。如果将列表框的 SelectedItem 绑定到VM中的属性:
<ListBox ItemSource={Binding SomeList} SelectedItem={Binding MyListSelectedItem} />
然后在VM的 MyListSelectedItem 属性中:
public object MyListSelectedItem
{
get { return _myListSelectedItem; }
set
{
bool changed = _myListSelectedItem != value;
if (changed)
{
_myListSelectedItem = value;
OnPropertyChanged("MyListSelectedItem");
}
}
}
private void OnPropertyChanged(string propertyName)
{
if (this.NotifyPropertyChanged != null)
this.NotifyPropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
要获取NotifyPropertyChanged
事件,只需在VM上实现INotifyPropertyChanged
接口(您应该已经完成)。这是基本的东西......你接下来的是VM本身的 NotifyPropertyChanged 事件处理程序:
private void ViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
switch (e.PropertyName)
{
case "MyListSelectedItem":
//at this point i know the value of MyListSelectedItem has changed, so
//i can now retrieve its value and use it to modify a secondary
//piece of data:
MySecondaryList = AllAvailableItemsForSecondaryList.Select(p => p.Id == MyListSelectedItem.Id);
break;
}
}
现在您只需要MySecondaryList通知它的值已经改变:
public List<someObject> MySecondaryList
{
get { return _mySecondaryList; }
set
{
bool changed = .......;
if (changed)
{
... etc ...
OnNotifyPropertyChanged("MySecondaryList");
}
}
}
并且任何绑定它的内容都会自动更新。再一次,似乎这是做事的漫长道路,但这意味着你已经避免了从View中获取UI事件的任何处理程序,你保持了View和ViewModel之间的抽象。
我希望这对你有所帮助。使用我的代码,我尝试让ViewModel对View一无所知,而View只知道ViewModel的最低限度(View接收ViewModel作为接口,因此它只能知道接口指定了什么)。
答案 2 :(得分:1)
关于绑定按钮点击事件我可以推荐Laurent Bugnion的MVVM Light Toolkit(http://www.galasoft.ch/mvvm/getstarted/)作为解决这个问题的方法,我将提供一个小例子,但Laurent的文档很可能是一个更好的方法。了解他的框架。
在xaml页面中引用几个程序集
xmlns:command="clr-namespace:GalaSoft.MvvmLight.Command;assembly=GalaSoft.MvvmLight.Extras"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
向按钮添加混合行为
<Button Content="Press Me">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<command:EventToCommand Command="{Binding ViewModelEventName}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
并在viewmodel中创建事件,单击该按钮时将调用该事件
public RelayCommand ViewModelEventName { get; protected set; }
...
public PageViewModel()
{
ViewModelEventName = new RelayCommand(
() => DoWork()
);
}
这支持传递参数,检查是否允许执行等。
虽然我自己没有使用它,但我认为Prism框架也允许你做类似的事情。