Reputation: 1579
The main-window is listening for plugging in/out USB-devices. If it is an usb-key/disk it collects a file-list from that device and show that list in a second window.
While debugging I can see that the NewUsbFiles observablecollection get's populated with 117 items. I see that the property UsbFile (before calling the showdialog) has 117 items, but nevertheless the listbox is empty.
Any thoughts ?
The method to populate / create that second window:
NewUsbFiles = new ObservableCollection<UsbFile>();
UpdateNewUsbFiles(driveName);
Application.Current.Dispatcher.Invoke(delegate
{
var usbFileSelector = new UsbFileSelector()
{
Owner = this,
UsbFiles = NewUsbFiles
};
usbFileSelector.ShowDialog();
});
The UsbFile-class:
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}";
}
}
The XAML of the second window :
<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>
The code-behind of the second window :
public partial class UsbFileSelector : Window
{
public ObservableCollection<UsbFile> UsbFiles { get; set; } = new ObservableCollection<UsbFile>();
public UsbFileSelector()
{
InitializeComponent();
}
}
Upvotes: 1
Views: 412
Reputation: 1521
All the answers already given are correct, the heart of your problem is the
UsbFiles = NewUsbFiles
which causes the binding to "break" - UsbFiles is no longer pointing to the collection that is bound to the Listbox.
Another possible way to solve this would be to simply leave the bound collection alone and just repopulate the contents.
var usbFileSelector = new UsbFileSelector()
{
Owner = this,
UsbFiles.Clear();
foreach (var uf in NewUsbFiles) {
UsbFiles.Add(uf);
}
};
Upvotes: 0
Reputation: 151
Inside the window you can see InitializeComponent method. It creates all of the stuff defined in XAML and applies all bindings. After binding has been appplied with your empty collecton (that you have created with default property value) the binding will not know about any change of that property, that was the right answer.
But implementing INotifyPropertyChanged is more about viewmodel instances, not visual.
I really suggest you use Dependency Property for windows and controls if you want to bind. There are some reasons for that:
Here is how your window will look like after change
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();
}
}
Also I strongly recommend you to use some WPF inspector tool while developing for the WPF, for example, snoop. You can navigate through the controls and properties while app is running and find issues much quickly you can from the code or from stackoverflow =)
Upvotes: 1
Reputation: 1579
Someone posted (and deleted the comment) that I should add DataContext = this;
To
public UsbFileSelector()
{
InitializeComponent();
DataContext = this;
}
Someone else mentioned (that comment too was deleted) that this was not necessary because of the
DataContext="{Binding ElementName=wUsbFileSelector}"
in the XAML.
BUT it turned out that removing the DataContext line from the XAML and setting it to this in code was the sollution. No idea why but that did it.
EDIT just to make clear that this is not a good solution and works only by accident, try the following:
// 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();
Upvotes: 0
Reputation: 128060
In
var usbFileSelector = new UsbFileSelector()
{
Owner = this,
UsbFiles = NewUsbFiles
};
you are assigning a new value to the UsbFiles
property without firing a property change notification for that property.
You could either implement INotifyPropertyChanged and fire the PropertyChanged event or make UsbFiles a dependency property.
Or you pass NewUsbFiles
as constructor argument and assign it before calling InitializeComponent
public UsbFileSelector(ObservableCollection<UsbFile> usbFiles)
{
UsbFiles = usbFiles;
InitializeComponent();
}
and call it like this:
var usbFileSelector = new UsbFileSelector(NewUsbFiles)
{
Owner = this
};
Note that if you always pass a new collection, using ObservableCollection isn't actually necessary. You never add or remove elements to/from the collection, so there is no need for a change notification.
Upvotes: 0