Caliburn.Micro BindableCollection не обновляется в коде View, используя код для настройки привязок - PullRequest
0 голосов
/ 22 апреля 2020

Я хочу создать приложение WPF, используя Caliburn.Micro MVVM. Один из видов должен хорошо рисовать данные, полученные для ViewModel. В представлении я хочу сделать рисунок в коде позади. Я использую скалярные свойства зависимости. Это отлично работает. Я также могу использовать данные, хранящиеся в BindableCollection (LocationList в примере кода ниже). Не работает обновление DependencyProperty, если я добавляю новые элементы в BindableCollection. Должно быть, это глупая ошибка, но я ее не вижу. Я создал демо-приложение, оставив как можно больше. Это стандартная настройка Caliburn.Micro. Я не показываю Bootstrapper, ShellView и ShellViewModel здесь. Они являются общими конфигурациями стартера.

ViewModel выглядит следующим образом:

    using Caliburn.Micro;
    using System.Collections.Generic;

    namespace BindingCodeBehind.ViewModels
        {
        public class DemoViewModel : Screen
            {
            private BindableCollection<string> _locationList;
            public BindableCollection<string> LocationList
                {
                get { return _locationList; }
                set
                    {
                    _locationList = value; 
                    NotifyOfPropertyChange(()=>LocationList);
                    }
                }

            public DemoViewModel()
                {
                LocationList = new BindableCollection<string>();
                }

            protected override void OnViewLoaded(object view)
                {
                base.OnViewLoaded(view);
                for (int i = 0; i < 3; i++)
                    {
                    // BindableCollection supports AddRange, but not Add. 
                    LocationList.AddRange( new List<string>{$"Test nr {i}"});
                    // LocationList.Add($"Test nr {i}");
                    }
                NotifyOfPropertyChange(()=>LocationList.Count);
                }
            }
        }

LocationList инициализируется в конструкторе. Я добавляю данные в обработчик событий OnViewLoaded.

Код XAML в представлении не является чем-то особенным, просто леса UserControl, ничего не добавлено. Код выглядит следующим образом:

using Caliburn.Micro;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Media;

namespace BindingCodeBehind.Views
    {
    public partial class DemoView : UserControl
        {
        public BindableCollection<string> LocationList
            {
            get { return (BindableCollection<string>) GetValue(LocationListProperty); }
            set { SetValue(LocationListProperty, value);}
            }

        public static readonly DependencyProperty LocationListProperty =
            DependencyProperty.Register("LocationList", 
                typeof(BindableCollection<string>), 
                typeof(DemoView),
                new FrameworkPropertyMetadata(null, 
                                            FrameworkPropertyMetadataOptions.AffectsRender,
                                            new PropertyChangedCallback(OnLocationListChanged)));

        public DemoView()
            {
            InitializeComponent();
            var myBinding = new Binding
                {
                Mode = BindingMode.TwoWay,
                Path = new PropertyPath("LocationList"),
                NotifyOnSourceUpdated = true
                };
            BindingOperations.SetBinding(this, LocationListProperty, myBinding);
            }

        protected override void OnRender(DrawingContext drawingContext)
            {
            // Data from LocationLst should be used in drawing 
            }

        private static void OnLocationListChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
            {
            // I assume this called if LocationList changes
            }
        }
    }

В коде создается свойство зависимости LocationList. В конструкторе я создал привязку для этого свойства. Я вижу, что в отладчике это действительно работает. OnLocationListChanged должен вызываться, если этот список изменяется. Я вижу, что это вызывается, когда я создаю LocationList в конструкторе ViewModel, но дескриптор больше не вызывается, когда я добавляю три элемента к LocationList. Поэтому я думаю, что проблема где-то в обработке INotifyChange. Чтобы лучше понять, я посмотрел исходный код класса BindableCollection и увидел, что метод Add не реализован, поэтому я изменил во ViewModel свой код, чтобы использовать метод AddRange, просто чтобы убедиться, что это не помогает.

Я много чего пробовал, но у меня совершенно нет идей, как заставить это работать. Я действительно надеюсь, что кто-то может мне помочь. По сути, все это должно заставить мое приложение снова вызвать OnRender, чтобы обновить чертеж. Это мое первое приложение для рисования WPF и мое первое приложение Caliburn.Micro, так что все это для меня все еще ново.

Моя среда разработки: Visual Studio 2019, выпуск для сообщества. Net Core 3.1 C# 8,0 Windows10. Все обновлено с последними исправлениями.

1 Ответ

1 голос
/ 23 апреля 2020

Ваше предположение, что " OnLocationListChanged должен быть вызван, если этот список меняет ", неверно.

Обратный вызов свойства зависимостей вызывается только тогда, когда для самого свойства установлено новое значение, т. Е. Когда вы присваиваете его новому экземпляру BindableCollection<string>.

Он не вызывается при добавлении или удалении элементов из коллекции. Существует событие CollectionChanged, которое можно обработать в ответ на добавление или удаление отдельных элементов.

...