Announcements
Autodesk Community will be read-only between April 26 and April 27 as we complete essential maintenance. We will remove this banner once completed. Thanks for your understanding

Change element mark value in schedule

Anonymous

Change element mark value in schedule

Anonymous
Not applicable

Hello,

 

I am trying to change the "Mark" values of family objects in a schedule view, but I am having a bit of trouble. Is there a way to do this by element ID? Currently I am just listing information about each family elements on a specific row,  but I would like to change the mark parameters (or other string data).

 

This is what I have so far:

	public class renumberBOM : IExternalCommand
	{
        
        public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
		{
			// Get document ID
			UIApplication uiapp = commandData.Application;
            UIDocument uidoc = uiapp.ActiveUIDocument;
            //Application app = uiapp.Application;
            Document doc = uidoc.Document;
            
        	
        	// Get active view type
        	Autodesk.Revit.DB.View activeView = doc.ActiveView;
        	
        	////////////////////////////
        	// Count schedule entries //
        	////////////////////////////
        	// check if active view is a schedule
        	if (activeView is Autodesk.Revit.DB.ViewSchedule)
        	{
        		////////////////////////////////////
        		// Active/open view is a schedule //
        		////////////////////////////////////
        		// Create new container for output string
				StringBuilder sb = new StringBuilder();
				ViewSchedule view = doc.ActiveView as ViewSchedule;
				//if (view==null) return;
				TableData table =  view.GetTableData();
				TableSectionData section = table.GetSectionData(SectionType.Body);
				
				// Create list of element rows to loop though
				List<ElementId> elems = new FilteredElementCollector(doc,view.Id).ToElementIds().ToList();
				// Create list of elements on row
				List<Element> ElementsOnRow = new List<Element>();
				List<ElementId> Remaining = null;
				// Set static row (2) to analyze
				int RowToAnalyse = 2;
				
				using(Transaction t = new Transaction(doc, "dummy"))
				{
					t.Start();
					// Remove row (2) to analyze
					using (SubTransaction st = new SubTransaction(doc))
					{
						st.Start();
						section.RemoveRow(RowToAnalyse);
						st.Commit();
					}
					
					// Iterate through set of elements
					Remaining = new FilteredElementCollector(doc,view.Id).ToElementIds().ToList();
					t.RollBack();
				}
				
				
				// List each component in row (2)
				int ctr = 1;
                int startValue = 0;
                
				// Get each element in schedule row (2) based on ID
				foreach(ElementId id in elems)
				{
			  		if(Remaining.Contains(id)) continue;
				   	ElementsOnRow.Add(doc.GetElement(id));
				   	
				}
				
				// List quantity of elements on row (2)
				sb.AppendLine(string.Format("{0} elements on row {1}", ElementsOnRow.Count, RowToAnalyse));
				sb.AppendLine("");
				
				foreach(Element e in ElementsOnRow)
				{
				  	// List each family-element-ID, family-name and family-type, family-category
				  	sb.AppendLine(string.Format("<{0}> {1} {2} {3}", e.Id, e.Name, e.GetType(), e.Category));
				  	
				  	///////////////////////////////////////////////////////////
				  	///////////////////////////////////////////////////////////
				  	// Set element mark values here????
				  	///////////////////////////////////////////////////////////
				  	///////////////////////////////////////////////////////////

				}
				
				TaskDialog.Show("Schedule Data", sb.ToString());
        	}
        	else
        	{
        		// Active/open view is NOT a schedule
        		TaskDialog.Show("Error", "Make sure a schedule is opened before running this tool");
        	}
			
			
			
        	/////////////////////
        	// Show dialog box //
        	/////////////////////
			//TaskDialog.Show("AHI Dialog", "BOM/Schedule renumbered");
			
			
			return Result.Succeeded;
        }
        
		
	}

 

0 Likes
Reply
Accepted solutions (1)
2,159 Views
14 Replies
Replies (14)

TripleM-Dev.net
Advisor
Advisor

Hi,

 

The Schedule is a View, just use GetElementIds from the selection of the current UIDocument, it will return the selection made in the schedule (aka Selected Rows).

So no need for a static row number (why, is it a fixed nr as a selection test?).

 

Also I would never use the "Delete/Undo" trick in a release addin or working model, imagine the number of message that would need to be supressed and the amount of time involved on a large model.

 

The delete (RemoveRow) could be used like this, but it's result can't be clear.

Imagine a Generic Model schedule, deleting a entry could also delete a nested shared Generic model in it.

That would then also be missing from the schedule and counted as being on that row, and there are many more conditions like this.

 

The select method I mentioned above works for model elements (and I suspect for detailitem schedule).

It doesn't work for Sheet and View schedules.

Haven't tested it on material takeoff schedule, imaging it returning the Material or the element it's in (or both?)

Schedules with links, the elements in links won't be in the selection. 

 

Now you have the ElementID's of the selected elements use GetParameters Method to get the parameters of the element and change it's value (if it's not read-only)

 

- Michel

 

0 Likes

Anonymous
Not applicable

@TripleM-Dev.net  Thank you for the info, but I am having a little trouble using the getParameter() method. Am I on the right track here?  vvvv

 

// Create element collector to hold components
List<ElementId> elems = new FilteredElementCollector(doc,currentViewSchedule.Id).ToElementIds().ToList();
// Create list of elements on row
List<Element> ElementsOnRow = new List<Element>();
List<ElementId> rowElement = null;

// Get each element on schedule row based on ID
foreach(ElementId id in elems)
{
	// Check if element contains ID
	if(rowElement.Contains(id)) continue;
	// If yes, get element ID
   	ElementsOnRow.Add(doc.GetElement(id));
   	Parameter elemParameter = ElementsOnRow.get_Parameter("Mark");
}

 

0 Likes

TripleM-Dev.net
Advisor
Advisor
Accepted solution

You're calling get_Parameter on a list of Elements, you should call it on a Element itself!

ElementsOnRow.get_Parameter("Mark"); // ElementsOnRow = is a list not a Element!

 

I also don't get the static row number, if this something fixed in the addin that a certain row needs to be handled?

 

As I stated before; Schedules handle the same as views (exept for a couple of  categories like view/sheets)

 

 

Below a small sample;

It gets the current selection (which can be selected rows in a schedule) and gets the Builtin Mark parameter.

It buffers the existing value in a dictionary and then changes the Mark to a counter value.

 [Transaction(TransactionMode.Manual)]
    public class TestCommand_C12_MarkOfSelection : IExternalCommand
    {
        
        public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
        {
            UIDocument uidoc = commandData.Application.ActiveUIDocument;
            Autodesk.Revit.DB.Document doc = uidoc.Document;

            var OriginalValuesById = new Dictionary<int, string>();
            ICollection<ElementId> SelectedElements = uidoc.Selection.GetElementIds();

            using (Transaction trans = new Transaction(doc, "Change selected Elements Mark"))
            {
                trans.Start();

                int i = 1000;
                foreach (ElementId id in SelectedElements)
                {
                    Element elem = doc.GetElement(id);
                    Parameter para = elem.get_Parameter(BuiltInParameter.ALL_MODEL_MARK);
                    if (para is Parameter)
                    {
                        OriginalValuesById.Add(id.IntegerValue, para.AsString());
                        i += 1;
                        para.Set(i.ToString());
                    }
                }

                trans.Commit();
            }

            MessageBox.Show(string.Join(Environment.NewLine, OriginalValuesById.Select(v => v.Key.ToString() + ";" + v.Value))); //Quick display

            return Result.Succeeded;
                        
        }
    }

 

Hope this helps in you're addin, good luck.

 

- Michel

0 Likes

Anonymous
Not applicable

@TripleM-Dev.net thank you for the quick example. Ultimately my plan is to be able to open a schedule, click a button that I currently have in a custom ribbon bar and have it sequentially number each row of the schedule (either Mark value, or custom parameter). I started with an initial example that I had found on the forum, which is where the static row value came from but after seeing your post I reconfigured things a bit. I think your example may help me solve part of the trouble I was having, but it seems that I still have a bit of work to do.

 

At this point, it seems that I have to loop through each element in a given row to make sure that they match, increment "i" by 1, and then move to the next row. Thank you for your help so far.

 

0 Likes

Anonymous
Not applicable

@TripleM-Dev.net Thank you for the example, but after looking this over a bit more, it isn't quite what I am trying to do. I don't want to have to select elements to get parameters. Is there a way to get ElementIds for components on a row of a schedule?

 

I have tried searching for this, but all of the posts that I have found which are supposedly "solved" do not answer the question that is asked. Because there can and likely will be more than one component on a schedule-row, I am trying to get a list of element IDs for each element on a given row. Is there a method/parameter that would allow me to get this information based on what is shown in the schedule?

0 Likes

TripleM-Dev.net
Advisor
Advisor

Hi,

 

The sample code was mainly a sample of how to set/get the Mark parameter.

 

I don't know what the input / purpose / scope of the addin is, does the Schedule have a fixed filter, grouping etc or should it be able to work on any schedule no matter it's configuration.

 

I wouldn't bother at all with trying to figure out what's on each row of the schedule.

1. Have the Addin collect all elements in the Schedule (by using it as a view argument of a filteredelementcollector)

2. apply fixed grouping/sorting (Linq) to get a collection of grouped elements in the correct order and apply the mark renaming on those.

 

If you need the setup as defined in a random schedule;

1. Again get all elements defined by the schedule (see above)

2. use the Schedule definition (Definition Property) to determine how the elements are sorted/grouped.

See GetSortGroupFields  for this. And apply that to the retrieved elements.

No need to look into the filter settings of the schedule as the filtered elements are already retrieved by 1.

 

- Michel

0 Likes

Anonymous
Not applicable

Thank you again for the quick reply, I greatly appreciate your help and I will look into these properties/methods.

 

To answer your question though, I am trying to create a addin that numbers a schedule (pipe, pipe fitting, pipe accessory, etc) sequentially, regardless of how the view is filtered and/or sorted, to number each row of the schedule sequentially. I do this manually now to reference the BOM to the view(s) on the sheet, but doing this is quite tedious when things constantly change, so I would like to automate the numbering portion of this.

 

I was trying to use the schedule view because it will already be filtered/sorted the way that I want by the time I run this, and I thought that I could loop through the rows in order to get a list of elementIds based on the filter/sorting of each row. It seems that this may not be the way to do it, but my thought was to loop through each row, and get each elementId of each element on a row to set the "Mark" or custom parameter value for each element to number the schedule rows.

 

0 Likes

Anonymous
Not applicable

I have a start that seems to work so far, but am I on the right track? What does the sortGroupFields get a list of? RevitAPIdocs says that it returns "a copy of the sorting/grouping field" but I am not sure what that means exactly. How would I get a list of ElementIDs from this? Also, for some reason my variable sortGroupCount only returns an integer value of 2, but there are 4 rows, made up of 11 elements (pipe segments) on my schedule.

 

if (activeView is Autodesk.Revit.DB.ViewSchedule)
{
	using (Transaction trans = new Transaction(doc, "Get elements on schedule rows"))
    {
        trans.Start();
        
		// Convert "activeView" to data type ViewScheule
        ViewSchedule activeViewSchedule = activeView as ViewSchedule;
		// Get elements in schedule
		List<ElementId> elems = new FilteredElementCollector(doc, activeView.Id).ToElementIds().ToList();
		
		try
		{
			// Schedule sort group quantity
			int sortGroupCount = activeViewSchedule.Definition.GetSortGroupFields().Count();
			
			// Sort/group elements listed in schedule
			if (sortGroupCount != 0)
			{
				// Sort schedule data
				string stringSortGroupFields = activeViewSchedule.Definition.GetSortGroupFields().ToString();
				
				IList<ElementId> sortGroupElements = activeViewSchedule.Definition.GetSortGroupFields();
				
				foreach (ElementId id in sortGroupElements)
				{
					// Loop through element IDs here??
				}
			}
			else
			{
				// No elements in schedule to renumber
				TaskDialog.Show("Error", "No elements in schedule" );
			}
		}
		catch
		{
			// No elements in schedule to renumber
			TaskDialog.Show("Catch", "Catch exception" );
		}
		
		
        trans.Commit();
	}
}

 

 

0 Likes

TripleM-Dev.net
Advisor
Advisor

Hi @Anonymous ,

 


@Anonymous wrote:

How would I get a list of ElementIDs from this?

You don't, the GetSortGroupFields return the id's of the fields used in the Grouping/Sorting and it's sortorder (ASC/DESC). The fields represent the Fields used in the schedule (a parameter of the element).

 

From this you can build you're grouping/sorting logic to apply to the elements found in the ViewSchedule.

For example;

The elements are grouped/sorted by Level and length, you would need to apply a Grouping to the FilteredElementCollector by these 2 parameter/properties.

Once the grouped list is obtained apply a Mark value to each element per group that's unique for that group.

 

"to number each row of the schedule sequentially", does this mean you actually want row numbers (like excel)?

What do you do if a element occurs in 2 Schedules on a different row number?

 

 

 

 

 

0 Likes

Anonymous
Not applicable

Since Revit does not have a schedule "row number" that I am aware of (like Excel), I need to number the elements in a schedule using the Mark value (or a custom value) to associate them with the views on a given sheet. I use this to number each piece of pipe, number fittings, accessories, etc. to number every component on a sheet for pre-fabrication. Unfortunately, if the component is on a different row in a different schedule, I have to use a different parameter, to give the same piece different schedule numbers.

 

I know that programs like Autodesk Inventor have this functionality built into the software, but I don't know of a better way to do this in Revit. So, somehow I need to number every item/row on a schedule and this is the best way that I could come up with and have it linked to the model to tag each component, to reference the schedule from a view.

 

0 Likes

TripleM-Dev.net
Advisor
Advisor

So;

 

I assume elements are not fabricated twice, so each element should only occur once between schedules/in project?

Also, each row represents a single element correct?

 

Does the order of the elements in the schedule even matter?

Why not give each element a unique Mark value and sort the schedule by the Mark value, it's Tag and value in the Schedule would then match...

 

You even could sort the elements by their family/type name or whatever before applying sequential Mark values.

With this you can also overcome the limit of 4 group fields in schedules.

0 Likes

Anonymous
Not applicable

Correct, nothing is fabricated twice, but there are multiple views/sheets with the same components shown and tagged for reference. Each component does only occur once in a given project and schedule, but there are multiple schedules with the same components. Also the order matters because I would like the schedule to number rows in sequential order. I can't just sort items by family type exactly, because the schedule only shows one family type (pipe, fittings, accessories) but multiple quantities of the same family type on a given line.

 

To give you an example, here is a section of a drawing that is numbered to reference a schedule (below) in a specific room or area. There are also spool drawings (tags shown in this view) to reference other drawings of individual spool section/assemblies of pipe/fittings that have more detail for fabrication (also below).

 

jeremer_0-1613675181399.png

 

This is a schedule uses "piece number" to reference the components on the view above, when someone is assembling the pieces in a given area. I just chose "Mark" value initially because I thought it might make things easier. Note that the quantity/count varies per line, causing part of my trouble.

 

 

 

 

 

 

 

 

 

 

Below is a view of one spool assembly, for part of the piping in the area above, to separate sections of pipe/fittings onto drawings for fabrication. These drawings have their own pipe, fitting, accessory schedules, per sheet with multiple (iso, ortho) views to dimension and tag.

jeremer_1-1613675349954.png

 

Hopefully this helps clarify this a bit. Thanks again for all of the help

0 Likes

Anonymous
Not applicable

...looks like the schedule image did not upload for some reason. See below:

jeremer_0-1613677492439.png

 

0 Likes

andrew.gill4BKJZ
Explorer
Explorer

I have exactly the same problem, wanting to populate the mark value down the Schedule 1, 2, 3, 4...etc  automatically.

Did you find a solution?

0 Likes