private void CollectElementMaps()
        {
            try
            {
                var hostDoc     = Instance.Document;
                var categoryIds = Categories.Keys.ToList();
                foreach (var catId in categoryIds)
                {
                    var collector           = new FilteredElementCollector(hostDoc);
                    var elements            = collector.OfCategoryId(catId).WhereElementIsNotElementType().ToElements().ToList();
                    var elementWithEntities = from element in elements where element.GetEntitySchemaGuids().Count > 0 select element;
                    if (elementWithEntities.Count() > 0)
                    {
                        elements = elementWithEntities.ToList();
                    }

                    foreach (var element in elements)
                    {
                        if (null == element.Location)
                        {
                            continue;
                        }                                           // unplaced rooms

                        var linkInfo = MoverDataStorageUtil.GetLinkedElementInfo(element);
                        if (null != linkInfo)
                        {
                            if (linkInfo.SourceLinkInstanceId != InstanceId)
                            {
                                continue;
                            }
                            if (element.Id != linkInfo.LinkedElementId)
                            {
                                continue;
                            }

                            var sourceElement = LinkedDocument.GetElement(linkInfo.SourceElementId);
                            if (null != sourceElement)
                            {
                                linkInfo = new LinkedElementInfo(linkInfo.LinkElementType, sourceElement, element, InstanceId, TransformValue);
                                if (!LinkedElements.ContainsKey(linkInfo.LinkedElementId))
                                {
                                    LinkedElements.Add(linkInfo.LinkedElementId, linkInfo);
                                }
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show("Failed to collect element maps.\n" + ex.Message, "Collect Element Maps", MessageBoxButton.OK, MessageBoxImage.Warning);
            }
        }
        private void CollectFamilyMaps()
        {
            try
            {
                var hostDoc     = Instance.Document;
                var categoryIds = Categories.Keys.ToList();
                foreach (var catId in categoryIds)
                {
                    var collector = new FilteredElementCollector(hostDoc);
                    var elements  = collector.OfCategoryId(catId).WhereElementIsElementType().ToElements().ToList();
                    foreach (var element in elements)
                    {
                        var elementType = element as ElementType;
                        if (null != elementType)
                        {
                            var familyInfo = MoverDataStorageUtil.GetLinkedFamilyInfo(elementType);
                            if (null != familyInfo)
                            {
                                if (familyInfo.SourceLinkInstanceId != InstanceId)
                                {
                                    continue;
                                }
                                if (element.Id != familyInfo.TargetTypeId)
                                {
                                    continue;
                                }

                                var sourceType = LinkedDocument.GetElement(familyInfo.SourceTypeId) as ElementType;
                                if (null != sourceType)
                                {
                                    var sourceTypeInfo = new ElementTypeInfo(sourceType);
                                    var targetTypeInfo = new ElementTypeInfo(elementType);

                                    familyInfo = new LinkedFamilyInfo(familyInfo.SourceLinkInstanceId, sourceTypeInfo, targetTypeInfo);
                                    if (!LinkedFamilies.ContainsKey(familyInfo.TargetTypeId))
                                    {
                                        LinkedFamilies.Add(familyInfo.TargetTypeId, familyInfo);
                                    }
                                }
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show("Failed to collect family maps.\n" + ex.Message, "Collect Family Maps", MessageBoxButton.OK, MessageBoxImage.Warning);
            }
        }
        private bool UpdateModifiedElements(Document doc, List <ElementId> modifiedElementIds)
        {
            var updated = false;

            try
            {
                var linkedInstances = handler.LinkInstances;
                foreach (var elementId in modifiedElementIds)
                {
                    var element = doc.GetElement(elementId);
                    if (element.GetEntitySchemaGuids().Count > 0)
                    {
                        var linkInfo = MoverDataStorageUtil.GetLinkedElementInfo(element);
                        if (null != linkInfo)
                        {
                            if (linkedInstances.ContainsKey(linkInfo.SourceLinkInstanceId))
                            {
                                var lip = linkedInstances[linkInfo.SourceLinkInstanceId];
                                if (lip.LinkedElements.ContainsKey(linkInfo.LinkedElementId))
                                {
                                    var sourceElement = lip.LinkedDocument.GetElement(linkInfo.SourceElementId);
                                    if (null != sourceElement)
                                    {
                                        linkInfo.Matched = LinkedElementInfo.CompareLocation(sourceElement, element, lip.TransformValue);
                                        lip.LinkedElements.Remove(linkInfo.LinkedElementId);
                                        lip.LinkedElements.Add(linkInfo.LinkedElementId, linkInfo);

                                        linkedInstances.Remove(lip.InstanceId);
                                        linkedInstances.Add(lip.InstanceId, lip);
                                    }
                                }
                            }
                        }
                    }
                }

                handler.LinkInstances = linkedInstances;
                if (linkedInstances.ContainsKey(handler.SelectedLink.InstanceId))
                {
                    handler.SelectedLink = linkedInstances[handler.SelectedLink.InstanceId];
                }
                handler.ApplyDocumentChanged();
            }
            catch (Exception ex)
            {
                Log.AppendLog(LogMessageType.EXCEPTION, ex.Message);
            }
            return(updated);
        }
        private static LinkedInstanceProperties DuplicateElements(Document recipientDoc, LinkedInstanceProperties lip, CategoryProperties cp, UpdateMode updateMode)
        {
            var updatedLIP = lip;

            try
            {
                var collector      = new FilteredElementCollector(updatedLIP.LinkedDocument);
                var elementsToCopy = collector.OfCategoryId(cp.CategoryId).WhereElementIsNotElementType().ToElements().ToList();

                var updatePbDelegate = new UpdateProgressBarDelegate(progressBar.SetValue);
                progressBar.Value   = 0;
                progressBar.Maximum = elementsToCopy.Count;

                var duplicated = 0;
                using (var tGroup = new TransactionGroup(recipientDoc))
                {
                    tGroup.Start("Duplicate Elements");
                    tGroup.IsFailureHandlingForcedModal = false;
                    var failureHanlder = new FailureHandler();

                    try
                    {
                        var options = new CopyPasteOptions();
                        options.SetDuplicateTypeNamesHandler(new HideAndAcceptDuplicateTypeNamesHandler());

                        double value = 0;
                        foreach (var sourceElement in elementsToCopy) //elements from link
                        {
                            value++;
                            System.Windows.Threading.Dispatcher.CurrentDispatcher.Invoke(updatePbDelegate, System.Windows.Threading.DispatcherPriority.Background, new object[] { ProgressBar.ValueProperty, value });

                            var foundElements = from element in updatedLIP.LinkedElements.Values where element.SourceUniqueId == sourceElement.UniqueId && element.SourceLinkInstanceId == updatedLIP.InstanceId select element;
                            if (foundElements.Count() > 0)
                            {
                                var linkInfo      = foundElements.First();
                                var linkedElement = recipientDoc.GetElement(linkInfo.LinkedElementId);
                                if (null != linkedElement)
                                {
                                    if (linkInfo.LinkElementType == LinkType.ByMap || updateMode == UpdateMode.UpdateLocationOnly)
                                    {
                                        if (!linkInfo.Matched)
                                        {
                                            using (var trans = new Transaction(recipientDoc))
                                            {
                                                trans.Start("Move Element");
                                                var failureOption = trans.GetFailureHandlingOptions();
                                                failureOption.SetForcedModalHandling(false);
                                                failureOption.SetFailuresPreprocessor(failureHanlder);
                                                failureOption.SetClearAfterRollback(true);
                                                trans.SetFailureHandlingOptions(failureOption);

                                                try
                                                {
                                                    var moved = LinkedElementInfo.MoveLocation(sourceElement, linkedElement, updatedLIP.TransformValue);
                                                    linkInfo.Matched = moved;
                                                    if (updatedLIP.LinkedElements.ContainsKey(linkedElement.Id))
                                                    {
                                                        updatedLIP.LinkedElements.Remove(linkedElement.Id);
                                                        updatedLIP.LinkedElements.Add(linkedElement.Id, linkInfo);
                                                    }
                                                    trans.Commit();
                                                    duplicated++;
                                                }
                                                catch (Exception ex)
                                                {
                                                    trans.RollBack();
                                                    var message = ex.Message;
                                                }
                                            }
                                        }
                                        continue;
                                    }
                                    else if (updateMode == UpdateMode.ReplaceElements)
                                    {
                                        using (var trans = new Transaction(recipientDoc))
                                        {
                                            trans.Start("Delete Element");
                                            var failureOption = trans.GetFailureHandlingOptions();
                                            failureOption.SetForcedModalHandling(false);
                                            failureOption.SetFailuresPreprocessor(failureHanlder);
                                            failureOption.SetClearAfterRollback(true);
                                            trans.SetFailureHandlingOptions(failureOption);

                                            try
                                            {
                                                var deletedIds = recipientDoc.Delete(linkInfo.LinkedElementId);
                                                if (updatedLIP.LinkedElements.ContainsKey(linkInfo.LinkedElementId))
                                                {
                                                    updatedLIP.LinkedElements.Remove(linkInfo.LinkedElementId);
                                                }
                                                trans.Commit();
                                            }
                                            catch (Exception ex)
                                            {
                                                var message = ex.Message;
                                                trans.RollBack();
                                            }
                                        }
                                    }
                                }
                            }

                            var elementIds = new List <ElementId>();
                            elementIds.Add(sourceElement.Id);
                            try
                            {
                                Element copiedElement = null;
                                var     linkType      = LinkType.None;
                                using (var trans = new Transaction(recipientDoc))
                                {
                                    trans.Start("Copy Element");
                                    var failureOption = trans.GetFailureHandlingOptions();
                                    failureOption.SetForcedModalHandling(false);
                                    failureOption.SetFailuresPreprocessor(failureHanlder);
                                    failureOption.SetClearAfterRollback(true);
                                    trans.SetFailureHandlingOptions(failureOption);

                                    try
                                    {
                                        copiedElement = CopyByFamilyMaps(recipientDoc, updatedLIP, sourceElement, options);
                                        if (null != copiedElement)
                                        {
                                            linkType = LinkType.ByMap;
                                        }
                                        else
                                        {
                                            linkType = LinkType.ByCopy;
                                            var copiedElementIds = ElementTransformUtils.CopyElements(updatedLIP.LinkedDocument, elementIds, recipientDoc, updatedLIP.TransformValue, options);
                                            if (copiedElementIds.Count > 0)
                                            {
                                                var copiedElementId = copiedElementIds.First();
                                                copiedElement = recipientDoc.GetElement(copiedElementId);
                                            }
                                        }
                                        trans.Commit();
                                    }
                                    catch (Exception ex)
                                    {
                                        trans.RollBack();
                                        var message = ex.Message;
                                    }
                                }

                                if (null != copiedElement)
                                {
                                    using (var trans = new Transaction(recipientDoc))
                                    {
                                        trans.Start("Update Link Info");
                                        var failureOption = trans.GetFailureHandlingOptions();
                                        failureOption.SetForcedModalHandling(false);
                                        failureOption.SetFailuresPreprocessor(failureHanlder);
                                        failureOption.SetClearAfterRollback(true);
                                        trans.SetFailureHandlingOptions(failureOption);

                                        try
                                        {
                                            var linkInfo = new LinkedElementInfo(linkType, sourceElement, copiedElement, updatedLIP.InstanceId, updatedLIP.TransformValue);
                                            if (!linkInfo.Matched)
                                            {
                                                var moved = LinkedElementInfo.MoveLocation(sourceElement, copiedElement, updatedLIP.TransformValue);
                                                linkInfo.Matched = moved;
                                            }
                                            var updated = MoverDataStorageUtil.UpdateLinkedElementInfo(linkInfo, copiedElement);
                                            if (!updatedLIP.LinkedElements.ContainsKey(linkInfo.LinkedElementId))
                                            {
                                                updatedLIP.LinkedElements.Add(linkInfo.LinkedElementId, linkInfo);
                                            }
                                            duplicated++;
                                            trans.Commit();
                                        }
                                        catch (Exception ex)
                                        {
                                            trans.RollBack();
                                            var message = ex.Message;
                                        }
                                    }
                                }
                            }
                            catch (Exception ex)
                            {
                                var message = ex.Message;
                            }
                        }


                        if (updatedLIP.Categories.ContainsKey(cp.CategoryId))
                        {
                            cp.Selected = false;
                            updatedLIP.Categories.Remove(cp.CategoryId);
                            updatedLIP.Categories.Add(cp.CategoryId, cp);
                        }

                        tGroup.Assimilate();
                    }
                    catch (Exception ex)
                    {
                        tGroup.RollBack();
                        MessageBox.Show("Failed to duplicate elements in the category " + cp.CategoryName + "\n" + ex.Message, "Duplicate Elements", MessageBoxButton.OK, MessageBoxImage.Warning);
                    }
                }

                messageBuilder.AppendLine(duplicated.ToString() + " of " + elementsToCopy.Count.ToString() + " elements in " + cp.CategoryName + " has been successfully copied or updated.");
            }
            catch (Exception ex)
            {
                MessageBox.Show("Failed to duplicate elements.\n" + ex.Message, "Duplicate Elements", MessageBoxButton.OK, MessageBoxImage.Warning);
            }
            return(updatedLIP);
        }
        public void Execute(UIApplication app)
        {
            try
            {
                CurrentDocument = app.ActiveUIDocument.Document;

                switch (MoverRequest.Take())
                {
                case RequestId.SelectLinkInstance:
                    MainWindowInstance.DozeOff();
                    var picked = PickLinkInstance(out selectedLink);
                    if (picked)
                    {
                        MainWindowInstance.DisplayCategories(selectedLink);
                    }
                    MainWindowInstance.WakeUp();
                    break;

                case RequestId.DuplicateElements:
                    MainWindowInstance.DozeOff();
                    try
                    {
                        selectedLink = ElementMoverUtil.DuplicateSelectedCategories(selectedLink, CurrentDocument, SelectedUpdateMode);
                        if (LinkInstances.ContainsKey(selectedLink.InstanceId))
                        {
                            LinkInstances.Remove(selectedLink.InstanceId);
                        }
                        LinkInstances.Add(selectedLink.InstanceId, selectedLink);
                        MainWindowInstance.UpdateLinkedInstanceProperties();
                    }
                    catch (Exception ex)
                    {
                        var message = ex.Message;
                    }
                    MainWindowInstance.WakeUp();
                    break;

                case RequestId.SelectMappingElements:
                    MappingWindowInstance.DozeOff();
                    Element sourceElement = null;
                    Element targetElement = null;
                    var     pickedMap     = PickMappingElements(out sourceElement, out targetElement);
                    if (pickedMap)
                    {
                        if (LinkInstances.ContainsKey(selectedLink.InstanceId))
                        {
                            LinkInstances.Remove(selectedLink.InstanceId);
                        }
                        LinkInstances.Add(selectedLink.InstanceId, selectedLink);
                        MappingWindowInstance.RefreshLinkInstance();
                    }
                    MappingWindowInstance.WakeUp();
                    break;

                case RequestId.DeleteMappingElements:
                    MappingWindowInstance.DozeOff();
                    if (LinkedElementToDelete.Count > 0)
                    {
                        using (var trans = new Transaction(CurrentDocument))
                        {
                            trans.Start("Delete Element Maps");
                            try
                            {
                                foreach (var linkedInfo in LinkedElementToDelete)
                                {
                                    if (selectedLink.LinkedElements.ContainsKey(linkedInfo.LinkedElementId))
                                    {
                                        selectedLink.LinkedElements.Remove(linkedInfo.LinkedElementId);
                                        var linkedElement = CurrentDocument.GetElement(linkedInfo.LinkedElementId);
                                        if (null != linkedElement)
                                        {
                                            var removed = MoverDataStorageUtil.RemoveLinkedElementInfo(linkedElement);
                                        }
                                    }
                                }
                                trans.Commit();
                                if (LinkInstances.ContainsKey(selectedLink.InstanceId))
                                {
                                    LinkInstances.Remove(selectedLink.InstanceId);
                                    LinkInstances.Add(selectedLink.InstanceId, selectedLink);
                                }
                            }
                            catch (Exception ex)
                            {
                                trans.RollBack();
                                MessageBox.Show("Failed to delete element maps.\n" + ex.Message, "Delete Element Maps", MessageBoxButton.OK, MessageBoxImage.Warning);
                            }
                        }
                        MappingWindowInstance.RefreshLinkInstance();
                    }
                    MappingWindowInstance.WakeUp();
                    break;

                case RequestId.ShowElement:
                    MappingWindowInstance.DozeOff();
                    if (null != SelectedLinkedInfo)
                    {
                        if (null != CurrentDocument.GetElement(SelectedLinkedInfo.LinkedElementId))
                        {
                            HighlightElement();
                        }
                    }
                    MappingWindowInstance.WakeUp();
                    break;

                case RequestId.AddFamilyMapping:
                    FamilyWindowInstance.DozeOff();
                    if (null != SelectedFamilyInfo)
                    {
                        var tType = CurrentDocument.GetElement(SelectedFamilyInfo.TargetTypeId) as ElementType;
                        if (null != tType)
                        {
                            using (var trans = new Transaction(CurrentDocument))
                            {
                                trans.Start("Add Family Map");
                                try
                                {
                                    if (selectedLink.LinkedFamilies.ContainsKey(SelectedFamilyInfo.TargetTypeId))
                                    {
                                        selectedLink.LinkedFamilies.Remove(SelectedFamilyInfo.TargetTypeId);
                                    }
                                    selectedLink.LinkedFamilies.Add(SelectedFamilyInfo.TargetTypeId, SelectedFamilyInfo);

                                    var updated = MoverDataStorageUtil.UpdateLinkedFamilyInfo(SelectedFamilyInfo, tType);

                                    if (LinkInstances.ContainsKey(selectedLink.InstanceId))
                                    {
                                        LinkInstances.Remove(selectedLink.InstanceId);
                                        LinkInstances.Add(selectedLink.InstanceId, selectedLink);
                                    }
                                    trans.Commit();
                                    MappingWindowInstance.RefreshLinkInstance();
                                }
                                catch (Exception ex)
                                {
                                    trans.RollBack();
                                    var message = ex.Message;
                                }
                            }
                        }
                    }

                    FamilyWindowInstance.WakeUp();
                    break;

                case RequestId.DeleteFamilyMapping:
                    MappingWindowInstance.DozeOff();
                    if (null != SelectedFamilyInfo)
                    {
                        using (var trans = new Transaction(CurrentDocument))
                        {
                            trans.Start("Delete Family Map");
                            try
                            {
                                foreach (var familyInfo in LinkedFamilyToDelete)
                                {
                                    if (selectedLink.LinkedFamilies.ContainsKey(familyInfo.TargetTypeId))
                                    {
                                        selectedLink.LinkedFamilies.Remove(familyInfo.TargetTypeId);
                                        var tType = CurrentDocument.GetElement(familyInfo.TargetTypeId) as ElementType;
                                        if (null != tType)
                                        {
                                            var removed = MoverDataStorageUtil.RemoveLinkedFamilyInfo(tType);
                                        }
                                    }
                                }
                                trans.Commit();
                                if (LinkInstances.ContainsKey(selectedLink.InstanceId))
                                {
                                    LinkInstances.Remove(selectedLink.InstanceId);
                                    LinkInstances.Add(selectedLink.InstanceId, selectedLink);
                                }
                                MappingWindowInstance.RefreshLinkInstance();
                            }
                            catch (Exception ex)
                            {
                                trans.RollBack();
                                var message = ex.Message;
                            }
                        }
                    }
                    MappingWindowInstance.WakeUp();
                    break;

                case RequestId.None:
                    return;
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show("Failed to execute external event handler.\n" + ex.Message, "Execute External Event Handler", MessageBoxButton.OK, MessageBoxImage.Warning);
            }
        }
        private bool PickMappingElements(out Element sourceElement /*in link*/, out Element targetElement /*in host*/)
        {
            var picked = false;

            sourceElement = null;
            targetElement = null;
            using (var trans = new Transaction(CurrentDocument))
            {
                trans.Start("Pick Mapping Elements");
                try
                {
                    var selection = m_app.ActiveUIDocument.Selection;
                    var reference = PickLinkedElement();
                    if (null != reference)
                    {
                        var linkedInstanceId = reference.ElementId;
                        if (LinkInstances.ContainsKey(linkedInstanceId) && reference.LinkedElementId != ElementId.InvalidElementId)
                        {
                            var lip       = LinkInstances[linkedInstanceId];
                            var linkedDoc = lip.LinkedDocument;

                            var linkedElement = linkedDoc.GetElement(reference.LinkedElementId); //element in linked model
                            if (null != linkedElement)
                            {
                                if (null != linkedElement.Category)
                                {
                                    var categoryId             = linkedElement.Category.Id;
                                    var categoryName           = linkedElement.Category.Name;
                                    ISelectionFilter selFilter = new TargetElementSelectionFilter(categoryId);
                                    var secondReference        = selection.PickObject(ObjectType.Element, "Pick a target item in the host model. The required category should be " + categoryName);
                                    if (null != secondReference)
                                    {
                                        var eId     = secondReference.ElementId;
                                        var element = CurrentDocument.GetElement(eId);
                                        if (null != element)
                                        {
                                            ElementTypeInfo sourceTypeInfo = null;
                                            var             sourceTypeId   = linkedElement.GetTypeId();
                                            var             sourceType     = linkedDoc.GetElement(sourceTypeId) as ElementType;
                                            if (null != sourceType)
                                            {
                                                sourceTypeInfo = new ElementTypeInfo(sourceType);
                                            }

                                            ElementTypeInfo targetTypeInfo = null;
                                            var             targetTypeId   = element.GetTypeId();
                                            var             targetType     = CurrentDocument.GetElement(targetTypeId) as ElementType;
                                            if (null != targetType)
                                            {
                                                targetTypeInfo = new ElementTypeInfo(targetType);
                                            }

                                            if (null != sourceTypeInfo && null != targetType)
                                            {
                                                sourceElement = linkedElement;
                                                targetElement = element;
                                                picked        = true;
                                            }
                                            else
                                            {
                                                var strBuilder = new StringBuilder();
                                                strBuilder.AppendLine("Source Family Name: " + sourceTypeInfo.FamilyName + ", Source Type Name: " + sourceTypeInfo.Name);
                                                strBuilder.AppendLine("Target Family Name: " + targetTypeInfo.FamilyName + ", Target Type Name: " + targetTypeInfo.Name);
                                                strBuilder.AppendLine("");
                                                strBuilder.AppendLine("Would you like to proceed with creating a map?");
                                                var result = MessageBox.Show(strBuilder.ToString(), "Mismatch Name", MessageBoxButton.YesNo, MessageBoxImage.Question);
                                                if (result == MessageBoxResult.Yes)
                                                {
                                                    sourceElement = linkedElement;
                                                    targetElement = element;
                                                    picked        = true;
                                                }
                                                else
                                                {
                                                    picked = false;
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }

                    if (picked && null != sourceElement && null != targetElement)
                    {
                        var linkInfo = new LinkedElementInfo(LinkType.ByMap, sourceElement, targetElement, selectedLink.InstanceId, selectedLink.TransformValue);
                        if (selectedLink.LinkedElements.ContainsKey(linkInfo.LinkedElementId))
                        {
                            selectedLink.LinkedElements.Remove(linkInfo.LinkedElementId);
                        }
                        selectedLink.LinkedElements.Add(linkInfo.LinkedElementId, linkInfo);

                        var updated = MoverDataStorageUtil.UpdateLinkedElementInfo(linkInfo, targetElement);
                    }

                    trans.Commit();
                }
                catch (Exception ex)
                {
                    trans.RollBack();
                    MessageBox.Show("Failed to pick mapping elements.\n" + ex.Message, "Pick Mapping Elements", MessageBoxButton.OK, MessageBoxImage.Warning);
                }
            }
            return(picked);
        }