0

I need to update the list of downloads when the progress has been changed.

XAML:

<ListView ItemsSource="{Binding UiList}" x:Name="MyListView">
  <ListView.View>
    <GridView>
      <GridViewColumn Header="Title"/>
      <GridViewColumn Header="Progress"/>
    </GridView>
  </ListView.View>
</ListView>

ViewModel that creates an instance of Logic class and updates the content of ListView:

class MainWindowViewModel : ViewModelBase
{
    private readonly Logic _logic;
    public List<Model> UiList { get; set; }        

    public MainWindowViewModel()
    {
        _logic = new Logic();
        _logic.Update += LogicUpdate;
        Start = new RelayCommand(() =>
                                     {
                                         var worker = new BackgroundWorker();
                                         worker.DoWork += (sender, args) => _logic.Start();
                                         worker.RunWorkerAsync();
                                     });
    }

    void LogicUpdate(object sender, EventArgs e)
    {
        UiList = _logic.List;
        RaisePropertyChanged("UiList");            
    }

    public ICommand Start { get; set; }
}

Logic:

public class Logic
{
    readonly List<Model> _list = new List<Model>();
    public event EventHandler Update;

    public List<Model> List
    {
        get { return _list; }
    }

    public void Start()
    {
        for (int i = 0; i < 100; i++)
        {
            _list.Clear();
            _list.Add(new Model{Progress = i, Title = "title1"});
            _list.Add(new Model { Progress = i, Title = "title2" });

            var time = DateTime.Now.AddSeconds(2);
            while (time > DateTime.Now)
            { }

            Update(this, EventArgs.Empty);
        }
    }
}

The code above would not update UI. I know two way how to fix this:

  1. In xaml codebehind call: Application.Current.Dispatcher.Invoke(new Action(() => MyListView.Items.Refresh()));

  2. In ViewModel change List<> to ICollectionView and use Application.Current.Dispatcher.Invoke(new Action(() => UiList.Refresh())); after the list has been updated.

Both ways cause the problem: the ListView blinks and Popup that should be open on user demand always closes after each "refresh":

Popup

<Popup Name="Menu" StaysOpen="False">

I can replace the popup with another control or panel, but I need its possibility to be out of main window's border (like on screen). But I believe that WPF has another way to update the content of ListView (without blinks).

PS: Sorry for long question, but I do not know how to describe it more briefly...

Ievgen Martynov
  • 7,870
  • 8
  • 36
  • 52

2 Answers2

1

I think the reason this line doesn't work:

RaisePropertyChanged("UiList"); 

Is because you haven't actually changed the list. You cleared it and repopulated it, but it's still the reference to the same list. I'd be interested to see what happens if, instead of clearing your list and repopulating, you actually created a new list. I think that should update your ListView as you expected. Whether or not it has an effect on your popup, I don't know.

Matt Burland
  • 44,552
  • 18
  • 99
  • 171
0

I've found the answer here: How do I update an existing element of an ObservableCollection?

ObservableCollection is a partial solution. ObservableCollection rises CollectionChanged event only when collection changes (items added, removed, etc.) To support updates of existent items, each object inside the collection (Model class in my case) must implement the INotifyPropertyChanged interface.

// I used this parent (ViewModelBase) for quick testing because it implements INotifyPropertyChanged
public class Model : ViewModelBase 
{
    private int _progress;
    public int Progress
    {
        get { return _progress; }
        set
        {
            _progress = value;
            RaisePropertyChanged("Progress");
        }
    }

    public string Title { get; set; }
}
Community
  • 1
  • 1
Ievgen Martynov
  • 7,870
  • 8
  • 36
  • 52