Detecting Selected Items (Layers) in a List Box via MVVM does not work properly

2799
5
Jump to solution
07-19-2022 08:51 AM
Amadeus111
Occasional Contributor II

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();
                    }
                }
            }
        }

 

 

 

 

 

Tags (3)
0 Kudos
1 Solution

Accepted Solutions
Wolf
by Esri Regular Contributor
Esri Regular Contributor

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.

 

View solution in original post

0 Kudos
5 Replies
Wolf
by Esri Regular Contributor
Esri Regular Contributor

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:

Wolf_0-1658328821946.png

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.

Amadeus111
Occasional Contributor II

@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:

Amadeus111_0-1658329596438.png

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

 

0 Kudos
Amadeus111
Occasional Contributor II

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 😄

0 Kudos
Wolf
by Esri Regular Contributor
Esri Regular Contributor

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.

 

0 Kudos
Amadeus111
Occasional Contributor II

@Wolf I looped and selected all items and it works fine. If that does not work somewhere I will try IsVirtualizing= false method. Thanks!