简体   繁体   中英

WPF ListBox not updating when Bound List is updated

I'm having a problem with my ListBox as it's not updating when I update the list it's bound to. I've already seen many of the same questions but none of it's answers solved my problem. My situation is that I have a ComboBox that when an option is selected, my ListBox will show options based on the ComboBox selection. I know that the list bound to the ListBox is being updated, but the items don't show on the list. If I manualy add an item on the declaration of the ViewModel class of the list than the item appear, but that's not what I want (or maybe it is and I'm seeing this from the wrong angle). Here's my code so far:

The VM for my ComboBox and the calling for updating the ListBox:

public class SelectorEventosViewModel : ObservableCollection<SelectorEvents>,INotifyPropertyChanged
{
    private SelectorEvents _currentSelection;
    public SelectorEventosViewModel()
    {
        PopulaSelectorEvents();
    }

    private void PopulaSelectorEvents()
    {
        Add(new SelectorEvents {Key = "evtInfoEmpregador", Value = "S1000"});
        Add(new SelectorEvents {Key = "evtTabEstab", Value = "S1005"});
        Add(new SelectorEvents {Key = "evtTabRubricas", Value = "S1010"});
    }

    public SelectorEvents CurrentSelection
    {
        get => _currentSelection;
        set
        {
            if (_currentSelection == value)
                return;
            _currentSelection = value;
            OnPropertyChanged(nameof(CurrentSelection));
            ValueChanged(_currentSelection.Key);
        }
    }
    //Here I detect selectiong and then call for the Update in my ListBox VM
    private void ValueChanged(string value)
    {
        EventFilesViewModel.Instance.GetFiles(value);
    }

    protected override event PropertyChangedEventHandler PropertyChanged;

    [NotifyPropertyChangedInvocator]
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

Here's my code for the ListBox VM:

public class EventFilesViewModel : ObservableCollection<EventFiles>
{
    private static EventFilesViewModel _instance = new EventFilesViewModel();
    public static EventFilesViewModel Instance => _instance ?? (_instance = new EventFilesViewModel());
    private string[] _filesList;
    //This would be my Update function, based on the ComboBox selection I would show some files in my ListBox, but it's not
    public void GetFiles(string ptr)
    {
        _filesList = Directory.GetFiles(@"\\mp-2624/c$/xampp/htdocs/desenv2/public/esocial/eventos/aguardando/");
        Clear();
        foreach (string file in _filesList)
        {
            var r = new Regex(ptr, RegexOptions.IgnoreCase);
            var tempFiles = new EventFiles {Key = file, Value = file.Split('/')[9]};
            if (r.Match(file).Success)
            {
                Add(tempFiles);
            }
        }
        OnPropertyChanged(nameof(EventFilesViewModel));
    }

    protected override event PropertyChangedEventHandler PropertyChanged;

    [NotifyPropertyChangedInvocator]
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

My models SelectorEvents and FileEvents both implement the INotifyPropertyChanged.

My XAML:

<ComboBox DataContext="{StaticResource ResourceKey=SelectorEventosViewModel}" Name="EventoBox" FontSize="20" SelectedIndex="0" Margin="20 10" Width="150" 
                                          ItemsSource="{StaticResource ResourceKey=SelectorEventosViewModel}"
                                          DisplayMemberPath="Value"
                                          SelectedItem="{Binding CurrentSelection}"
                                          IsSynchronizedWithCurrentItem="True"/>

<ListBox DataContext="{StaticResource ResourceKey=EventFilesViewModel}"
                                             Grid.Column="0" Name="EventoListBox" FontSize="20" Margin="10 10"
                                             HorizontalAlignment="Stretch"
                                             ItemsSource="{StaticResource ResourceKey=EventFilesViewModel}"
                                             IsSynchronizedWithCurrentItem="True"
                                             DisplayMemberPath="Value">

Thanks in advance.

So I'm not certain how you intended to have this work, but here's a solution which is more MVVM.

Essentially we collapse both of your VM's into a MainWindowVm, which handles shuttling messages between the two. That way your child VM's remain independent of each other, and thus more configurable down the line.

Based on my testing the binding for your ItemSource on the second record was not succeeding (I added a button and examined the value for ItemSource at run time after confirming the EventFilesViewModel had been updated).

EventFilesViewModel

public class EventFilesViewModel : INotifyPropertyChanged
{
   public ObservableCollection<EventFiles> EventFiles { get; } = new ObservableCollection<EventFiles>();

   private string[] _filesList;

