简体   繁体   中英

Binding not working against application resource

I have an application in which I'd like to have a single instance of a collection of items that can be accessed by more than one view model, where each view model is associated with exactly one view. To accomplish this, I have put the collection into an application resource to which the view model obtains a reference in its constructor, which it makes available as a public property.

I have a ListBox in the view that has its DataContext set to the view model, then its ItemsSource is set to the view model property that references the collection in the application. The ListItems, then, bind to properties of the item within the collection.

Unfortunately, while the collection gets populated, the ListBox does not.

Here's the App.xaml:

    <Application x:Class="TestBindingToAppResource.App"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:TestBindingToAppResource"
        StartupUri="MainWindow.xaml">
    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
            </ResourceDictionary.MergedDictionaries>
            <!--Declaring this here so that a single instance of the people list is available to the entire application.-->
            <local:People x:Key="MyPeople"/>
        </ResourceDictionary>
    </Application.Resources>
</Application>

And the individual item and its collection:

    using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;

namespace TestBindingToAppResource
{
    // Contains the data for a single person.
    public class Person : INotifyPropertyChanged
    {
        private string theFirstName;
        public string FirstName { get { return theFirstName; }  set { if (value != theFirstName) { theFirstName = value; NotifyPropertyChanged(); } } }

        private string theLastName;
        public string LastName { get { return theLastName; } set { if (value != theLastName) { theLastName = value; NotifyPropertyChanged(); } } }

        public event PropertyChangedEventHandler PropertyChanged;

        public Person()
        {
            FirstName = "John";
            LastName = "Doe";
        }

        public Person(string firstName, string lastName)
        {
            FirstName = firstName;
            LastName = lastName;
        }

        private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    public class People : ObservableCollection<Person>
    {
        public People()
        {

        }
    }
}

The main window merely contains a UserControl that contains the list.

    <Window x:Class="TestBindingToAppResource.MainWindow"
                xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                xmlns:local="clr-namespace:TestBindingToAppResource"
                Title="MainWindow" Height="350" Width="525">
    <Grid>
        <local:PeopleList x:Name="ListOfPeople"/>
    </Grid>
</Window>

Here's the UserControl that contains the ListBox:

    <UserControl x:Class="TestBindingToAppResource.PeopleList"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:TestBindingToAppResource"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">
    <UserControl.DataContext>
        <local:PeopleViewModel/>
    </UserControl.DataContext>
    <Grid>
        <!--... Other objects ...-->
        <StackPanel Orientation="Vertical" Margin="5,5,5,5">
            <TextBlock Text="Here's a list of people"/>
            <ListBox Name="ListOfPeople" ItemsSource="{Binding People}" Margin="5,0,5,5">
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <StackPanel Orientation="Horizontal">
                            <TextBlock Name="theFirstName" Text="{Binding FirstName}" Margin="5,5,5,5"/>
                            <TextBlock Name="theLastName" Text="{Binding LastName}" Margin="0,5,5,5"/>
                        </StackPanel>
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>
            <!--... Other objects ...-->
        </StackPanel>
        <!--... Other objects ...-->
    </Grid>
</UserControl>

Finally, here's the view model that ties them together:

    using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;

namespace TestBindingToAppResource
{
    // A view model that allows the list in the view to access the people list in the model.

    public class PeopleViewModel
    {
        public People thePeople { get; private set; }

        public PeopleViewModel()
        {
            // A single instance of the people array is instantiated in the Application object as a resource.
            // This allows any number of view models to access a single instance of the data model.

            thePeople = (People)Application.Current.Resources["MyPeople"];

            // The view model should load the people collection.

            LoadThePeople(thePeople);
        }

        private void LoadThePeople (People thePeople)
        {
            thePeople.Add(new Person("Adam", "Baker"));
            thePeople.Add(new Person("Cindy", "Douglas"));
            thePeople.Add(new Person("Edward", "Fox"));
            thePeople.Add(new Person("Gloria", "Herbst"));
        }
    }
}

Here's the results:

在此输入图像描述

So, my main question is, of course, why isn't this working?

But, I believe that many people have the same goal of having a single instance of the data model that can be accessed by a number of view models throughout the application. How is this typically done?

I am using:

  • WPF
  • C#
  • Win10
  • VS 2019 Community

The dataContext of a user control is a PeopleViewModel instance

<UserControl.DataContext>
    <local:PeopleViewModel/>
</UserControl.DataContext>

Then you are binding ListBox to People property

<ListBox Name="ListOfPeople" ItemsSource="{Binding People}" Margin="5,0,5,5">

... but PeopleViewModel doesn't have People property, only thePeople . So make it:

<ListBox Name="ListOfPeople" ItemsSource="{Binding thePeople}" Margin="5,0,5,5">

You should have seen binding warning about missing property People in the Output window of Visual Studio

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM