A few years ago, I was seeking a way to convert a list of elements into the current Selection (to use for "Copy To Level" or "Copy To Current View". My particular situation is post-program from a "Smart Filter" that allows the user to select multiple family names/types, not just "Structural Framing (girder)" as in Revit's built in filter.
The solution HAD BEEN:
SelElementSet SelSet = uiDoc.Selection.Elements;
SelSet.Add(Element1);
SelSet.Remove(Element2);
The problem is, this no longer seems to be working in Revit 2016 (+). Running the code with these lines now causes a program ending error:
"Revit encountered a System.MissingMethodException: Autodesk.Revit.UI.Selection.SelElementSet Autodesk.Revit.UI.Selection.Selection.get_Elements();"
(I assume the line "SelElementSet SelSet = uiDoc.Selection.Elements" invoked .get_Elements)
I am able (at the start of my program) to obtain the current selection using
Selection All_Guys = uiDoc.Selection;
and from this I can convert everything to Ilist or List etc., based on using Tree nodes to remove particular categories/family names/family types. But then I need to be able to convert this all back to the current selection (hopefully using SelSet.Remove(Element2) for the elements that do not match the filtering), and every time I use SelElementSet, I get the program ending error above.
Note that in September, 2014 I asked a SIMILAR question. I know there are powerful arbiters on this site that are itching to mark questions as already answered -- this goes under a category "previous answer no longer works". Please read the question more carefully and don't have it thrown out just because you have the power.
I have discovered that this is because SelElementSet was removed for Revit 2015 and beyond, and has been replaced with the following (type of) structure. In my example, I clear the selection and add specified elements, but I could also have ....elementids.Remove(One_Element) from another collection of elements:
if (SmartCopyLoad.ResetSelection)
{
ICollection<ElementId> elementIds = uiDoc.Selection.GetElementIds();
elementIds.Clear();
foreach (Element One_Element in SmartCopy.MatchingElements) { elementIds.Add(One_Element.Id); }
uiDoc.Selection.SetElementIds(elementIds);
return Autodesk.Revit.UI.Result.Succeeded;
}
The result of this is the specified elements as a collection (as SelElementSet used to allow).
Note also that a major part of the problem was using old References. My years old code still referenced RevitAPI from 2014, which allowed SelElementSet, but wouldn't work in Revit2016. A word of warning to others: use older references only if necessary for programs running in the older software. Since we are only using 2015 and beyond, I can use the newer references.
Related
Hello everybody,
around two or three month ago I started to learn Dynamo for Revit... finally :)
After learning and testing a lot, I got a few own scripts working. Then I learned Python, because I couldn't create the next script only with Dynamo-Nodes.
Then I thought "Let's see how difficult it is to get something done as a PlugIn".
I watched some Videos and read a lot of stuff.
Finally I got the Revit-AddIn-Wizard installed and made my first small Test-PlugIn.
Great...
Now I have a few problems which I do not understand... so I thought I will try my luck here... because I got so much information and help, reading through this site.
My goal was/is the following: (I tell you what I have now)
A form with a few buttons, comboboxes and a DataGridView.
I can load an Excelfile, click on "Show" to show it in the DataGridView.
The header of each row will be automatically put into 3 comboboxes.
In the first combobox you select the first search-parameter, in the second you CAN select another search-parameter and in the third combobox you select the parameter you want to set.
I have a checkbox to switch from type- to instance-parameter for the search- and the set-operation.
There is also a button which shows another small form with a list of categories (I won't search for ALL, only nearly all modelcategories).
PlugIn
I took me a lot of "watching Videos, reading through the internet, testing, testing and testing".
Thanks to this site here and a few others... I managed to get this whole PlugIn nearly 100% working.
But now I have a few strange issues and I have absolutely no clue on how to fix them or if it is possible. And I really hope that someone can help me.
First... I just tell you my problems and perhaps someone can say "this really IS an issue!" or that it is possible to get it done. Then I would post some code.
So... what do I do?!
1. I have a FilteredElementCollector which filters ALL elements.
2. Depending on my "Type/Instance-Checkboxes" I do .WhereElementIsElementType OR .WhereElementIsNotElementType.
3. Then it passes a MultiCategoryFilter to get the big list down to only the modelcategories.
4. Next, the collection passes one of ten different "methods" depending on all settings. There I filter this collection depending on the searchlists-comboboxes. When the combobox says "Familie" or "Typ" then it filters for ".BuiltInParameter.SymbolFamilyName" or ".Name" otherwise it just uses ".LookupParameter".
After that I have a collection with only the elements of selected categories which contains the values from the Excellist.
5. Depending on what my search- and set-settings are (e.g. search for type and set instance) I have to get the instances from the collected types or the other way around.
6. Then I pass it down to another method where I finally set the parameter.
So... Excelheader goes into comboboxes, depending on what you select in there it creates lists with the values of the selected rows.
I hope you all understand.
Now... where are my problems?
When I search for type-familynames or instance-parameter and set a typeparameter it works for ALL categories without any error.
1. When I try to set an instanceparameter (doesn't matter what my search-setting are) it works for all "normal" families but not for the systemfamilies (e.g. walls, floors, pipes etc.). No error, just nothing happens WHY? It seems that I cannot set an instance-parameter for system-families.
2. Roofs, Stairs, CurtainPanels and GenericModel make problems when I search for a typeparameter Error is something like "The object reference was not set to an object instance". Only with these 4 categories and it doesn't matter what I want to set... but when I search for family-/typeNAME or Instance-Parameter, then I can set type or instance and it works (except instance for sysfam).
3. When I try to search AND set an instance-parameter it works for ALL categories EXCEPT if one wall does not contain a search value... it really is enough that ONE wall does not have a search-param-value that everything will be cancelled.
I have a few other small problems... but I hope someone can help me with these problems... I would be extremely thankfull
greetings and have a nice day or night :)
Philipp
Tl; dr.
The three problems you describe sound like your own. I have no heard anybody else runAsk three separate questions and provide three separate minimal code snippets describing how they arise,. into those. I suggest that you create three separate independent minimal reproducible cases to demonstrate all three issues. Chances are, when you simplify and minimalise your code, the problem will go away. If it does not, it might just possibly be in a small and manageable enough state for other people to help you take a look at it. Given the long-winded description above, nobody in the world can help you.
Thank you for your answer Jeremy,
as I said, as a first start it is ok for me if you don't say "With theses categories, there are indeed some issues!"
I think I've managed to create 3 small examples of my problems.
For each problem I made a zip-file containing the complete visual-studio folder, a small exampleproject and a readme.txt with (I hope) enough information to understand everything in detail.
Problem1
Problem3
You only need to compile them or copy the .addin and .ddl files into the Revit AddIn folder. Then you get the new ribbons.
Short problem summary = I get problems when searching for parametervalues and setting values to another parameter.
Edit: I just solved the 2. problem when searching for familynames and setting system-families-parameter.
I used:
ElementClassFilter ecf = new ElementClassFilter(typeof(FamilyInstance));
FilteredElementColletor colle2 = new FilteredElementCollector(doc);
colle2.WherePasses(ecf);
I simply deleted the ClassFilter and do it now like in the other cases where I need instances.
FilteredElementCollector colle2 = new FilteredElementCollector(doc);
colle2.WhereElementIsNotElementType();
The 1. and 3. problem still exist :/
I would be thankful for any help someone can provide :)
I'm familiar with how to group a range in Excel VSTO/COM interop:
ws.EnableOutlining = true;
ws.Outline.SummaryRow = XlSummaryRow.xlSummaryAbove;
var rng = GetRangeSomeHow();
rng.EntireRow.Group();
rng.EntireRow.OutlineLevel = someLevel;
What is the most efficient way to do this in Excel-DNA? I would imagine there must be a C-API way to do it, encapsulated cleverly in Excel-DNA somehow, but for the life of me, I can't figure it out via online documentation (incl. Google).
There's a lot of posts using code similar to my sample above, but these are pretty expensive calls, especially since I need to do this ~5000 times overall (I have a really big data set).
EDIT:
So there seems to be this method call:
XlCall.Excel(XlCall.xlfGroup...)
The only problem is, I have no idea what the parameters are. It seems an ExcelReference should be passed in, but how is the .EntireRow resolved? Will the C API just handle it for me - in which case I just need to pass a new ExcelReference(1,100,1,1) and be done with it... or is there more to this?
Thanks in advance to anyone who can answer my question!
I don't think the C API GROUP function is te one you're looking for. The documentation says:
GROUP
Creates a single object from several selected objects and returns the
object identifier of the group (for example, "Group 5"). Use GROUP to
combine a number of objects so that you can move or resize them
together.
If no object is selected, only one object is selected, or a group is
already selected, GROUP returns the #VALUE! error value and interrupts
the macro.
I'd suggest you use the COM object model for this kind of thing, even in an Excel-DNA add-in. The C API has not really been updated over the years for the general sheet manipulation like this case, so you're likely to run into some features that don't work right or are incomplete relative to the COM object model.
From your Excel-DNA add-in, just make sure your get hold of the right Application root object with a call to ExcelDnaUtil.Application.
For improved performance of this kind of sheet editing, you pretty much have to use the same tricks as from VBA or VSTO - disable screen updating and calculations etc.
I've been having this problem for awhile in Visual Studio 2013. It doesn't seem to understand how to apply the indentation rules properly to lambda expressions when they've been lined up incorrectly. Here is a simplified example:
var s = new Action(() =>
{
});
In the second and third row, the indent is only 3 spaces instead of 4 (the real code example is much, much larger with the inner expression spanning hundreds of lines - this was checked in by my colleague and I'm trying to fix it). I've tried every combination of reformat code, document, re-creating the curly brace, etc. Nothing seems to work. It refuses to automatically update the indentation properly.
I normally wouldn't bother with it, but it causes all the code inside to be off by 1 character as well. When I'm typing lines in the middle, the tab/shift+tab markers are 1 character off from the lines above and below and I constantly have to adjust to get things lined up again. The closest thing I can find to reference this issue is this Connect Feedback from 2013 that is supposedly fixed, but I'm on Update 4 (released Nov 2014) and still experiencing the issue.
Short of manually going through and updating the indentation for every line in the lambda expression, does anyone have an idea how I can quickly fix this code?
Blatantly ignoring the issue in Visual Studio, and providing a solution to the problem right away. Hold alt to enable block selection, select all lines, and type a single space. Just to illustrate:
If you type Hello World!, the result would be:
As a 'rant': a single lambda should not contain hundreds of lines of code, it is a very big nono maintainability wise.
I'm building an Orchard content part for location data which include latitude and longitude fields. Whenever content is saved (created or updated), I would like to compute the bounding lat/lng for various max distances (20 miles, 50 miles, etc.) and save for later reference to search within a given radius of a specific location.
I already have all the necessary calculations for geolocation. The problem at hand is actually computing the derived value upon save (create/update), and setting the derived values to new fields on the content part before persisting to its repository.
I have a feeling adding filters like OnCreated in the associated ContentHandler might be a step in the right direction, but wasn't able to quickly locate any discussion related to a similar use case. So, I just wanted to reach out to the community and hear your thoughts on this particular problem before I proceed.
Thanks all!
You are on the right track with adding an OnCreated filter!
You could use OnUpdateEditorShape if you are only bothered about detecting when the content item is updated via the dashboard (or more generally, when the content item is updated using IContentManager.UpdateEditor(...)).
OnVersioning/OnVersioned will give you access to the "before" and "after" versions of a content item when it is updated, if your record class inherits from ContentPartVersionRecord (this will work with ContentPartRecord, but the "before" and "after" parameters will be the same).
You might want to look at this similar question.
Edit: "before" and "after" versions are called Existing and Building in VersionContentContext.
Basically, I have my DOM objects set up and am calling the .getAttribute method on an IMG element that is defined as below:
<IMG style="WIDTH: 134px; HEIGHT: 75px;" src="...">
Assuming hElement is the object reference to above element, when I call the following:
MsgBox hElement.getAttribute("style")
I get the following returned: "WIDTH" only, ie: part including and after the : character is ignored. To add to this, if I do a hElement.getAttribute("width") on the IMG element, it returns the actual width as "134" even though I don't explicitely have a width="..." attribute defined. So, basically, I am asking, how can I ensure I get back things as they are written, and not as they are reconstructed and stored by IE, as that is what it seems to be doing with the inferred WIDTH attribute. Also, not sure why it is ignoring everything including and after the : character right next to the WIDTH - how can I make this work properly?
Odd thing is, if I do the following, it shows everything as it should be:
hElement.outerHTML
The style attribute's value is shown as defined above. If I have to parse .outerHTML in order to get correct readings, that is just going to be depressing - I'm really disappointed in MS's half baked efforts. If you're looking for something extra to read, feel free to refer to another issue I noted about attributes returning odd behaviour when using capitals and when not (solved through a hack).
I'm using VB6, but it is all just the same, mshtml.dll, I gather, I am using IHTMLElement to define the hElement, not sure if I should be using something else but I think IHTMLElement is good for IE6+...?
UPDATE:
If I declare the hElement as IHTMLElement5 or IHTMLElement6 then it WORKS AS EXPECTED. If I use any of the following, it doesn't work: IHTMLElement, IHTMLElement2, IHTMLElement3 and IHTMLElement4. Since it only goes up to 6, only 5 & 6 work. It seems as though these are added later for newer versions of IE, and although you don't see the .getAttribute object in intellisense in 2 and above, you can still use it for some reason (not sure if its based on what IE version is installed). Does anyone know what version of IE IHTMLElement5 and IHTMLElement6 stands for? How can I get consistent behaviour for IE6+ as I don't have control on what versions are installed on the clients. If I use IHTMLElement5 or IHTMLElement6 does this mean it will not work on IE6 clients?
More:
Turns out IHTMLElement5 is for IE8+ and IHTMLElement6 is for IE9+ - any ideas or recommendations on how to get consistent behaviour for IE6+? Thanks.
UPDATE2: I have added a new question, which spawns from this question, it can be found here for those interested:
What happens when using IHTMLElement5/6 (for IE8/9) in IE6/7? Does it divert to IHTMLElement behaviour?
You might wanna take a look at this http://robertnyman.com/2006/04/24/get-the-rendered-style-of-an-element/
SO in your case you should try using the cssText property which will return a string consisting of all the css properties
hElement.style.cssText
Flags are not supported anymore? http://msdn.microsoft.com/en-us/library/ms536429%28v=vs.85%29.aspx
However, this works:
styleContent=hElement.style.cssText;
It seems, that getAttribute('style') returns an object instead of the value of the attribute, since the code below works too.
styleContent=hElement.getAttribute('style').cssText;