我需要在更改进度时更新下载列表。
XAML:
<ListView ItemsSource="{Binding UiList}" x:Name="MyListView">
<ListView.View>
<GridView>
<GridViewColumn Header="Title"/>
<GridViewColumn Header="Progress"/>
</GridView>
</ListView.View>
</ListView>
ViewModel,它创建Logic类的实例并更新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; }
}
逻辑:
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);
}
}
}
上面的代码不会更新UI。我知道如何解决这个问题的两种方法:
在xaml代码隐藏调用中:Application.Current.Dispatcher.Invoke(new Action(() => MyListView.Items.Refresh()));
在ViewModel中,将List<>
更改为ICollectionView
,并在更新列表后使用Application.Current.Dispatcher.Invoke(new Action(() => UiList.Refresh()));
。
两种方式都会导致问题:应按用户要求打开的ListView 闪烁和弹出始终在每次“刷新”后关闭:
<Popup Name="Menu" StaysOpen="False">
我可以用另一个控件或面板替换弹出窗口,但我需要它可能超出主窗口的边框(如屏幕上)。但我相信WPF还有另一种更新ListView内容的方法(没有闪烁)。
PS:很抱歉很长时间的问题,但我不知道如何更简短地描述它......
答案 0 :(得分:1)
我认为这条线不起作用的原因:
RaisePropertyChanged("UiList");
是因为您实际上没有更改列表。你清除它并重新填充它,但它仍然是对同一列表的引用。我有兴趣看看如果您实际创建了一个新列表,而不是清除列表并重新填充,会发生什么。我认为应按预期更新ListView。不管它是否对你的弹出窗口产生影响,我都不知道。
答案 1 :(得分:0)
我在这里找到了答案:How do I update an existing element of an ObservableCollection?
ObservableCollection是部分解决方案。 ObservableCollection仅在集合更改(添加,删除项目等)时才会上升CollectionChanged事件。为了支持现有项目的更新,集合中的每个对象(在我的情况下为Model类)必须实现INotifyPropertyChanged接口。
// 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; }
}