#019 Введение в возможности 3D на WPF

Автор: Topol Воскресенье, Май 6th, 2012 Нет комментариев

Рубрика: Операционные системы

Windows Presentation Foundation предлагает действительно широкие возможности в сфере отображения контента — будь то растровая графика, векторная анимация, видео или 3D. Именно о трехмерной графике мы и начнем говорить в сегодняшней статье.

Этот материал можно считать кратким вступлением в технологии трехмерной графики в WPF .

Создайте новый проект типа «WinFX Windows Application» и задайте ему имя «my3Dtest». Внесем небольшие изменения в свойства окна для того, чтобы оно больше соответствовало отображаемому контенту:

Код:
<Window x:Class=»Window1″
xmlns=»http://schemas.microsoft.com/winfx/2006/xaml/presentation»
xmlns:x=»http://schemas.microsoft.com/winfx/2006/xaml»
Title=»Возможности 3D в WPF»
Height=»600″ Width=»800″ MinHeight =»480″ MinWidth =»640″
>

<Window.Background >
<LinearGradientBrush >
<LinearGradientBrush.RelativeTransform >
<RotateTransform Angle =»90″/>
</LinearGradientBrush.RelativeTransform>
<LinearGradientBrush.GradientStops >
<GradientStop Offset =»0″ Color =»Gold»/>
<GradientStop Offset =»1″ Color =»White»/>
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</Window.Background>

<Grid>

</Grid>
</Window>

Здесь мы задали новые значения для размеров формы, а также определили градиентную заливку для фона окна (Window.Background). Обратите внимание как можно изменить заливку, просто развернув ее на 90 градусов (LinearGradientBrush.RelativeTransform).

Внимание — материал данной статьи повышенной сложности. Если вы не изучали предыдущие статьи, возможно у Вас не получится выполнить задание этого проекта!

Давайте определимся с тем, что мы реализуем в данном проекте. Мы разделим контейнер Grid на две строки — в верхней будет находиться трехмерная сцена, а в нижней элемент ListBox:

В ListBox мы будем загружать изображения автоматически из некой папки (допустим C:\Images) а в трехмерной сцене будет отображаться выбранное изображение в трех экземплярах с эффектом отражения.

Давайте организуем нужную разметку контейнера Grid и сразу поместим во вторую строку ListBox. Трехмерной сценой мы займемся позже:

Код:
<Grid>
<Grid.RowDefinitions >
<RowDefinition />
<RowDefinition Height =»Auto»/>
</Grid.RowDefinitions>

<ListBox Grid.Row =»1″/>

</Grid>

Разбивку контейнера Grid на строки и столбцы, а также размещение элемента в нужной строке или столбце мы подробно рассматривали в статье №017, поэтому я не буду комментировать этот блок кода.

Мы уже рассматривали размещение изображений в качестве контента для ListBox, однако сегодня перед нами стоит более сложная задача. Источником данных для списка будет папка C:\Images. Перед выполнением дальнейших инструкций создайте такую папку и разместите в ней несколько изображений в формате JPG.

Выберите команду меню Visual Studio «Project->Add Class» и в появившемся окне задайте имя «images.vb». Замените содержимое этого класса на следующее:

Код:
Imports System
Imports System.Collections.ObjectModel
Imports System.IO
Imports System.Windows.Media.Imaging

Public Class Photo
Public Sub New(ByVal path As String)
_source = path
End Sub

Public Overrides Function ToString() As String
Return Source
End Function

Private _source As String
Public ReadOnly Property Source() As String
Get
Return _source
End Get
End Property
End Class

Public Class PhotoList
Inherits ObservableCollection(Of Photo)
Public Sub New()
End Sub

Public Sub New(ByVal path As String)
Me.New(New DirectoryInfo(path))
End Sub

Public Sub New(ByVal directory As DirectoryInfo)
_directory = directory
Update()
End Sub

Public Property Path() As String
Get
Return _directory.FullName
End Get
Set(ByVal Value As String)
_directory = New DirectoryInfo(Value)
Update()
End Set
End Property

Public Property Directory() As DirectoryInfo
Get
Return _directory
End Get
Set(ByVal Value As DirectoryInfo)
_directory = Value
Update()
End Set
End Property
Private Sub Update()
Clear()
Dim f As FileInfo
For Each f In _directory.GetFiles(«*.jpg»)
Add(New Photo(f.FullName))
Next
End Sub
Dim _directory As DirectoryInfo
End Class

Примеры я привожу на базе Visual Basic. Код достаточно прост и, при необходимости, его легко транслировать на C# если есть такая необходимость. Здесь мы создали два класса — один с именем Photo — содержит описание файла для отображения, а второй с именем PhotoList — представляет собой коллекцию элементов типа Photo. Пожалуйста обратите внимание на этот код, так как этот метод является базовым для выполнения задач, сходных с нашей. Вы можете в последствии использовать его как базис для собственного приложения!

При помощи команды «Project — > Add new Item…» добавьте к проекту новый файл типа «WinFX ResourceDictionary» и задайте ему имя «MyStyles.xaml». Перейдите в режим редактирования XAML-кода только что созданного файла и внесите такой код:

Код:
<Style TargetType=»{x:Type ListBox}»>
<Setter Property=»Foreground» Value=»White» />
<Setter Property=»Template»>
<Setter.Value>
<ControlTemplate TargetType=»{x:Type ListBox}» >
<Border
Width=»120″
Padding=»5″
BorderThickness=»0.5″
CornerRadius=»6″
VerticalAlignment=»Center»
HorizontalAlignment=»Center»
>

<Border.BorderBrush>
<LinearGradientBrush StartPoint=»0,0″ EndPoint=»0,1″>
<LinearGradientBrush.GradientStops>
<GradientStop Offset=»0.0″ Color=»#88000000″ />
<GradientStop Offset=»1.0″ Color=»#DDFFFFFF» />
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</Border.BorderBrush>
<Border.Background>
<LinearGradientBrush StartPoint=»0,0″ EndPoint=»0,1″>
<LinearGradientBrush.GradientStops>
<GradientStop Offset=»0.0″ Color=»#CCFFFFFF» />
<GradientStop Offset=»1.0″ Color=»#55FFFFFF» />
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</Border.Background>
<ScrollViewer VerticalScrollBarVisibility=»Auto»>

<StackPanel
IsItemsHost=»true»
Orientation=»Vertical»
VerticalAlignment=»Center»
HorizontalAlignment=»Center»
/>

</ScrollViewer>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

<Style x:Key=»{x:Type ListBoxItem}» TargetType=»{x:Type ListBoxItem}»>

<Setter Property=»FocusVisualStyle» Value=»{x:Null}» />
<Setter Property=»Opacity» Value=»0.5″ />
<Setter Property=»MaxWidth» Value=»75″ />
<Setter Property=»Template»>
<Setter.Value>
<ControlTemplate TargetType=»{x:Type ListBoxItem}»>
<Border
x:Name=»ContentBorder»
BorderThickness=»1″
BorderBrush=»Transparent»>
<ContentPresenter />
</Border>
<ControlTemplate.Triggers>
<Trigger Property=»IsSelected» Value=»True»>
<Setter Property=»Opacity» Value=»1.0″ />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>

<Style.Triggers>

<EventTrigger RoutedEvent=»Mouse.MouseEnter»>
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Duration=»0:0:0.2″
Storyboard.TargetProperty=»MaxWidth»
To=»90″  />
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>

<EventTrigger RoutedEvent=»Mouse.MouseLeave»>
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Duration=»0:0:1″
Storyboard.TargetProperty=»MaxWidth» To =»75″  />
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>

</Style.Triggers>

</Style>

Так же необходимо подключить этот словарь ресурсов к нашему окну Window1:

Код:
<Window.Resources>

<ResourceDictionary >
<ResourceDictionary.MergedDictionaries >
<ResourceDictionary Source =»MyStyles.xaml»/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>

</Window.Resources>

Использование стилей и словарей стилей рассматривалось в статье №013. Просмотрите эту статью, если у вас возникли трудности в выполнении этой части проекта.
Теперь необходимо добавить шаблоны данных (с этим понятием мы знакомились в статье №017) в секцию Window.Resources внутрь блока ResourceDictionary:

