WPF ListView not showing contents of Observable Collection - c#

I have a listview in a WPF application which is supposed to display the contents of an observable collection. I had it displaying in an older build, but in trying to make the listview update more often I changed the XAML quite a lot and after putting it back it now doesn't display anything, ever.
Here is the XAML:
<TabItem GotFocus="TabSelect">
<TabItem.Header>
<TextBlock FontSize="12pt">error log</TextBlock>
</TabItem.Header>
<Grid><ListView Name="JfifoList" ItemsSource="{Binding JFifoErrorCollection}" Background="Transparent">
<ListView.View>
<GridView>
<GridViewColumn DisplayMemberBinding="{Binding Time}" Header="time" Width="100" />
<GridViewColumn DisplayMemberBinding="{Binding FEStatus}" Header="fe status" Width="100" />
<GridViewColumn DisplayMemberBinding="{Binding BEStatus}" Header="be status" Width="100" />
<GridViewColumn DisplayMemberBinding="{Binding Trigger}" Header="trigger" Width="100" />
</GridView>
</ListView.View>
</ListView></Grid>
</TabItem>
I do set up the list's DataContext as "this" window:
JfifoList.DataContext = this;
When debugging I can see that the collection is populated, so there is data to display... Though also when debugging, I repeatedly see an error that says "A first chance exception of type 'System.NotSupportedException' occurred in PresentationFramework.dll" in the debug output.
EDIT: I've looked around some more and found that UI elements must be updated in the UI thread... for some reason (I haven't created any in the code) there are numerous threads running in my program, and they all seem to be updating my ObservableCollection... Don't know whether this might be the problem...

Modifications on the content of lists which are binded to a view, have to be done in a UI thread.
To catch your App UI thread, you can use something like this:
Application.Current.Dispatcher.Invoke(DispatcherPriority.Render, new Action(() => JfifoList = whateverData));

Related

How to bind ObservableCollection to the ListView in a UserControl?

I have the following xaml file:
<Page ...>
<Grid>
<ListView ItemsSource="{Binding MissingValues}">
<ListView.View>
<GridView AllowsColumnReorder="true" ColumnHeaderToolTip="Information on Missing Values">
<GridViewColumn DisplayMemberBinding="{Binding Path=Element.Name}" Header="Element" Width="100" />
<GridViewColumn DisplayMemberBinding="{Binding Path=Description}" Header="Description" />
</GridView>
</ListView.View>
</ListView>
</Grid>
Code behind is only:
DataContext = new MyViewModel();
And MyViewModel has public ObservableCollection<MissingValue> MissingValues initialized in the constructor. On the button click, items are added to this property and everything works as exptected.
Since the xaml file became too big, i wanted to make it smaller and easier to manage by extracting this ListView in a UserControl called MissingValuesListView and invoking it using the following code:
<local:MissingValuesListView></local:MissingValuesListView>
However, the list is no longer updated when the new elements are added.
MissingValuesListView's code behind contains only DataContext = new MyViewModel();.
I don't think it's of any relevance (since the code works when part of the main xaml file, but not when moved to UserControl), but both MyViewModel and MissingValue implment INotifyPropertyChanged.
My question is why is this not working? I assume i'm missing some code or some binding, but i failed to find out what. Any help is highly appreciated!
MissingValuesListView's code behind contains only DataContext = new MyViewModel();.
It shouldn't. Try to remove this line. You should simply copy the following XAML into MissingValuesListView.xaml and replace it with <local:MissingValuesListView /> in the current XAML file:
<ListView ItemsSource="{Binding MissingValues}">
<ListView.View>
<GridView AllowsColumnReorder="true" ColumnHeaderToolTip="Information on Missing Values">
<GridViewColumn DisplayMemberBinding="{Binding Path=Element.Name}" Header="Element" Width="100" />
<GridViewColumn DisplayMemberBinding="{Binding Path=Description}" Header="Description" />
</GridView>
</ListView.View>
</ListView>
If you do this, the UserControl will inherit the DataContext where the MissingValues property is defined from its parent element and the binding should work just like before.

Binding WPF ListView to Dictionary<string, int> only updates on the first change

I have bound my ListView to a Dictionary but it is only updating for the first time i add something to the Dictionary. Any subsequent changes don't get updated.
WPF:
<ListView Name="LogLevelListView" ItemsSource="{Binding MyDictionary, Mode=TwoWay}" >
<ListView.View>
<GridView>
<GridViewColumn Header="Name" Width="Auto" DisplayMemberBinding="{Binding Key}" />
<GridViewColumn Header="Level" Width="Auto" DisplayMemberBinding="{Binding Value}" />
</GridView>
</ListView.View>
</ListView>
C# Code:
MyDictionary.Add(MyKey, MyValue);
OnPropertyChanged("MyDictionary");
Any idea why only the first update works?
You should use a collection that implements INotifyCollectionChanged for instance ObservableCollection
Represents a dynamic data collection that provides notifications when
items get added, removed, or when the whole list is refreshed.
Update
If you use the binding in that way your collection will not update due to the way ItemsControl.ItemsSource it is implemented.
Go to here line 1687 and you will see something like:
if (_collectionView == view)
return;
Your collection reference is the same, hence it will never refresh.
That is why it only works the first update.
You can have a look at ObservableDictionary implementionation by Dr. WPF.

How do I get a click event from a GridViewColumn header?

