2008-12-08 3 views
4

У меня возникла проблема с ObservableCollection, получающей новые элементы, но не отражающие эти изменения в ListView. У меня достаточно причуд в том, как я реализую это, что мне сложно определить, в чем проблема.ObservableCollection <T> не обновляется UI

Мой ObservableCollection осуществляется таким образом:

public class MessageList : ObservableCollection<LobbyMessage> 
{ 
    public MessageList(): base() 
    { 
     Add(new LobbyMessage() { Name = "System", Message = "Welcome!" }); 
    } 
} 

хранить коллекцию в статическом свойстве (так что его легко доступны из нескольких пользовательских элементов управления):

static public MessageList LobbyMessages { get; set; } 

В OnLoad случае моего main NavigationWindow У меня есть следующая строка:

ChatHelper.LobbyMessages = new MessageList(); 

My X AML в UserControl, где находится ListView гласит:

<ListBox IsSynchronizedWithCurrentItem="True" 
     ItemsSource="{Binding Mode=OneWay}" 
     x:Name="ListBoxChatMessages" 
     d:UseSampleData="True" 
     ItemTemplate="{DynamicResource MessageListTemplate}" 
     IsEnabled="True"> 
     <ListBox.DataContext> 
     <Magrathea_Words_Tools:MessageList/> 
     </ListBox.DataContext> 
    </ListBox> 

Исходное сообщение, которое я добавил в конструкторе в пользовательском интерфейсе появляется просто отлично.

Теперь, когда я добавляю новые элементы в коллекцию, это из CallBack, поступающего из службы WCF. У меня был этот код, работающий в приложении WinForms, и было необходимо обработать обратный вызов для потока пользовательского интерфейса, поэтому я оставил этот код на месте. Вот сокращенный вариант метода:

Helper.Context = SynchronizationContext.Current; 

#region IServiceMessageCallback Members 

/// <summary> 
/// Callback handler for when the service has a message for 
/// this client 
/// </summary> 
/// <param name="serviceMessage"></param> 
public void OnReceivedServiceMessage(ServiceMessage serviceMessage) 
{ 
    // This is being called from the WCF service on it's own thread so 
    // we have to marshall the call back to this thread. 
    SendOrPostCallback callback = delegate 
    { 
     switch (serviceMessage.MessageType) 
     { 
      case MessageType.ChatMessage: 
       ChatHelper.LobbyMessages.Add(
         new LobbyMessage() 
         { 
          Name = serviceMessage.OriginatingPlayer.Name, 
          Message = serviceMessage.Message 
         }); 
       break; 

      default: 
       break; 
     } 
    }; 

    Helper.Context.Post(callback, null); 
} 

При отладке я могу видеть коллекцию получать обновленную с сообщениями от службы, но интерфейс не отражает эти дополнения.

Любые идеи о том, чего мне не хватает, чтобы ListView отражал эти новые элементы в коллекции?

ответ

4

Я решил эту проблему.

Ни статическое свойство, ни контекст входящих данных не имели ничего общего с проблемой (что кажется очевидным в ретроспективе).

XAML, который был создан из Expression Blend, по какой-то причине не соответствовал задаче. Все, что я сделал, чтобы заставить это работать, - назначить ItemSource для коллекции на C#.

ListBoxChatMessages.ItemsSource = ChatHelper.LobbyMessages.Messages; 

Теперь мой XAML упрощен.

<ListBox IsSynchronizedWithCurrentItem="True" 
     ItemsSource="{Binding Mode=OneWay}" Background="#FF1F1F1F" 
     Margin="223,18.084,15.957,67.787" x:Name="ListBoxChatMessages" 
     ItemTemplate="{DynamicResource MessageListTemplate}" 
     IsEnabled="True"/> 

Я немного смущен, почему это работает. Я читал статьи MSDN о том, как привязывать данные в WPF, и они включали несколько объектов привязки, ссылаясь на свойства объекта и т. Д. Я не понимаю, почему они столкнулись со всей проблемой, когда одна строка кода в конструкторе UserControl выполняет трюк просто отлично.

+0

Как я понимаю, намерения MS в WPF было сделать такие вещи, как установление обязательных источников (например, свойства ItemsSource) настройки статических данных в XAML, а не в коде. Более опытный разработчик переделал мою сложную программу практически без C# и только немного больше xaml. xaml> код. – 2008-12-09 14:46:07

0

Вам нужно сделать свой класс poco в реализации ObservableCollection INotifyPropertyChanged.

Пример:

<viewModels:LocationsViewModel x:Key="viewModel" /> 
. 
. 
.  
<ListView 
    DataContext="{StaticResource viewModel}" 
    ItemsSource="{Binding Locations}" 
    IsItemClickEnabled="True" 
    ItemClick="GroupSection_ItemClick" 
    ContinuumNavigationTransitionInfo.ExitElementContainer="True"> 

    <ListView.ItemTemplate> 
     <DataTemplate> 
      <StackPanel Orientation="Horizontal"> 
       <TextBlock Text="{Binding Name}" Margin="0,0,10,0" Style="{ThemeResource ListViewItemTextBlockStyle}" /> 
       <TextBlock Text="{Binding Latitude, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Style="{ThemeResource ListViewItemTextBlockStyle}" Margin="0,0,5,0"/> 
       <TextBlock Text="{Binding Longitude, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Style="{ThemeResource ListViewItemTextBlockStyle}" Margin="5,0,0,0" /> 
      </StackPanel> 
     </DataTemplate> 
    </ListView.ItemTemplate> 
</ListView> 

public class LocationViewModel : BaseViewModel 
{ 
    ObservableCollection<Location> _locations = new ObservableCollection<Location>(); 
    public ObservableCollection<Location> Locations 
    { 
     get 
     { 
      return _locations; 
     } 
     set 
     { 
      if (_locations != value) 
      { 
       _locations = value; 
       OnNotifyPropertyChanged(); 
      } 
     } 
    } 
} 

public class Location : BaseViewModel 
{ 
    int _locationId = 0; 
    public int LocationId 
    { 
     get 
     { 
      return _locationId; 
     } 
     set 
     { 
      if (_locationId != value) 
      { 
       _locationId = value; 
       OnNotifyPropertyChanged(); 
      } 
     } 
    } 

    string _name = null; 
    public string Name 
    { 
     get 
     { 
      return _name; 
     } 
     set 
     { 
      if (_name != value) 
      { 
       _name = value; 
       OnNotifyPropertyChanged(); 
      } 
     } 
    } 

    float _latitude = 0; 
    public float Latitude 
    { 
     get 
     { 
      return _latitude; 
     } 
     set 
     { 
      if (_latitude != value) 
      { 
       _latitude = value; 
       OnNotifyPropertyChanged(); 
      } 
     } 
    } 

    float _longitude = 0; 
    public float Longitude 
    { 
     get 
     { 
      return _longitude; 
     } 
     set 
     { 
      if (_longitude != value) 
      { 
       _longitude = value; 
       OnNotifyPropertyChanged(); 
      } 
     } 
    } 
} 

public class BaseViewModel : INotifyPropertyChanged 
{ 
    #region Events 
    public event PropertyChangedEventHandler PropertyChanged; 
    #endregion 

    protected void OnNotifyPropertyChanged([CallerMemberName] string memberName = "") 
    { 
     if (PropertyChanged != null) 
     { 
      PropertyChanged(this, new PropertyChangedEventArgs(memberName)); 
     } 
    } 
} 

 Смежные вопросы

  • Нет связанных вопросов^_^