Код:
<Window.Resources>

<ResourceDictionary >
<ResourceDictionary.MergedDictionaries >
<ResourceDictionary Source =»MyStyles.xaml»/>
</ResourceDictionary.MergedDictionaries>

<ObjectDataProvider
x:Name=»PhotosODP»
x:Key=»Photos»
ObjectType=»{x:Type ps:PhotoList}»/>

<DataTemplate DataType=»{x:Type ps:Photo}»>
<Border
VerticalAlignment=»Center»
HorizontalAlignment=»Center»
Padding=»4″
Margin=»2″
Background=»White»>
<Image Source=»{Binding Source}» />
</Border>
</DataTemplate>

</ResourceDictionary>

</Window.Resources>

Необходимо отредактировать описание окна Window1. Приведите код в соответствии с указанным ниже:

Код:
<Window x:Class=»Window1″
xmlns=»http://schemas.microsoft.com/winfx/2006/xaml/presentation»
xmlns:x=»http://schemas.microsoft.com/winfx/2006/xaml»
Title=»Возможности 3D в WPF»
Height=»600″ Width=»800″ MinHeight =»480″ MinWidth =»640″
xmlns:ps=»clr-namespace:my3Dtest»
Loaded=»WindowLoaded»
>

…дальнейший код…
</Window>

Дело в том, что созданные недавно шаблоны данных ссылаются на классы, определенные в файле images.vb и для того, чтобы эти классы были «видны» в XAML-коде мы создали ссылку с именем «ps» указывающим на пространство имен нашего приложения. В дальнейшем мы использовали эту ссылку для обозначения наших шаблонов данных — x:Type ps:Photo и т.д.
Также мы определили процедуру WindowLoaded которая будет выполняться при загрузке окна. Давайте внесем необходимый код. Перейдите в режим редактирования VB-кода окна и внесите следующие изменения:

Код:
Partial Public Class Window1
Inherits Window

Dim CurrentDir As String = vbNullString
Dim Photos As PhotoList

Public Sub New()
InitializeComponent()
End Sub

Private Sub WindowLoaded(ByVal sender As Object, _
ByVal e As System.Windows.RoutedEventArgs) _
Handles Me.Loaded

Photos = CType((CType(Me.Resources(«Photos»), _
ObjectDataProvider)).Data, PhotoList)

CurrentDir = «C:\Images»
Photos.Path = CurrentDir
End Sub

Что делает этот код? Мы создали две переменные — CurrentDir для хранения адреса той папки, из которой будут загружаться изображения и Photos — образец класса PhotoList для хранения изображений. В методе WindowLoaded мы присваеваем созданной переменной Photos значение того шаблона данных, который мы определили в XAML-коде. Именно так мы смогли объединить данные определенные в xaml и код visual basic. После этого мы передаем шаблону данных адрес нашей папки — C:\Images

Все, что нам осталось сделать — это подключить созданные шаблоны данных к списку ListBox:

Код:
<ListBox Grid.Row =»1″
Name=»PhotosListBox»
DataContext=»{Binding Source={StaticResource Photos}}»
ItemsSource=»{Binding }»/>

Давайте запустим проект и посмотрим на результат:

Мы добились желаемого — ListBox отображает содержимое папки C:\Images.! Так же при помощи стилей мы придали нашему списку необычный вид и необычное поведение. Обратите внимание, как плавно и красиво двигаются элементы списка при наведении мыши! Единственное, что мы недоглядели — это ориентация списка. В данный момент она вертикальная, а мы задумывали ее как горизонтальную. Давайте это исправим.

Внесите небольшое изменение в код стиля списка в соответствии с листингом:

Код:
<Style TargetType=»{x:Type ListBox}»>
<Setter Property=»Foreground» Value=»White» />
<Setter Property=»Template»>
<Setter.Value>
<ControlTemplate TargetType=»{x:Type ListBox}» >
<Border
Height=»120″
Padding=»5″
BorderThickness=»0.5″
CornerRadius=»6″
VerticalAlignment=»Center»
HorizontalAlignment=»Center»
>