I can get events from everything under the headers, but I can't get an event from clicking the headers. Here's the XAML; notice the event is for the entire ListView, so it should activate when anything is clicked:
<ListView x:Name="myListView" MouseLeftButtonUp="myListView_MouseLeftButtonUp" Margin="10">
<ListView.View>
<GridView>
<GridView.Columns>
<GridViewColumn Header="File Path"
DisplayMemberBinding="{Binding Path=Path}" />
<GridViewColumn Header="File Size"
DisplayMemberBinding="{Binding Path=Size}" />
</GridView.Columns>
</GridView>
</ListView.View>
</ListView>
And the event itself is very simple. Just show me that something has happened:
private void myListView_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
string output = sender.ToString();
MessageBox.Show(output);
}
Clicking anywhere under the headers responds perfectly:
"System.Windows.Controls.ListView Items.Count:0"
Clicking the "File Path" header does nothing. Clicking the "File Size" header does nothing.
MSDN says:
https://msdn.microsoft.com/en-us/library/vstudio/ms745786(v=vs.100).aspx
<ListView x:Name='lv' Height="150" HorizontalAlignment="Center" VerticalAlignment="Center"
GridViewColumnHeader.Click="GridViewColumnHeaderClickedHandler">
Visual Studio says there's no such thing as GridViewColumnHeader, so none of the code on MSDN works.
That's how WPF UI events work by default. They bubble up. If somebody eats the message along the way (which is what button type controls do), the higher level controls won't get it. You can either use the Preview version of the event, or the cleaner way to do it:
AddHandler(GridViewColumnHeader.ClickEvent, new RoutedEventHandler(ListView_OnColumnClick));
<GridViewColumn Width="150" >
<GridViewColumn.Header>
<GridViewColumnHeader Click="GridViewColumnHeader_Click">click me</GridViewColumnHeader>
</GridViewColumn.Header>
</GridViewColumn>
Even if a bit late, but in XAML you can do something like:
<!-- ListView -->
<GridView AllowsColumnReorder="True">
<GridViewColumn Width="220">
<GridViewColumnHeader Content="{DynamicResource ResourceKey=explorer_column_name}" Command="{Binding Path=ISortBy}" CommandParameter="{x:Static enu:SortType.Name}"/>
<GridViewColumn.CellTemplate>
//...
</GridViewColumn.CellTemplate>
</GridViewColumn>
//..
</GridView>
AddHandler should be called on the ListView, like:
myListView.AddHandler(GridViewColumnHeader.ClickEvent, new RoutedEventHandler(myListView_OnColumnClick));
Then e.OriginalSource can be casted to GridViewColumnHeader in your myListView_OnColumnClick and you can identify your column.

Unable to bind the collection to Listview in WPF application?

I am developing a WPF application where i can read some properties from the windows services list which are currently running on my system.I am able to read the properties of a particular service and passing to a collection which in turn show up in my UI under a Listview.
I want the same scenario to be used for list of services .i.e.i am trying in the following way..but i am not sure where did i miss the point...
Here is my code
foreach (string serviceName in sList)
{
ServiceController controller = new ServiceController(serviceName);
StatusCollection.Add (new StatusData
{
Name = name,
Status = status
});
lvStatus.DataContext = StatusCollection;
lvStatus.ItemsSource = StatusCollection;
}
<ListView Height="166" HorizontalAlignment="Left" Margin="23,0,0,0" Name="lvStatus" VerticalAlignment="Top" Width="264" >
<ListView.View>
<GridView>
<GridViewColumn Header="Name" DisplayMemberBinding="{Binding Name}"></GridViewColumn>
<GridViewColumn Header="Status" DisplayMemberBinding="{Binding Status}"></GridViewColumn>
</GridView>
</ListView.View>
</ListView>
Remove the DataContext line of code.
If you set your ItemsSource in the code behind make sure you do it before InitializeComponent is called. If not, you will need to refresh your Items collection.
However, I would setup a View Model for your Window or Control which implements INotifyPropertyChanged. Create your collection as an ObservableCollection and bind to it in the XAML:
<ListView ... ItemsSource={Binding Path=ServiceList}>
<ListView.View>
<GridView>
<GridViewColumn Header="Name" DisplayMemberBinding="{Binding Path=Name}" />
<GridViewColumn Header="Status" DisplayMemberBinding="{Binding Path=Status}" />
</GridView>
</ListView.View>
</ListView>
Let me know if you need an example of using a View Model.

How do I edit a usercontrol's subcontrol template in xaml when adding the usercontrol to a window?

I have a user control that contains a listview. In the windows where I use this usercontrol, I want to be able to do something like what is shown below where I set the template for a control within the user control.
<ct:AutoCompleteComboBox
SelectedValue="{Binding Path=SelectedCountry}"
ItemsSource="{Binding Path=CountryList}" >
<ct:AutoCompleteComboBox.DropDownList >
<ListView
Background="Blue"
MinWidth="150"
MaxHeight="200">
<ListView.View>
<GridView>
<GridView.Columns>
<GridViewColumn Header=" Name" DisplayMemberBinding="{Binding Name}" >
</GridViewColumn>
<GridViewColumn Header="Desc" DisplayMemberBinding="{Binding Description}" >
</GridViewColumn>
</GridView.Columns>
</GridView>
</ListView.View>
</ListView>
</ct:AutoCompleteComboBox.DropDownList>
</ct:AutoCompleteComboBox>
How do I go about doing this? I found that if I made AutoCompleteComboBox.DropDownList a dependency property, then the code would compile. However, this DropDownList property is not actually added to my control (it is added to the parent container of ct:AutoCompleteComboBox).
Any idea of the proper way to do this?
Thanks in advance
Bind subcontrol's property you want to be available outside to the property you created in the container control.

Categories

Resources