WPF Observable Collection。刷新时计数== 0但有很多项目

时间:2011-03-05 10:00:22

标签: wpf multithreading count backgroundworker observablecollection

早上好,

为即将提供的大量文字道歉但......

我有一个WPF ListView,其ItemsSource绑定到它的各自ViewModel中的ObservableCollection。加载窗口时,可通过命令从Web服务填充可观察集合。但是,在程序运行时,BackgroundWorker线程会定期更新此集合,以便向ObservableCollection添加新项目。

这种机制很好用。在UI线程和后台线程上更新ListView没有问题。但是,当双击ListView中的项目时,将打开一个新窗口以显示上述ObservableCollection中包含的Ticket对象的详细信息。

我有一个私有方法,每次调用ObservableCollection的set方法时都会触发该方法,该方法用于从新窗口中打开的集合中查找Ticket项,并根据新更新的ObservableCollection中的项更新其属性。在进行此更新之前,我检查以确保ObservableCollection.Count大于1,如果没有要更新的内容,没有必要进行更新!

我的问题是ObservableCollection.Count属性总是等于0.但我知道这不是真的,因为ListView仍在使用添加到此集合的新Ticket对象更新其项目,如果此集合的计数确实是0,然后这将由ListView反映,其中没有任何项目,因为它绑定到此集合。

那么这里发生了什么?我想知道可能是因为BackgroundWorker正在调用;

myCollection = new ObservableCollection();

在与UI不同的线程上,当我检查UI线程上的计数时,错误的集合对象实际上是针对'Count'进行测试的。但这仍然无法解释为什么ListView反映ObservableCollection的内容而没有问题。

再一次,为墙文本道歉,但我想完全解释这个问题。 感谢您抽出时间和任何意见。

更详细的编辑

用户控件的列表视图部分

<ListView x:Name="lvTicketSummaries" ItemsSource="{Binding Path=TicketSummaries}" Grid.Row="1" Width="Auto" Height="Auto" SizeChanged="lvTicketSummaries_SizeChanged" SelectionMode="Single" Foreground="Black" Background="#3BFFFFFF" ItemContainerStyle="{DynamicResource ListViewItem}">
        <ListView.View>
            <GridView AllowsColumnReorder="True">
                <GridViewColumn Header="ID"
                                DisplayMemberBinding="{Binding ID}"
                                Width="25"/>

                <GridViewColumn Header="Status"
                                DisplayMemberBinding="{Binding Status}"
                                Width="25"/>

                <GridViewColumn Header="Subject"
                                DisplayMemberBinding="{Binding Subject}"
                                Width="25"/>

                <GridViewColumn Header="Requester"
                                DisplayMemberBinding="{Binding Owner.Name}"
                                Width="25"/>
            </GridView>                
        </ListView.View>
    </ListView>

上述用户控件的视图模型

在这里,您可以看到列表视图绑定的TicketSummaries集合,以及用于更新子视图模型中Ticket属性的refreshOpenTicket()方法,该模型是新刷新集合中的新实例。

 public class MainWindowViewModel : ViewModelBase
{

    private DispatcherTimer timer;
    private BackgroundWorker worker_TicketLoader;

    private ObservableCollection<Ticket> ticketSummaries;
    public ObservableCollection<Ticket> TicketSummaries
    {
        get { return ticketSummaries; }
        set
        {
            ticketSummaries = value;
            this.RaisePropertyChanged(p => p.TicketSummaries);
            refreshOpenTicket();
        }
    }

    private void refreshOpenTicket()
    {
        // Check there are actually some tickets to refresh
        if (TicketSummaries.Count < 1)
            return;

        // Check we have created the view model
        if (TicketDetailsViewModel != null)
        {
            // Check the ticket loaded correctly
            if (TicketDetailsViewModel.Ticket != null)
            {
                // Find a ticket in the collection with the same id
                Ticket openTicket = TicketSummaries.Where(
                    ticket => ticket.ID == TicketDetailsViewModel.Ticket.ID
                    ).First();

                // Make sure we are not going to overrite with a null reference
                if (openTicket != null)
                    TicketDetailsViewModel.Ticket = openTicket;
            }
        }
    }

此集合通过以下命令从各种来源更新

private void Execute_GetAgentsTickets(object agent)
    {
        TicketSummaries = new ObservableCollection<Ticket>();
        var agentsTickets = ticketService.GetAgentsTickets((Agent)agent);
        agentsTickets.ForEach(
            ticket => TicketSummaries.Add(ticket)
            );

        AppSettings.LoggedAgent = (Agent)agent;
        RequeryCommands();

    }

但偶尔这个集合将由后台工作者

进行异地修改
void worker_TicketLoader_DoWork(object sender, DoWorkEventArgs e)
    {
        State = "Loading Tickets";
        IsLoadingTickets = true;
        var agentsTickets = ticketService.GetAgentsTickets(AppSettings.LoggedAgent);

        TicketSummaries = new ObservableCollection<Ticket>();
        foreach (Ticket ticket in agentsTickets)
        {
            TicketSummaries.AddOnUIThread<Ticket>(ticket);
        }
        refreshOpenTicket();
        lastRefresh = DateTime.Now;
    }

以防它有所作为,TicketSummaries.AddOnUIThread(ticket);是我在StackOverflow上发现的一个解决方案,试图将项添加到集合中,该集合是线程外UI控件的绑定源,并且是;

public static void AddOnUIThread<T>(this ICollection<T> collection, T item)
{
    Action<T> addMethod = collection.Add;
    Application.Current.Dispatcher.BeginInvoke(addMethod, item);
}

我希望这有助于更多地了解情况。 再次感谢您的时间。

1 个答案:

答案 0 :(得分:0)

您没有提供足够的信息来诊断,但我猜测ListView已绑定到某个属性,当您替换ObservableCollection时,您不会更新该属性。因此,当您的VM代码使用新代码时,ListView仍附加到原始集合。

为什么要更换OC?为什么不在项目来自中间层时更新它?如果你真的必须更换它,请务必通过一个属性并提出该属性的更改通知。