質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.32%

  • C#

    7726questions

    C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

  • WPF

    738questions

    Windows Presentation Foundation (WPF) は、魅力的な外観のユーザー エクスペリエンスを持つ Windows クライアント アプリケーションを作成するための次世代プレゼンテーション システムです

ツリービューで選択した項目を通知したい(WPF)

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 1,374

sa_sa

score 9

こんにちは。WPFを勉強中の者です。
プログラミング自体あまり明るくないため、具体的な改善点まで教えていただけると嬉しいです。

前提・実現したいこと

TreeView上で選択状態にある項目を削除できるようにしたいです。
ツリーでの選択状態の通知ができれば解決すると考えています。
ツリーの任意の項目を選択した状態でボタンを押すと削除できるという仕組みにするつもりです。

発生している問題・エラーメッセージ

ViewModelに存在するコレクションをTreeViewにバインディングさせています。
以前ListBoxで同じことを行った際にはSelectedItemプロパティを使用することで、選択している項目をViewModel側へ通知しましたが、TreeViewのSelectedItemはListBoxのそれとは仕様が異なるのか同じようにいきません。

以下にここまで作ったソースコードを載せます。当初はxamlのTreeViewに
SelectedItem="{Binding SelectedPerson}"
と記述しViewModelにあるSelectedPersonプロパティとバインディングさせ、removeMethodに
Remove(SelectedPerson)
を書くことで選択項目を削除するつもりでした。

該当のソースコード

TreeViewAddRemoveViewプロジェクト

MainWindow.xaml
------------------------------------------
<Window x:Class="TreeAddRemove.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="20" />
        </Grid.RowDefinitions>

        <TreeView Name="treeview" Grid.Row="0" ItemsSource="{Binding TreeObject.People}">
            <TreeView.ItemTemplate>
                <HierarchicalDataTemplate ItemsSource="{Binding Children}">
                    <TextBlock Text="{Binding Name}" />
                </HierarchicalDataTemplate>
            </TreeView.ItemTemplate>
        </TreeView>

        <Button Grid.Row="1" Name="button" Content="RemoveButton" Command="{Binding Person.ChopCommand}" />
    </Grid>
</Window>
MainWindow.xaml.cs
---------------------------------------
using System.Windows;
using TreeAddRemoveViewModel;

namespace TreeAddRemove
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            DataContext = new MainWindowViewModel();
        }
    }
}

TreeViewAddRemoveViewModelプロジェクト

MainWindowViewModel.cs
--------------------------------------------
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using System.Collections.ObjectModel;

namespace TreeAddRemoveViewModel
{
    public class MainWindowViewModel: ViewModelBase
    {
        public TreeObject TreeObject { get; private set; }
        public Person Person { get; private set; }

        public MainWindowViewModel()
        {
            TreeObject = new TreeObject();
            Person = new Person();
        }
    }

    public class TreeObject : ViewModelBase
    {
        private ObservableCollection<Person> people;
        public ObservableCollection<Person> People
        {
            get { return people; }
            set { people = value; }
        }

        public TreeObject()
        {
            var p = new Person()
            {
                Children =
                { 
                    new Person("Human01")
                    {
                        Children = 
                        {
                            new Person("Human02"),
                            new Person("Human03")
                        }
                    }
                }
            };
            p.SetParent();
            People = p.Children;
        }
    }

    public class Person : ViewModelBase
    {
        public Person() { }
        public Person(string name)
        {
            Name = name;
        }
        private string name;
        public string Name
        {
            get { return name; }
            set { name = value; }
        }
        public Person Parent { get; set; }

        private ObservableCollection<Person> children = new ObservableCollection<Person>();
        public ObservableCollection<Person> Children 
        {
            get { return children; }
            set { children = value; }
        }

        /// <summary>
        /// 親子関係の設定
        /// </summary>
        public void SetParent()
        {
            foreach (var item in Children)
            {
                item.Parent = this;
                item.SetParent();
            }
        }

        public void AddChild(Person p)
        {
            this.Children.Add(p);
            p.Parent = this;
        }

        private void chop()
        {
            if(this.Parent != null)
            {
                this.Parent.Children.Remove(this);
            }
            //this.Parent?.Children.Remove(this);
        }
        #region remove Command
        /// <summary>
        /// Gets the remove.
        /// </summary>
        public RelayCommand ChopCommand
        {
            get { return _chopCommand ?? (_chopCommand = new RelayCommand(() => { chop(); })); }
        }
        private RelayCommand _chopCommand;
        #endregion
    }

}

補足情報(言語/FW/ツール等のバージョンなど)

言語 C#
開発環境 Visual Studio 2013
Microsoft .NETFramework 4.6.1

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

checkベストアンサー

0

ソースコードはこっちのほうがきれいに成形されるのでこっちで回答します。

親子関係を定義してあげると、子から親にアクセスできて、その親に対して削除処理をすればいいということですね。

あと、public class Person : ObservableCollection<Person>
を修正ました。Listの継承はやらないほうがいい。

MvvmLightを使ってます。

using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using System.Collections.ObjectModel;

namespace WpfApp2.ViewModel
{
    public class MainWindowViewModel: ViewModelBase
    {
        private ObservableCollection<Person> people;
        public ObservableCollection<Person> People
        {
            get { return people; }
            set { people = value; }
        }

        private Person selectedPerson = null;
        public Person SelectedPerson
        {
            get { return selectedPerson; }
            set { selectedPerson = value; }
        }

        public MainWindowViewModel()
        {
            var p = new Person()
            {
                Children ={ new Person("Human01")
                {
                    Children = {
                        new Person("Human02"),
                        new Person("Human03")
                    }
                }
                }
            };
            p.SetParent();
            People = p.Children;
        }

    }

