Click here to Skip to main content
Click here to Skip to main content
Add your own
alternative version

WPF Amazon Explorer Using 3D

, 27 Feb 2008 CPOL
WPF Amazon Explorer Using 3D
explorer3d.zip
Explorer3D
Explorer3D
3dTools
3DTools.dll
bin
Debug
3DTools.dll
Explorer3D.application
Explorer3D.exe
Explorer3D.exe.manifest
Explorer3D.vshost.application
Explorer3D.vshost.exe
Controls
Explorer3d.csproj.user
Misc Classes
obj
Debug
Controls
TempPE
Properties
app.manifest
Settings.settings
Resources
Amazon.png
arrows.png
banner.png
Favourites.png
Service References
AmazonService
AmazonSearch.wsdl
configuration.svcinfo
Explorer3D.AmazonService.GetTransactionDetailsResponse.datasource
Explorer3D.AmazonService.ListingProductDetails.datasource
Explorer3D.AmazonService.MarketplaceSearch.datasource
Explorer3D.AmazonService.ProductInfo.datasource
Explorer3D.AmazonService.ProductLine.datasource
Explorer3D.AmazonService.SellerProfile.datasource
Explorer3D.AmazonService.SellerSearch.datasource
Explorer3D.AmazonService.ShoppingCart.datasource
Reference.svcmap
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 Service 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.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Sacha Barber
Software Developer (Senior)
United Kingdom United Kingdom
I currently hold the following qualifications (amongst others, I also studied Music Technology and Electronics, for my sins)
 
- MSc (Passed with distinctions), in Information Technology for E-Commerce
- BSc Hons (1st class) in Computer Science & Artificial Intelligence
 
Both of these at Sussex University UK.
 
Award(s)

I am lucky enough to have won a few awards for Zany Crazy code articles over the years

  • Microsoft C# MVP 2015
  • Codeproject MVP 2015
  • Microsoft C# MVP 2014
  • Codeproject MVP 2014
  • Microsoft C# MVP 2013
  • Codeproject MVP 2013
  • Microsoft C# MVP 2012
  • Codeproject MVP 2012
  • Microsoft C# MVP 2011
  • Codeproject MVP 2011
  • Microsoft C# MVP 2010
  • Codeproject MVP 2010
  • Microsoft C# MVP 2009
  • Codeproject MVP 2009
  • Microsoft C# MVP 2008
  • Codeproject MVP 2008
  • And numerous codeproject awards which you can see over at my blog

| Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.150327.1 | Last Updated 27 Feb 2008
Article Copyright 2008 by Sacha Barber
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid