ObservableCollection не обновляет View - PullRequest
10 голосов
/ 19 декабря 2011

Я только начинаю с MVVM и натолкнулся на препятствие, с которым, я надеюсь, кто-то может мне помочь. Я пытаюсь создать простой вид с 2 списками. Выбор из первого списка будет заполнять второй список. У меня создан класс, в котором хранится информация, к которой я хочу привязаться.

MyObject Class (Observable Object - это просто базовый класс, который реализует INotifyPopertyChanged)

public class MyObject : ObservableObject
{
    String _name = String.Empty;
    ObservableCollection<MyObject> _subcategories;

    public ObservableCollection<MyObject> SubCategories
    {
        get { return _subcategories; }

        set
        {
            _subcategories = value;
            RaisePropertyChanged("SubCategories");
        }
    }

    public String Name
    {
        get { return _name; }
        set
        {
            _name = value;
            RaisePropertyChanged("Name");
        }
    }


    public MyObject()
    {
        _subcategories = new ObservableCollection<EMSMenuItem>();
    }
}

В моей модели представления у меня создано две коллекции ObservableCollections

public ObservableCollection<EMSMenuItem> Level1MenuItems { get; set; }
public ObservableCollection<EMSMenuItem> Level2MenuItems { get; set; }

В моем конструкторе ViewModel у меня есть:

this.Level1MenuItems = new ObservableCollection<EMSMenuItem>();
this.Level2MenuItems = new ObservableCollection<EMSMenuItem>();
this.Level1MenuItems = LoadEMSMenuItems("Sample.Xml");

Это прекрасно работает для предметов уровня 1, и они правильно отображаются в представлении. Однако у меня есть команда, которая вызывается, когда пользователь щелкает элемент в списке, который имеет следующее:

Level2MenuItems = ClickedItem.SubCategories;

По какой-то причине это не обновляет интерфейс второго списка. Если я поставлю точку останова в этом месте, я увижу, что в Level2MenuItems хранится правильная информация. Если я напишу цикл foreach и добавлю их по отдельности в коллекцию Level2MenuItems, он будет отображаться правильно.

Также в качестве теста я добавил следующее в конструктор:

Level2MenuItems = Level1MenuItems[0].SubCategories;

И это обновлено правильно.

Так почему же код работает так, как ожидалось, в конструкторе или при циклическом переходе, но не тогда, когда пользователь нажимает на элемент в списке?

Ответы [ 3 ]

8 голосов
/ 19 декабря 2011

Вам необходимо поднять уведомление об изменении свойства Level2MenuItems.

Вместо того, чтобы

public ObservableCollection<EMSMenuItem> Level2MenuItems { get; set; }

вам нужно

private ObservableCollection<EMSMenuItem> _level2MenuItems;
public ObservableCollection<EMSMenuItem> Level2MenuItems
{
    get { return _level2MenuItems; }
    set 
     {
        _level2MenuItems = value; 
        RaisePropertyChanged("Level2MenuItems");
     }
 }

Причина, по которой первые из них работают в конструкторе, заключается в том, что связывание еще не выполнено. Однако, поскольку вы изменяете ссылку с помощью команды execute, которая происходит после привязки, вам необходимо сообщить представлению, что она изменилась

1 голос
/ 09 апреля 2014

Вам нужно сделать так, чтобы ваш класс poco в ObservableCollection реализовал INotifyPropertyChanged.

Пример:

<viewModels:LocationsViewModel x:Key="viewModel" />
.
.
.    
<ListView
    DataContext="{StaticResource viewModel}"
    ItemsSource="{Binding Locations}"
    IsItemClickEnabled="True"
    ItemClick="GroupSection_ItemClick"
    ContinuumNavigationTransitionInfo.ExitElementContainer="True">

    <ListView.ItemTemplate>
        <DataTemplate>
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="{Binding Name}" Margin="0,0,10,0" Style="{ThemeResource ListViewItemTextBlockStyle}" />
                <TextBlock Text="{Binding Latitude, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Style="{ThemeResource ListViewItemTextBlockStyle}" Margin="0,0,5,0"/>
                <TextBlock Text="{Binding Longitude, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Style="{ThemeResource ListViewItemTextBlockStyle}" Margin="5,0,0,0" />
            </StackPanel>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

public class LocationViewModel : BaseViewModel
{
    ObservableCollection<Location> _locations = new ObservableCollection<Location>();
    public ObservableCollection<Location> Locations
    {
        get
        {
            return _locations;
        }
        set
        {
            if (_locations != value)
            {
                _locations = value;
                OnNotifyPropertyChanged();
            }
        }
    }
}

public class Location : BaseViewModel
{
    int _locationId = 0;
    public int LocationId
    {
        get
        {
            return _locationId;
        }
        set
        {
            if (_locationId != value)
            {
                _locationId = value;
                OnNotifyPropertyChanged();
            }
        }
    }

    string _name = null;
    public string Name
    {
        get
        {
            return _name;
        }
        set
        {
            if (_name != value)
            {
                _name = value;
                OnNotifyPropertyChanged();
            }
        }
    }

    float _latitude = 0;
    public float Latitude 
    { 
        get
        {
            return _latitude;
        }
        set
        {
            if (_latitude != value)
            {
                _latitude = value;
                OnNotifyPropertyChanged();
            }
        }
    }

    float _longitude = 0;
    public float Longitude
    {
        get
        {
            return _longitude;
        }
        set
        {
            if (_longitude != value)
            {
                _longitude = value;
                OnNotifyPropertyChanged();
            }
        }
    }
}

public class BaseViewModel : INotifyPropertyChanged
{
    #region Events
    public event PropertyChangedEventHandler PropertyChanged;
    #endregion

    protected void OnNotifyPropertyChanged([CallerMemberName] string memberName = "")
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(memberName));
        }
    }
}
0 голосов
/ 19 декабря 2011
...