<Border.BorderBrush>
<LinearGradientBrush StartPoint=»0,0″ EndPoint=»0,1″>
<LinearGradientBrush.GradientStops>
<GradientStop Offset=»0.0″ Color=»#88000000″ />
<GradientStop Offset=»1.0″ Color=»#DDFFFFFF» />
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</Border.BorderBrush>
<Border.Background>
<LinearGradientBrush StartPoint=»0,0″ EndPoint=»0,1″>
<LinearGradientBrush.GradientStops>
<GradientStop Offset=»0.0″ Color=»#CCFFFFFF» />
<GradientStop Offset=»1.0″ Color=»#55FFFFFF» />
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</Border.Background>
<ScrollViewer VerticalScrollBarVisibility=»Auto»>

<StackPanel
IsItemsHost=»true»
Orientation=»Horizontal»
VerticalAlignment=»Center»
HorizontalAlignment=»Center»
/>

</ScrollViewer>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

<Style x:Key=»{x:Type ListBoxItem}» TargetType=»{x:Type ListBoxItem}»>

<Setter Property=»FocusVisualStyle» Value=»{x:Null}» />
<Setter Property=»Opacity» Value=»0.5″ />
<Setter Property=»MaxHeight» Value=»75″ />
<Setter Property=»Template»>
<Setter.Value>
<ControlTemplate TargetType=»{x:Type ListBoxItem}»>
<Border
x:Name=»ContentBorder»
BorderThickness=»1″
BorderBrush=»Transparent»>
<ContentPresenter />
</Border>
<ControlTemplate.Triggers>
<Trigger Property=»IsSelected» Value=»True»>
<Setter Property=»Opacity» Value=»1.0″ />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>

<Style.Triggers>

<EventTrigger RoutedEvent=»Mouse.MouseEnter»>
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Duration=»0:0:0.2″
Storyboard.TargetProperty=»MaxHeight»
To=»90″  />
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>

<EventTrigger RoutedEvent=»Mouse.MouseLeave»>
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Duration=»0:0:1″
Storyboard.TargetProperty=»MaxHeight» To =»75″  />
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>

</Style.Triggers>

</Style>

Давайте запустим проект:

Итак, мы закончили выполнение первой части нашего задания. Давайте перейдем к созданию трехмерной сцены в первой строке нашего контейнера Grid. Как видно из схемы проекта, который я привел в начале статьи, нам необходимо создать три фигуры в трехмерном пространстве. Давайте определим эти три фигуры. Внесите нижеизложенный код в секцию Window.Resources в подсекцию ResourceDictionary после определения шаблонов для списка:

Код:
<Window.Resources>
<ResourceDictionary >

…код относящийся к ListBox…

<MeshGeometry3D  x:Key=»Mesh»
Positions=»-0.52,1.2,0 0.44,1,0 0.5,0,0 -0.5,0.2,0 0.5,-1,0.2 -0.5,-.7,0.2″
Normals=»0,0,1 0,0,1 0,0,1 0,0,1 0,0,1 0,0,1″
TextureCoordinates=»0,0 1,0 1,1 0,1 1,0 0,0″
TriangleIndices=»1 0 3 2 1 3 4 3 5 4 2 3″ />

<MeshGeometry3D  x:Key=»Mesh1″
Positions=»-0.485,1,0 0.485,1,0 0.5,0,0 -0.5,0,0 0.5,-1,0 -0.5,-1,0 »
Normals=»0,0,1 0,0,1 0,0,1 0,0,1 0,0,1 0,0,1″
TextureCoordinates=»0,0 1,0 1,1 0,1 1,0 0,0″
TriangleIndices=»1 0 3 2 1 3 4 3 5 4 2 3″ />

<MeshGeometry3D  x:Key=»Mesh2″
Positions=»-0.44,01,0 0.52,1.2,0 0.5,0.2,0 -0.5,0,0 0.5,-1,0 -0.5,-1.2,0 »
Normals=»0,0,1 0,0,1 0,0,1 0,0,1 0,0,1 0,0,1″
TextureCoordinates=»0,0 1,0 1,1 0,1 1,0 0,0″
TriangleIndices=»1 0 3 2 1 3 4 3 5 4 2 3″ />

