After Part 4, we will partly refactor the code to build a custom component.
We define a new assembly called FlowComponent where we will move most of the code. First, the c# code will be part of a new class called FlowControl. This class herits from UserControl :
The code from the TestWindow constructor is included in a new method called Load. It will build covers with the images from one directory. We also provide two new methods GoToNext and GoToPrevious to navigate between covers.
This is the same in the xaml code : we migrate the Grid (its background) and the Viewport3D to our custom UserControl :
We can set the size we want. It will be overriden in the owner component. We set the ClipToBounds property to True to avoid covers from exceeding the bounds of our component.
The Cover class is also moved to the FlowComponent assembly.
As we are creating a CoverFlow component, it is a good idea to deal with rectangle images. We update thePoint3D coordinates with the ImageSource size. The ImageSource will be stored as a class attribute.
With this new improvement, it is possible to use the component to browse photos :
There is nothing much left in the TestWindow class. In the xaml code, after we've referenced our component assembly, we just use our FlowControl as main Content :
In the C# code, we just keep the Window_KeyDown method and the constructor :
That's it ! We've built a CoverFlow component reusable in other WPF applications. But this component needs improvements. We will add virtualization in the next part...
We define a new assembly called FlowComponent where we will move most of the code. First, the c# code will be part of a new class called FlowControl. This class herits from UserControl :
- using[...]
- namespace Ded.Tutorial.Wpf.CoverFlow.Part5.FlowComponent
- {
- public partial class FlowControl : UserControl
- {
- #region Fields
- private int index;
- private readonly List<Cover> coverList = new List<Cover>();
- #endregion
- #region Private stuff
- private void RotateCover(int pos)[...]
- private void UpdateIndex(int newIndex)[...]
- private void viewPort_MouseDown(object sender, MouseButtonEventArgs e)[...]
- #endregion
- public FlowControl()
- {
- InitializeComponent();
- }
- public void Load(string imagePath)
- {
- coverList.Clear();
- var imageDir = new DirectoryInfo(imagePath);
- int doneImages = 0;
- foreach (FileInfo image in imageDir.GetFiles("*.jpg"))
- {
- var cover = new Cover(image.FullName, doneImages++);
- coverList.Add(cover);
- visualModel.Children.Add(cover);
- }
- }
- public void GoToNext()
- {
- if (index < coverList.Count - 1)
- UpdateIndex(index + 1);
- }
- public void GoToPrevious()
- {
- if (index > 0)
- UpdateIndex(index - 1);
- }
- }
- }
This is the same in the xaml code : we migrate the Grid (its background) and the Viewport3D to our custom UserControl :
- <UserControl x:Class="Ded.Tutorial.Wpf.CoverFlow.Part5.FlowComponent.FlowControl"
- xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
- xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- Height="282" Width="490">
- <Grid ClipToBounds="True">
- <Grid.Background[...]>
- <Viewport3D x:Name="viewPort" Grid.Column="0" Grid.Row="0" ClipToBounds="False" MouseDown="viewPort_MouseDown">
- <Viewport3D.Camera[...]>
- <Viewport3D.Children[...]>
- </Viewport3D>
- </Grid>
- </UserControl>
The Cover class is also moved to the FlowComponent assembly.
As we are creating a CoverFlow component, it is a good idea to deal with rectangle images. We update thePoint3D coordinates with the ImageSource size. The ImageSource will be stored as a class attribute.
- private readonly ImageSource imageSource;
- private double RectangleDx()
- {
- if (imageSource.Width > imageSource.Height)
- return 0;
- else
- return 1 - imageSource.Width / imageSource.Height;
- }
- private double RectangleDy()
- {
- if (imageSource.Width > imageSource.Height)
- return 1 - imageSource.Height / imageSource.Width;
- else
- return 0;
- }
- private Geometry3D Tessellate()
- {
- double dx = RectangleDx();
- double dy = RectangleDy();
- var p0 = new Point3D(-1 + dx, -1 + dy, 0);
- var p1 = new Point3D(1 - dx, -1 + dy, 0);
- var p2 = new Point3D(1 - dx, 1 - dy, 0);
- var p3 = new Point3D(-1 + dx, 1 - dy, 0);
- ...
- }
- private Geometry3D TessellateMirror()
- {
- double dx = RectangleDx();
- double dy = RectangleDy();
- var p0 = new Point3D(-1 + dx, -3 + 3 * dy, 0);
- var p1 = new Point3D(1 - dx, -3 + 3 * dy, 0);
- var p2 = new Point3D(1 - dx, -1 + dy, 0);
- var p3 = new Point3D(-1 + dx, -1 + dy, 0);
- ...
- }
- public Cover(string imagePath, int pos)
- {
- ...
- imageSource = LoadImageSource(imagePath);
- ...
- }

There is nothing much left in the TestWindow class. In the xaml code, after we've referenced our component assembly, we just use our FlowControl as main Content :
- <Window x:Class="Ded.Tutorial.Wpf.CoverFlow.Part5.TestWindow"
- xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
- xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- Title="WPF Coverflow" Width="512" Height="320" KeyDown="Window_KeyDown"
- xmlns:flow="clr-namespace:Ded.Tutorial.Wpf.CoverFlow.Part5.FlowComponent;assembly=Ded.Tutorial.Wpf.CoverFlow.Part5.FlowComponent">
- <flow:FlowControl x:Name="flow" Margin="0"></flow:FlowControl>
- </Window>
- using System.Windows;
- using System.Windows.Input;
- namespace Ded.Tutorial.Wpf.CoverFlow.Part5
- {
- public partial class TestWindow : Window
- {
- #region Private stuff
- private void Window_KeyDown(object sender, KeyEventArgs e)
- {
- switch (e.Key)
- {
- case Key.Right:
- flow.GoToNext();
- break;
- case Key.Left:
- flow.GoToPrevious();
- break;
- }
- }
- #endregion
- public TestWindow()
- {
- InitializeComponent();
- flow.Load(@"c:\_covers");
- }
- }
- }
Download source. Continue with Part 6.
转载自:http://d3dal3.blogspot.com/2008/10/wpf-cover-flow-tutorial-part-5.html
版权归原作者所有。