Наблюдаемая коллекция WPF. Счет == 0 при обновлении, но имеет много элементов - PullRequest
0 голосов
/ 05 марта 2011

Доброе утро,

Извинения за массу текста, которую я собираюсь предоставить, но ...

У меня есть WPF ListView с его ItemsSource, связанным с ObservableCollection в его соответствующей ViewModel.Когда окно загружается, наблюдаемая коллекция заполняется из веб-службы с помощью команды.Однако во время работы программы эта коллекция периодически обновляется потоком BackgroundWorker для добавления новых элементов в коллекцию ObservableCollection.

Этот механизм работает нормально.ListView обновляется без проблем как в потоке пользовательского интерфейса, так и в фоновом потоке.Однако при двойном щелчке по элементу в ListView открывается новое окно для отображения сведений об объекте Ticket, содержащемся в вышеупомянутой коллекции ObservableCollection.

У меня есть частный метод, который запускается каждый раз, когда вызывается метод set ObservableCollection, который служит для поиска элемента Ticket из коллекции, которая была открыта в новом окне, и обновления его свойств в соответствии с элементами внедавно обновленная коллекция ObservableCollection.Перед выполнением этого обновления я проверяю, чтобы значение ObservableCollection.Count было больше 1, нет смысла делать обновление, если обновлять нечего!

Моя проблема в том, что свойство ObservableCollection.Count ВСЕГДА равнов 0. Но я знаю, что это не так, поскольку ListView по-прежнему обновляет свои элементы новыми объектами Ticket, добавленными в эту коллекцию. Если счет этой коллекции действительно равен 0, то это будет отражено в ListView, также не имеющем элементовв нем, как оно связано с этой коллекцией.

Так что здесь происходит?Мне интересно, может быть, потому что BackgroundWorker вызывает;

myCollection = new ObservableCollection();

в потоке, отличном от пользовательского интерфейса, что когда я проверяю счет в потоке пользовательского интерфейса, неправильный объект коллекции фактически проверяется на «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, с которой связано представление списка, а также метод refreshOpenTicket (), используемый для обновлениясвойство Ticket в дочерней модели представления, которая представляет собой новый экземпляр новой обновленной коллекции.

 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, чтобы попытаться добавить элементы в коллекцию, которая является источником привязки к элементам управления пользовательского интерфейса вне потока и имеет вид:

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

Надеюсь, это поможет пролить немного света на ситуацию,Еще раз спасибо за ваше время.

1 Ответ

0 голосов
/ 05 марта 2011

Вы не предоставили достаточно информации для диагностики, но я предполагаю, что ListView привязан к свойству, и когда вы заменяете ObservableCollection, вы не обновляете это свойство. Следовательно, ListView все еще прикреплен к исходной коллекции, в то время как код вашей виртуальной машины работает с новой.

Зачем вообще заменять OC? Почему бы просто не обновить его, когда элементы поступают со среднего уровня? Если вам действительно необходимо заменить его, обязательно просмотрите свойство и отправьте уведомление об изменении этого свойства.

...