</ResourceDictionary>

</Window.Resources>

С помощью элемента MeshGeometry3D мы создали три фигуры с именами Mesh, Mesh1 и Mesh2. Обратите внимание на то, как много параметров приходится определять, чтобы построить трехмерную фигуру. При этом все координаты указываются не в двух, а в трех измерениях — по осям X, Y и Z. Пусть на данный момент вас не беспокоит вопрос о том, как определить нужные координаты, так как целью сегодняшнего введения в 3D стоит изучение концепции внедрения трехмерных объектов, а не их определение. В будущем мы обязательно рассмотрим средства визуального моделирования трехмерных фигур.

Для отображения трехмерного контента чаще всего используется специальный контейнер Viewport3D. Именно его мы разместим в верхней строке нашего Grid для отображения этих трех фигур. Для удобства мы вложим наш Viewport3D в еще один Grid. Теперь давайте определим еще один шаблон, который будет отображать наше изображение с эффектом отражения. По аналогии с предыдущими шаблонами внесите нижеизложенный код в секцию Window.Resources в подсекцию ResourceDictionary после определения шаблонов для списка:

Код:
<DataTemplate x:Key=»ReflectedPhoto» >
<Grid HorizontalAlignment=»Center»>
<Grid.RowDefinitions>
<RowDefinition Height=»180″ />
<RowDefinition />
</Grid.RowDefinitions>

<!— reflection—>
<Border
Grid.Row=»1″
VerticalAlignment=»Top»
Width=»{Binding ElementName=MainBorder, Path=ActualWidth}»
Background=»White»
BorderBrush=»#EEEEEE»
BorderThickness=»1″>
<Border.LayoutTransform>
<ScaleTransform ScaleX=»1″ ScaleY=»-0.5″/>
</Border.LayoutTransform>
<Border.OpacityMask>
<LinearGradientBrush StartPoint=»0,0″ EndPoint=»0,1″>
<LinearGradientBrush.GradientStops>
<GradientStop Offset=»0.0″ Color=»Transparent» />
<GradientStop Offset=»1.0″ Color=»#50000000″ />
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</Border.OpacityMask>
<Image
Margin=»7″
Width=»{Binding ElementName=MainImage, Path=ActualWidth}»
Source=»{Binding Source}»/>
</Border>

<!— shadow —>
<Rectangle
Grid.Row=»0″
VerticalAlignment=»Bottom»
HorizontalAlignment=»Center»
Width=»{Binding ElementName=MainBorder, Path=ActualWidth}»
Height=»30″>
<Rectangle.RenderTransform>
<TranslateTransform X=»0″ Y=»15″ />
</Rectangle.RenderTransform>
<Rectangle.Fill>
<RadialGradientBrush>
<RadialGradientBrush.GradientStops>
<GradientStop Offset=»0.0″ Color=»#B0000000″ />
<GradientStop Offset=»1.0″ Color=»Transparent» />
</RadialGradientBrush.GradientStops>
</RadialGradientBrush>
</Rectangle.Fill>
</Rectangle>
<!— main image —>
<Border
Grid.Row=»0″
x:Name=»MainBorder»
Background=»White»
BorderBrush=»#DDDDDD»
BorderThickness=»1″
HorizontalAlignment=»Center»
VerticalAlignment=»Bottom»  >
<Image
Margin=»7″
x:Name=»MainImage»
Source=»{Binding Source}» />
</Border>
</Grid>

</DataTemplate>

Этот шаблон определяет внешний вид изображения с эффектом отражения — именно таким шаблоном мы зальем все три фигуры нашей трехмерной сцены.

Внесите следующий блок кода внутрь контейнера Grid сразу после определения списка ListBox:

