I am trying to read selected items in a Listbox if selected, and a layer is created. I would like to do it with MVVM. My code does not work properly. It selects no more than 11 items, yet creates 12 layers out of it. I am puzzled. I would appreciate any help. I am using 3.0 but I get the same result with 2.9
UPDATE:
I just noticed even though all items selected in the list box. They are not detected unless the scroll is all the way down. Selected items are detected according to the scroll position. I don't know why it acts like that.
XAML:
<ListBox x:Name="DataListBox" SelectionMode="Extended" HorizontalAlignment="Left" Margin="5,5,0,0" Grid.Row="1" Grid.Column="0" Grid.RowSpan="8"
VerticalAlignment="Top" Height="200" Width="200"
ItemsSource="{Binding DataListBoxItemsSource, UpdateSourceTrigger=PropertyChanged}"
SelectedItem="{Binding DataListBoxSelectedItem, UpdateSourceTrigger=PropertyChanged}"
>
<ListBox.InputBindings>
<KeyBinding Command="ApplicationCommands.SelectAll" Modifiers="Ctrl" Key="A" />
</ListBox.InputBindings>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="IsSelected" Value="{Binding IsSelected}"/>
</Style>
</ListBox.ItemContainerStyle>
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged" >
<i:CallMethodAction TargetObject="{Binding}" MethodName="DataListBox_SelectionChanged"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</ListBox>
View Model
public async void CreateLayersTOC()
{
if (MapView.Active != null)
{
if (DataListBoxSelectedItem != null || FavoriteTabsSelectedItem != null)
MainStackPanelIsEnabled = false;
LayerNames = new List<string>();
await Task.Run(() =>
{
MessageBox.Show("source count " +DataListBoxItemsSource.Count); //78 and All items selected in the UI
if (DataListBoxSelectedItem != null)
foreach (ItemPresenter itemP in DataListBoxItemsSource)
{
if (itemP.IsSelected)
{
if (LayerNames.Contains(itemP.ToString()) == false)
LayerNames.Add(itemP.ToString());
}
}
if (FavoriteTabsSelectedItem != null)
{
foreach (ItemPresenter itemP in FavListBoxItemsSource)
{
if (itemP.IsSelected)
{
if (LayerNames.Contains(itemP.ToString()) == false)
LayerNames.Add(itemP.ToString());
}
}
}
MessageBox.Show("Coll" + LayerNames.Count); // Only 11 ??
});
foreach (KeyValuePair<string, List<string>> kvp in LayerDict)
{
if (LayerNames.Contains(kvp.Value[2]))
{
await QueuedTask.Run(() =>
{
Uri layerURI = new Uri(System.IO.Path.Combine(kvp.Value[4], kvp.Value[5] + ".lyr"));
MapView mapView = MapView.Active;
Map activeMap = mapView.Map;
Layer mapLayer = LayerFactory.Instance.CreateLayer(layerURI, activeMap, 0, kvp.Value[2]);
//MainStackPanelIsEnabled = true;
});
}
//LayerNames.Remove(kvp.Value[2]);
}
}
else
MessageBox.Show("Make sure to have a map available before adding layers to a map");
MainStackPanelIsEnabled = true;
}
public class ItemPresenter : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private readonly string _value;
public ItemPresenter(string value)
{
_value = value;
}
public override string ToString()
{
return _value;
}
private bool _isSelected;
public bool IsSelected
{
get { return _isSelected; }
set
{
if (_isSelected != value)
{
_isSelected = value;
OnPropertyChanged();
}
}
}
}
Solved! Go to Solution.
Ok, got it ... the screenshot helps. So the problem is that you do a 'select all' and only items in the visible range of the listbox are selected? I think that's a newly introduced bug in WPF (or maybe an 'optimization').
I would suggest two possible solutions:
Try the following in your listbox definition:
<ListBox VirtualizingStackPanel.IsVirtualizing="False" ... />
Or when you 'select' all items loop manually through the list and set 'IsSelected' for each item in the list.
Let me know if this doesn't work and i enhance my test app to duplicate the issue.
I couldn't quite figure out what you were trying to do, but if you are populating a list and creating layers from the selection (checkbox) on that list like shown below, i simplified your code a bit:
the simplified XAML looks like this:
<ListBox Grid.Row="0" Margin="5" ItemsSource="{Binding MyLayers}"
ItemContainerStyle="{DynamicResource Esri_ListBoxItemHighlightBrush}"
SelectedItem="{Binding MyLayersSelection}"
BorderBrush="{DynamicResource Esri_TextControlBrush}" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<CheckBox Content="{Binding Name}" IsChecked="{Binding IsChecked}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
and the code behind looks like this:
private ObservableCollection<MyLayer> _myLayers = new ObservableCollection<MyLayer>();
/// <summary>
/// This is the list of MyLayer objects
/// </summary>
public ObservableCollection<MyLayer> MyLayers
{
get { return _myLayers; }
set
{
SetProperty(ref _myLayers, value, () => MyLayers);
}
}
i attached my 3.0 test add-in. If you need to act upon individual checkbox changed events the MyLayer class has to implement INotifyPropertyChanged.
@Wolf Thanks for the reply but checkbox for each item is not a good option for me. It gets more space and users need to check them all.
Here is the UI:
Users select the layers (strings at this point) from the highlighted listbox and click a button. All selected layers found in a string array dictionary and created and add to the TOC. In some cases, there are more than 40 layers. So, I created a button which selects the all layers in the listbox. It looks like all layers selected in the UI. However, when I put them in the loop to check their IsSelected property. It says only 12 of them selected. If I play with the scroll it shows more selected. I even tried auto scroll end of the listbox but still not all selected.
I hope it is more clear now. It is a weird problem ad difficult to explain too
Finally, I found the problem. I was making the selection (ListBox.SelectAll()) in the code-behind. I didnt know it was going to be problem. The issue was gone. As soon as I carried that into the view-model.
@Wolf I appreciate your help anyway. Somehow, my problems are gone as soon as you post an answer to my questions 😄
Ok, got it ... the screenshot helps. So the problem is that you do a 'select all' and only items in the visible range of the listbox are selected? I think that's a newly introduced bug in WPF (or maybe an 'optimization').
I would suggest two possible solutions:
Try the following in your listbox definition:
<ListBox VirtualizingStackPanel.IsVirtualizing="False" ... />
Or when you 'select' all items loop manually through the list and set 'IsSelected' for each item in the list.
Let me know if this doesn't work and i enhance my test app to duplicate the issue.
@Wolf I looped and selected all items and it works fine. If that does not work somewhere I will try IsVirtualizing= false method. Thanks!