using System; using System.Collections.Generic; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Animation; using System.Windows.Media.Media3D; using System.Windows.Media.Imaging; using System.Windows.Shapes; using System.Windows.Threading; using System.Configuration; using System.Collections.ObjectModel; using System.Linq; using AmazonService; using _3DTools; namespace Explorer3D { /// <summary> /// This window allows the user to search Amazon using the Amazon Web Service. /// The results obtained from Amazon are then put into 3d Meshes which are then /// arranged randomly in 3D space. Each result is then brought into focus by animating /// the camera within the ViewPort. As an added bonus I have also wrapped each of the /// 3d meshes in the _3DTools.InteractiveVisual3D object, which allows a 3D mesh to host /// a normal 2d UI element. And the entire Viewport3D is then wrapped by the /// _3DTools.Interactive3DDecorator and _3DTools.TrackballDecorator objects. Which allow /// a trackball to be applied to the 3D model, allowing the user to pan and tilt the 3D /// model. /// </summary> public partial class ExplorerWindow : System.Windows.Window { #region Instance Fields private string subscriberID = String.Empty; private Point3DAnimation pa; private Random ran = new Random(); private Point3D[] p3s; private InteractiveVisual3D[] mvs = new InteractiveVisual3D[9]; private int count = 0; private DispatcherTimer dt = new DispatcherTimer(); private PerspectiveCamera cam; private ModelVisual3D model; private string category; private int detailsCount = 0; private bool showingFavs = false; private bool fetching = false; private DispatcherTimer timerFavouritesShow = new DispatcherTimer(); private DispatcherTimer timerFavouritesHide = new DispatcherTimer(); private Details[] details; internal ObservableCollection<AmazonFavourite> favouriteDataItems = new ObservableCollection<AmazonFavourite>(); //delegate that will be called aysychronously to fetch Amazon details internal delegate Details[] FetchAmazonDetailDelegate(string searchword, string category); #endregion #region Ctor public ExplorerWindow() { InitializeComponent(); this.canvFavourites.MouseEnter += new MouseEventHandler(canvFavourites_MouseEnter); this.canvFavourites.MouseLeave += new MouseEventHandler(canvFavourites_MouseLeave); this.svFavourites.MouseEnter += new MouseEventHandler(svFavourites_MouseEnter); this.svFavourites.MouseLeave += new MouseEventHandler(svFavourites_MouseLeave); setFavouritesVisibility(false); timerFavouritesHide.Interval = new TimeSpan(0, 0, 0, 2); timerFavouritesShow.Interval = new TimeSpan(0, 0, 0, 2); timerFavouritesHide.Tick += new EventHandler(timerFavouritesHide_Tick); timerFavouritesHide.IsEnabled = timerFavouritesHide.IsEnabled = false; timerFavouritesShow.Tick += new EventHandler(timerFavouritesShow_Tick); } #endregion #region Properties /// <summary> /// Returns a ObservableCollection of AmazonFavourite objects /// </summary> public ObservableCollection<AmazonFavourite> FavouriteDataItems { get { return favouriteDataItems; } } /// <summary> /// returns the Amazon Web Services (mine basically) subcription string /// </summary> private string SubscriberID { get { if (string.IsNullOrEmpty(subscriberID)) { // Retrieve Amazon subscriber id from configuration file. subscriberID = ConfigurationManager.AppSettings["AmazonSubscriberID"].ToString(); } return subscriberID; } } #endregion #region Private Helpers private void svFavourites_MouseLeave(object sender, MouseEventArgs e) { showingFavs = false; } private void svFavourites_MouseEnter(object sender, MouseEventArgs e) { showingFavs = true; } private void canvFavourites_MouseLeave(object sender, MouseEventArgs e) { showingFavs = false; timerFavouritesShow.IsEnabled = false; } private void timerFavouritesHide_Tick(object sender, EventArgs e) { if (!showingFavs) { svFavourites.Visibility = Visibility.Hidden; lblFavourites.Content = "Show favourites"; stackFavourites.Height = lblFavourites.Height; ; showingFavs = false; timerFavouritesHide.IsEnabled = false; } } private void canvFavourites_MouseEnter(object sender, MouseEventArgs e) { showingFavs = true; timerFavouritesShow.IsEnabled = true; } private void timerFavouritesShow_Tick(object sender, EventArgs e) { if (showingFavs) { showingFavs = true; svFavourites.Visibility = Visibility.Visible; lblFavourites.Content = "Showing favourites"; stackFavourites.Height = lblFavourites.Height + svFavourites.Height; timerFavouritesShow.IsEnabled = false; timerFavouritesHide.IsEnabled = true; } } private void setFavouritesVisibility(bool showFavs) { svFavourites.Visibility = showFavs ? Visibility.Visible : Visibility.Hidden; lblFavourites.Content = showFavs ? "Hide favourites" : "Show favourites"; if (svFavourites.Visibility == Visibility.Visible) stackFavourites.Height = lblFavourites.Height + svFavourites.Height; else stackFavourites.Height = lblFavourites.Height; } private void createViewPortCamera() { cam = new PerspectiveCamera(); cam.Position = new Point3D(0,0,10); cam.FarPlaneDistance = 600; cam.NearPlaneDistance = 0.1; cam.FieldOfView = 90; cam.LookDirection = new Vector3D(0,0,-1); view3D.Camera = cam; } private void createViewPortLight() { model = new ModelVisual3D(); model.Content = new AmbientLight(); view3D.Children.Add(model); } //Method called by aysychronous delegate to fetch Amazon Details private Details[] FetchAmazonDetail(string searchword, string category) { return doAmazonSearch(searchword, category); } private void createViewPort3DPlanes(string searchword, string category) { try { fetching = true; count = 0; dt.Interval = TimeSpan.FromSeconds(2); dt.Tick -= new EventHandler(dt_Tick); //Fetch the details asynchronously. Basically assume we will not get results quickly FetchAmazonDetailDelegate fetchDetails = FetchAmazonDetail; IAsyncResult asynchResult = fetchDetails.BeginInvoke(searchword, category,null, null); //wait for the results from the asynch call. while (!asynchResult.AsyncWaitHandle.WaitOne(5000, false)) { //waiting for result for exactly 5 seconds } //get the results of the asynch call details = fetchDetails.EndInvoke(asynchResult); #region If details OK if (details != null) { p3s = new Point3D[details.Length]; //create an animation for each item recieved detailsCount = details.Length; //for each Detail obtained for current search, create a new 3d Mesh and store //its Point3D. To allow camera to be animated to later for (int i = 0; i < details.Length; i++) { MeshGeometry3D plMesh = this.TryFindResource("planeMesh") as MeshGeometry3D; InteractiveVisual3D mv = new InteractiveVisual3D(); mv.IsBackVisible = true; mv.Geometry = plMesh; mv.Visual = createAmazonDetail(details[i]); view3D.Children.Add(mv); //position item randomly in 3D space, but always ensure Z is (-) Matrix3D trix = new Matrix3D(); double x = ran.NextDouble() * 50 - 50; double y = ran.NextDouble() * 2 - 2; double z = -i * 10; p3s[i] = new Point3D(x, y, z); trix.Append(new TranslateTransform3D(x, y, z).Value); mv.Transform = new MatrixTransform3D(trix); } //create animation, and bring item 1 into view pa = new Point3DAnimation(p3s[0], TimeSpan.FromMilliseconds(300)); pa.AccelerationRatio = 0.3; pa.DecelerationRatio = 0.3; pa.Completed += new EventHandler(pa_Completed); cam.BeginAnimation(PerspectiveCamera.PositionProperty, pa); fetching = false; dt.Tick += new EventHandler(dt_Tick); } #endregion } //As its more than likely the web service, not much I can do about it, //just catch the Exception catch { MessageBox.Show("Error obtaining amazon results"); } } private Details[] doAmazonSearch(string keyword, string mode) { try { KeywordRequest keywordReq = new KeywordRequest(); keywordReq.locale = "us"; keywordReq.type = "lite"; keywordReq.sort = "reviewrank"; keywordReq.mode = mode; keywordReq.keyword = keyword; keywordReq.tag = this.SubscriberID; keywordReq.devtag = this.SubscriberID; AmazonSearchPortClient ams = new AmazonSearchPortClient(); ProductInfo productInfo = ams.KeywordSearchRequest(keywordReq); return productInfo.Details; } catch { return null; } } private StackPanel createAmazonDetail(Details amazonDetail) { StackPanel sp = new StackPanel(); sp.Background = Brushes.Transparent; AmazonItem item = new AmazonItem(amazonDetail); item.ItemClicked += new AmazonItem.AmazonItemClickedEventHandler(item_ItemClicked); sp.Children.Add(item); return sp; } private void item_ItemClicked(object sender, AmazonItemRoutedEventArgs e) { if (e.AmazonDetail != null) { FlowDocumentWindow flowDocWin = new FlowDocumentWindow(); flowDocWin.Owner = this; flowDocWin.AmazonDetail = e.AmazonDetail; flowDocWin.CreateDocumentFromDetail(); flowDocWin.ShowDialog(); this.inter3d.Focus(); } } private void CreateSearch(string category) { if (!string.IsNullOrEmpty(txtSearchWord.Text)) { try { //setup 3d view port view3D.Children.Clear(); createViewPortCamera(); createViewPortLight(); createViewPort3DPlanes(txtSearchWord.Text, category); } catch { } } } private void btnFavMain_Click(object sender, RoutedEventArgs e) { AmazonFavourite favouriteItem = (sender as Button).DataContext as AmazonFavourite; if (favouriteItem.AmazonDetail != null) { FlowDocumentWindow flowDocWin = new FlowDocumentWindow(); flowDocWin.Owner = this; flowDocWin.AmazonDetail = favouriteItem.AmazonDetail; flowDocWin.CreateDocumentFromDetail(); flowDocWin.ShowDialog(); this.inter3d.Focus(); } } private void btnDeleteFavourite_Click(object sender, RoutedEventArgs e) { AmazonFavourite favouriteItem = (sender as Button).DataContext as AmazonFavourite; favouriteDataItems.Remove(favouriteItem); e.Handled = true; } private void rbBooks_Checked(object sender, RoutedEventArgs e) { category = "books"; } private void rbMusic_Checked(object sender, RoutedEventArgs e) { category = "music"; } private void rbDVD_Checked(object sender, RoutedEventArgs e) { category = "dvd"; } private void imgDoSearch_MouseDown(object sender, MouseButtonEventArgs e) { CreateSearch(category); } private void txtSearchWord_KeyUp(object sender, KeyEventArgs e) { if (e.Key.Equals(Key.Enter)) { CreateSearch(category); } } #endregion #region 3D Animation (move camera to item[i] Point3D in ViewPort) private void dt_Tick(object sender, EventArgs e) { dt.Stop(); if (count == detailsCount-1) count = 0; else count++; pa = new Point3DAnimation(new Point3D(p3s[count].X, p3s[count].Y + 0.5, p3s[count].Z + 2), TimeSpan.FromMilliseconds(500)); pa.AccelerationRatio = 0.3; pa.DecelerationRatio = 0.3; pa.Completed += new EventHandler(pa_Completed); cam.BeginAnimation(PerspectiveCamera.PositionProperty, pa); } private void pa_Completed(object sender, EventArgs e) { //need to try and catch as user may some times change the search 1/2 way through an animation //and the current animation has not yet completed. try { pa = new Point3DAnimation(new Point3D(p3s[count].X, p3s[count].Y + 0.5, p3s[count].Z + 1.6), TimeSpan.FromMilliseconds(3100)); pa.Completed += new EventHandler(dt_Tick); cam.BeginAnimation(PerspectiveCamera.PositionProperty, pa); } catch { } } #endregion } }
By viewing downloads associated with this article you agree to the Terms of use and the article's licence.
If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.
This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)
I am lucky enough to have won a few awards for Zany Crazy code articles over the years
The Next Version of Android - Some of What's Coming