2008-12-08 77 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(); 

我的X AML在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> 

我在构造添加的初始消息在UI显示就好了。

现在,我向集合中添加新项目的方式来自于来自WCF服务的CallBack。我有这个代码在WinForms应用程序中工作,并且需要将回调调入UI线程,所以我留下了该代码。下面是该方法的一个简化版本:

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); 
} 

在调试我可以看到越来越采集与服务的消息更新,但是UI没有反映这些补充。

有关我缺少什么来获取ListView以反映集合中的这些新项目的任何想法?

回答

4

我解决了这个问题。

传入数据的静态属性或上下文与问题无关(后来看起来很明显)。

从Expression Blend生成的XAML由于某种原因未达到任务要求。我所做的所有工作就是将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"/> 

我,为什么这个作品有点困惑。我正在阅读关于如何在WPF中绑定数据的MSDN文章,他们包括几个绑定对象,引用对象的属性等。我不明白为什么他们遇到了所有麻烦,当UserControl的构造函数中的一行代码执行把戏很好。

+0

据我所知,MS在WPF意图是使事情,如设置结合源(像ItemsSource属性)的静态数据设置在XAML,而不是在代码。 一位更有经验的开发人员重拍我的复杂程序,几乎没有C#,只有更多的xaml。 xaml> code。 – 2008-12-09 14:46:07

0

您需要让ObservableCollection中的poco类实现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)); 
     } 
    } 
}