2008-12-08 80 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)); 
     } 
    } 
}