Список WPF не заполнен элементами из ObservableCollection

Главное окно прослушивает подключение / отключение USB-устройств. Если это USB-ключ / диск, он собирает список файлов с этого устройства и показывает этот список во втором окне.

Во время отладки я вижу, что наблюдаемая коллекция NewUsbFiles заполнена 117 элементами. Я вижу, что свойство UsbFile (до вызова showdialog) содержит 117 элементов, но, тем не менее, список пуст.

Есть предположения ?

Метод заполнения / создания второго окна:

NewUsbFiles = new ObservableCollection<UsbFile>();
UpdateNewUsbFiles(driveName);

Application.Current.Dispatcher.Invoke(delegate
{
   var usbFileSelector = new UsbFileSelector()
   {
       Owner = this,
       UsbFiles = NewUsbFiles
    };
    usbFileSelector.ShowDialog();
});

Класс UsbFile:

 public class UsbFile 
    {
        public string UsbFileName { get; set; }
        public string OnTableFileName { get; set; }
        public bool Ignored { get; set; } = false;

        public UsbFile(string fileName)
        {
            var fileInfo = new FileInfo(fileName);
            UsbFileName = fileInfo.FullName;
            OnTableFileName = $"{fileInfo.CreationTime:yyMMddHHmmsss}_{fileInfo.Name}";
        }
    }

XAML второго окна:

<Window
        xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x = "http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d = "http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc = "http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:MainWindow = "clr-namespace:PartyPictures.WPF.MainWindow" x:Name = "wUsbFileSelector"
    x:Class = "PartyPictures.WPF.UsbFileSelector"
        mc:Ignorable = "d"
        Title = "USB" HorizontalAlignment = "Center" VerticalAlignment = "Center" WindowStyle = "ToolWindow" ScrollViewer.VerticalScrollBarVisibility = "Auto" SizeToContent = "WidthAndHeight">
    <StackPanel x:Name = "spUsbFileList">
        <ListBox  x:Name = "ImageListbox"
                  DataContext = "{Binding ElementName=wUsbFileSelector}"
                 ItemsSource = "{Binding UsbFiles}" 
                 Background = "AliceBlue" ScrollViewer.HorizontalScrollBarVisibility = "Disabled" MinWidth = "200" MinHeight = "200">
        </ListBox>
    </StackPanel>
</Window>

Код второго окна:

public partial class UsbFileSelector : Window
    {
        public ObservableCollection<UsbFile> UsbFiles { get; set; } = new ObservableCollection<UsbFile>();

        public UsbFileSelector()
        {
            InitializeComponent();
        }
    }
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
0
248
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

В

var usbFileSelector = new UsbFileSelector()
{
   Owner = this,
   UsbFiles = NewUsbFiles
};

вы назначаете новое значение свойству UsbFiles без отправки уведомления об изменении свойства для этого свойства.

Вы можете реализовать INotifyPropertyChanged и запустить событие PropertyChanged или сделать UsbFiles свойством зависимости.

Или вы передаете NewUsbFiles в качестве аргумента конструктора и назначаете его перед вызовом InitializeComponent

public UsbFileSelector(ObservableCollection<UsbFile> usbFiles)
{
    UsbFiles = usbFiles;
    InitializeComponent();
}

и назовите это так:

var usbFileSelector = new UsbFileSelector(NewUsbFiles)
{
   Owner = this
};

Обратите внимание: если вы всегда передаете новую коллекцию, использование ObservableCollection на самом деле не обязательно. Вы никогда не добавляете и не удаляете элементы в / из коллекции, поэтому нет необходимости в уведомлении об изменении.

Кто-то опубликовал (и удалил комментарий), что я должен добавить DataContext = this;

К

public UsbFileSelector()
{
   InitializeComponent();
   DataContext = this;
}

Кто-то еще упомянул (этот комментарий также был удален), что в этом нет необходимости из-за

DataContext = "{Binding ElementName=wUsbFileSelector}"

в XAML.

НО оказалось, что удаление строки DataContext из XAML и установка ее в коде было решением. Понятия не имею, почему, но это сработало.


ИЗМЕНИТЬ, чтобы прояснить, что это не лучшее решение и работает только случайно, попробуйте следующее:

// this works
var usbFileSelector = new UsbFileSelector();
usbFileSelector.Owner = this;
usbFileSelector.UsbFiles = NewUsbFiles;
usbFileSelector.ShowDialog();

// this does not
var usbFileSelector = new UsbFileSelector();
usbFileSelector.Owner = this;
await Task.Delay(10);
usbFileSelector.UsbFiles = NewUsbFiles;
usbFileSelector.ShowDialog();

Теперь работает только случайно. Если вы не запускаете уведомление об изменении свойства, вам не следует устанавливать свойство после InitializeComponent. Это может не всегда работать.

Clemens 02.05.2018 12:37

На самом деле есть вопрос о переполнении стека по теме Ссылка на сайт

Mike 02.05.2018 13:05
Ответ принят как подходящий

Внутри окна вы можете увидеть метод InitializeComponent. Он создает все, что определено в XAML, и применяет все привязки. После того, как привязка была применена с вашей пустой коллекцией (которую вы создали со значением свойства по умолчанию), привязка не будет знать ни о каких изменениях этого свойства, это был правильный ответ.

Но реализация INotifyPropertyChanged больше касается экземпляров модели представления, а не визуального представления.

Я действительно предлагаю вам использовать Dependency Property для окон и элементов управления, если вы хотите привязать. Для этого есть несколько причин:

  1. Установщик свойств зависимостей имеет встроенный механизм уведомления.
  2. Если вы привязываете один DP к другому DP, значение распределяется между ними.
  3. В конце концов, это подход WPF =)

Вот как будет выглядеть ваше окно после изменения

    public partial class UsbFileSelector : Window
    {
        public static readonly DependencyProperty UsbFilesProperty = 
            DependencyProperty.Register("UsbFiles", typeof(ObservableCollection<UsbFile>), typeof(UsbFileSelector));

        public ObservableCollection<UsbFile> UsbFiles 
        {
            get { return (ObservableCollection<UsbFile>) GetValue(UsbFilesProperty); }
            set { SetValue(UsbFilesProperty, value); }
        }

        public UsbFileSelector()
        {
            InitializeComponent();
        }
    }

Также я настоятельно рекомендую вам использовать какой-нибудь инструмент инспектора WPF при разработке для WPF, например, snoop. Вы можете перемещаться по элементам управления и свойствам во время работы приложения и быстро находить проблемы, которые вы можете сделать из кода или из stackoverflow =)

Все уже даны правильные ответы, суть вашей проблемы - это

UsbFiles = NewUsbFiles

что приводит к "разрыву" привязки - UsbFiles больше не указывает на коллекцию, привязанную к Listbox.

Другой возможный способ решить эту проблему - просто оставить связанную коллекцию в покое и просто повторно заполнить содержимое.

var usbFileSelector = new UsbFileSelector()
{
   Owner = this,
   UsbFiles.Clear();
   foreach (var uf in NewUsbFiles) {
       UsbFiles.Add(uf);
   }
};

Другие вопросы по теме