i have an item type that contains a property. this property has Is Array set to true and its type is a custom property type that i made.
trying to figure out how to add a value to this array property.
Not seeing it in the sdk. i see there is a XmlStringValue that is a get/set. but when i try to set it, it just crashes. assuming i cant use that to add a new value to an array but could use it to change existing values??
Anyways anyone see a way to do this. i hope im just missing it.
EDIT:
adding some code that i have, the array property is named Values, the custom type is named linker. the custom type contains several string properties...
CustomItemHost host = new CustomItemHost(Views.LinkSetup._MainWindowViewModel.selTextElement, false); IList<BD.DgnEC.IDgnECInstance> ItemTypes = host.CustomItems; //loop each itemtype attached to element foreach (BD.DgnEC.IDgnECInstance curItemType in ItemTypes) { //find item type with specfied name if (curItemType.ClassDefinition.Name.ToUpper().Contains(Globals.itemTypeName.ToUpper())) {//found, update //find array property (its named Values) foreach (var curprop in curItemType) { if (curprop.Property.DisplayLabel.ToUpper() == "VALUES") { //add entry to values array //stuck on how to do this.. //try and modify xml string to add another value to the VALUES propety (which is a custom propety tyoe named linker) string newxmlstring = curprop.XmlStringValue; newxmlstring.Replace("</Values>", ""); newxmlstring += "<linker><isFromReference>False</isFromReference><ElementID>5</ElementID><ModelName>f</ModelName><PropertyName>df</PropertyName><FilePath>test2</FilePath><PropertyType>Element</PropertyType><PropertyValue>df</PropertyValue><AccessString>afsdfdfasf</AccessString><ItemTypeName /></linker></Values>"; //causes crash curprop.XmlStringValue = newxmlstring; } } } }
i was thinking i would need to get the custom property type and populate it with what i want, then add it as a value to the item type property. but see nothing in the SDK for this...i starting to do this to get the property type...
BD.ItemTypeLibrary OHDOTLinkersLib = BD.ItemTypeLibrary.FindByName(Globals.itemTypeLibName, activeFile); BD.ItemType it_TextFieldLinker = OHDOTLinkersLib.GetItemTypeByName(Globals.itemTypeName); BD.CustomProperty itProp = it_TextFieldLinker.GetPropertyByName("Values"); BD.CustomPropertyType itPropType = OHDOTLinkersLib.GetCustomTypeByName("linker");
Editing the XML directly sounds like a recipe for disaster...
All the EC stuff originated as C# code. The API is extremely broad. I believe it is documented separately as part of the ECFramework.
In this particular case your "values" property is an IECArrayValue. IECArrayValue.Add() appends a new entry to the array and returns it. You can then modify the properties of that returned object (assuming it is a struct type).
Robert Hook can hopefully tell you where to find the ECFramework documentation.
Answer Verified By: John Drsek
Paul Connelly said:Editing the XML directly sounds like a recipe for disaster...
I did not want to edit the xml just wasn't seeing anything else.
I swear the first thing I tried was exactly what your saying but there was no Add function.
Just went back and checked and I had it as a ECDArrayValue but casting it to IECArrayValue then gives me the add method.
I figured it was going to be something simple, cant tell you how long I was messing around with this
THANK YOU!..ill post some code in a little bit for anyone that might find it beneficial..
....side note...
it appears if the array property does not have an entry yet it wont show up as a property when looping the itemtype properties.
CustomItemHost host = new CustomItemHost(Views.LinkSetup._MainWindowViewModel.selTextElement, false); IList<BD.DgnEC.IDgnECInstance> ItemTypes = host.CustomItems; foreach (BD.DgnEC.IDgnECInstance curItemType in ItemTypes) { if (curItemType.ClassDefinition.Name.ToUpper().Contains(Globals.itemTypeName.ToUpper())) {//found, update foreach (var curprop in curItemType) {//entpy array property wont show up in this loop!! } } }
have to look into this.. but thinking you need to use something like this
curItemType.ClassDefinition.FindLocalProperty("Values");
then somehow add it to the item type instance...
Hi John,
I am late in this discussion, so just a few comments ;-)
There are several topics presented in the discussion:
John Drsek said:I figured it was going to be something simple, cant tell you how long I was messing around with this
I have no CE code available to share, all my production EC Framework code is still V8 based, but concepts and API structure remain the same. When working with ItemTypes (which I do not plan at all, to create custom EC schemas is easier and less limiting for me ;-), it's crucial to understand what is "behind the scene":
From code perspective (extending information provided by Paul):
John Drsek said:it appears if the array property does not have an entry yet it wont show up as a property when looping the itemtype properties.
I have not tested it, but it seems to be logical: Whereas a property definition exists always in ECClass definition, the value instance existence is not mandatory. I think foreach enumeration can be imagined as enumerating XML structure, so when a node (representing a particular property) does not exists, because its value has not been set, it's not included in the enumeration.
I think you should use IECInstance.GetEnumerator(bool includeAll) when you want to receive also not existing (not set yet) properties. See ecobjects.chm for more information how to work with temporary IECPropertyValue instances.
John Drsek said:if (curItemType.ClassDefinition.Name.ToUpper().Contains(Globals.itemTypeName.ToUpper()))
I do not know context (and in fact I do not know CE EC API well enough yet too ;-), but it looks really weird. What is an intention of this test?
With regards,
Jan
Bentley Accredited Developer: iTwin Platform - AssociateLabyrinth Technology | dev.notes() | cad.point
Jan, its been awhile but im getting back to this. first thank you for your input
Jan Šlegr said:I have not tested it, but it seems to be logical: Whereas a property definition exists always in ECClass definition, the value instance existence is not mandatory. I think foreach enumeration can be imagined as enumerating XML structure, so when a node (representing a particular property) does not exists, because its value has not been set, it's not included in the enumeration. I think you should use IECInstance.GetEnumerator(bool includeAll) when you want to receive also not existing (not set yet) properties. See ecobjects.chm for more information how to work with temporary IECPropertyValue instances.
this is exactly whats going on.. i think. but im still having trouble getting this to work. when i use IECArrayValue.Add(). and then check the count on IECArrayValue the count goes up by one (meaning it was added). but i cant seem to actually get that to apply to the element. if i just use the IECArrayValue.Add() and then finish the method like that, when i go and check the element the additional struct in the property array its not there. Is there something else i need to do to apply or save after i call the Add method??
the other thing i dont know if im doing right is when i go and set values for properties in that new struct that i just added to the property array.
here is my code
//get host container for item types on selected element CustomItemHost host = new CustomItemHost(_MainWindowViewModel.selElementData.selTextElement, false); //get host container for item types on selected element IList<BD.DgnEC.IDgnECInstance> ItemTypes = host.CustomItems; //loop item types on selected element foreach (BD.DgnEC.IDgnECInstance curItemType in ItemTypes) { //find desired item type instance if (curItemType.ClassDefinition.Name.ToUpper() == GlobalVars.LinkeritemTypeName.ToUpper()) {//found, update //loop each property in item type instance on element foreach (var curprop in curItemType) { if (curprop.Property.DisplayLabel.ToUpper() == "XRef".ToUpper()) {//found array property, property is a struct (has multiple sub-properties) //add another entry to array Bentley.ECObjects.Instance.IECArrayValue curproptest = (Bentley.ECObjects.Instance.IECArrayValue)curprop; Bentley.ECObjects.Instance.IECPropertyValue newentry = curproptest.Add(); //get enumerator to get to temporary IECPropertyValues (properties that dont have values) Bentley.ECObjects.Instance.IECValueContainer test = (Bentley.ECObjects.Instance.IECValueContainer)curproptest[newentry.ArrayIndex]; IEnumerator<IECPropertyValue> iecPropvals2 = test.GetEnumerator(true); //loop through properties in the new stuct that was added to the property array while (iecPropvals2.MoveNext()) { if (iecPropvals2.Current.AccessString.ToUpper().Contains("TestProperty")) { //cant set value on Temporary IECPropertyValue get permanent one (per ECObjects.chm) IECPropertyValue permanent = iecPropvals2.Current.Instance[iecPropvals2.Current.AccessString]; permanent.StringValue = "I DID IT"; break; } } break; } } break; } }
any help??
can you post your DGN to illustrate, how your ItemType definition looks like? E.g. DGN with ItemType definition plus one element with some data attached?
Regards,
yeah its attached,
4118.testing.dgn
So basically in SS4 we had a cross reference app.when a plan sheet had a text element that called out to another sheet like "FOR DETAILS SEE SHEET 7" we had an app that would make the the sheet number a data entry region and would store details about the dgn file/model that sheet 7 existed at. then the app has an update mode. so if at the end of the project the page number changed you could run this app and it would update the text element to show the correct sheet number.
So im remaking that for CONNECT and thought text fields and item types would be easier.
basically i want to do exactly what a text field does. but i cant have a text field link to an element property from another file/model thats not referenced. so i made a item type that will get applied to the text element. this item type contains the needed information to find the correct value(sheet number) in another dgn file. The app will add a text field to the text element that represents the sheet number. this text field is linked to the property in the item type that represents the sheet number.
the item type property as an array because the text element could have more then one cross reference. Now all the app needs to do to update the values is to just update the item type value and the text field will automatically get updated so i dont actually have to edit the text element itself to update the value.
hopefully that made sense. but regardless. my example code above is just adding a new struct to an array property then updating the property values for that new struct in the array. dont know why its not working. but it could be applied to numerous cases.
wow i cant believe i forgot to use .WriteChanges() on my item type to apply the changes...ugh that was stupid my code above works.
John Drsek said:wow i cant believe i forgot to use
Welcome to the club! :-))) ... such things happen every day
John Drsek said:here is my code
The code looks a bit weird to me, even when I guess functional (when WriteChanges() is used). Maybe it's only because of using full classes names including namespaces (which I treat as very bad habit personally). But e.g. to use DisplayString to evaluate anything is wrong idea generally.
Not sure whether I will find enough time, but I should think whether better code (whatever it means) can be written.
John Drsek said:So im remaking that for CONNECT and thought text fields and item types would be easier.
I agree. Not because of text fields and item types specifically, but because of using standard MicroStation features.
And item types are always good, because when not sufficient, can be extended to general EC data, that can handle ... anything ;-)
John Drsek said:basically i want to do exactly what a text field does.
Well, you can ... but you can't. The text fields background is (I guess) really complex, implemented using more other technologies (DependencyManager at the first case), so to achieve the same functionality is about to use C++ (NET is not enough) and many months of work. But it's probably not your aim in fact.
John Drsek said:so i dont actually have to edit the text element itself to update the value.
This behavior makes the whole solution much (much much much) easier :-)
John Drsek said:hopefully that made sense
Yes, it does :-)
Of course when I read your explanation, some questions arose, but the main idea is fine I think.
John Drsek said: i want to do exactly what a text field does
The API for text and text node elements in CONNECT revolves around the TextBlock class. The C++ MicroStationAPI provides TextBlock.InsertField() etc., but I don't see those methods in the DgnPlatformNet .NET API.
TextBlock
TextBlock.InsertField()
File a request to have the .NET TextBlock expanded to include TextField manipulation.
John Drsek said:all the app needs to do to update the values is to just update the item type value and the text field will automatically get updated
Correct! Unfortunately, C++ is again ahead of .NET. The MicroStationAPI provides the TextField class, but I can't find that in the .NET documentation (but that could be me — I often fail to find things in the multi-file .NET documentation).
TextField
File another request to have the TextField included in .NET.
Jan Šlegr said:the main idea is fine I think
I agree with Jan, but you're hamstrung by deficiencies in the .NET API.
Regards, Jon Summers LA Solutions
Jan Šlegr said:The code looks a bit weird to me, even when I guess functional (when WriteChanges() is used). Maybe it's only because of using full classes names including namespaces (which I treat as very bad habit personally).
i dont normally use full namespaces but when i post code i copy the part and edit it up to either show the using statements or add in the full namespaces to help other users. i guess i could also add what dll they are coming from to be complete. Also this code was after 2 hours of trying many different ways of doing the same thing trying to get it to work so it got a little messy in my frustration (until i remembered the write changes). i have since cleaned it up a little bit.
Jan Šlegr said:But e.g. to use DisplayString to evaluate anything is wrong idea generally.
i just gravitate towards the display string because i normally try to write the app as dynamic as i can so others can apply it. so ill have the app read a config variable to get names of things like item types. and its easier for others to use the name they see in their item types when setting the config variable. I still have a validation method to ensure the found item type meets the min. requirements. so i see what your saying but i think i built in enough safe guards to use it. so i hardcoded some text with i will be reading in instead in the final app.
Jan Šlegr said:The text fields background is (I guess) really complex, implemented using more other technologies (DependencyManager at the first case), so to achieve the same functionality is about to use C++ (NET is not enough) and many months of work. But it's probably not your aim in fact.
yeah i have figured that out. i had another post where i posted my c++ function exported to C and then wrapper in c# to just add the text field.for now thats all i need.
as always thanks for your input
Jon Summers said:Correct! Unfortunately, C++ is again ahead of .NET. The MicroStationAPI provides the TextField class, but I can't find that in the .NET documentation (but that could be me — I often fail to find things in the multi-file .NET documentation). File another request to have the TextField included in .NET.
yes i had to export a c++ function to c and then wrap it in C# to add a text field, it was a pain but got it working. thats all i need for now. didnt even try to figure out how to enter the field to a specific spot..maybe version 2.
i can put in a S.R. for get it added. last time i submitted a defect on an api call can was told all SDK related stuff should be posted to the communities.
John Drsek said:Last time I submitted a defect on an API call can was told all SDK related stuff should be posted to the communities
Others have implied that to summon Robert Hook is our best (and possibly only) recourse. Prepare a pentagram and light candles.
Jon Summers said:Prepare a pentagram and light candles.
Maybe also to make a floral sacrifice and to do some invocation? :-)