В прошлой статье мы сделали иерархический элемент TreeView наполненный разными объектами. В этой статье мы увидим, как перемещаться по различным пользовательским элементам управления, отображая их данные в ContentControl.
У нас есть стандартное приложение с иерархическим списком объектов. В нашем случае это Города, Улицы и дома. При выборе города должны отобразится свойства этого города. Если же мы перейдем на другой объект, будь то другой город, улица или дом, то содержимое должно замениться на соответствующие текущему объекту.
Макет главного окна имеет TreeView с левой стороны дескриптора и ContentControl с правой стороны для отображения соответствующего объекта.
- Шаг 1 Создание ExtendedTreeView. Модификация стандартного TreeView, для получения выделенного объекта.
- Шаг 2 Создадим ViewModel для объектов:
- Шаг 3 добавим UserControls для наших объектов:
- Шаг 4 Создание DataTemplate в файле MainWindow.xaml
- Шаг 5 Отредактируем файл MainWindow.Xaml
- Шаг 6 Добавление функционала в MainWindowViewModel.cs
Шаг 1 Создание ExtendedTreeView. Модификация стандартного TreeView, для получения выделенного объекта.
- Создадим новый класс и назовем его ExtendedTreeView, который будет наследоваться от TreeView.
- Содержимое класса
1234567891011121314151617181920212223public class ExtendedTreeView : TreeView{public ExtendedTreeView(): base(){this.SelectedItemChanged += new RoutedPropertyChangedEventHandler<object>(___ICH);}void ___ICH(object sender, RoutedPropertyChangedEventArgs<object> e){if (SelectedItem != null){SetValue(SelectedItem_Property, SelectedItem);}}public object SelectedItem_{get { return (object)GetValue(SelectedItem_Property); }set { SetValue(SelectedItem_Property, value); }}public static readonly DependencyProperty SelectedItem_Property = DependencyProperty.Register("SelectedItem_", typeof(object), typeof(ExtendedTreeView), new UIPropertyMetadata(null));} - В файл MainWindowViewModel.cs добавим новое свойство — Item (object). В это свойство будет передаваться выбранный объект.
123456private itempublic object Item{get { return _item; }set { _item = value; OnPropertyChanged("Item"); }}
- Заменим стандартный элемент TreeView в MainWindowView.xaml. И забиндим поле Item в наш добавленный параметр SelectedItem_
123<local:ExtendedTreeView HorizontalAlignment="Stretch" Margin="10" VerticalAlignment="Stretch"ItemsSource="{Binding CityList}"SelectedItem_="{Binding Path=Item, Mode=TwoWay}">
Шаг 2 Создадим ViewModel для объектов:
- Создадим в проекте папку ViewModels.
- Добавим в папку новый класс CityViewModel
- Укажем поле SelectedCity — Выбранный город
- Конструктор принимает объект типа City и сохраняет его в поле SelectedCity
- Код класса
123456789101112131415using TreeViewMVVMExample.Models;namespace TreeViewMVVMExample.ViewModels{public class CityViewModel : ViewModelBase{private City _city;public City SelectedCity { get { return _city; } set { _city = value; OnPropertyChanged("City"); } }public CityViewModel(City city){_city = city;}}}
- Добавим новый класс StreetViewModel
- Укажем поле StreetName — Наименование улицы
- Конструктор принимает объект типа Street и сохраняет его в поле StreetName
- Код класса
1234567891011121314using TreeViewMVVMExample.Models;namespace TreeViewMVVMExample.ViewModels{public class StreetViewModel:ViewModelBase{private Street _selectedStreet;public Street SelectedStreet { get { return _selectedStreet; } set { _selectedStreet = value; OnPropertyChanged("SelectedStreet"); } }public StreetViewModel(Street street){_selectedStreet = street;}}}
- Добавим новый класс HouseViewModel
- Укажем поле HouseName — Номер дома
- Конструктор принимает объект типа House и сохраняет его в поле StreetName
- Код класса
1234567891011121314using TreeViewMVVMExample.Models;namespace TreeViewMVVMExample.ViewModels{public class HouseViewModel:ViewModelBase{private House _selectedHouse;public House SelectedHouse { get { return _selectedHouse; } set { _selectedHouse= value; OnPropertyChanged("HouseNumber"); } }public HouseViewModel(House house){_selectedHouse = house;}}}
Шаг 3 добавим UserControls для наших объектов:
- Создадим папку Views
- Добавим новый пользовательский элемент управления в папку Views. CityView.xaml
12345678910111213141516<UserControl x:Class="TreeViewMVVMExample.Views.CityView"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"xmlns:local="clr-namespace:TreeViewMVVMExample.Views"mc:Ignorable="d"d:DesignHeight="450" d:DesignWidth="800"><Grid><Border CornerRadius="20" BorderBrush="Black" BorderThickness="1" Background="Red"/><StackPanel Margin="20" Background="Gray"><TextBlock Text="Это пример для города" Foreground="#FFD41A1A" FontSize="20" FontStyle="Oblique" FontFamily="Blackadder ITC"/><TextBlock Text="{Binding SelectedCity.CityName }" FontSize="20" Margin="10" HorizontalAlignment="Center"/></StackPanel></Grid></UserControl> - Так же нужно создать пользовательские элементы для других объектов. Вот код для StreetView.xaml
123456789101112131415<UserControl x:Class="TreeViewMVVMExample.Views.StreetView"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"xmlns:local="clr-namespace:TreeViewMVVMExample.Views"mc:Ignorable="d"d:DesignHeight="450" d:DesignWidth="800"><Grid><StackPanel Margin="20" Background="Bisque"><TextBlock Text="Это пример для Улицы" Foreground="#FFD41A1A" FontSize="16" /><TextBlock Text="{Binding SStreet.StreetName}" FontSize="20" Margin="10" HorizontalAlignment="Center"/></StackPanel></Grid></UserControl>
- Код для HouseView.xaml
123456789101112131415<UserControl x:Class="TreeViewMVVMExample.Views.HouseView"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"xmlns:local="clr-namespace:TreeViewMVVMExample.Views"mc:Ignorable="d"d:DesignHeight="450" d:DesignWidth="800"><Grid><StackPanel Margin="20" Background="CadetBlue"><TextBlock Text="Это пример для Дома" Foreground="#FFD41A1A" FontSize="16" /><TextBlock Text="{Binding SHouse.HouseNumber}" FontSize="20" Margin="10" HorizontalAlignment="Center"/></StackPanel></Grid></UserControl>
Шаг 4 Создание DataTemplate в файле MainWindow.xaml
- Создайте DataTemplate типа CityViewModel, который должен содержать в себе элемент управления CityView.
123<DataTemplate DataType="{x:Type viewmodel:CityViewModel}"><view:CityView/></DataTemplate>
- Аналогично нужно добавить DataTemplate для типа StreetViewModel, которой содержит в себе элемент управления StreetView
123<DataTemplate DataType="{x:Type viewmodel:StreetViewModel}"><view:StreetView/></DataTemplate>
- И для типа HouseViewModel, с элементом управления HouseView
123<DataTemplate DataType="{x:Type viewmodel:HouseViewModel}"><view:HouseView/></DataTemplate>
Шаг 5 Отредактируем файл MainWindow.Xaml
- Подключите ссылки на папки viewmodel и view.
12xmlns:viewmodel="clr-namespace:TreeViewMVVMExample.ViewModels"xmlns:view="clr-namespace:TreeViewMVVMExample.Views" - Добавьте вышеуказанные Шаблоны данных(DataTemplate) в качестве ресурсов MainWindow.
1234567891011<Window.Resources><DataTemplate DataType="{x:Type viewmodel:CityViewModel}"><view:CityView/></DataTemplate><DataTemplate DataType="{x:Type viewmodel:StreetViewModel}"><view:StreetView/></DataTemplate><DataTemplate DataType="{x:Type viewmodel:HouseViewModel}"><view:HouseView/></DataTemplate></Window.Resources>
- Добавьте ContentControl, который будет действовать как контейнер, и после завершения навигации соответствующий UserControl будет помещен в этот ContentControl.
123<StackPanel Grid.Column="1"><ContentControl x:Name="Pages" Content="{Binding SelectedViewModel}"/></StackPanel>
- Код класса
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950<Window x:Class="TreeViewMVVMExample.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"xmlns:local="clr-namespace:TreeViewMVVMExample"xmlns:viewmodel="clr-namespace:TreeViewMVVMExample.ViewModels"xmlns:view="clr-namespace:TreeViewMVVMExample.Views"mc:Ignorable="d"Title="MainWindow" Height="450" Width="800"><Window.Resources><DataTemplate DataType="{x:Type viewmodel:CityViewModel}"><view:CityView/></DataTemplate><DataTemplate DataType="{x:Type viewmodel:StreetViewModel}"><view:StreetView/></DataTemplate><DataTemplate DataType="{x:Type viewmodel:HouseViewModel}"><view:HouseView/></DataTemplate></Window.Resources><Grid><Grid.ColumnDefinitions><ColumnDefinition/><ColumnDefinition/></Grid.ColumnDefinitions><local:ExtendedTreeView HorizontalAlignment="Stretch" Margin="10" VerticalAlignment="Stretch"ItemsSource="{Binding CityList}"SelectedItem_="{Binding Path=Item, Mode=TwoWay}"><TreeView.ItemTemplate><HierarchicalDataTemplate ItemsSource="{Binding StreetList}"><Label Content="{Binding CityName}"/><HierarchicalDataTemplate.ItemTemplate><HierarchicalDataTemplate ItemsSource="{Binding Houses}"><Label Content="{Binding StreetName}"/><HierarchicalDataTemplate.ItemTemplate><DataTemplate><Label Content="{Binding HouseNumber}"/></DataTemplate></HierarchicalDataTemplate.ItemTemplate></HierarchicalDataTemplate></HierarchicalDataTemplate.ItemTemplate></HierarchicalDataTemplate></TreeView.ItemTemplate></local:ExtendedTreeView><StackPanel Grid.Column="1"><ContentControl x:Name="Pages" Content="{Binding SelectedViewModel}"/></StackPanel></Grid></Window>
Шаг 6 Добавление функционала в MainWindowViewModel.cs
- Добавим поле SelectedViewModel для отображения
- Создадим метод для навигации по моделям.
123456789private void openViewModel(object obj){if (obj.GetType() == typeof(City))SelectedViewModel = new CityViewModel((City)obj);else if (obj.GetType() == typeof(Street))SelectedViewModel = new StreetViewModel((Street)obj);else if (obj.GetType() == typeof(House))SelectedViewModel = new HouseViewModel((House)obj);}
- При изменении поля Item, т.е при выборе элемента из TreeView, будет вызываться метод, который загрузит в ContentControl соответствующий элемент управления
12345set {_item = value;OnPropertyChanged("Item");openViewModel(value);}
- Код класса MainWindowViewModel.cs
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950using System;using System.Collections.Generic;using System.Text;using TreeViewMVVMExample.Models;using System.Diagnostics;namespace TreeViewMVVMExample.ViewModels{public class MainWindowViewModel : ViewModelBase{private List<City> cityList;private object _item;private object selectedViewModel;public object SelectedViewModel{get { return selectedViewModel; }set { selectedViewModel = value; OnPropertyChanged("SelectedViewModel"); }}public object Item{get { return _item; }set { _item = value; OnPropertyChanged("Item"); openViewModel(value); }}public List<City> CityList{get { return cityList; }set { cityList = value; OnPropertyChanged("CityList"); }}public MainWindowViewModel(){cityList = new List<City>(){new City("Город 1"),new City("Город 2"),new City("Город 3"),new City("Город 4"),};}private void openViewModel(object obj){if (obj.GetType() == typeof(City))SelectedViewModel = new CityViewModel((City)obj);else if (obj.GetType() == typeof(Street))SelectedViewModel = new StreetViewModel((Street)obj);else if (obj.GetType() == typeof(House))SelectedViewModel = new HouseViewModel((House)obj);}}}
При нажатии кнопки employee свойству SelectedViewModel присваивается EmployeeViewModel, и, следовательно, в ContentControl содержимому будет присвоен DataTemplate объекта DataType EmployeeViewModel, а EmployeeView будет загружен в ContentControl.Ну вот. Мы выполнили простую навигацию в приложении WPF с помощью MVVM.