I am working on a Task in which the code automatically opens a drawing selected by the user [in a UI] and selects all the objects in the drawing and starts to explode all the of them till they cant be exploded anymore. While doing this I face a problem, the original (un-exploded 3D object) is still present in the drawing, super imposed by the Exploded object. Every recursive call of the Explode function creates a new exploded 3D object of that object.
Here is a snippet of the code I working on:
PromptSelectionResult ss = ed.SelectAll();
using (DocumentLock acLckDoc = doc.LockDocument())
{
using (Transaction tr = db.TransactionManager.StartTransaction())
{
objs = new DBObjectCollection();
foreach (SelectedObject so in ss.Value)
{
Entity ent = (Entity)tr.GetObject(so.ObjectId, OpenMode.ForWrite);
if (!(ent is Solid3d))
{
ent.Explode(objs);
ent.UpgradeOpen();
ent.Erase();
}
}
tr.Commit();
}
}
As soon as the control comes on to the ent.Erase() statement - it throws an exception, eCannotBeErasedByCaller. I cant figure out why? I have unlocked all layers, opened the entity for Write, CommandFlags have been set to Session and UsePickSet (shuffled through all).
Anybody got any suggestions?
Looking at your description, you probably need a recursive explode. Sometime ago I did a code around this, for other type of entities, but you can adjust it.
private List<DBObject> FullExplode(Entity ent)
{
// final result
List<DBObject> fullList = new List<DBObject>();
// explode the entity
DBObjectCollection explodedObjects = new DBObjectCollection();
ent.Explode(explodedObjects);
foreach (Entity explodedObj in explodedObjects)
{
// if the exploded entity is a blockref or mtext
// then explode again
if (explodedObj.GetType() == typeof(BlockReference) ||
explodedObj.GetType() == typeof(MText))
{
fullList.AddRange(FullExplode(explodedObj));
}
else
fullList.Add(explodedObj);
}
return fullList;
}
source: http://adndevblog.typepad.com/infrastructure/2013/04/get-cogopoint-label-text.html
I finally found out the reason why the Original objects werent getting erased.
In the earlier part of the code, a AutoCAD Plant3D dwg is exported to AutoCAD (ExporttoAutoCAD / Saveas), this was creating Proxy items. These cant be deleted manually or via code.
Only way is to explode the PipeLines and Inline assets before exporting the file. This happens automatically if you export the file, but if you use saveas, you will have to explode the Pipe components before you export the file.
Wasted a lot of time understanding the cause, but finally got it!
Related
Is this activity-diagram showing well how my method works by first checking if the input is valid, if it isn't throw an exception, if it is: create 4 objects containing lists?
Not quite sure if activity diagrams are meant for this? Im trying to illustrate this:
public void MyMethod(string fileName) {
if (condition) {
var file = ReadFile("Levels", fileName);
object1 = new object1();
object1.Parse(file);
object2 = new object2();
object2.Parse(file);
object3 = new object3();
object3.Parse(file);
object4 = new object4();
object4.Parse(file);
} else {
throw new ArgumentException("Parser cannot load");
}
}
I'm new to UML. Not sure if the activity diagram is valid this way and if it even makes sense to others.
This is not a valid UML activity diagram. There are a number of ways to get a valid diagram. Personally, I would make the following changes:
Put yes and no in square brackets: [yes] and [no].
Reverse the direction of the flow connected to Throw exception.
Add a so-called activity final node (a circle with a bullet inside) behind Throw exception and add a flow from Throw exception to the final node.
Remove the green part of your diagram completely and replace it with an action Let every object parse itself.
Add an activity final node below Let every object parse itself and add a flow from Let every object parse itself to the final node.
FilteredElementCollector Lincoln = new FilteredElementCollector(doc);
Lincoln.OfCategory(BuiltInCategory.OST_RvtLinks); Autodesk.Revit.DB.View CurrentView = uiDoc.ActiveView;
ICollection<ElementId> Toggle_On = Lincoln.ToElementIds(); Toggle_On.Clear();
ICollection<ElementId> Toggle_Off = Lincoln.ToElementIds(); Toggle_Off.Clear();
List<Element> Processed = new List<Element>();
List<string> Revit_On = new List<string>(); List<string> Revit_Off = new List<string>();
List<string> Revit_Names = new List<string>();
foreach (Element One_Link in Lincoln)
{
string Revit_Name = One_Link.Name;
if (!Revit_Names.Contains(Revit_Name))//prevents processing same link twice;/but does NOT change anyway!!!!
{
Revit_Names.Add(Revit_Name);
Boolean Is_Hidden = One_Link.IsHidden(CurrentView);//
if (Is_Hidden)
{
Toggle_On.Add(One_Link.Id); Revit_On.Add(One_Link.Name);
}//this apparently does detect what is hidden;
else
{
Toggle_Off.Add(One_Link.Id); Revit_Off.Add(One_Link.Name);
}
}
}
Transaction Do_Toggle = new Transaction(doc, "DoToggle");
Do_Toggle.Start();
if (!Toggle_Off.Count.Equals(0)) { CurrentView.HideElements(Toggle_Off); }
if (!Toggle_On.Count.Equals(0)) { CurrentView.UnhideElements(Toggle_On); }
Do_Toggle.Commit();
Is somehow the transaction failing? Undo is not available, so it doesn't think it has done anything that might need to be undone. Note that this EXACT code is used in another one of my addins (in which multiple optional sub-programs are controlled by picking radio options on a form). But when I am trying to use the code in a standalone version, it fails (without errors). Note also that I've inserted multiple TaskDialog entries to verify that it is indeed finding the RvtLinks that are either visible or hidden in the current view. But it is simply refusing to actually change their visibility. If I run the dialog controlled version, everything toggles, but if I immediately run the standalone, nothing toggles (proving it isn't uneditable pinned links). I have made this option available to users by making "Toggle Links" the default so they can call up my collected program and just hit a carriage return, but I need this to be a true standalone.
You code confuses me. For instance, why do you initialise the Toggle_On and Toggle_Off collections with member values, only to clear them immediately afterwards?
In any case, you use of the transaction does not follow the recommended pattern of enclosing it in a using statement.
Please refer to The Building Coder topic group on Handling Transactions and Transaction Groups for more information on using transactions in the Revit API.
I have the following code that opens a session with RavenDB, gets the relevant IDs, uses those ideas to load the entities, change them, and finally save them.
List<EventDescriptor> events;
using (var session = raven.OpenSession())
{
session.Store(aggregate);
session.SaveChanges();
events = (from descriptor in session.Query<EventDescriptor>() where descriptor.AggregateId == aggregate.Id select descriptor).ToList();
}
using (var session = raven.OpenSession())
{
foreach (var #event in events)
{
var e = session.Load<EventDescriptor>("EventDescriptors/" + #event.Id.ToString());
e.Saved = true;
}
session.SaveChanges();
}
The problem however is that the changes in the entities don't seem to be tracked, and I can't delete the entities either (gives me unknown entity error), even though the object is loaded. I already tried calling SaveChanges inside the loop, but that didn't help either. I looked at the Raven documentation but I don't see what I'm doing wrong here.
Yes, we can't track changes on structs, because every time that you change them, you create a new copy
The problem was that EventDescriptor was a struct, and not a class. Changing this solved the problem. I assume it's because a struct is a valuetype and not a referencetype.
I have a program that uses clipboard but I want to restore the clipboard to its former state after I am done with it.
This is my code :
IDataObject temp = Clipboard.GetDataObject();
//Some stuff that change Cliboard here
Clipboard.SetText("Hello");
//Some stuff that change Cliboard here
Clipboard.SetDataObject(temp);
But it if I copy a text, and run this code, I get nothing on notepad.
NOTE : I can't use Clipboard.Contains because I want to preserve the Clipboard EXACLY how it was before, even if the user copied a file.
I cannot confirm whether this will work, but I see no reason why you shouldn't be able to back up the data using the longer approach of actually reading the data and restoring it afterwards.
Read here: http://msdn.microsoft.com/en-us/library/system.windows.forms.idataobject.aspx
You would do something like (pseudo-code)
//Backup
var lBackup = new Dictionary<string, object>();
var lDataObject = Clipboard.GetDataObject();
var lFormats = lDataObject.GetFormats(false);
foreach(var lFormat in lFormats)
{
lBackup.Add(lFormat, lDataObject.GetData(lFormat, false));
}
//Set test data
Clipboard.SetText("asd");
//Would be interesting to check the contents of lDataObject here
//Restore data
foreach(var lFormat in lFormats)
{
lDataObject.SetData(lBackup[lFormat]);
}
//This might be unnecessary
Clipboard.SetDataObject(lDataObject);
Is your application exiting after resetting the clipboard?
Assuming it is a Win Form app. (not sure how it works in wpf though)
You could use one of the other overloaded version of Clipboard.SetDataObject
public static void SetDataObject(object data, bool copy)
which preserves the data even after your app exits.
ex: in your case after removing the text content you could call Clipboard.SetDataObject(iDataObject, true);
EDIT:2
I Could source step through Clipboard.cs .NET Frameword 4 / VS 2010.
Download the .NET Framework 4 from here http://referencesource.microsoft.com/netframework.aspx.
Follow the below steps and if it asks for the source (Clipboard.cs) it would be in the Source sub-dir of the installation dir.
EDIT:1
Not sure why the same code doesn't work.
Cannot be a security/permission issue as the code doesn't throw an exception as you say.
There is another approach - source stepping into Framework code - Clipboard.cs
Based on the VS version and .NET framework it may vary ( I couldn't get the source stepping work for .NET 4 as the info is that the symbols with source support haven't yet been released). I'm trying my luck by downloading it manually from here (.NET Version 4)
If you are running VS 2008 and older version of .NET then the below steps should work for you.
More details are here. For .NET Framework 4 - here
This cannot be done. You cannot backup/restore the clipboard without causing unintended consequences.
Please see my post on a similar question. My answer is the one that starts with "It's folly to try to do this".
How do I backup and restore the system clipboard in C#?
Furthermore, I suspect that your motivation for wanting to backup/restore the clipboard is because you want to use it as a crutch to move data, without the user's knowledge or consent. Please read:
http://www.clipboardextender.com/developing-clipboard-aware-programs-for-windows/common-general-clipboard-mistakes
and
http://www.flounder.com/badprogram.htm#clipboard
Lastly, please read and understand this quote:
“Programs should not transfer data into our out of the clipboard without an explicit instruction from the user.” — Charles Petzold, Programming Windows 3.1, Microsoft Press, 1992
I tested the pseudocode from Lukas and found out doesn't work always, this works in all my tests:
// Backup clipboard
lBackup = new Dictionary<string, object>();
lDataObject = Clipboard.GetDataObject();
lFormats = lDataObject.GetFormats(false);
foreach (var lFormat in lFormats)
{
lBackup.Add(lFormat, lDataObject.GetData(lFormat, false));
}
//Set test data
Clipboard.SetText("asd");
//Restore clipboard
lDataObject = new DataObject();
foreach (var lFormat in lFormats)
{
lDataObject.SetData(lFormat, lBackup[lFormat]);
}
//This might be unnecessary
Clipboard.SetDataObject(lDataObject);
I have had success with this.
...to a certain degree.
Where I am currently falling down is trying to copy and restore Bitmaps of varying size.
I can successfully copy and restore a Bitmap of smallish size.
I then tried to do the same for (as the fore-warning Chris Thornton suggested) a gargantuan Excel worksheet with both thousands of cell data, as well as two sets of data on a graph, lying on the same worksheet.
I have found that the data copies and restores without problem. Where it falls down in this instance is allowing the 2-set graph with the worksheet copy.
If any of you have had a problem in copying and restoring Bitmaps, let me suggest what worked for me: when attempting to restore the Clipboard, iterate through the list of formats in reverse order and set each data object that way. (i.e. It seems that a Clipboard must be set in reverse order that it was copied in)
Regarding the case of the gargantuan Excel worksheet and accompanying graph, I also hit another stumbling block: I could not successfully copy the data object whose format was "Meta Data File". That could be the reason why Copy/Restore doesn't work in this case.
I got this far about two weeks ago, and tabled it for more pressing issues.
I wanted to put this out there to let anyone else trying to do the same that it seems like it can be done. (anything can be done in computer science. anything.)
I compiled this code and it seems to work for me. I am persisting via converting to and from json. (Note. It will not do steams so adapt if you need it to)
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
namespace Clipboard
{
class Program
{
static void Main(string[] args)
{
Execute(() =>
{
var backup = Backup();
System.Windows.Forms.Clipboard.SetText("text"); //just to change clipboard
Restore(backup);
});
}
private static void Execute(Action action)
{
var thread = new Thread(() => action());
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
thread.Join();
}
private static List<ClipboardItem> Backup()
{
var backup = new List<ClipboardItem>();
var data = System.Windows.Forms.Clipboard.GetDataObject();
System.Windows.Forms.Clipboard.SetDataObject(data, copy: true); //This seems to be needed to be able to serialize data later.
data = System.Windows.Forms.Clipboard.GetDataObject();
var formats = data.GetFormats(false).ToList();
formats.ForEach(f =>
{
if (data.GetData(f, false) != null && !(data.GetData(f, false) is Stream))
{
backup.Add(new ClipboardItem()
{
Format = f,
ObjectType = data.GetData(f, false).GetType(),
ObjectJson = JsonConvert.SerializeObject(data.GetData(f, false))
});
}
});
return backup;
}
private static void Restore(List<ClipboardItem> backup)
{
var data = new System.Windows.Forms.DataObject();
backup.ForEach(item =>
{
data.SetData(item.Format, JsonConvert.DeserializeObject(item.ObjectJson, item.ObjectType));
});
System.Windows.Forms.Clipboard.SetDataObject(data, copy: true);
}
}
public class ClipboardItem
{
public string Format { get; set; }
public Type ObjectType { get; set; }
public string ObjectJson { get; set; }
}
}
I'm trying to create a standalone application, which loads a ArcGis map, selects a few objects in one layer and zooms to them.
Loading and displaying the map does work, using something like this:
AxMapControl _mapControl;
// in constructor:
_mapControl = new AxMapControl();
// in loading
_mapControl.LoadMxFile(#"C:\Users\me\Documents\TestProject.mxd");
This does work nicely and does display the map as full extent (of course the AxMapControl is embedded into a WindowsFormsHost, but this shouldn't be a problem).
But now I need to select one or more objects and zoom to them. I tried to select in one layer for testing, but this does not work at all:
IFeatureSelection features = _mapControl.Map.Layer[0] as IFeatureSelection;
if (features != null)
{
QueryFilter qf = new QueryFilterClass();
qf.WhereClause = "[Name]='FS4711000'";
features.SelectFeatures(qf, esriSelectionResultEnum.esriSelectionResultNew, false);
}
on the SelectFeatures call I get an COM error 80004005 (E_Fail) in ESRI.ArcGIS.Carto, without much more explanation. Probably I'm doing it all wrong.
Maybe someone has a sample how to select objects in a layer?
I think your issue is as simple as the [square brackets] around your field name in the query string.
This works:
IFeatureSelection features = _currentLayer as IFeatureSelection;
if (features != null)
{
QueryFilter qf = new QueryFilter();
qf.WhereClause = "Type='1'";
features.SelectFeatures(qf, esriSelectionResultEnum.esriSelectionResultNew, false);
}
_axMapControl.Refresh();
Whereas this fails with COM-error E_FAIL:
IFeatureSelection features = _currentLayer as IFeatureSelection;
if (features != null)
{
QueryFilter qf = new QueryFilter();
qf.WhereClause = "[Type]='1'";
features.SelectFeatures(qf, esriSelectionResultEnum.esriSelectionResultNew, false);
}
_axMapControl.Refresh();
Also, notice that the map (or at least the IActiveView returned by AxMapControl.ActiveView) needs to be manually refreshed, or the selection is not displayed before the map is redrawn.