数据绑定 ObservableCollections 与 IoC

Data Binding ObservableCollections with an IoC

我正在编写一个针对 Windows 8.1 的通用应用程序,并且正在重写它以使用 IoC 容器。但是,我发现了一些让我有点困惑的事情。

在我使用 IoC 之前,我会在代码隐藏中创建我的 VM 实例并绑定到它,如下所示:

MainPage.xaml

<ListBox ItemsSource="{Binding ItemList}" DisplayMemberPath="Title"  />

MainPage.xaml.cs

private MainPageVM Data = new MainPageVM();

public MainPage()
{
    this.InitializeComponent();
    this.DataContext = Data;
}

MainPageVM.cs

public ObservableCollection<MenuItem> ItemList { get; set; }

public MainPageVM()
{
    ItemList = new ObservableCollection<MenuItem>();
}

这很好用。但是,现在我设置应用程序的方式非常不同,如下所示:

App.xaml.cs

private IUnityContainer _Container;

public App()
{
    _Container = new UnityContainer();
    this.InitializeComponent();
}

protected override void OnLaunched(LaunchActivatedEventArgs e)
{
    _Container.RegisterType<MainPage>(new ContainerControlledLifetimeManager());
    _Container.RegisterType<Frame>(new ContainerControlledLifetimeManager());

    _Container.RegisterType<MainPageVM>(
        new ContainerControlledLifetimeManager(),
        new InjectionConstructor(typeof(Frame),
                                 typeof(MainPage)));

    Frame rootFrame = Window.Current.Content as Frame;

    if (rootFrame == null)
    {
        rootFrame = _Container.Resolve<Frame>();
        rootFrame.CacheSize = 1;
    }

    if (rootFrame.Content == null)
    {
        var mainPageVM = _Container.Resolve<MainPageVM>();
        mainPageVM.Show();
    }

    Window.Current.Activate();
}

这会创建一个 MainPageVM() 的实例:

MainPageVM.cs

private Frame _Frame;
private Page _View;

public ObservableCollection<MenuItem> ItemList { get; set; }

public MainPageVM(
    Frame frame, 
    Page view)
{
    _Frame = frame;
    _View = view;

    _View.DataContext = this;

    ItemList = new ObservableCollection<MenuItem>();
    OnPropertyChanged("ItemList");
}

public void Show()
{
    _Frame.Content = _View;
    Window.Current.Content = _Frame;
}

MainPage.xaml.cs

public MainPage()
{
    this.InitializeComponent();
}

MainPage.xaml没变。

我的问题是,为什么我必须在第二个版本而不是第一个版本中发出 OnPropertyChanged("ItemList"); 信号来激活绑定?我是不是做错了什么?

我的整个存储库可以在 GitHub 上找到:Learn OneNote

在这两行之间切换应该可以解决这个问题:

_View.DataContext = this;

ItemList = new ObservableCollection<MenuItem>();

发生的情况是您当前首先设置 DataContext,这会导致重新评估所有视图的绑定,然后才修改 ItemsList。由于 ItemsList 的 setter 不调用 OnPropertyChanged,视图在更改时不会更新。通过在行之间切换,您首先初始化数据上下文,然后才重新评估绑定。

一个更具可读性的解决方案是在您的 setter 中包含对 OnPropertyChanged 的调用(这样您就不必换行):

private ObservableCollection<MenuItem> itemList
public ObservableCollection<MenuItem> ItemList
{
    get { return itemsList; }
    set
    {
        if (itemsList != value)
        {
            itemsList = value;
            OnPropertyChanged("ItemsList");
        }
    }
}

虽然最好先初始化数据上下文,然后再设置它以避免控件尝试绑定两次。

这是因为在第一个版本中,ItemList 在视图模型构造函数中实例化,视图模型构造函数本身在数据绑定之前在视图初始化时构造。但是在第二个版本中,首先绑定数据,然后实例化 ItemList。