1

我正在将 C# WPF 应用程序转换为 MVVM 模式并有几个问题:

我有一个 ViewModel 绑定到具有构造函数的模型,该构造函数需要参数,该参数是从返回到搜索查询的 JSON 列表中选择单个对象的结果。我想这意味着在执行搜索之前我无法实例化这个 ViewModel。

以前这不是问题,因为我不需要将 View 数据绑定到 ViewModel,并且只在所有数据都到位并且我准备好用它做一些事情时从 GUI 收集文本值来实例化对象(模型) .

对于 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 将 Owner 文本框设置为所选项目的 Fullname 属性。

在这种情况下,我要绑定到 ObjectViewModel 中的 data.Owner 什么?

4

2 回答 2

2

您的视图模型应该代表您的视图的业务逻辑和状态。所以关于你的第一个问题,如果你的视图没有搜索是一个有效的状态,那么它不应该是视图模型的构造函数参数。相反,它可能应该是一个属性。

通常在 WPF 中,您会通过命令将按钮绑定到视图模型上的某种按钮ICommand(我通常使用DelegateCommand)。该命令应该执行搜索,然后适当地更新视图模型的状态。意思是,它应该进行搜索,收集结果,并使用它收集的数据设置与搜索结果相关的属性值。

这样做的好处是,假设您有一些 UI 组件绑定到这些搜索结果,一旦您的命令完成,您的 UI 就会自动刷新结果,而无需额外的代码。

更新

回应您的评论:一切都应该绑定到您的视图模型,并且您的视图模型使用这些绑定来完成所需的工作并更新视图。

所以你的文本框Owner应该绑定到一个属性,然后你ICommand会看起来像这样:

private void ClickCommandExecute() 
{
      var results = searchHelper.SearchFor(this.Owner);
      this.ComboSource = results;
}

同样,您的组合框的SelectedValue属性将绑定到您的视图模型上的另一个属性。在您的属性的设置器中,您可以对选择更改做出反应:

public string SelectedResult
{
    get { ... }
    set 
    {
        _selectedResult = value;
        OnSelectedResultChanged(); // Go do what you need to do here
    }
 }
于 2013-09-11T19:33:27.790 回答
1

好吧,您认为将参数移出构造函数是正确的。这是有道理的,因为您的 VM 应该代表备份视图的数据。所以它应该有一个下拉菜单的项目列表、标签的字符串等。当按下搜索时,你可以像在 MVC 中那样实际执行搜索并反序列化结果。

要将其放到 UI 上,请将 ComboBox 绑定到 View Model 上的一个属性,该属性是一个列表。该列表将是您在反序列化完成后分配的内容。通过分配给这个变量,您将更新 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}" />
于 2013-09-11T19:32:04.830 回答