Hi everyone 🙂
As far as I understood the rooms are responsible for creating spaces in ifc files. As I want to have an organized ifc file, I want to change the associated level of rooms to have them on the right building story. I see both using c# and user interface, the level parameter of the rooms is "readonly" and it is not possible to modify it. Is there any other method I can use to reach my goal? for example copying rooms or creating new rooms? Does anyone have experience?
Thanks
Solved! Go to Solution.
Solved by Sean_Page. Go to Solution.
I think the best way to go is probably to delete the existing rooms and create new ones.
I found that with Dynamo you can group rooms and change the level accordingly. Unfortunately the room ID changes once you ungroup the elements again. Is there really no other way by now instead of deleting and replacing?
I am not aware of any better way. Sorry.
This is untested, but if you are worried about losing the Id once you ungroup, could you try getting and holding onto the GUID of the room element rather than the ElementId?
Also, I would suggest that you Cut and Paste (Copy) the rooms to retain ALL the parameters, then delete the one that you don't need. You could also then transfer any remaining data like Room numbers that need to be updated. This is essentially just like you would have to do it manually in Revit, just automated.
I don't know any way of changing the level direct.
But if you delete the room, you get a warning that a room is deleted but still exists in the project. (the "not placed" message in room schedule)
Now place a new room at the correct level, but don't place a entirely new room but pick the correct unplaced room.
It will have all thesame properties as the deleted one (also it's roomnumber!)
So the concept of this intrigued me so I set off on a way to make this happen and I think I have an OK solution that does not involve groups or really an work around at all.
using Autodesk.Revit.DB.Architecture;
using Autodesk.Revit.DB;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace YourApp.Testing
{
public partial class TestForm : System.Windows.Forms.Form
{
Document doc;
public TestForm(Document _doc)
{
InitializeComponent();
doc = _doc;
}
private void btnClose_Click(object sender, EventArgs e)
{
Close();
}
private void TestForm_Load(object sender, EventArgs e)
{
foreach (Room room in Helpers.Collectors.ByCategory(doc, BuiltInCategory.OST_Rooms))
{
if (room.Area > 0 && room.Location != null)
{
ListViewItem item = listView1.Items.Add(room.Number + " - " + room.Name);
item.Tag = room;
}
}
SortedList<string, Level> levels = new SortedList<string, Level>();
foreach (Level level in Helpers.Collectors.ByCategoryNotElementType(doc, BuiltInCategory.OST_Levels))
{
levels.Add(level.Name, level);
}
cboLevel.DataSource = levels.ToList();
cboLevel.DisplayMember = "Key";
cboLevel.ValueMember = "Value";
}
private void btmOK_Click(object sender, EventArgs e)
{
try
{
using (TransactionGroup transGroup = new TransactionGroup(doc))
{
transGroup.Start("Move Rooms");
foreach (ListViewItem item in listView1.CheckedItems)
{
Room room = (Room)item.Tag;
LocationPoint rmPT = room.Location as LocationPoint;
XYZ roomPoint = rmPT.Point;
double offset = room.LimitOffset;
using (Transaction trans1 = new Transaction(doc))
{
trans1.Start("Unplace Room");
FailureHandlingOptions failOptions = trans1.GetFailureHandlingOptions();
failOptions.SetFailuresPreprocessor(new UnplacedRoomWarning());
trans1.SetFailureHandlingOptions(failOptions);
room.Unplace();
trans1.Commit();
}
using (Transaction trans2 = new Transaction(doc))
{
trans2.Start("New Room");
Level level = (Level)cboLevel.SelectedValue;
PlanTopology topo = doc.get_PlanTopology(level);
PlanCircuitSet circuits = topo.Circuits;
foreach (PlanCircuit circuit in circuits)
{
if (!circuit.IsRoomLocated)
{
Room newRoom = doc.Create.NewRoom(room, circuit);
newRoom.get_Parameter(BuiltInParameter.ROOM_UPPER_LEVEL).Set(level.Id);
newRoom.LimitOffset = offset;
XYZ move = new XYZ(roomPoint.X - rmPT.Point.X, roomPoint.Y - rmPT.Point.Y, rmPT.Point.Z);
ElementTransformUtils.MoveElement(doc, newRoom.Id, move);
break;
}
}
trans2.Commit();
}
}
transGroup.Assimilate();
Close();
}
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
}
public class UnplacedRoomWarning : IFailuresPreprocessor
{
public FailureProcessingResult PreprocessFailures(FailuresAccessor failuresAccessor)
{
IList<FailureMessageAccessor> failures = new List<FailureMessageAccessor>();
failures = failuresAccessor.GetFailureMessages();
foreach (FailureMessageAccessor failure in failures)
{
if (failure.GetSeverity() == FailureSeverity.Warning)
{
if (failure.GetFailureDefinitionId() == BuiltInFailures.RoomFailures.RoomUnplaceWarning || failure.GetFailureDefinitionId() == BuiltInFailures.RoomFailures.RoomHeightNegative)
{
failuresAccessor.DeleteWarning(failure);
}
}
else
{
failuresAccessor.ResolveFailure(failure);
return FailureProcessingResult.ProceedWithCommit;
}
}
return FailureProcessingResult.Continue;
}
}
}
Form Designer Code
namespace YourApp.Testing
{
partial class TestForm
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.btnClose = new System.Windows.Forms.Button();
this.btmOK = new System.Windows.Forms.Button();
this.listView1 = new System.Windows.Forms.ListView();
this.columnHeader3 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
this.cboLevel = new System.Windows.Forms.ComboBox();
this.label1 = new System.Windows.Forms.Label();
this.label2 = new System.Windows.Forms.Label();
this.SuspendLayout();
//
// btnClose
//
this.btnClose.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.btnClose.Location = new System.Drawing.Point(216, 526);
this.btnClose.Name = "btnClose";
this.btnClose.Size = new System.Drawing.Size(75, 23);
this.btnClose.TabIndex = 0;
this.btnClose.Text = "Close";
this.btnClose.UseVisualStyleBackColor = true;
this.btnClose.Click += new System.EventHandler(this.btnClose_Click);
//
// btmOK
//
this.btmOK.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.btmOK.Location = new System.Drawing.Point(297, 526);
this.btmOK.Name = "btmOK";
this.btmOK.Size = new System.Drawing.Size(75, 23);
this.btmOK.TabIndex = 1;
this.btmOK.Text = "OK";
this.btmOK.UseVisualStyleBackColor = true;
this.btmOK.Click += new System.EventHandler(this.btmOK_Click);
//
// listView1
//
this.listView1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.listView1.CheckBoxes = true;
this.listView1.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
this.columnHeader3});
this.listView1.FullRowSelect = true;
this.listView1.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.None;
this.listView1.HideSelection = false;
this.listView1.Location = new System.Drawing.Point(12, 27);
this.listView1.Margin = new System.Windows.Forms.Padding(3, 5, 3, 5);
this.listView1.Name = "listView1";
this.listView1.Size = new System.Drawing.Size(360, 464);
this.listView1.Sorting = System.Windows.Forms.SortOrder.Ascending;
this.listView1.TabIndex = 2;
this.listView1.UseCompatibleStateImageBehavior = false;
this.listView1.View = System.Windows.Forms.View.Details;
//
// columnHeader3
//
this.columnHeader3.Text = "";
this.columnHeader3.Width = 350;
//
// cboLevel
//
this.cboLevel.FormattingEnabled = true;
this.cboLevel.Location = new System.Drawing.Point(78, 499);
this.cboLevel.Name = "cboLevel";
this.cboLevel.Size = new System.Drawing.Size(294, 21);
this.cboLevel.TabIndex = 3;
//
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(12, 502);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(60, 13);
this.label1.TabIndex = 4;
this.label1.Text = "Pick Level:";
//
// label2
//
this.label2.AutoSize = true;
this.label2.Location = new System.Drawing.Point(12, 9);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(112, 13);
this.label2.TabIndex = 4;
this.label2.Text = "Select Placed Rooms:";
//
// TestForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(384, 561);
this.Controls.Add(this.label2);
this.Controls.Add(this.label1);
this.Controls.Add(this.cboLevel);
this.Controls.Add(this.listView1);
this.Controls.Add(this.btmOK);
this.Controls.Add(this.btnClose);
this.Name = "TestForm";
this.Text = "TestForm";
this.Load += new System.EventHandler(this.TestForm_Load);
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.Button btnClose;
private System.Windows.Forms.Button btmOK;
private System.Windows.Forms.ListView listView1;
private System.Windows.Forms.ColumnHeader columnHeader3;
private System.Windows.Forms.ComboBox cboLevel;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.Label label2;
}
}
I am not really sure why some of my posts don't stick around, but I had an interest in this post and so I think this is a solution to the issue, with maybe a little extra.
Here is the command code:
using Autodesk.Revit.DB.Architecture;
using Autodesk.Revit.DB;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace MyApp.Testing
{
public partial class TestForm : System.Windows.Forms.Form
{
Document doc;
public TestForm(Document _doc)
{
InitializeComponent();
doc = _doc;
}
private void btnClose_Click(object sender, EventArgs e)
{
Close();
}
private void TestForm_Load(object sender, EventArgs e)
{
foreach (Room room in Helpers.Collectors.ByCategory(doc, BuiltInCategory.OST_Rooms))
{
if (room.Area > 0 && room.Location != null)
{
ListViewItem item = listView1.Items.Add(room.Number + " - " + room.Name);
item.Tag = room;
}
}
SortedList<string, Level> levels = new SortedList<string, Level>();
foreach (Level level in Helpers.Collectors.ByCategoryNotElementType(doc, BuiltInCategory.OST_Levels))
{
levels.Add(level.Name, level);
}
cboLevel.DataSource = levels.ToList();
cboLevel.DisplayMember = "Key";
cboLevel.ValueMember = "Value";
}
private void btmOK_Click(object sender, EventArgs e)
{
try
{
using (TransactionGroup transGroup = new TransactionGroup(doc))
{
transGroup.Start("Move Rooms");
foreach (ListViewItem item in listView1.CheckedItems)
{
Room room = (Room)item.Tag;
LocationPoint rmPT = room.Location as LocationPoint;
XYZ roomPoint = rmPT.Point;
double offset = room.LimitOffset;
using (Transaction trans1 = new Transaction(doc))
{
trans1.Start("Unplace Room");
FailureHandlingOptions failOptions = trans1.GetFailureHandlingOptions();
failOptions.SetFailuresPreprocessor(new UnplacedRoomWarning());
trans1.SetFailureHandlingOptions(failOptions);
room.Unplace();
trans1.Commit();
}
using (Transaction trans2 = new Transaction(doc))
{
trans2.Start("New Room");
Level level = (Level)cboLevel.SelectedValue;
PlanTopology topo = doc.get_PlanTopology(level);
PlanCircuitSet circuits = topo.Circuits;
foreach (PlanCircuit circuit in circuits)
{
if (!circuit.IsRoomLocated)
{
Room newRoom = doc.Create.NewRoom(room, circuit);
newRoom.get_Parameter(BuiltInParameter.ROOM_UPPER_LEVEL).Set(level.Id);
newRoom.LimitOffset = offset;
XYZ move = new XYZ(roomPoint.X - rmPT.Point.X, roomPoint.Y - rmPT.Point.Y, rmPT.Point.Z);
ElementTransformUtils.MoveElement(doc, newRoom.Id, move);
break;
}
}
trans2.Commit();
}
}
transGroup.Assimilate();
Close();
}
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
}
public class UnplacedRoomWarning : IFailuresPreprocessor
{
public FailureProcessingResult PreprocessFailures(FailuresAccessor failuresAccessor)
{
IList<FailureMessageAccessor> failures = new List<FailureMessageAccessor>();
failures = failuresAccessor.GetFailureMessages();
foreach (FailureMessageAccessor failure in failures)
{
if (failure.GetSeverity() == FailureSeverity.Warning)
{
if (failure.GetFailureDefinitionId() == BuiltInFailures.RoomFailures.RoomUnplaceWarning || failure.GetFailureDefinitionId() == BuiltInFailures.RoomFailures.RoomHeightNegative)
{
failuresAccessor.DeleteWarning(failure);
}
}
else
{
failuresAccessor.ResolveFailure(failure);
return FailureProcessingResult.ProceedWithCommit;
}
}
return FailureProcessingResult.Continue;
}
}
}
Simple Win Form Layout:
Here is the Win Form designer code:
namespace MyApp.Testing
{
partial class TestForm
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.btnClose = new System.Windows.Forms.Button();
this.btmOK = new System.Windows.Forms.Button();
this.listView1 = new System.Windows.Forms.ListView();
this.columnHeader3 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
this.cboLevel = new System.Windows.Forms.ComboBox();
this.label1 = new System.Windows.Forms.Label();
this.label2 = new System.Windows.Forms.Label();
this.SuspendLayout();
//
// btnClose
//
this.btnClose.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.btnClose.Location = new System.Drawing.Point(216, 526);
this.btnClose.Name = "btnClose";
this.btnClose.Size = new System.Drawing.Size(75, 23);
this.btnClose.TabIndex = 0;
this.btnClose.Text = "Close";
this.btnClose.UseVisualStyleBackColor = true;
this.btnClose.Click += new System.EventHandler(this.btnClose_Click);
//
// btmOK
//
this.btmOK.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.btmOK.Location = new System.Drawing.Point(297, 526);
this.btmOK.Name = "btmOK";
this.btmOK.Size = new System.Drawing.Size(75, 23);
this.btmOK.TabIndex = 1;
this.btmOK.Text = "OK";
this.btmOK.UseVisualStyleBackColor = true;
this.btmOK.Click += new System.EventHandler(this.btmOK_Click);
//
// listView1
//
this.listView1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.listView1.CheckBoxes = true;
this.listView1.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
this.columnHeader3});
this.listView1.FullRowSelect = true;
this.listView1.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.None;
this.listView1.HideSelection = false;
this.listView1.Location = new System.Drawing.Point(12, 27);
this.listView1.Margin = new System.Windows.Forms.Padding(3, 5, 3, 5);
this.listView1.Name = "listView1";
this.listView1.Size = new System.Drawing.Size(360, 464);
this.listView1.Sorting = System.Windows.Forms.SortOrder.Ascending;
this.listView1.TabIndex = 2;
this.listView1.UseCompatibleStateImageBehavior = false;
this.listView1.View = System.Windows.Forms.View.Details;
//
// columnHeader3
//
this.columnHeader3.Text = "";
this.columnHeader3.Width = 350;
//
// cboLevel
//
this.cboLevel.FormattingEnabled = true;
this.cboLevel.Location = new System.Drawing.Point(78, 499);
this.cboLevel.Name = "cboLevel";
this.cboLevel.Size = new System.Drawing.Size(294, 21);
this.cboLevel.TabIndex = 3;
//
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(12, 502);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(60, 13);
this.label1.TabIndex = 4;
this.label1.Text = "Pick Level:";
//
// label2
//
this.label2.AutoSize = true;
this.label2.Location = new System.Drawing.Point(12, 9);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(112, 13);
this.label2.TabIndex = 4;
this.label2.Text = "Select Placed Rooms:";
//
// TestForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(384, 561);
this.Controls.Add(this.label2);
this.Controls.Add(this.label1);
this.Controls.Add(this.cboLevel);
this.Controls.Add(this.listView1);
this.Controls.Add(this.btmOK);
this.Controls.Add(this.btnClose);
this.Name = "TestForm";
this.Text = "TestForm";
this.Load += new System.EventHandler(this.TestForm_Load);
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.Button btnClose;
private System.Windows.Forms.Button btmOK;
private System.Windows.Forms.ListView listView1;
private System.Windows.Forms.ColumnHeader columnHeader3;
private System.Windows.Forms.ComboBox cboLevel;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.Label label2;
}
}
I see both your posts, and your solution looks brilliant to me!
Thanks @jeremytammik, it seems like ant time I have a Screencast attached I see this behavior. I checked multiple times throughout the day on different devices and nothing showed up, nor did this post jump to the top of the list. So, I posted again in the evening, but still didn't see the first post until after someone (you) replied.
Thanks for sharing this amazing solution. I raised this question around one year ago and solved it by copying, pasting, and deleting.
Your solution would be a great help for the successors ; )
really great, thanks for the effort... I am trying to make your code work for me... might be a weird question for those who are really into the api but what do you refer to as "Helpers"? Is it a special class?
And what do you mean by circuit? so you collect the rooms from level 1, save their location, move to the desired level and place them there accordingly?
Thanks in advance!!
@Anonymousthose "Helpers" are custom classes that I have built to help me with FilteredElementCollection and disposing / translating the elements.
All this is doing is collecting the levels.
SortedList<string, Level> levels = new SortedList<string, Level>();
foreach (Level level in Helpers.Collectors.ByCategoryNotElementType(doc, BuiltInCategory.OST_Levels))
{
levels.Add(level.Name, level);
}
And would look like this without wrapping it in the Helper:
SortedList<string, Level> levels = new SortedList<string, Level>();
using (FilteredElementCollector fec = new FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_Levels).WhereElementIsNotElementType())
{
foreach (Level level in fec.ToElements())
{
levels.Add(level.Name, level);
}
}
Circuit is a Revit API terminology that refers to the boundaries that can contain a spatial element like a Room. So, I have to find a "Circuit" that is empty on the level that the room is going too so I can place it within the bounding elements or circuit. I beleive the API call only allows you to place rooms in an open Circuit or one without a room already in it, or at the very least will keep the user from getting the "Multiple Rooms" warning. Once I have them placed in an open circuit, I just move them based on the saved location.
I got the error in Revit when starting the Plugin: system.MissingMethodException constructor cant be found.
I´m guessing
that i need to wrap it all up inside a "public class classname : IExternal Command" ? Can you guys give me further information to this? This is what my External Command Class look like at this moment:
You need to "Show" the form, not activate.
TestForm form = new TestForm(doc);
if(form.ShowDialog() == DialogResult.OK)
{
return Result.Succedd;
}
else
{
return Result.Failed;
}
Am i missing something here? I get this error while debugging: InvalidOperationException (see pic attached)
This is the code i run:
public class RoomChanger : IExternalCommand
{
public Document dbdoc;
public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
{
UIApplication uiapp = commandData.Application;
dbdoc = uiapp.ActiveUIDocument.Document; //error happens here
TestForm form = new TestForm(dbdoc);
if (form.ShowDialog() == DialogResult.OK)
return Result.Succeeded;
else return Result.Failed;
}
}
Are you really getting this error?
Or are you just seeing that message in Document properties browser panel in the debugger?
In a project environment, it is totally expected for the FamilyCreate property not to work; it only works in the family editor context, in an RFA file.
I figured that the "DialogeResult" gets overwritten by calling the "Close() Method" (see original script from @Sean_Page) , resulting in always aborting the command last second.
Can't find what you're looking for? Ask the community or share your knowledge.