2012-12-28 34 views
0

我想通過使用ViewModel動態地填充WPF樹,但是,由於某種原因它不工作。要麼綁定要麼不正確,要麼我在後臺代碼的某個地方搞亂了。WPF TreeView沒有被填充

下面是我所擁有的一個樣本。 在XAML我定義我的TreeView是這樣的...

<TreeView DockPanel.Dock="Left" Width="200" DataContext="{Binding MessageTree}" ItemsSource="{Binding MessageTree}"> 
    <TreeView.ItemContainerStyle> 
      <Style TargetType="{x:Type TreeViewItem}"> 
      <Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" /> 
      <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" /> 
      <Setter Property="FontWeight" Value="Normal" /> 
      <Style.Triggers> 
       <Trigger Property="IsSelected" Value="True"> 
        <Setter Property="FontWeight" Value="Bold" /> 
       </Trigger> 
      </Style.Triggers> 
     </Style> 
     </TreeView.ItemContainerStyle> 
     <TreeView.ItemTemplate> 
      <HierarchicalDataTemplate DataType="viewModel:Mail" ItemsSource="{Binding Children}"> 
      <TextBlock Text="{Binding Subject}" /> 
     </HierarchicalDataTemplate> 
    </TreeView.ItemTemplate> 
</TreeView> 

代碼Behing我有...

private Mail MessageTree { get; set; } 

而且

using (var mail = new MailParser()) 
{ 
    int count = mail.GetMessageCount(DateTime.Today.AddDays(-10), DateTime.Today.AddDays(1)); 
    MessageTree = new Mail(); 
    for (int i = count - 1; i >= 0; i--) 
    { 
     MailMessage msg = mail.RetrieveMessage(i); 
     if (msg != null) 
     { 
      MessageTree.Add(msg); 
     } 
     if (backgroundWorker != null) 
     { 
      decimal perc = (100.0m - (((i + 1.0m)*100.0m)/count)); 
      backgroundWorker.ReportProgress((int) perc, "Recebendo mensagens... " + perc.ToString("N2") + "%"); 
      if (backgroundWorker.CancellationPending) 
      { 
       e.Cancel = true; 
       break; 
      } 
     } 
    } 
} 

郵件被定義爲

public sealed class Mail : INotifyPropertyChanged 
{ 
    private readonly ObservableCollection<Mail> _children; 
    private readonly MailMessage _msg; 
    private readonly Mail _parent; 

    private bool _isExpanded; 
    private bool _isSelected; 

    public Mail() 
    { 
     _msg = new MailMessage {Subject = "Empty"}; 
     _parent = null; 
     _children = new ObservableCollection<Mail>(); 
    } 

    public Mail(MailMessage msg, Mail parent = null) 
    { 
     _msg = msg; 
     _parent = parent; 
     _children = new ObservableCollection<Mail>(); 
    } 

    public IEnumerable<Mail> Children 
    { 
     get { return _children; } 
    } 

    public string Subject 
    { 
     get { return _msg.Subject; } 
    } 

    public bool IsExpanded 
    { 
     get { return _isExpanded; } 
     set 
     { 
      if (value != _isExpanded) 
      { 
       _isExpanded = value; 
       OnPropertyChanged(); 
      } 
      if (_isExpanded && _parent != null) 
       _parent.IsExpanded = true; 
     } 
    } 

    public bool IsSelected 
    { 
     get { return _isSelected; } 
     set 
     { 
      if (value != _isSelected) 
      { 
       _isSelected = value; 
       OnPropertyChanged(); 
      } 
     } 
    } 

    public event PropertyChangedEventHandler PropertyChanged; 

    public void Add(MailMessage msg) 
    { 
     _children.Add(new Mail(msg, this)); 
     OnPropertyChanged("Children"); 
    } 

    private void OnPropertyChanged([CallerMemberName] string propertyName = null) 
    { 
     PropertyChangedEventHandler handler = PropertyChanged; 
     if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName)); 
    } 
} 

我找不到任何東西與網上發現的例子截然不同,它不起作用。 Add方法是不完整的,我仍然需要一些邏輯來決定是將它們添加到集合還是集合成員的集合中,但正如我所有的Mail對象都添加到集合中但沒有顯示在集合中樹視圖。

我錯過了什麼完全明顯的東西?當我向物品添加物品時,TreeView是否應該自動更新?

我想要的是爲TreeView顯示MessageTree屬性的子項以及這些子項的子項。

回答

1

編輯:無法看到我的手機上的整個事情 - 修正答案基於實際看到的一切的能力。 :)

MOREEDIT:根據評論更新,讓我們從頭開始!我們先讓它成爲'INotifyPropertyChange ...下一步,讓我們讓「MessageTree」成爲一個郵件集合,而不僅僅是一個郵件集合(它是'會作出有約束力的語義更容易,相信我)

public class WhateverContainsTheTree : Window, INotifyPropertyChanged 
{ 
    public WhateverContainsTheTree() 
    { 
     this.Loaded += OnLoaded; 
     this._messageTree = new ObservableCollection<Mail>(); 
     this.DataContext = this; 
    } 

    private void OnLoaded(object sender, RoutedEventArgs e) 
    { 
     _worker = new BackgroundWorker(); 
     _worker.DoWork += WorkerWorkin; 
     _worker.RunWorkerAsync(); 
    } 

    private BackgroundWorker _worker; 
    private ObservableCollection<Mail> _messageTree;  
    public ObservableCollection<Mail> MessageTree 
    { 
     get { return _messageTree; } 
     set { _messageTree = value; RaisePropertyChanged("MessageTree"); } 
    } 

    public event PropertyChangedEventHandler PropertyChanged = delegate {}; 
    private void RaisePropertyChanged(string propertyName) 
    { 
     PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); 
    } 

    private void WorkerWorkin(object sender, DoWorkEventArgs e) 
    { 
       // obviously, change this to your stuff; I added a ctor so I could pass a string 
     Thread.Sleep(3000); 
     Console.WriteLine("Ok, setting message tree"); 
     Dispatcher.Invoke(
      System.Windows.Threading.DispatcherPriority.Normal, 
      (Action)(() => 
      { 
       var mail1 = new Mail("Mail#1:Changed from background thread"); 
       var mail2 = new Mail("Mail#2:Submail of mail #1"); 
       var mail3 = new Mail("Mail#3:Submail of mail #2"); 
       var mail4 = new Mail("Mail#4:Submail of mail #1"); 
       var mail5 = new Mail("Mail#5:Submail of mail #4"); 
       mail1.Children.Add(mail2); 
       mail1.Children.Add(mail4); 
       mail2.Children.Add(mail3); 
       mail4.Children.Add(mail5); 
       MessageTree.Add(mail1); 
      }) 
     ); 
    } 
} 

另外,就像我在原來的響應會說,讓我們稍微調整Mail.Children:

public ObservableCollection<Mail> Children 
{ 
    get { return _children; } 
} 

和這裏的什麼我用樹視圖xaml:

<TreeView DockPanel.Dock="Left" Width="200" ItemsSource="{{Binding MessageTree}}"> 
    <TreeView.ItemContainerStyle> 
      <Style TargetType="{{x:Type TreeViewItem}}"> 
      <Setter Property="IsExpanded" Value="{{Binding IsExpanded, Mode=TwoWay}}" /> 
      <Setter Property="IsSelected" Value="{{Binding IsSelected, Mode=TwoWay}}" /> 
      <Setter Property="FontWeight" Value="Normal" /> 
      <Style.Triggers> 
       <Trigger Property="IsSelected" Value="True"> 
        <Setter Property="FontWeight" Value="Bold" /> 
       </Trigger> 
      </Style.Triggers> 
     </Style> 
     </TreeView.ItemContainerStyle> 
     <TreeView.ItemTemplate> 
      <HierarchicalDataTemplate DataType="viewModel:Mail" ItemsSource="{{Binding Children}}"> 
       <TextBlock Text="{{Binding Subject}}" /> 
      </HierarchicalDataTemplate> 
    </TreeView.ItemTemplate> 
</TreeView> 

如果這個STILL不起作用,我會粘貼整個LINQPad blob來放在一起來測試它。

+0

不起作用 - 對不起 – 537mfb

+0

不應該datacontext是什麼持有的項目嗎?在這種情況下MessageTree? – 537mfb

+0

啊哈 - 這就是我在電話裏看到這個的時候...部分被切斷了。讓我修改我的答案... – JerKimball

1

沒有看到整個設置,我不積極,但我的猜測是,因爲MessageTree是一個普通的CLR屬性(而不是引發PropertyChanged或DependencyProperty或什麼的東西,該綁定發生在您撥打MessageTree = new Mail();之前。當你將它設置爲一個新的實例時,由於綁定系統是一個普通屬性,因此它沒有得到通知。

另一個潛在的問題是,你說代碼在代碼隱藏中,只是使用綁定語法不會從代碼隱藏中挑選一個屬性,你可能會在代碼中的其他位置設置它,但是一般情況下,你不會從View中綁定到代碼隱藏,你會綁定到一個ViewModel被用作DataContext爲視圖本身。

+0

MessageTree是類型郵件至極是我的ViewModel並且實現INotifyPropertyChanged - 如果這就是你的意思。 – 537mfb

+0

至於新的郵件()線,這是一個好點 - 我會在哪裏初始化它呢?我嘗試設置base.DataContext到MessageTree後,我呼籲新的權利,但沒有奏效 – 537mfb

+0

好吧給了你+1因爲你帶領我一半的答案 - 加入DataContext = MessageTree.Children;使我必須將MessageTree.Add行放在調度程序調用中,因爲綁定 - 仍然沒有在TreeView中顯示任何內容,儘管 – 537mfb

0

只好給一個名字到TreeView(樹),然後

MessageTree = new Mail(); 

插入

Tree.ItemSource = MessageTree.Children; 

後,我覺得這是醜陋,但至少現在的工作。謝謝大家的幫助。

+0

這真的很奇怪......你究竟在哪裏做MessageTree = new Mail()調用?我看到使用...塊,但實際發生在哪裏? – JerKimball

+0

在backgroundworker中 - backgroundworker是window_loaded事件的啓動者,用於使用來自gmail帳戶的郵件填充ViewModel – 537mfb

+0

Aha!這解釋了一些事情......讓我再次重新修改我的答案... – JerKimball