powered by simpleCommunicator - 2.0.49     © 2025 Programmizd 02
Форумы / WPF, Silverlight [игнор отключен] [закрыт для гостей] / Помогите разметку XAML для treeview сделать
13 сообщений из 13, страница 1 из 1
Помогите разметку XAML для treeview сделать
    #39389247
Arik
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Добрый день!

Пару дней назад начал писать своё первое приложение WPF, пока ещё не совсем разобрался как работает привязка данных. Помогите разобраться на примере файловой системы. Набор данных следующий:

VB.NET DataSet
Код: vbnet
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
        Dim dtHDD As New DataTable("Disks")
        dtHDD.Columns.Add("ID")
        dtHDD.Columns.Add("DiskName")

        dtHDD.Rows.Add(1, "Диск 1")
        dtHDD.Rows.Add(2, "Диск 2")


        Dim dtFolders As New DataTable("Folders")
        dtFolders.Columns.Add("ID")
        dtFolders.Columns.Add("DiskID")
        dtFolders.Columns.Add("FolderID")
        dtFolders.Columns.Add("FolderName")

        dtFolders.Rows.Add(1, 2, DBNull.Value, "Музыка")
        dtFolders.Rows.Add(2, 1, DBNull.Value, "Документы")
        dtFolders.Rows.Add(3, 1, 2, "Проекты")
        dtFolders.Rows.Add(4, 1, 3, "Чертежи")
        dtFolders.Rows.Add(5, 1, 3, "Наброски")
        dtFolders.Rows.Add(6, 1, 2, "Корзина")
        dtFolders.Rows.Add(7, 2, DBNull.Value, "Фильмы")
        dtFolders.Rows.Add(8, 1, 5, "Ещё одна папка")


        Dim dtFiles As New DataTable("Files")
        dtFiles.Columns.Add("ID")
        dtFiles.Columns.Add("FolderID")
        dtFiles.Columns.Add("FileName")

        dtFiles.Rows.Add(1, 8, "файл1")
        dtFiles.Rows.Add(2, 2, "файл2")
        dtFiles.Rows.Add(3, 3, "файл3")
        dtFiles.Rows.Add(4, 2, "файл4")
        dtFiles.Rows.Add(5, 5, "файл5")
        dtFiles.Rows.Add(6, 5, "файл6")
        dtFiles.Rows.Add(7, 8, "файл7")
        dtFiles.Rows.Add(8, 8, "файл8")
        dtFiles.Rows.Add(9, 1, "файл9")

        Dim ds As New DataSet
        ds.Tables.Add(dtHDD)
        ds.Tables.Add(dtFolders)
        ds.Tables.Add(dtFiles)
        ds.Relations.Add("DiskFolder", ds.Tables("Disks").Columns("ID"), ds.Tables("Folders").Columns("DiskID"))
        ds.Relations.Add("FolderFolder", ds.Tables("Folders").Columns("ID"), ds.Tables("Folders").Columns("FolderID"))
        ds.Relations.Add("FolderFile", ds.Tables("Folders").Columns("ID"), ds.Tables("Files").Columns("FolderID"))

        treeTest.ItemsSource = ds.Tables("Disks").DefaultView


C# DataSet
Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
DataTable dtHDD = new DataTable("Disks");
dtHDD.Columns.Add("ID");
dtHDD.Columns.Add("DiskName");

dtHDD.Rows.Add(1, "Диск 1");
dtHDD.Rows.Add(2, "Диск 2");


DataTable dtFolders = new DataTable("Folders");
dtFolders.Columns.Add("ID");
dtFolders.Columns.Add("DiskID");
dtFolders.Columns.Add("FolderID");
dtFolders.Columns.Add("FolderName");

dtFolders.Rows.Add(1, 2, DBNull.Value, "Музыка");
dtFolders.Rows.Add(2, 1, DBNull.Value, "Документы");
dtFolders.Rows.Add(3, 1, 2, "Проекты");
dtFolders.Rows.Add(4, 1, 3, "Чертежи");
dtFolders.Rows.Add(5, 1, 3, "Наброски");
dtFolders.Rows.Add(6, 1, 2, "Корзина");
dtFolders.Rows.Add(7, 2, DBNull.Value, "Фильмы");
dtFolders.Rows.Add(8, 1, 5, "Ещё одна папка");


DataTable dtFiles = new DataTable("Files");
dtFiles.Columns.Add("ID");
dtFiles.Columns.Add("FolderID");
dtFiles.Columns.Add("FileName");

dtFiles.Rows.Add(1, 8, "файл1");
dtFiles.Rows.Add(2, 2, "файл2");
dtFiles.Rows.Add(3, 3, "файл3");
dtFiles.Rows.Add(4, 2, "файл4");
dtFiles.Rows.Add(5, 5, "файл5");
dtFiles.Rows.Add(6, 5, "файл6");
dtFiles.Rows.Add(7, 8, "файл7");
dtFiles.Rows.Add(8, 8, "файл8");
dtFiles.Rows.Add(9, 1, "файл9");

DataSet ds = new DataSet();
ds.Tables.Add(dtHDD);
ds.Tables.Add(dtFolders);
ds.Tables.Add(dtFiles);
ds.Relations.Add("DiskFolder", ds.Tables("Disks").Columns("ID"), ds.Tables("Folders").Columns("DiskID"));
ds.Relations.Add("FolderFolder", ds.Tables("Folders").Columns("ID"), ds.Tables("Folders").Columns("FolderID"));
ds.Relations.Add("FolderFile", ds.Tables("Folders").Columns("ID"), ds.Tables("Files").Columns("FolderID"));

treeTest.ItemsSource = ds.Tables("Disks").DefaultView;



XAML разметка для TreeView следующая:
Код: xml
1.
2.
3.
4.
5.
6.
7.
         <TreeView x:Name="treeTest">
            <TreeView.ItemTemplate>
                <HierarchicalDataTemplate ItemsSource="{Binding DiskFolder}">
                    <TextBlock Text="{Binding DiskName}"/>
                </HierarchicalDataTemplate>
            </TreeView.ItemTemplate>
        </TreeView>



Тут по идее должен был вывести список дисков, но получается абсолютно пустое дерево. Помогите разметку сделать под заданный набор данных. В идеале, каждый тип ещё и соответствующей иконкой выделить.
...
Рейтинг: 0 / 0
Помогите разметку XAML для treeview сделать
    #39389420
vb_sub
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Arik,
нужно в первую очередь отойти от DataTable и DataSet и начать работать с коллекциями.
...
Рейтинг: 0 / 0
Помогите разметку XAML для treeview сделать
    #39389608
Arik
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
А если в данном случае удобнее работать именно с DataSet?
...
Рейтинг: 0 / 0
Помогите разметку XAML для treeview сделать
    #39389774
vb_sub
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Arik,
тогда Вам будет неудобнее работать со всем WPF. Тем более что мешает преобразовать Dataset в структуру с коллекциями.
...
Рейтинг: 0 / 0
Помогите разметку XAML для treeview сделать
    #39389966
Arik
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
vb_sub,

Хорошо, допустим получили мы следующую структуру:
Код: vbnet
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
Public Class DisksAndFolders
    Public Property ID As Integer
    Public Property Name As String
    Public Property Type As FolderType
    Sub New(myID As Integer, myName As String, myType As FolderType)
        ID = myID
        Name = myName
        Type = myType

        FoldersList = New List(Of DisksAndFolders)
        FilesList = New List(Of Files)
    End Sub

    Public FoldersList As List(Of DisksAndFolders)
    Public FilesList As List(Of Files)
End Class

Public Class Files
    Public Property ID As Integer
    Public Property Name As String
    Sub New(myID As Integer, myName As String)
        ID = myID
        Name = myName
    End Sub
End Class

Public Enum FolderType
    Disk = 1
    Folder = 2
End Enum



Наполнили данными:
Код: vbnet
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
        Dim myList As New List(Of DisksAndFolders)

        Dim disk1 As New DisksAndFolders(1, "disk1", FolderType.Disk)
        Dim disk2 As New DisksAndFolders(2, "disk2", FolderType.Disk)

        Dim folder As New DisksAndFolders(1, "folder1", FolderType.Folder)
        disk1.FoldersList.Add(folder)

        folder = New DisksAndFolders(2, "folder2", FolderType.Folder)
        folder.FilesList.Add(New Files(1, "file1"))
        folder.FilesList.Add(New Files(2, "file2"))

        Dim subFolder As New DisksAndFolders(3, "folder3", FolderType.Folder)
        subFolder.FilesList.Add(New Files(3, "file3"))

        folder.FoldersList.Add(subFolder)

        disk1.FoldersList.Add(folder)

        myList.Add(disk1)
        myList.Add(disk2)

        treeTest.ItemsSource = myList



