我正在将C#WPF应用程序转换为MVVM模式并且有几个问题:
我有一个ViewModel绑定到Model,其构造函数需要参数,该参数是从返回到搜索查询的JSON列表中选择单个对象的结果。 我想这意味着我无法在执行搜索之前实例化此ViewModel。
这不是以前的问题,因为我不需要将View数据绑定到ViewModel,并且只收集GUI中的文本值以在所有数据到位时实例化对象(Model)并且我准备好了有它的东西。
使用MVVM,这是一个问题,因为我不想强制此搜索成为第一个用户操作 - 用户应该能够修改GUI中绑定到ViewModel的任何字段。
处理此类情况的实用方法有哪些?似乎我必须要么:a)等待在实例化VM之前选择搜索结果,或者b)从构造函数中删除参数,而是创建一个方法,该方法将在实例化VM上调用,用于计算/设置属性,否则由构造函数设置。
第二个问题:如何实现搜索功能 - 也就是说,如何在单击搜索按钮后暂存结果列表?以前我会在SearchButton_Click方法中反序列化列表,并将组合框的绑定设置为生成的集合。使用MVVM,我无法描述返回结果列表和选择单个结果之间的状态。我是否创建了一个单独的ViewModel,其中包含绑定到组合框的目标类型的空列表和绑定到搜索文本框的SearchTerm属性,并从SearchButton命令ICommand填充组合框?然后,如何将所选项目绑定到原始视图模型?
视图模型:
class ObjectViewModel
{
public CustomObject data;
public ICommand Search;
public ObjectViewModel()
{
this.data = new CustomObject();
}
}
型号:
[DataContract]
public class User
{
[DataMember(Name = "EmailAddress")]
public string EmailAddress { get; set; }
[DataMember(Name = "FirstName")]
public string FirstName { get; set; }
[DataMember(Name = "FullName")]
public string FullName { get; set; }
...
}
[DataContract]
public class CustomObject
{
public User Owner;
...
}
查看(尚未重写):
<TextBox Margin="5,0" Name="Owner"></TextBox>
<Button Name="Search" Content="Lookup" Click="OwnerLookUp_Click"></Button>
<ComboBox Name="OwnerMatches" SelectionChanged="OwnerMatches_SelectionChanged" Visibility="Hidden"/>
OwnerLookUp_Click从Owner文本框中获取文本并返回ObservableCollection并将其绑定到OwnerMatches。 OwnerMatches_SelectionChanged将所有者文本框设置为所选项目的Fullname属性。
在这种情况下,我将在ObjectViewModel中绑定到data.Owner?
答案 0 :(得分:2)
您的视图模型应代表您的视图的业务逻辑和状态。因此,对于您的第一个问题,如果您的视图是不进行搜索的有效状态,则它不应该是视图模型的构造函数参数。相反它应该是一个属性。
通常在WPF中,您可以通过命令将按钮绑定到视图模型上的某种ICommand
(我通常使用DelegateCommand
)。该命令应该执行搜索,然后适当地更新视图模型的状态。这意味着,它应该进行搜索,收集结果,并使用收集的数据设置与搜索结果相关的属性值。
这里的好处是,假设您有一些UI组件绑定到这些搜索结果,一旦您的命令完成,您的UI将自动刷新结果,而无需其他代码。
<强>更新强>
响应您的评论:所有内容都应该绑定到您的视图模型,并且您的视图模型使用这些绑定来完成所需的工作并更新视图。
因此,您的文本框Owner
应该绑定到一个属性,然后您的ICommand
看起来像这样:
private void ClickCommandExecute()
{
var results = searchHelper.SearchFor(this.Owner);
this.ComboSource = results;
}
同样,您的组合框的SelectedValue
属性将绑定到视图模型上的另一个属性。在您的属性的setter中,您可以对选择更改做出反应:
public string SelectedResult
{
get { ... }
set
{
_selectedResult = value;
OnSelectedResultChanged(); // Go do what you need to do here
}
}
答案 1 :(得分:1)
嗯,你认为你将参数从构造函数中移出是正确的。这是有道理的,因为您的VM应该代表备份视图的数据。因此,它应该有一个下拉菜单的项目列表,标签的字符串等。当按下搜索时,您可以实际执行搜索并反序列化结果,就像在MVC中一样。
要将其添加到UI上,请将ComboBox绑定到作为列表的视图模型上的属性。此列表将是反序列化完成时分配的内容。通过分配此变量,您将更新UI,并且瞧。
public class MyViewModel : INotifyPropertyChanged
{
private ObservableCollection<string> _retrievedItems;
private string _selectedItem;
public ObservableCollection<string> RetrievedItems
{
get
{
return _retrievedItems;
}
set
{
_retrievedItems = value;
OnPropertychanged("RetrievedItems");
}
}
public string SelectedItem
{
// same as other property, but with _selectedItem
}
public MyViewModel()
{
// Do whatever you normally do to initialize the view model
}
public void Search(string searchParamThatWasInConstructor)
{
// do something to get results (deserialization)
// var results = new JavascriptSerializer( ).Deserialize<List<string>>( searchParamThatWasInConstructor );
// That's just a fake example
RetrievedItems = new ObservableCollection<string>(results);
SelectedItem = RetrievedItems.Count > 0 ? RetrievedItems[0] : string.Empty;
}
}
并按如下方式绑定您的组合框:
<ComboBox ItemsSource="{Binding RetrievedItems}" SelectedItem="{Binding SelectedItem}" />