最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

c# - WPF CollectionViewSource not updating UI - Stack Overflow

programmeradmin1浏览0评论

I have a very simple application, where I want populate the ListView when the user presses a button. I am using CollectionViewSource to update the ListView when the button is pressed, but I cannot populate the ListView items.

Here is the xaml containing 2 buttons and a ListView:

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"></RowDefinition>
        <RowDefinition Height="*"></RowDefinition>
    </Grid.RowDefinitions>
    <StackPanel Margin="20" Grid.Row="0" Orientation="Horizontal">
        <Button Command="{Binding GetAllDoorItemsCommand}">Get All Door Items</Button>
        <Button Margin="20,0,0,0">Modiy Door Open Close</Button>
    </StackPanel>
    <ListView Grid.Row="1" Margin="0,20,0,0" x:Name="lstViewLockItems" ItemsSource="{Binding DoorCollection}">
        <ListView.ItemsPanel>
            <ItemsPanelTemplate>
                <StackPanel Orientation="Horizontal"></StackPanel>
            </ItemsPanelTemplate>
        </ListView.ItemsPanel>
        <ListView.ItemTemplate>
            <DataTemplate>
                <StackPanel Orientation="Horizontal">
                    <Image Height="80" Width="80">
                        <Image.Style>
                            <Style TargetType="{x:Type Image}">
                                <Setter Property="Source" Value="pack://application:,,,/Images/door.jpg"></Setter>
                                <Style.Triggers>
                                    <DataTrigger Binding="{Binding Open}" Value="True">
                                        <Setter Property="Source" Value="pack://application:,,,/Images/dooropen.jpg"/>
                                    </DataTrigger>
                                </Style.Triggers>
                            </Style>
                        </Image.Style>
                    </Image>
                </StackPanel>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>
</Grid>

In the code behind, I am setting the DataContext as follows:

public class MainWindowViewModel {
  private ICollectionView _doorView;
  public ICollectionView DoorCollection {
    get {
      return _doorView;
    }
    set {
      _doorView = value;
    }
  }
  public ICommand GetAllDoorItemsCommand {
    get;
    set;
  }

  public MainWindowViewModel() {
    GetAllDoorItemsCommand = new RelayCommand < object > (GetAllItems);
  }

  private void GetAllItems(object obj) {
    var Items = new List < Door > ();
    Items.Add(new Door() {
      Name = "Front", Open = true
    });
    Items.Add(new Door() {
      Name = "Back", Open = false
    });
    DoorCollection = CollectionViewSource.GetDefaultView(Items);
    DoorCollection.Refresh();
  }
}

public class Door: INotifyPropertyChanged {
  public event PropertyChangedEventHandler PropertyChanged;

  private string _name;
  public string Name {
    get => _name;
    set {
      _name = value;
      OnPropertyChanged("Name");
    }
  }

  private bool _open;
  public bool Open {
    get => _open;
    set {
      _open = value;
      OnPropertyChanged("Open");
    }
  }

  protected virtual void OnPropertyChanged(string propertyName) {
    PropertyChangedEventHandler handler = PropertyChanged;
    if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
  }
}

I would appreciate any help.

I have a very simple application, where I want populate the ListView when the user presses a button. I am using CollectionViewSource to update the ListView when the button is pressed, but I cannot populate the ListView items.

Here is the xaml containing 2 buttons and a ListView:

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"></RowDefinition>
        <RowDefinition Height="*"></RowDefinition>
    </Grid.RowDefinitions>
    <StackPanel Margin="20" Grid.Row="0" Orientation="Horizontal">
        <Button Command="{Binding GetAllDoorItemsCommand}">Get All Door Items</Button>
        <Button Margin="20,0,0,0">Modiy Door Open Close</Button>
    </StackPanel>
    <ListView Grid.Row="1" Margin="0,20,0,0" x:Name="lstViewLockItems" ItemsSource="{Binding DoorCollection}">
        <ListView.ItemsPanel>
            <ItemsPanelTemplate>
                <StackPanel Orientation="Horizontal"></StackPanel>
            </ItemsPanelTemplate>
        </ListView.ItemsPanel>
        <ListView.ItemTemplate>
            <DataTemplate>
                <StackPanel Orientation="Horizontal">
                    <Image Height="80" Width="80">
                        <Image.Style>
                            <Style TargetType="{x:Type Image}">
                                <Setter Property="Source" Value="pack://application:,,,/Images/door.jpg"></Setter>
                                <Style.Triggers>
                                    <DataTrigger Binding="{Binding Open}" Value="True">
                                        <Setter Property="Source" Value="pack://application:,,,/Images/dooropen.jpg"/>
                                    </DataTrigger>
                                </Style.Triggers>
                            </Style>
                        </Image.Style>
                    </Image>
                </StackPanel>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>