    public class Person : ViewModelBase
    {
        public Person() { }
        public Person(string name)
        {
            Name = name;
        }
        private string name;
        public string Name
        {
            get { return name; }
            set { name = value; }
        }
        public Person Parent { get; set; }
        public ObservableCollection<Person> Children { get; set; } = new ObservableCollection<Person>();

        /// <summary>
        /// 親子関係の設定
        /// </summary>
        public void SetParent()
        {
            foreach (var item in Children)
            {
                item.Parent = this;
                item.SetParent();
            }
        }

        public void AddChild(Person p)
        {
            this.Children.Add(p);
            p.Parent = this;
        }

        private void chop()
        {
            this.Parent?.Children.Remove(this);
        }
        #region remove Command
        /// <summary>
        /// Gets the remove.
        /// </summary>
        public RelayCommand ChopCommand
        {
            get { return _chopCommand ?? (_chopCommand = new RelayCommand(() => { chop(); })); }
        }
        private RelayCommand _chopCommand;
        #endregion

    }
}

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/03/24 17:58

    追記のほど、ありがとうございます。
    MvvmLightというものが初見でしたので少し躓きましたが調べてなんとかインストールしました。

    少し確認させていただきたいのですが、
    >public ObservableCollection<Person> Children { get; set; } = new ObservableCollection<Person>();
    はこのままではエラーになるため、
    private ObservableCollection<Person> children;
    public ObservableCollection<Person> Children
    {
    get{ return children; }
    set{ children = value; }
    }
    と書き換えましたが問題ありませんか?
    それとchopメソッドの
    >this.Parent?.Children.Remove(this);
    にあるParentのあとの?は単なるタイプミスでしょうか?それとも何か意味のあるものなのでしょうか?一応タイプミスと判断して?は取り去って使わせていただいています。
    プログラミング自体ほぼ初心者なもので、見当外れなことをやったり聞いたりしてしまっていたらすみません…

    その上でなのですが、ボタンのCommandプロパティをChopCommandにバインディングしましたが、chop()内の処理を実行できずエラーを吐き出してしまいます。
    質問ばかりで心苦しいですがお答えいただけるとありがたく思います。

    キャンセル

  • 2017/03/24 18:09

    あー、開発環境が Visual Studio 2013 か。これらは、たぶん2015あたりで新しい文法ですね。http://www.buildinsider.net/language/csharplang/0600 ここを参考にするといいかと。個人で使うなら、無料なので Visual Studio 2017を使うといいともいます。エラーは、おそらくNullチェックができてない、ParentがNullになっているので、適切にParentを設定するようにするといいと思います。

    キャンセル

  • 2017/03/24 18:52

    私の環境では使えない文法だったのですね。理解いたしました。
    貼っていただいたリンクを拝見いたしましたが、これに従うと
    this.Parent?.Children.Remove(this);
    とは、Parentがnullならnullを返し、Parentがnullでなければ
    this.Children.Remove(this)の結果を返すというイメージで間違いありませんか?

    キャンセル

  • 2017/03/24 19:04

    これを省略しているだけです。
    if ( this.Parent != null)
    {
    this.Parent.Children.Remove(this);
    }

    キャンセル

  • 2017/03/24 20:20

    ありがとうございます。そのように書き換えて実行してみました。
    結果としては、ツリーのどの項目を選択してもParentがnullのままであるためchop()メソッドを素通りするということになりました。
    これは選択内容が通知できていないからと考えるべきでしょうか。

    なお、記載していただいたコードのままではButtonのCommandプロパティにPersonクラスのChopCommandをバインディングできないためViewModelの内容を少しいじっています。
    質問文に載せているソースコードを現状のものに更新したためよろしければそちらをご覧いただきたく思います。

    キャンセル

  • 2017/03/24 21:23 編集

    想定していたのは、こういう風に、アイテムの近くに削除ボタンが置くことです。

    <TreeView Name="treeview" Grid.Row="0" ItemsSource="{Binding TreeObject.People}">
    <TreeView.ItemTemplate>
    <HierarchicalDataTemplate ItemsSource="{Binding Children}">
    <StackPanel Orientation="Horizontal">
    <Button Content="x" Command="{Binding ChopCommand}"></Button>
    <TextBlock Text="{Binding Name}" />
    </StackPanel>
    </HierarchicalDataTemplate>
    </TreeView.ItemTemplate>
    </TreeView>

    キャンセル

  • 2017/03/27 10:37

    そういうことだったのですね。私の考えていた仕様とは少し違ったために気が付きませんでした。
    おかげさまで正しく動作するようになりました。
    何度も質問に答えていただき本当にありがとうございました!

    キャンセル

0

質問の答えにはなっていませんが、
Personの立場で、削除するという感じで
Personにdelete用の ICommandを実装すると削除するはできますね。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/03/21 12:12

    ご回答ありがとうございます。
    しかし当方の知識不足によりそれだけでは理解がしきれなかったため、もしよろしければもう少し詳細を伺ってもよろしいでしょうか?
    現在MainWindowViewModelに記述している
    private ICommand removeElement;
    public ICommand RemoveElement
    {
    get { return removeElement = new DelegateCommand(removeMethod); }
    }
    private void removeMethod()
    {

    }
    をPersonクラスの方に移すことになるのでしょうか?
    そうしますとremoveMethodにはどのような記述をすることになるのでしょうか?

    ご面倒をおかけしますがよろしくお願いいたします。

    キャンセル

同じタグがついた質問を見る

  • C#

    7726questions

    C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

  • WPF

    738questions

    Windows Presentation Foundation (WPF) は、魅力的な外観のユーザー エクスペリエンスを持つ Windows クライアント アプリケーションを作成するための次世代プレゼンテーション システムです