Список WPF не заполнен элементами из ObservableCollection - PullRequest
0 голосов
/ 02 мая 2018

Главное окно прислушивается к подключению / отключению 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();
        }
    }

Ответы [ 4 ]

0 голосов
/ 03 мая 2018

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

UsbFiles = NewUsbFiles

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

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

var usbFileSelector = new UsbFileSelector()
{
   Owner = this,
   UsbFiles.Clear();
   foreach (var uf in NewUsbFiles) {
       UsbFiles.Add(uf);
   }
};
0 голосов
/ 02 мая 2018

Кто-то опубликовал (и удалил комментарий), что я должен добавить 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();
0 голосов
/ 02 мая 2018

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

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

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

  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 =)

0 голосов
/ 02 мая 2018

В

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 на самом деле не нужно. Вы никогда не добавляете и не удаляете элементы в / из коллекции, поэтому нет необходимости в уведомлении об изменениях.

...