</Grid>

In the code behind, I am setting the DataContext as follows:

public class MainWindowViewModel {
  private ICollectionView _doorView;
  public ICollectionView DoorCollection {
    get {
      return _doorView;
    }
    set {
      _doorView = value;
    }
  }
  public ICommand GetAllDoorItemsCommand {
    get;
    set;
  }

  public MainWindowViewModel() {
    GetAllDoorItemsCommand = new RelayCommand < object > (GetAllItems);
  }

  private void GetAllItems(object obj) {
    var Items = new List < Door > ();
    Items.Add(new Door() {
      Name = "Front", Open = true
    });
    Items.Add(new Door() {
      Name = "Back", Open = false
    });
    DoorCollection = CollectionViewSource.GetDefaultView(Items);
    DoorCollection.Refresh();
  }
}

public class Door: INotifyPropertyChanged {
  public event PropertyChangedEventHandler PropertyChanged;

  private string _name;
  public string Name {
    get => _name;
    set {
      _name = value;
      OnPropertyChanged("Name");
    }
  }

  private bool _open;
  public bool Open {
    get => _open;
    set {
      _open = value;
      OnPropertyChanged("Open");
    }
  }

  protected virtual void OnPropertyChanged(string propertyName) {
    PropertyChangedEventHandler handler = PropertyChanged;
    if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
  }
}

I would appreciate any help.

Share Improve this question edited Apr 1 at 11:54 AztecCodes 5966 gold badges16 silver badges30 bronze badges asked Mar 28 at 18:32 nikhilnikhil 1,7603 gold badges26 silver badges64 bronze badges 3
  • 1 Just reloading the collection in this case doesn't tell the binding engine anything. You need to "notify" of the collection change; or set the DataContext (i.e. ItemSource) so there is less ambiguity; which is usually in the Loaded event; not the constructor. – Gerry Schmitz Commented Mar 28 at 18:50
  • 3 Looks like the issue lies in how the DoorCollection property is implemented. You are assigning a new value to it (CollectionViewSource.GetDefaultView(Items)), but your UI isn't being notified that the DoorCollection property has changed. This is because you're not raising the PropertyChanged event when setting DoorCollection. – AztecCodes Commented Mar 28 at 19:01
  • I think you have several problems there. Calling refresh on the collection view might have worked but for your null initial collection. If you bind to a null collection then you get nothing. First thing I'd try would be make the backer a new list initially. Then observable collection and raise property changed. – Andy Commented Mar 30 at 9:26
Add a comment  | 

1 Answer 1

Reset to default 1

According to the documentation (emphasis mine):

You can think of a collection view as a layer on top of a binding source collection that allows you to navigate and display the collection based on sort, filter, and group queries, all without having to manipulate the underlying source collection itself. If the source collection implements the INotifyCollectionChanged interface, the changes that raise the CollectionChanged event are propagated to the views.

So, the underlying collection is responsible for notifying about changes, and List does not do that. A good choice would be ObservableCollection. Here is a simplified implementation using your code:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        DataContext = this;
        GetAllDoorItemsCommand = new RelayCommand<object>(GetAllItems);
        DoorCollection = CollectionViewSource.GetDefaultView(_doorView);
    }

    private readonly ObservableCollection<Door> _doorView = new();

    public ICollectionView DoorCollection { get; }

    public ICommand GetAllDoorItemsCommand { get; }

    private void GetAllItems(object obj)
    {
        _doorView.Clear();
        _doorView.Add(new Door { Name = "Front", Open = true });
        _doorView.Add(new Door { Name = "Back", Open = false });
        DoorCollection.Refresh();
    }
}
发布评论

评论列表(0)

  1. 暂无评论