Главное окно прослушивает подключение / отключение 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();
}
}
В
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. Он создает все, что определено в XAML, и применяет все привязки. После того, как привязка была применена с вашей пустой коллекцией (которую вы создали со значением свойства по умолчанию), привязка не будет знать ни о каких изменениях этого свойства, это был правильный ответ.
Но реализация INotifyPropertyChanged больше касается экземпляров модели представления, а не визуального представления.
Я действительно предлагаю вам использовать Dependency Property для окон и элементов управления, если вы хотите привязать. Для этого есть несколько причин:
Вот как будет выглядеть ваше окно после изменения
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);
}
};
Теперь работает только случайно. Если вы не запускаете уведомление об изменении свойства, вам не следует устанавливать свойство после InitializeComponent. Это может не всегда работать.