Код:
<Grid Grid.Row =»0″ Background=»Black» Height=»600″ Width=»800″>
<Viewport3D  ClipToBounds=»false» Width=»400″ Margin=»10″>
<Viewport3D.Camera>
<PerspectiveCamera FarPlaneDistance=»20″ LookDirection=»0,-2,-15″
UpDirection=»0,1,0″ NearPlaneDistance=»1″
Position=»0,1,4″ FieldOfView=»45″ />
</Viewport3D.Camera>
<Viewport3D.Children>
<ModelVisual3D>
<ModelVisual3D.Content>
<Model3DGroup>
<Model3DGroup.Children>
<DirectionalLight Color=»#FFFFFFFF» Direction=»2,-4,-5″ />
<GeometryModel3D Geometry=»{StaticResource Mesh1}»>
<GeometryModel3D.Material>
<DiffuseMaterial>
<DiffuseMaterial.Brush>
<VisualBrush
Visual=»{Binding ElementName=Image}»/>
</DiffuseMaterial.Brush>
</DiffuseMaterial>
</GeometryModel3D.Material>
</GeometryModel3D>
<GeometryModel3D Geometry=»{StaticResource Mesh2}»>
<GeometryModel3D.Transform>
<Transform3DGroup>
<RotateTransform3D>
<RotateTransform3D.Rotation>
<AxisAngleRotation3D Axis=»0,1,0″ Angle=»20″/>
</RotateTransform3D.Rotation>
</RotateTransform3D>
<TranslateTransform3D OffsetX=»-1″ OffsetZ=»1″/>
</Transform3DGroup>
</GeometryModel3D.Transform>
<GeometryModel3D.Material>
<DiffuseMaterial>
<DiffuseMaterial.Brush>
<VisualBrush
Visual=»{Binding ElementName=Image}»/>
</DiffuseMaterial.Brush>
</DiffuseMaterial>
</GeometryModel3D.Material>
</GeometryModel3D>
<GeometryModel3D Geometry=»{StaticResource Mesh}»>
<GeometryModel3D.Transform>
<Transform3DGroup>
<RotateTransform3D>
<RotateTransform3D.Rotation>
<AxisAngleRotation3D Axis=»0,1,0″ Angle=»-20″/>
</RotateTransform3D.Rotation>
</RotateTransform3D>
<TranslateTransform3D OffsetX=»1″ OffsetZ=»1″/>
</Transform3DGroup>
</GeometryModel3D.Transform>
<GeometryModel3D.Material>
<DiffuseMaterial>
<DiffuseMaterial.Brush>
<VisualBrush
Visual=»{Binding ElementName=Image}»/>
</DiffuseMaterial.Brush>
</DiffuseMaterial>
</GeometryModel3D.Material>
</GeometryModel3D>

</Model3DGroup.Children>
</Model3DGroup>
</ModelVisual3D.Content>
</ModelVisual3D>
</Viewport3D.Children>
</Viewport3D>

<ContentControl Name =»Image»
Content=»{Binding ElementName=PhotosListBox, Path=SelectedItem}»
ContentTemplate=»{StaticResource ReflectedPhoto}»/>

</Grid>

Итак, мы добавили внутрь Grid еще один Grid с двумя элементами — Viewport3D и ContentControl. Первый создает сцену для отображения трех зарание определенных фигур Mesh, Mesh1 и Mesh2. Каждая из этих фигур при помощи кисти VisualBrush (см. Статью №015) заливается элементом ContentControl с именем «Image». В свою очередь внешний вид ContentControl (который и служит заливкой для трех геометрических фигур) определен стилем ReflectedPhoto, который мы описали ранее.

Запускаем проект:

Проект работает именно так как и задумывалось. В качестве самостоятельного упражнения предлагаю вам поэкспериментировать с шаблонами отображения изображений. Изменяйте значения различных свойств, заливок и т.д. — придайте своему проекту индивидуальный вид!

Подведем итог этого проекта:
- закреплена работа с контейнером Grid
- закреплена работа со стилями и словарями стилей
- изучены нестандартные методы подключения данных к проекту из внешнего источника (в данном случае из папки на жестком диске)
- рассмотрены основы построения трехмерной сцены
- закреплен опыт работы с VisualBrush

Источник: thevista.ru

Оставить комментарий

Чтобы оставлять комментарии Вы должны быть авторизованы.

Похожие посты