Какая должна быть XAML разметка для TreeView, что к чему привязывать?
...
Рейтинг: 0 / 0
Помогите разметку XAML для treeview сделать
    #39390205
Roman Mejtes
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Arik,

Самый простой вариант будет работать примерно так:

вы создаете базовый класс али FileSystemObject внутри которого есть свой коллекция Children, либо сам объект унаследован от List<FileSystemObject>, так вы быстро получите нужную иерархию объектов производных от FileSystemObject. В TreeView к свойству ItemsSource нужно будет биндить либо коллекцию таких объектов, либо сам объект (root).
Затем в <HierarchyDataTemplate> вы указываете в ItemsScoute через Binding список дочерних объектов. Это либо сам объект, если он унаследован от List<T>, либо свойство Children.

В TreeView, каждый элемент дерева является ItemsControl'ом, что позволяет добиться их иерархии относительно друг друга.
Виртуализация в базовом контроле работает нормально.
Есть более сложный путь, когда вы создаете ICollectionView на основе иерархии и поддержки основных интерфейсов для коллекций на обновление, получение представления, текущем элементе и прочим, но представляете её в виде списка для 1ого ItemsControl'а, то есть дерево будет в 1 ItemsControl'е, вертикальная виртуализация будет так же работать в ListBox и использовать можно будет в любом элементе управления унаследованном в ItemsControl.

Вывод:
a) в ViewModel должна быть иерархия базового класса для элементов TreeView

Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
class FileSystemObject
{
public string Name {get;}
public IList<FileSystemObject> Children { get; }
}

class MainViewModel
{
public IList<FileSystemObject> FileSystem { get; }
}



Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
class abstract FileSystemObject : List<FileSystemObject> 
{
public virtual string Name {get;}
}

class MainViewModel
{
public IList<FileSystemObject> FileSystem { get; }
}

это только пример, он не рабочий, нужна реализация.


б) в HierarchyDataTemplate нужно указать источник дочерних объектов
Код: xml
1.
2.
3.
4.
5.
6.
<Window.Resource>
<HierarchicalDataTemplate x:Key="{x:Type nmsspc:FileSystemObject}" 
DataType="{x:Type nmsspc:FileSystemObject}" ItemsSource="{Binding Children}">
<TextBlock Text="{Binding Name}"/>
</HierarchicalDataTemplate>
</Window.Resource>


Код: xml
1.
2.
3.
4.
5.
6.
<Window.Resource>
<HierarchicalDataTemplate  x:Key="{x:Type nmsspc:FileSystemObject}" 
DataType="{x:Type nmsspc:FileSystemObject}" ItemsSource="{Binding}">
<TextBlock Text="{Binding Name}"/>
</HierarchicalDataTemplate>
</Window.Resource>


в) для TreeView нужно в свойстве ItemsSource указать источник (коллекцию объектов 1ого уровня)
Код: xml
1.
2.
3.
<!--DataType:MainViewModel-->
<TreeView ItemsSource="{Binding FileSystem}" ItemTemplate="{StaticResource {x:Type nmsspc:FileSystemObject}}">
</TreeView>



Так как объекты производные от базового типа могут отображаться по разному, (такие как class FolderObject : FileSystemObject {} class FileObject : FileSystemObject {}). Можно использовать DataTemplateSelector для свойства ItemTemplateSelector. Тогда шаблон папки будет отличаться от шаблона файла. Так же в DataTemplate'ах работают триггеры и можно как угодно менять состояние объектов. Да и биндингу всё равно, но лучше делать каждый шаблон под отдельный тип.

p.s. код делал на коленке, приболел, нужна доп. реализация, копипаст работать 100% не будет.
...
Рейтинг: 0 / 0
Помогите разметку XAML для treeview сделать
    #39391469
Arik
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Roman Mejtes,

Спасибо! Натолкнул на мысль, что можно с DataTemplateSelector поиграть.

Накидал следующее:
C# DataSet
Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
DataTable dtHDD = new DataTable("Disks");
dtHDD.Columns.Add("ID");
dtHDD.Columns.Add("DiskName");

dtHDD.Rows.Add(1, "Disc1");
dtHDD.Rows.Add(2, "Disc2");

DataTable dtRoot = new DataTable("Root");
dtRoot.Columns.Add("ID");
dtRoot.Columns.Add("DiskID");
dtRoot.Columns.Add("FolderName");

dtRoot.Rows.Add(1, 2, "Music");
dtRoot.Rows.Add(2, 1, "Documents");
dtRoot.Rows.Add(7, 2, "Moves");

DataTable dtFolders = new DataTable("Folders");
dtFolders.Columns.Add("ID");
dtFolders.Columns.Add("FolderID");
dtFolders.Columns.Add("FolderName");

dtFolders.Rows.Add(3, 2, "Scatches");
dtFolders.Rows.Add(4, 3, "Images");
dtFolders.Rows.Add(5, 3, "Folder1");
dtFolders.Rows.Add(6, 2, "Bin");
dtFolders.Rows.Add(8, 5, "Another one");

DataTable dtFiles = new DataTable("Files");
dtFiles.Columns.Add("ID");
dtFiles.Columns.Add("FolderID");
dtFiles.Columns.Add("FileName");

dtFiles.Rows.Add(1, 8, "file1");
dtFiles.Rows.Add(2, 2, "file2");
dtFiles.Rows.Add(3, 3, "file3");
dtFiles.Rows.Add(4, 2, "file4");
dtFiles.Rows.Add(5, 5, "file5");
dtFiles.Rows.Add(6, 5, "file6");
dtFiles.Rows.Add(7, 8, "file7");
dtFiles.Rows.Add(8, 8, "file8");
dtFiles.Rows.Add(9, 1, "file9");

DataSet ds = new DataSet();
ds.Tables.Add(dtHDD);
ds.Tables.Add(dtRoot);
ds.Tables.Add(dtFolders);
ds.Tables.Add(dtFiles);
ds.Relations.Add("DiskRoot", ds.Tables("Disks").Columns("ID"), ds.Tables("Root").Columns("DiskID"));
ds.Relations.Add("RootFolder", ds.Tables("Root").Columns("ID"), ds.Tables("Folders").Columns("FolderID"), false);
ds.Relations.Add("FolderFolder", ds.Tables("Folders").Columns("ID"), ds.Tables("Folders").Columns("FolderID"), false);
ds.Relations.Add("RootFile", ds.Tables("Root").Columns("ID"), ds.Tables("Files").Columns("FolderID"), false);
ds.Relations.Add("FolderFile", ds.Tables("Folders").Columns("ID"), ds.Tables("Files").Columns("FolderID"), false);

treeTest.ItemsSource = ds.Tables("Disks").DefaultView;



DataTemplateSelector
Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
public class TemplateSelector : DataTemplateSelector
{

	public DataTemplate DiskTemplate { get; set; }
	public DataTemplate RootTemplate { get; set; }
	public DataTemplate FolderTemplate { get; set; }
	public DataTemplate FileTemplate { get; set; }

	public override DataTemplate SelectTemplate(object item, DependencyObject container)
	{

		dynamic drv = (System.Data.DataRowView)item;
		switch (drv.DataView.Table.TableName) {
			case "Disks":
				return DiskTemplate;
			case "Root":
				return RootTemplate;
			case "Folders":
				return FolderTemplate;
			case "Files":
				return FileTemplate;
		}

		return null;
	}
}



XAML
Код: xml
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
    <Window.Resources>
        <HierarchicalDataTemplate x:Key="diskTemplate" ItemsSource="{Binding DiskRoot}">
            <TextBlock Text="{Binding DiskName}"/>
        </HierarchicalDataTemplate>

        <HierarchicalDataTemplate x:Key="rootTemplate" ItemsSource="{Binding RootFolder}">
            <TextBlock Text="{Binding FolderName}"/>
        </HierarchicalDataTemplate>

        <HierarchicalDataTemplate x:Key="folderTemplate" ItemsSource="{Binding FolderFolder}">
            <TextBlock Text="{Binding FolderName}"/>
        </HierarchicalDataTemplate>

        <DataTemplate x:Key="fileTemplate">
            <TextBlock Text="{Binding FileName}"/>
        </DataTemplate>

        <local:TemplateSelector x:Key="TemplateSelector"
                            DiskTemplate="{StaticResource diskTemplate}"
                            RootTemplate="{StaticResource rootTemplate}"
                            FolderTemplate="{StaticResource folderTemplate}"
                            FileTemplate="{StaticResource fileTemplate}"/>
    </Window.Resources>
    <Grid>
        <TreeView x:Name="treeTest" ItemTemplateSelector="{StaticResource TemplateSelector}" />
    </Grid>



Все замечательно, только не могу понять почему DataTemplate не отображает листья дерева.
...
Рейтинг: 0 / 0
Помогите разметку XAML для treeview сделать
    #39391475
Arik
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Пример с файловой системой не совсем удачный, но структура именно такая и 4 разных DataTemplate будет использоваться
...
Рейтинг: 0 / 0
Помогите разметку XAML для treeview сделать
    #39391691
Arik
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Очень не хотелось переходить на коллекции... В общем перделал проект, все работает на ура, спасибо!


Теперь появился вопрос как отследить какой из элементовы выбрал пользователь.
...
Рейтинг: 0 / 0
Помогите разметку XAML для treeview сделать
    #39393560
Rocketeer88888
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
...
Рейтинг: 0 / 0
Помогите разметку XAML для treeview сделать
    #39393572
Rocketeer88888
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Rocketeer88888Читали эту статью? https://www.codeproject.com/Articles/26288/Simplifying-the-WPF-TreeView-by-Using-the-ViewMode
Там в том числе говориться и о том, как отследить выбранный элемент. Пользователь может выбрать элемент и код будет об этом знать. А можно в коде выбрать элемент, и пользователь увидит разворот выбранного элемента меню.
...
Рейтинг: 0 / 0
Помогите разметку XAML для treeview сделать
    #39393640
Roman Mejtes
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Rocketeer88888Rocketeer88888Читали эту статью? https://www.codeproject.com/Articles/26288/Simplifying-the-WPF-TreeView-by-Using-the-ViewMode
Там в том числе говориться и о том, как отследить выбранный элемент. Пользователь может выбрать элемент и код будет об этом знать. А можно в коде выбрать элемент, и пользователь увидит разворот выбранного элемента меню.
у всех этой идеи есть некоторые недостатки, я бы даже сказал баги.
На счет TreeView я сейчас не скажу, врать не буду, на сколько я помню выделенный элемент в нём не виртуализируется, но точно я уже не помню.
Я уже описывал ситуацию с таким подходом, вот просто пример:
1) В ListBox делает такой же стиль для элементов с Binding'ом IsSelected к модели элементов.
2) Выделяем элемент А
3) Через ICollectionView отфильтровываем элемент
4) Выделяем элемент Б

Так как в момент выделения элемента А не существовало (во View) то и свойство IsSelected не изменилось, в результате мы имеем 2 выделенных элемента, хотя выбрали SingleSelection и так косячат абсолютно все ItemsControl'ы, коим является TreeView.

Было бы на много разумнее весь код отвечающий за выделение элементов перенести в ICollectionView и контролировать его там, тогда бы мы имели возможность управлять выделением и из View и из ViewModel.
...
Рейтинг: 0 / 0
Помогите разметку XAML для treeview сделать
    #39393744
Rocketeer88888
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Roman MejtesЯ уже описывал ситуацию с таким подходом, вот просто пример:
1) В ListBox делает такой же стиль для элементов с Binding'ом IsSelected к модели элементов.
2) Выделяем элемент А
3) Через ICollectionView отфильтровываем элемент
4) Выделяем элемент Б

Так как в момент выделения элемента А не существовало (во View) то и свойство IsSelected не изменилось, в результате мы имеем 2 выделенных элемента, хотя выбрали SingleSelection и так косячат абсолютно все ItemsControl'ы, коим является TreeView.

Было бы на много разумнее весь код отвечающий за выделение элементов перенести в ICollectionView и контролировать его там, тогда бы мы имели возможность управлять выделением и из View и из ViewModel.
Ничего не понял. У меня были простые тривью, выполненные по подходу в статье, но двух выделенных у меня не было. Какой-то специальный случай?
...
Рейтинг: 0 / 0
13 сообщений из 13, страница 1 из 1
Форумы / WPF, Silverlight [игнор отключен] [закрыт для гостей] / Помогите разметку XAML для treeview сделать
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]