I am using WPF and have a Listbox that I have bound in the code behind to an ObservableCollection. It is displayed in multiple views and has worked perfectly (adding/removing, & staying in sync between views) until I tried to add an icon to the list items. Now it isn’t even consistently adding items to the listbox when they are added to the ObservableCollection. What I was attempting to change was instead of just displaying the name of the item in the listbox, when the items ‘monitoring’ attribtute is true I want it to display an image beside the name and to remove this image when the monitoring attribute is false. This part is working, the icon appears and is removed correctly. To get this icon to display correctly I modified my xaml by adding a ListBox.ItemTemplate->DataTemplate as seen below.
Previous XAML:
<ListBox
Name="CameraTargetsListbox"
Style="{DynamicResource NeuronListBoxStyle}"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch"
Margin="0,8,0,4"
Height="140"
ItemContainerStyle="{DynamicResource NeuronListBoxItem_CameraTargets}"/>
Current XAML:
<ListBox
Name="CameraTargetsListbox"
Style="{DynamicResource NeuronListBoxStyle}"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch"
Margin="0,8,0,4"
Height="140"
ItemContainerStyle="{DynamicResource NeuronListBoxItem_CameraTargets}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<ContentPresenter
Content="{Binding MonitoringItemIcon, UpdateSourceTrigger=PropertyChanged}"
Margin="-4,0,10,0" />
<TextBlock Text="{Binding DisplayName, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I should note that I added ‘UpdateSourceTrigger=PropertyChanged’ in an attempt to fix the issue but it made no difference.
Then in the code behind I linked the ObservableCollection, nothing about this code has changed:
public CameraTargetsControl()
{
InitializeComponent();
this.DataContext = this;
CameraTargetsListbox.ItemsSource = PropertyList;
...
For some reason, since I changed the listbox to display a template instead of allowing it to default display the info, the listbox is not updating consistently. Now when I add an item, sometimes it shows up in the listbox and sometimes it doesn’t. If I look at another view it will show the items added, but sometimes the items get added as blanks (as in, first item listed correctly, then a blank item (no text just space in the listbox), then the third item added. I cannot for the life of me figure out why this has broken. The list is no longer in sync between views and doesn’t even update in the current view! I have read a lot about using IPropertyChanged, but I don’t see why I should need to implement this here, as the observable collection should take care of that, and in fact it did until I tried to update the list item template. If it completely broke I could see needing the PropertyChanged notification, but it still works… sometimes.
All help is appreciated. Here is the code that I am using to update the list, essentially everytime an item is added/deleted a new list is received which is checked against the ObservableCollection (PropertyList) and the updates processed.
_ = Dispatcher.BeginInvoke(() =>
{
List<int> addList = new();
List<int> deleteList = new();
List<(int, int)> updateList = new(); // index from incoming list, index from listbox
CameraTarget selectedTarget = null;
CameraTargetsListbox.SelectionChanged -= CameraTargetsListbox_SelectionChanged;
if (CameraTargetsListbox.SelectedIndex != -1) selectedTarget = (CameraTarget)CameraTargetsListbox.SelectedItem;
// Check for additions and deletions
foreach (var target in targetList)
{
bool found = false;
foreach (var listedTarget in PropertyList)
{
if (target.Class == CameraTarget.TargetClass.AIS)
{
if (listedTarget.MMSI == target.MMSI)
{
updateList.Add((targetList.IndexOf(target), PropertyList.IndexOf(listedTarget)));
found = true;
break;
}
}
else if (listedTarget.TargetIndex == target.TargetIndex)
{
updateList.Add((targetList.IndexOf(target), PropertyList.IndexOf(listedTarget)));
found = true;
break;
}
}
if (!found) { addList.Add(targetList.IndexOf(target)); }
}
// Check for deletions
foreach (var listedTarget in PropertyList)
{
bool found = false;
foreach (var target in targetList)
{
if (target.Class == CameraTarget.TargetClass.AIS)
{
if (listedTarget.MMSI == target.MMSI)
{
found = true;
break;
}
}
else if (listedTarget.TargetIndex == target.TargetIndex)
{
found = true;
break;
}
}
if (!found) deleteList.Add(PropertyList.IndexOf(listedTarget));
}
if (updateList.Count > 0)
{
foreach (var index in updateList)
{
if (index.Item2 == CameraTargetsListbox.SelectedIndex)
{
if (PropertyList[index.Item2].Monitoring && PropertyList[index.Item2].MonitoringItemIcon == null)
{
PropertyList.RemoveAt(index.Item2);
PropertyList.Insert(index.Item2, targetList[index.Item1]);
}
else PropertyList[index.Item2] = targetList[index.Item1];
CameraTargetsListbox.SelectedIndex = index.Item2;
RefreshSelectedInfo();
}
else if(PropertyList[index.Item2].Monitoring && PropertyList[index.Item2].MonitoringItemIcon == null)
{
PropertyList.RemoveAt(index.Item2);
PropertyList.Insert(index.Item2, targetList[index.Item1]);
}
// Capture name updates
else if (PropertyList[index.Item2].ToString() != targetList[index.Item1].ToString())
{
PropertyList.RemoveAt(index.Item2);
PropertyList.Insert(index.Item2, targetList[index.Item1]);
}
else PropertyList[index.Item2] = targetList[index.Item1];
}
}
if (deleteList.Count > 0)
{
foreach (int index in deleteList)
{
if (index == CameraTargetsListbox.SelectedIndex) DeleteSelectedTarget(null, null);
else PropertyList.RemoveAt(index);
}
}
if (addList.Count > 0)
{
foreach (int index in addList)
{
PropertyList.Add(targetList[index]);
}
}
CameraTargetsListbox.SelectionChanged += CameraTargetsListbox_SelectionChanged;
CheckPropertyList();
});
This is my first time posting a question so please let me know what else I can provide 🙂