   //This would be my Update function, based on the ComboBox selection I would show some files in my ListBox, but it's not
   public void GetFiles(string ptr)
   {
      _filesList = Directory.GetFiles(@"\\mp-2624/c$/xampp/htdocs/desenv2/public/esocial/eventos/aguardando/");
      EventFiles.Clear();
      foreach (string file in _filesList)
      {
         var r = new Regex(ptr, RegexOptions.IgnoreCase);
         var tempFiles = new EventFiles { Key = file, Value = file.Split('/')[9] };
         if (r.Match(file).Success)
         {
            EventFiles.Add(tempFiles);
         }
      }
      OnPropertyChanged(nameof(EventFilesViewModel));
   }

   public event PropertyChangedEventHandler PropertyChanged;

   [NotifyPropertyChangedInvocator]
   protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
   {
      PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
   }
}

SelectorEventosViewModel

public class SelectorEventosViewModel : INotifyPropertyChanged
{
   public ObservableCollection<SelectorEvents> SelectorEvents { get; set; } = new ObservableCollection<SelectorEvents>();

   private SelectorEvents _currentSelection;

   public SelectorEventosViewModel()
   {
      PopulaSelectorEvents();
   }

   private void PopulaSelectorEvents()
   {
      SelectorEvents.Add(new SelectorEvents { Key = "evtInfoEmpregador", Value = "S1000" });
      SelectorEvents.Add(new SelectorEvents { Key = "evtTabEstab", Value = "S1005" });
      SelectorEvents.Add(new SelectorEvents { Key = "evtTabRubricas", Value = "S1010" });
   }

   public SelectorEvents CurrentSelection
   {
      get => _currentSelection;
      set
      {
         if (_currentSelection == value)
            return;

         _currentSelection = value;
         OnPropertyChanged();
      }
   }

   public event PropertyChangedEventHandler PropertyChanged;

   [NotifyPropertyChangedInvocator]
   protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
   {
      PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
   }
}

MainWindowVm.cs

public class MainWindowVm : INotifyPropertyChanged
{
   public event PropertyChangedEventHandler PropertyChanged;

   [NotifyPropertyChangedInvocator]
   protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
   {
      PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
   }

   public SelectorEventosViewModel SelectorEventosViewModel { get; } = new SelectorEventosViewModel();

   public EventFilesViewModel EventFilesViewModel { get; } = new EventFilesViewModel();

   public MainWindowVm()
   {
      // CRITICAL--ensures that your itemsource gets updated
      SelectorEventosViewModel.PropertyChanged += SelectorEventosViewModel_PropertyChanged;
   }

   private void SelectorEventosViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
   {
      // CRITICAL--ensures that your itemsource gets updated
      EventFilesViewModel.GetFiles(SelectorEventosViewModel.CurrentSelection.Key);
   }
}

MainWindow.xaml

<Window x:Class="_52370275.MainWindow"
        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:local="clr-namespace:_52370275"
        mc:Ignorable="d"
        Title="MainWindow"
        Height="450"
        Width="800"
        d:DataContext="{d:DesignInstance local:MainWindowVm}">
   <Grid>
      <Grid.RowDefinitions>
         <RowDefinition Height="Auto"/>
         <RowDefinition Height="Auto"/>
         <RowDefinition Height="Auto"/>
         <RowDefinition />
      </Grid.RowDefinitions>
      <ComboBox
         Name="EventoBox"
         FontSize="20"
         SelectedIndex="0"
         Margin="20 10"
         Width="150"
         Grid.Row="0"
         ItemsSource="{Binding SelectorEventosViewModel.SelectorEvents}"
         DisplayMemberPath="Value"
         SelectedItem="{Binding SelectorEventosViewModel.CurrentSelection}"
         IsSynchronizedWithCurrentItem="True" />

      <ListBox Grid.Column="0"
               Name="EventoListBox"
               FontSize="20"
               Margin="10 10"
               Grid.Row="1"
               HorizontalAlignment="Stretch"
               ItemsSource="{Binding EventFilesViewModel.EventFiles}"
               IsSynchronizedWithCurrentItem="True"
               DisplayMemberPath="Value" />
   </Grid>
</Window>

MainWindow.xaml.cs

public partial class MainWindow : Window
{
   public MainWindow()
   {
      InitializeComponent();
      DataContext = new MainWindowVm();
   }
}

All together, this allows you to define the static values for your initial combobox in code-behind, and have the list box itemsource be updated.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM