WPF TreeView未被填充

时间:2012-12-28 14:47:39

标签: wpf treeview viewmodel populate

我正在尝试使用ViewModel动态填充WPF树,但是,由于某种原因它无法正常工作。要么绑定要么不正确,要么我在后面的代码中弄乱了。

以下是我所拥有的样本。 在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>

在Code 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属性的子节点以及这些子节点的子节点。

3 个答案:

答案 0 :(得分:1)

在没有看到整个设置的情况下,我不是肯定的,但我的猜测是因为MessageTree是一个普通的CLR属性(而不是引发PropertyChanged或DependencyProperty之类的东西,所以绑定发生在{{1}之前当你将它设置为一个新实例时,绑定系统没有收到通知,因为它是一个普通的属性。

另一个潜在的问题是你说代码在代码隐藏中。仅使用该Binding语法将不会从代码隐藏中获取属性。你有可能在代码中的其他地方设置了那些你没有告诉我们的东西。但是通常你不会从View绑定到代码隐藏,你将绑定到一个ViewModel,它被用作视图本身的DataContext。

答案 1 :(得分:1)

编辑:无法在手机上看到整个内容 - 根据实际查看所有内容的能力修改了答案。 :)

MOREEDIT:根据评论更新,让我们从头开始!

首先,如果你开始使用window / whatever作为datacontext,那就让它成为`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; }
}

这就是我用于treeview 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,我就把它放在一起进行测试。

答案 2 :(得分:0)

必须为TreeView(树)命名,然后在

之后
MessageTree = new Mail();

插入

Tree.ItemSource = MessageTree.Children;

我觉得这很丑,但至少它现在有用了。谢谢大家的帮助。