构建视图模型后,如何确保数据绑定?

时间:2017-12-13 04:18:48

标签: c# wpf mvvm data-binding

我在使用MVVM架构时遇到了一个主要问题,我还没有看到它直接解决甚至提到过。在多线程应用程序中,视图可以在完全构造视图模型之前启动数据绑定,从而创建绑定失败。

以下是一个例子:

public class StocksViewModel
{

    // blah blah

    public IOrderedEnumerable<KeyValuePair<string, string>> AvailableStocks
    {
        get
        {
            // This could take a minute or two
            return StockService.GetAvailableStocks(User);
        }
    }

    public double Quantity
    {
        get => Quantity;
        set
        {
            Quantity = value; RaisePropertyChanged(nameof(Quantity));
        }
    }

    public double Symbol
    {
        get => Symbol;
        set
        {
            Symbol = value; RaisePropertyChanged(nameof(Symbol));
        }
    }
}

这里是XAML:

        <ComboBox x:Name="Quantity"  Grid.ColumnSpan="2" Grid.Column="2" SelectedValue="{x:Bind Mode=TwoWay, Path=ViewModel.Quantity}"  DisplayMemberPath="Value" SelectedValuePath="Key" ItemsSource="{x:Bind ViewModel.AvailableStocks}"   />

        <ComboBox x:Name="Symbol"  Grid.ColumnSpan="2" Grid.Column="2" SelectedValue="{x:Bind Mode=TwoWay, Path=ViewModel.Symbol}"  DisplayMemberPath="Value" SelectedValuePath="Key" ItemsSource="{x:Bind ViewModel.AvailableStocks}"   />

如您所见,数据绑定可能无法预测。如果StockService需要几秒钟,则数量和符号永远不会有机会数据绑定到AvailableStock。

处理这种情况的处方方法是什么?

1 个答案:

答案 0 :(得分:1)

我的方法是采取与你在这里不同的道路。在我的视图模型的构造函数中,我将创建一个readonly ObservableCollection<KeyValuePair<string, string>>。那时我也会开始一个新的线程来呼叫GetAvailableStocks。结果返回后,您可以填充ObservableCollection。但是,在检索数据时,我会包含加载图标或类似的想法,以便用户知道我们仍在收集结果。

视图模型应该加载它可以的所有数据,并准备好在收到数据时显示其他数据。如果数据可以预先加载并传递到视图模型中,那就更好了,但这种情况总是不会发生。

代码示例(请原谅我在移动设备上的格式化):

public class ViewModel
{
    readonly ObservableCollection<KeyValuePair<string, string>> _availableStocks;

    public ObservableCollection<KeyValuePair<string, string>> AvailableStocks 
    {
        get => _availableStocks;
    }
    public bool IsExecuting { get; set; }

    public ViewModel()
    {
        _availableStocks = new ObservableCollection<KeyValuePair<string, string>>();
        IsExecuting = true;
        TaskFactory.StartNew(() =>
        {
            var results = StockService.GetAvailableStocks(User);
            foreach (var stock in results)
                AvailableStocks.Add(stock);
            IsExecuting = false;
        }
    }
}

此代码未经测试,但我希望它可以帮助您解决问题!