/// <summary>
        /// Invoked when an unhandled DragDrop.DragEnter attached event reaches an element in its route that is derived from this class.
        /// </summary>
        /// <param name="dragEventArgs">The DragEventArgs that contains the event data.</param>
        protected override void OnDrop(DragEventArgs dragEventArgs)
        {
            // Close (and dereference) the window used to display the object being dragged.
            this.dragWindow.Close();
            this.dragWindow = null;

            // Attempt to find the element currently selected in the page.  This item will be the parent of any object dropped onto the surface.
            AssetNetworkItem parentItem = ExplorerHelper.FindExplorerItem(this.DataContext as AssetNetworkItem, this.Source) as AssetNetworkItem;

            if (parentItem != null)
            {
                // We can import files dropped onto the surface.  If the format specifies one or more files dragged from the Windows Explorer, then set call a
                // handler to import each filed based on the file type (that is, the extension).
                String[] paths = dragEventArgs.Data.GetData("FileDrop") as String[];
                if (paths != null)
                {
                    foreach (String path in paths)
                    {
                        DocumentProperty externalObject;
                        if (DirectoryPage.documentProperties.TryGetValue(Path.GetExtension(path).ToLower(), out externalObject))
                        {
                            DirectoryPage.CreateDocument(parentItem, path, externalObject);
                        }
                    }
                }
            }
        }
        /// <summary>
        /// Creates a data model entry for the given path element.
        /// </summary>
        /// <param name="parentItem"></param>
        /// <param name="path">The full file name of the document to be loaded.</param>
        /// <param name="externalDocument">The properties used to build the document.</param>
        static void CreateDocument(AssetNetworkItem parentItem, String path, DocumentProperty externalDocument)
        {
            // The general idea here is to create the data structure that will be passed to the server to create the documents.  Once built, the same data is used
            // to modify the client side so it will look like the file was imported instantaneously. If the operation to add the document fails, then we will undo
            // the transaction but most of the time it will just look zippin' quick.  This is list of metadata properties used to create the document.
            List <PropertyStoreInfo> propertyStoreList = new List <PropertyStoreInfo>();

            // Each document requires an image so we can display an icon on the directory page.  The icon used for the documents is acquired from the type
            // information (each type is associated with an icon).
            DataModel.TypeRow typeRow = DataModel.Type.TypeKey.Find(externalDocument.typeId);

            // IMPORTANT CONCEPT: In order to create the document on the client side, we need to have unique identifiers.  These unique identifiers will be passed
            // to the server when it is time to create the document and all the records that go with it (metadata, entity tree, etc.).  If the operation is not
            // successful, then we roll back this transaction and all these records will go away, but if the operation is successful, then we'll get back the
            // actual database record which will, by virtue of having the same identifiers created here, update these records with the actual server information
            // (most importantly, we'll get back a RowVersion and all the other server-side defaults).
            CreateDocumentInfo createDocumentInfo = new CreateDocumentInfo();

            createDocumentInfo.EntityId       = Guid.NewGuid();
            createDocumentInfo.EntityTreeId   = Guid.NewGuid();
            createDocumentInfo.Name           = Path.GetFileName(path);
            createDocumentInfo.ParentEntityId = parentItem.EntityId;
            createDocumentInfo.TypeId         = typeRow.TypeId;

            // If a viewer has been specified for this file type, then create a metadata property for the viewer.
            if (!String.IsNullOrEmpty(externalDocument.ViewerUri))
            {
                PropertyStoreInfo viewerPropertyInfo = new PropertyStoreInfo();
                viewerPropertyInfo.PropertyStoreId = Guid.NewGuid();
                viewerPropertyInfo.PropertyId      = PropertyId.Viewer;
                viewerPropertyInfo.Value           = Encoding.Unicode.GetBytes(externalDocument.ViewerUri);
                propertyStoreList.Add(viewerPropertyInfo);
            }

            // This will create a metadata property for the data that goes with an item.  In this case, the data is the file contents.
            PropertyStoreInfo dataPropertyInfo = new PropertyStoreInfo();

            dataPropertyInfo.PropertyId      = PropertyId.Data;
            dataPropertyInfo.PropertyStoreId = Guid.NewGuid();
            using (FileStream fileStream = new FileStream(path, FileMode.Open, FileAccess.Read))
            {
                MemoryStream memoryStream = new MemoryStream();
                DirectoryPage.Copy(fileStream, memoryStream);
                Byte[] buffer = new Byte[memoryStream.Length];
                Array.Copy(memoryStream.GetBuffer(), buffer, memoryStream.Length);
                dataPropertyInfo.Value = buffer;
            }
            propertyStoreList.Add(dataPropertyInfo);

            // Package the properties up in an array.
            createDocumentInfo.Properties = propertyStoreList.ToArray();

            // At this point, we have enough data to create the document on the server.  We could quit now and just send the message off to the server and wait for
            // the server to update the client with the newly created document.  However, this involves a lag time which can be noticable on public systems.  To
            // make it look like the operation was instantaneous, we're going to create the document on the client side.  If the server accepts the web service
            // call, then all this data will be overwritten by the actual server version.  If the operation fails, all this data will be rolled back.
            DataModel.EntityRow entityRow = DataModel.Entity.NewRow() as DataModel.EntityRow;
            entityRow.EntityId     = createDocumentInfo.EntityId;
            entityRow.CreatedTime  = DateTime.Now;
            entityRow.ImageId      = typeRow.ImageId;
            entityRow.Name         = createDocumentInfo.Name;
            entityRow.ModifiedTime = DateTime.Now;
            entityRow.RowVersion   = 0;
            entityRow.IsContainer  = false;
            entityRow.TypeId       = createDocumentInfo.TypeId;
            DataModel.Entity.Rows.Add(entityRow);

            // This will create the client side version of all the properties associated with this document.
            foreach (PropertyStoreInfo propertyStoreInfo in createDocumentInfo.Properties)
            {
                DataModel.PropertyStoreRow viewerPropertyRow = DataModel.PropertyStore.NewRow() as DataModel.PropertyStoreRow;
                viewerPropertyRow.EntityId        = createDocumentInfo.EntityId;
                viewerPropertyRow.PropertyId      = propertyStoreInfo.PropertyId;
                viewerPropertyRow.PropertyStoreId = propertyStoreInfo.PropertyStoreId;
                viewerPropertyRow.RowVersion      = 0;
                viewerPropertyRow.Value           = propertyStoreInfo.Value;
                DataModel.PropertyStore.Rows.Add(viewerPropertyRow);
            }

            // Finally, we need a relation built between the parent container and this document.
            DataModel.EntityTreeRow entityTreeRow = DataModel.EntityTree.NewRow() as DataModel.EntityTreeRow;
            entityTreeRow.ParentId     = parentItem.EntityId;
            entityTreeRow.ChildId      = createDocumentInfo.EntityId;
            entityTreeRow.EntityTreeId = createDocumentInfo.EntityTreeId;
            entityTreeRow.RowVersion   = 0;
            DataModel.EntityTree.Rows.Add(entityTreeRow);

            // The object will be added to the common data model from a background thread.
            ThreadPool.QueueUserWorkItem(DirectoryPage.CreateItemThread, createDocumentInfo);
        }