0

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>

CodeBehingでは...

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メソッドは不完全です。コレクションに追加するか、コレクションメンバーの1つのコレクションに追加するかを決定するロジックが必要ですが、すべてのメールオブジェクトがコレクションに追加されていますが、ツリー表示。

私が見逃している完全に明白なことは何ですか?コレクションにアイテムを追加するときに、TreeViewが自動的に更新されるべきではありませんか?

私が欲しいのは、TreeViewがMessageTreeプロパティの子とそれらの子の子を表示することです。

4

3 に答える 3

1

セットアップ全体を見ていないと、私は肯定的ではありませんが、MessageTreeはプレーンなCLRプロパティであるため(PropertyChangedやDependencyPropertyなどを発生させるものではなく、MessageTree = new Mail();呼び出しの前にバインディングが発生していると思います。設定すると新しいインスタンスに対しては、バインディングシステムはプレーンプロパティであるため、通知を受け取りません。

もう1つの潜在的な問題は、コードがコードビハインドにあると言うことです。そのBinding構文を使用するだけでは、分離コードからプロパティを取得することはできません。表示しなかったコードの別の場所に設定している可能性があります。ただし、通常、ビューからコードビハインドにバインドすることはなく、ビュー自体のDataContextとして使用されたViewModelにバインドします。

于 2012-12-28T15:06:05.513 に答える
1

編集:私の電話ですべてを見ることができませんでした-実際にすべてを見る能力に基づいて回答を修正しました。:)

詳細編集:コメントに基づいて更新されました。最初から始めましょう。

まず、ウィンドウなどをデータコンテキストとして使用するように設定されている場合は、それを `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>

それでも機能しない場合は、これをテストするためにまとめたLINQPadブロブ全体を貼り付けます。

于 2012-12-28T15:23:36.313 に答える
0

TreeView(Tree)に名前を付けてから

MessageTree = new Mail();

入れる

Tree.ItemSource = MessageTree.Children;

これは醜いですが、少なくとも今は機能します。助けてくれてありがとう。

于 2012-12-28T17:03:41.067 に答える