I am new to revit api . I want to create a panel in revit empty project. I can create walls . I want to know how can I add panel data (studs , panel name , openings etc ) in the wall . Also I am new to construction terminologies.
here is my code:
UIApplication uiapp = commandData.Application;
UIDocument uidoc = uiapp.ActiveUIDocument;
Document doc = uiapp.ActiveUIDocument.Document;
WallType w = getWallType(doc);
RetrievingLevels(doc);
Level newLevel = CreateLevels(doc);
if(newLevel != null)
{
IList<Curve> curves = new List<Curve>();
XYZ first = new XYZ(0, 0, 0);
XYZ second = new XYZ(20, 0, 0);
XYZ third = new XYZ(20, 0, 15);
XYZ fourth = new XYZ(0, 0, 15);
curves.Add(Line.CreateBound(first, second));
curves.Add(Line.CreateBound(second, third));
curves.Add(Line.CreateBound(third, fourth));
curves.Add(Line.CreateBound(fourth, first));
//Line l = Line.CreateBound(a1, b1);
Transaction trans = new Transaction(doc);
try
{
trans.Start("create walls");
Wall.Create(doc, curves, w.Id,newLevel.Id, true);
trans.Commit();
return Result.Succeeded;
}
catch (Exception ex)
{
trans.Dispose();
return Result.Failed;
}
}
return Result.Failed;
//IList<Curve> curves = new List<Curve>();
}
private WallType getWallType(Document doc)
{
FilteredElementCollector collector = new FilteredElementCollector(doc).OfClass(typeof(WallType));
IList<Element> WallTypes = collector.ToElements();
return WallTypes.First() as WallType;
}
private Level CreateLevels(Document document)
{
double elevation = 33.0;
Transaction t = new Transaction(document);
// Begin to create a level
t.Start("create Level");
try
{
Level level = Level.Create(document, elevation);
if (null == level)
{
throw new Exception("Create a new level failed.");
}
// Change the level name
level.Name = "New level";
t.Commit();
return level;
}
catch (Exception e)
{
}
return null;
}
Also please suggest me from where can I learn about revit APIS .
Well, it will help a lot to understand Revit and BIM from a user point of view before you try to start programming it, cf. before getting started. Then, you should work through the rest of the Revit API getting started material.
Next, you should learn how to research to find a Revit API solution for yourself.
The first step is always definitely to install the RevitLookup interactive Revit BIM database exploration tool to view and navigate element properties and relationships.
With that in place, you can start by manually creating the model that you wish to generate programmatically in the user interface first. Explore that using RevitLookup and other tools such as BipChecker and the element lister to discover what kind of elements have been generated by the manual process and their properties and relationships.
Once you understand all that, you will be all set to generate the same model programatically using the Revit API.
Good luck and have fun!
I'm trying to add custom tags to PowerPoint slides using OpenXml component in c#.
But after adding the tags dynamically, the presentation gets corrupted and is causing the powerpoint to stop working.
Following is the code I'm using for adding the tags:
public static void AddSlideTag(string presentationPath, string tagValue)
{
using (PresentationDocument presentation = PresentationDocument.Open(presentationPath, true))
{
var presPart = presentation.PresentationPart;
// Copy each slide in the source presentation, in order, to
// the destination presentation.
var slideIndex = 0;
foreach (var openXmlElement in presPart.Presentation.SlideIdList)
{
var slideId = (SlideId)openXmlElement;
// Create a unique relationship id.
var sp = (SlidePart)presPart.GetPartById(slideId.RelationshipId);
//create userDefinedTag
Tag slideObjectTag = new Tag() { Name = "CustomTag", Val = tagValue};
UserDefinedTagsPart userDefinedTagsPart1 = sp.AddNewPart<UserDefinedTagsPart>();
if (userDefinedTagsPart1.TagList == null)
userDefinedTagsPart1.TagList = new TagList();
userDefinedTagsPart1.TagList.Append(slideObjectTag);
//add tag to CustomerDataList element
var id = sp.GetIdOfPart(userDefinedTagsPart1);
if (sp.Slide.CommonSlideData == null)
{
sp.Slide.CommonSlideData = new CommonSlideData();
}
if (sp.Slide.CommonSlideData.CustomerDataList == null)
sp.Slide.CommonSlideData.CustomerDataList = new CustomerDataList();
CustomerDataTags tags = new CustomerDataTags();
tags.Id = id;
sp.Slide.CommonSlideData.CustomerDataList.AppendChild(tags);
slideIndex++;
sp.Slide.Save();
}
presPart.Presentation.Save();
}
}
It seems like the xml of the slides is broken after executing this code, but I cannot figure out what is the problem.
Does anyone know what is wrong with the code above, or what is causing the powerpoint to gets corrupted after running this code? Am I missing something here?
I'm using DocumentFormat.OpenXml.2.5 package.
I have been trying to read .ppt files from last 3 days. I searched a lot on internet and I came up with different source code snippets but nothing was perfect. And now i tried this code, and it is not printing "Check4" because of some unidentified problem in "Foreach" statement, and throwing an exception. Please guide me. I need it badly.
public static void ppt2txt (String source)
{
string fileName = System.IO.Path.GetFileNameWithoutExtension(source);
string filePath = System.IO.Path.GetDirectoryName(source);
Console.Write("Check1");
Application pa = new Microsoft.Office.Interop.PowerPoint.ApplicationClass ();
Microsoft.Office.Interop.PowerPoint.Presentation pp = pa.Presentations.Open (source,
Microsoft.Office.Core.MsoTriState.msoTrue,
Microsoft.Office.Core.MsoTriState.msoFalse,
Microsoft.Office.Core.MsoTriState.msoFalse);
Console.Write("Check2");
String pps = "";
Console.Write("Check3");
foreach (Microsoft.Office.Interop.PowerPoint.Slide slide in pp.Slides)
{
foreach (Microsoft.Office.Interop.PowerPoint.Shape shape in slide.Shapes)
pps += shape.TextFrame.TextRange.Text.ToString ();
}
Console.Write("Check4");
Console.WriteLine(pps);
}
Thrown exception is
System.ArgumentException: The specified value is out of range.
at Microsoft.Office.Interop.PowerPoint.TextFrame.get_TextRange()
at KareneParser.Program.ppt2txt(String source) in c:\Users\Shahmeer\Desktop\New folder (2)\KareneParser\Program.cs:line 323
at KareneParser.Program.Main(String[] args) in c:\Users\Shahmeer\Desktop\New folder (2)\KareneParser\Program.cs:line 150
Line 323 on which exception is caught
pps += shape.TextFrame.TextRange.Text.ToString ();
Thanks in advance.
It looks like you need to check your shape objects to see if they have a TextFrame and Text present.
In your nested foreach loop try this:
foreach (Microsoft.Office.Interop.PowerPoint.Slide slide in pp.Slides)
{
foreach (Microsoft.Office.Interop.PowerPoint.Shape shape in slide.Shapes)
{
if(shape.HasTextFrame == Microsoft.Office.Core.MsoTriState.msoTrue)
{
var textFrame = shape.TextFrame;
if(textFrame.HasText == Microsoft.Office.Core.MsoTriState.msoTrue)
{
var textRange = textFrame.TextRange;
pps += textRange.Text.ToString ();
}
}
}
}
This is of course untested on my part, it looks to me though that as your foreach loops, you're trying to access some shapes in the powerpoint doc that don't have text present, hence the out of range exception. I've added in checking to make sure it only appends text to your pps string if it has Text present.
Not all shapes have text. Lines etc are also shapes.
Check for HasText first:
foreach (Microsoft.Office.Interop.PowerPoint.Shape shape in slide.Shapes)
{
if(shape.TextFrame.HasText)
{
pps += shape.TextFrame.TextRange.Text;
}
}
After trying out several solutions, I am in desperate need for help.
I tried several approaches, before finally copying and still being stuck with the solution from Getting full contents of a Datagrid using UIAutomation.
Let's talk code, please consider the comments:
// Get Process ID for desired window handle
uint processID = 0;
GetWindowThreadProcessId(hwnd, out processID);
var desktop = AutomationElement.RootElement;
// Find AutomationElement for the App's window
var bw = AutomationElement.RootElement.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.ProcessIdProperty, (int)processID));
// Find the DataGridView in question
var datagrid = bw.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.AutomationIdProperty, "dgvControlProperties"));
// Find all rows from the DataGridView
var loginLines = datagrid.FindAll(TreeScope.Children, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.DataItem));
// Badumm Tzzz: loginLines has 0 items, foreach is therefore not executed once
foreach (AutomationElement loginLine in loginLines)
{
var loginLinesDetails = loginLine.FindAll(TreeScope.Children, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Custom));
for (var i = 0; i < loginLinesDetails.Count; i++)
{
var cacheRequest = new CacheRequest
{
AutomationElementMode = AutomationElementMode.None,
TreeFilter = Automation.RawViewCondition
};
cacheRequest.Add(AutomationElement.NameProperty);
cacheRequest.Add(AutomationElement.AutomationIdProperty);
cacheRequest.Push();
var targetText = loginLinesDetails[i].FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.ClassNameProperty, "TextBlock"));
cacheRequest.Pop();
var myString = targetText.Cached.Name;
}
}
I can neither fetch a GridPattern, nor a TablePattern instance from datagrid, both results in an exception:
GridPattern gridPattern = null;
try
{
gridPattern = datagrid.GetCurrentPattern(GridPattern.Pattern) as GridPattern;
}
catch (InvalidOperationException ex)
{
// It fails!
}
TablePattern tablePattern = null;
try
{
tablePattern = datagrid.GetCurrentPattern(TablePattern.Pattern) as TablePattern;
}
catch (InvalidOperationException ex)
{
// It fails!
}
The rows were added to the DataGridView beforehand, like this:
dgvControlProperties.Rows.Add(new object[] { false, "Some Text", "Some other text" });
I am compiling to .Net Framework 4.5. Tried both regular user rights and elevated admin rights for the UI Automation client, both yielded the same results described here.
Why does the DataGridView return 0 rows?
Why can't I get one of the patterns?
Kudos for helping me out!
Update:
James help didn't to the trick for me. The following code tough returns all rows (including the headers):
var rows = dataGrid.FindAll(TreeScope.Children, PropertyCondition.TrueCondition);
Header cells can then be identified by their ControlType of ControlType.Header.
The code that you are copying is flawed. I just tested this scenario with an adaptation of this sample program factoring in your code above, and it works.
The key difference is that the code above is using TreeScope.Children to grab the datagrid element. This option only grabs immediate children of the parent, so if your datagrid is nested it's not going to work. Change it to use TreeScope.Descendants, and it should work as expected.
var datagrid = bw.FindFirst(TreeScope.Descendants, new PropertyCondition(AutomationElement.AutomationIdProperty, "dgvControlProperties"));
Here is a link to how the various Treescope options behave. Also, I don't know how your binding the rows to the grid, but I did it like this in my test scenario and it worked flawlessly.
Hopefully this helps.
public class DataObject
{
public string FieldA { get; set; }
public string FieldB { get; set; }
public string FieldC { get; set; }
}
List<DataObject> items = new List<DataObject>();
items.Add(new DataObject() {FieldA="foobar",FieldB="foobar",FieldC="foobar"});
items.Add(new DataObject() { FieldA = "foobar", FieldB = "foobar", FieldC = "foobar" });
items.Add(new DataObject() { FieldA = "foobar", FieldB = "foobar", FieldC = "foobar" });
dg.ItemsSource = items;
Your code looks fine though this could be a focus issue.
Even though you are getting a reference to these automation element objects you should set focus on them (using the aptly named SetFocus method) before using them.
Try:
var desktop = AutomationElement.RootElement;
desktop.SetFocus();
// Find AutomationElement for the App's window
var bw = desktop.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.ProcessIdProperty, (int)processID));
and if that does not work try explicitly focusing on the dataGrid prior to calling "FindAll" on it i.e.
datagrid.SetFocus()
Why does the DataGridView return 0 rows?
The DataGridViewRows have a ControlType of ControlType.Custom. So I modified the line
// Find all rows from the DataGridView
var loginLines = datagrid.FindAll(TreeScope.Children, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.DataItem));
To
var loginLines = datagrid.FindAll(TreeScope.Children, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Custom));
That gets you all the rows within the DataGridView. But your code has that within the loop.
What pattern is supported by DataGridView (not DataGrid)?
LegacyIAccessiblePattern. Try this-
LegacyIAccessiblePattern legacyPattern = null;
try
{
legacyPattern = datagrid.GetCurrentPattern(LegacyIAccessiblePattern.Pattern) as LegacyIAccessiblePattern;
}
catch (InvalidOperationException ex)
{
// It passes!
}
As I commented on #James answer, there is no support for UIA for DataGridView (again, not DataGrid).
If you search in google with terms: "UI Automation DataGridView" the first result has an incomplete answer. Mike replies with what provider class (Obviously one that extends DataGridView)to create within the source, unfortunately I have no clue as to how to make UIA load that class a provider. If anyone can shed some clues, Phil and I will be extremely pleased!
EDIT
Your DataGridView should implement the interfaces in that link above. Once you do that the ControlType of Row will be ControlType.DataItem instead of ControlType.Custom. You can then use it how you'd use a DataGrid.
EDIT
This is what I ended up doing -
Creating a Custom DataGridView like below. Your datagridview could subclass this too. That will make it support ValuePattern and SelectionItemPattern.
public class CommonDataGridView : System.Windows.Forms.DataGridView,
IRawElementProviderFragmentRoot,
IGridProvider,
ISelectionProvider
{.. }
The complete code can be found # this msdn link.
Once I did that, I played a bit with visualUIVerify source. Here is how I can access the gridview's cell and change the value. Also, make note of the commented code. I didn't need that. It allows you to iterate through rows and cells.
private void _automationElementTree_SelectedNodeChanged(object sender, EventArgs e)
{
//selected currentTestTypeRootNode has been changed so notify change to AutomationTests Control
AutomationElementTreeNode selectedNode = _automationElementTree.SelectedNode;
AutomationElement selectedElement = null;
if (selectedNode != null)
{
selectedElement = selectedNode.AutomationElement;
if (selectedElement.Current.ClassName.Equals("AutomatedDataGrid.CommonDataGridViewCell"))
{
if(selectedElement.Current.Name.Equals("Tej")) //Current Value
{
var valuePattern = selectedElement.GetCurrentPattern(ValuePattern.Pattern) as ValuePattern;
valuePattern.SetValue("Jet");
}
//Useful ways to get patterns and values
//System.Windows.Automation.SelectionItemPattern pattern = selectedElement.GetCurrentPattern(System.Windows.Automation.SelectionItemPattern.Pattern) as System.Windows.Automation.SelectionItemPattern;
//var row = pattern.Current.SelectionContainer.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.ClassNameProperty, "AutomatedDataGrid.CommonDataGridViewRow", PropertyConditionFlags.IgnoreCase));
//var cells = row.FindAll(TreeScope.Children, new PropertyCondition(AutomationElement.ClassNameProperty, "AutomatedDataGrid.CommonDataGridViewCell", PropertyConditionFlags.IgnoreCase));
//foreach (AutomationElement cell in cells)
//{
// Console.WriteLine("**** Printing Cell Value **** " + cell.Current.Name);
// if(cell.Current.Name.Equals("Tej")) //current name
// {
// var valuePattern = cell.GetCurrentPattern(ValuePattern.Pattern) as ValuePattern;
// valuePattern.SetValue("Suraj");
// }
//}
//Select Row
//pattern.Select();
//Get All Rows
//var arrayOfRows = pattern.Current.SelectionContainer.FindAll(TreeScope.Children, new PropertyCondition(AutomationElement.ClassNameProperty, "AutomatedDataGrid.CommonDataGridViewRow", PropertyConditionFlags.IgnoreCase));
//get cells
//foreach (AutomationElement row in arrayOfRows)
//{
// var cell = row.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.ClassNameProperty, "AutomatedDataGrid.CommonDataGridViewCell", PropertyConditionFlags.IgnoreCase));
// var gridItemPattern = cell.GetCurrentPattern(GridItemPattern.Pattern) as GridItemPattern;
// // Row number.
// Console.WriteLine("**** Printing Row Number **** " + gridItemPattern.Current.Row);
// //Cell Automation ID
// Console.WriteLine("**** Printing Cell AutomationID **** " + cell.Current.AutomationId);
// //Cell Class Name
// Console.WriteLine("**** Printing Cell ClassName **** " + cell.Current.ClassName);
// //Cell Name
// Console.WriteLine("**** Printing Cell AutomationID **** " + cell.Current.Name);
//}
}
}
_automationTests.SelectedElement = selectedElement;
_automationElementPropertyGrid.AutomationElement = selectedElement;
}
Hopefully this helps.
I run into this problem too, after researching, i figured out that you can only access data grid view with LegacyIAccessible. However, .NET does not support this. So, here are the steps:
there are 2 versions of UIA: managed version and unmanaged version (native code). In some cases, the unmanaged version can do what the managed version can't.
=> as said here, using tblimp.exe (go along with Windows SDK) to generate a COM wrapper around the unmanaged UIA API, so we can call from C#.
I have done it here
we can use this to access DataGridView now but with very limited data, you can use Inspect.exe to see.
The code is:
using InteropUIA = interop.UIAutomationCore;
if (senderElement.Current.ControlType.Equals(ControlType.Custom))
{
var automation = new InteropUIA.CUIAutomation();
var element = automation.GetFocusedElement();
var pattern = (InteropUIA.IUIAutomationLegacyIAccessiblePattern)element.GetCurrentPattern(10018);
Logger.Info(string.Format("{0}: {1} - Selected", pattern.CurrentName, pattern.CurrentValue));
}
Ok, I guess I'm having a brain fart here and cant find my way out of it. What I'm trying to accomplish is to list all startup items (applications, processes, etc) and display them on a form (like what you get with msconfig.exe). I thought this code would do it:
private List<StartupItems> getStartupItems()
{
try
{
ManagementClass cls = new ManagementClass("Win32_StartupCommand");
ManagementObjectCollection coll = cls.GetInstances();
List<StartupItems> items = new List<StartupItems>();
foreach (ManagementObject obj in coll)
{
items.Add(
new StartupItems
{
Command = obj["Command"].ToString(),
Description = obj["Description"].ToString(),
Name = obj["Name"].ToString(),
Location = obj["Location"].ToString(),
User = obj["User"].ToString()
});
}
return items;
}
catch (Exception ex)
{
_message = ex.ToString();
_status = false;
return null;
}
But all that gets are the enabled ones with my username. What I'm trying to get is all items, either my username or system, all the enabled ones and all the disabled ones as well (just like msconfig). I've done tons of searching and cannot find anything really any different than what I'm using.
Have you considered reading directly from the registry?
One alternative would be to run autorunssc (it's the command-line version of autoruns) in the background and read its response.