使用MVVM实现搜索功能

时间:2013-09-11 19:25:14

标签: c# .net wpf mvvm

我正在将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?

2 个答案:

答案 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}" />