Revit APP Blog

Information on Revit APP.

Browsing Posts published in August, 2010

We introduced one way to retrieve revit thumbnail in post: http://blog.revitapp.com/2010/07/revit-thumbnails/. However, we found that that tool has some unknown issue which will make Revit crash.

Here we introduce another class to get the file preview (including Revit thumbnail):

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;

using System.Drawing;
using System.Runtime.InteropServices;
using System.Drawing.Imaging;

namespace RevitApp
{
   internal class ThumbnailCreator : IDisposable
   {
      // Fields
      private IMalloc alloc;
      private Size desiredSize;
      private bool disposed;
      private Bitmap thumbnail;

      // Methods
      public ThumbnailCreator()
      {
         this.desiredSize = new Size(128, 128);
      }

      public ThumbnailCreator(int width, int height)
      {
         desiredSize = new Size(128, 128);
         desiredSize.Width = width;
         desiredSize.Height = height;
      }

      public void Dispose()
      {
         if (!this.disposed)
         {
            if (this.alloc != null)
            {
               Marshal.ReleaseComObject(this.alloc);
            }
            this.alloc = null;
            if (this.thumbnail != null)
            {
               this.thumbnail.Dispose();
            }
            this.disposed = true;
         }
      }

      ~ThumbnailCreator()
      {
         this.Dispose();
      }

      private bool getThumbNail(string file, IntPtr pidl, IShellFolder item)
      {
         bool CS;
         IntPtr hBmp = IntPtr.Zero;
         IExtractImage extractImage = null;
         try
         {
            if (Path.GetFileName(PathFromPidl(pidl)).ToUpper().Equals(Path.GetFileName(file).ToUpper()))
            {
               int prgf;
               IUnknown iunk = null;
               Guid iidExtractImage = new Guid(“BB2E617C-0920-11d1-9A0B-00C04FC2D6C1″);
               item.GetUIObjectOf(IntPtr.Zero, 1, ref pidl, ref iidExtractImage, out prgf, ref iunk);
               extractImage = (IExtractImage)iunk;
               if (extractImage != null)
               {
                  SIZE sz = new SIZE
                  {
                     cx = this.desiredSize.Width,
                     cy = this.desiredSize.Height
                  };
                  StringBuilder location = new StringBuilder(260, 260);
                  int priority = 0;
                  int requestedColourDepth = 0×20;
                  EIEIFLAG flags = EIEIFLAG.IEIFLAG_SCREEN | EIEIFLAG.IEIFLAG_ASPECT;
                  int uFlags = (int)flags;
                  extractImage.GetLocation(location, location.Capacity, ref priority, ref sz, requestedColourDepth, ref uFlags);
                  extractImage.Extract(out hBmp);

                  if (hBmp != IntPtr.Zero)
                  {
                     this.thumbnail = Image.FromHbitmap(hBmp);
                  }
                  Marshal.ReleaseComObject(extractImage);
                  extractImage = null;
               }
               return true;
            }
            CS = false;
         }
         catch (Exception)
         {
            if (hBmp != IntPtr.Zero)
            {
               UnManagedMethods.DeleteObject(hBmp);
            }
            if (extractImage != null)
            {
               Marshal.ReleaseComObject(extractImage);
            }
            throw;
         }
         return CS;
      }

      public Bitmap GetThumbNail(string file)
      {
         if (!File.Exists(file) && !Directory.Exists(file))
         {
            throw new FileNotFoundException(string.Format(“The file ‘{0}’ does not exist”, file), file);
         }
         if (this.thumbnail != null)
         {
            this.thumbnail.Dispose();
            this.thumbnail = null;
         }
         IShellFolder folder = getDesktopFolder;
         if (folder != null)
         {
            IntPtr pidlMain;
            try
            {
               int cParsed;
               int pdwAttrib;
               string filePath = Path.GetDirectoryName(file);
               folder.ParseDisplayName(IntPtr.Zero, IntPtr.Zero, filePath, out cParsed, out pidlMain, out pdwAttrib);
            }
            catch (Exception)
            {
               Marshal.ReleaseComObject(folder);
               throw;
            }
            if (pidlMain != IntPtr.Zero)
            {
               Guid iidShellFolder = new Guid(“000214E6-0000-0000-C000-000000000046″);
               IShellFolder item = null;
               try
               {
                  folder.BindToObject(pidlMain, IntPtr.Zero, ref iidShellFolder, ref item);
               }
               catch (Exception)
               {
                  Marshal.ReleaseComObject(folder);
                  this.Allocator.Free(pidlMain);
                  throw;
               }
               if (item != null)
               {
                  IEnumIDList idEnum = null;
                  try
                  {
                     item.EnumObjects(IntPtr.Zero, ESHCONTF.SHCONTF_NONFOLDERS | ESHCONTF.SHCONTF_FOLDERS, ref idEnum);
                  }
                  catch (Exception)
                  {
                     Marshal.ReleaseComObject(folder);
                     this.Allocator.Free(pidlMain);
                     throw;
                  }
                  if (idEnum != null)
                  {
                     IntPtr pidl = IntPtr.Zero;
                     bool complete = false;
                     while (!complete)
                     {
                        int fetched;
                        if (idEnum.Next(1, ref pidl, out fetched) != 0)
                        {
                           pidl = IntPtr.Zero;
                           complete = true;
                        }
                        else if (this.getThumbNail(file, pidl, item))
                        {
                           complete = true;
                        }
                        if (pidl != IntPtr.Zero)
                        {
                           this.Allocator.Free(pidl);
                        }
                     }
                     Marshal.ReleaseComObject(idEnum);
                  }
                  Marshal.ReleaseComObject(item);
               }
               this.Allocator.Free(pidlMain);
            }
            Marshal.ReleaseComObject(folder);
         }
         return this.thumbnail;
      }

      private static string PathFromPidl(IntPtr pidl)
      {
         StringBuilder path = new StringBuilder(260, 260);
         if (UnManagedMethods.SHGetPathFromIDList(pidl, path) != 0)
         {
            return path.ToString();
         }
         return string.Empty;
      }

      // Properties
#region Properties
      private IMalloc Allocator
      {
         get
         {
            if (!this.disposed && (this.alloc == null))
            {
               UnManagedMethods.SHGetMalloc(out this.alloc);
            }
            return this.alloc;
         }
      }

      public Size DesiredSize
      {
         get
         {
            return this.desiredSize;
         }
         set
         {
            this.desiredSize = value;
         }
      }

      public Bitmap ThumbNail
      {
         get
         {
            return this.thumbnail;
         }
      }

      private static IShellFolder getDesktopFolder
      {
         get
         {
            IShellFolder ppshf;
            UnManagedMethods.SHGetDesktopFolder(out ppshf);
            return ppshf;
         }
      }
#endregion
#region COMDefinitions
      // Nested Types
      private enum EIEIFLAG
      {
         IEIFLAG_ASPECT = 4,
         IEIFLAG_ASYNC = 1,
         IEIFLAG_CACHE = 2,
         IEIFLAG_GLEAM = 0×10,
         IEIFLAG_NOBORDER = 0×100,
         IEIFLAG_NOSTAMP = 0×80,
         IEIFLAG_OFFLINE = 8,
         IEIFLAG_ORIGSIZE = 0×40,
         IEIFLAG_QUALITY = 0×200,
         IEIFLAG_SCREEN = 0×20
      }

      [Flags]
      private enum ESFGAO
      {
         SFGAO_CANCOPY = 1,
         SFGAO_CANDELETE = 0×20,
         SFGAO_CANLINK = 4,
         SFGAO_CANMOVE = 2,
         SFGAO_CANRENAME = 0×10,
         SFGAO_CAPABILITYMASK = 0×177,
         SFGAO_COMPRESSED = 0×4000000,
         SFGAO_CONTENTSMASK = -2147483648,
         SFGAO_DISPLAYATTRMASK = 0xf0000,
         SFGAO_DROPTARGET = 0×100,
         SFGAO_FILESYSANCESTOR = 0×10000000,
         SFGAO_FILESYSTEM = 0×40000000,
         SFGAO_FOLDER = 0×20000000,
         SFGAO_GHOSTED = 0×80000,
         SFGAO_HASPROPSHEET = 0×40,
         SFGAO_HASSUBFOLDER = -2147483648,
         SFGAO_LINK = 0×10000,
         SFGAO_READONLY = 0×40000,
         SFGAO_REMOVABLE = 0×2000000,
         SFGAO_SHARE = 0×20000,
         SFGAO_VALIDATE = 0×1000000
      }

      [Flags]
      private enum ESHCONTF
      {
         SHCONTF_FOLDERS = 0×20,
         SHCONTF_INCLUDEHIDDEN = 0×80,
         SHCONTF_NONFOLDERS = 0×40
      }

      [Flags]
      private enum ESHGDN
      {
         SHGDN_FORADDRESSBAR = 0×4000,
         SHGDN_FORPARSING = 0×8000,
         SHGDN_INFOLDER = 1,
         SHGDN_NORMAL = 0
      }

      [Flags]
      private enum ESTRRET
      {
         STRRET_WSTR,
         STRRET_OFFSET,
         STRRET_CSTR
      }

      [ComImport, Guid("000214F2-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
      private interface IEnumIDList
      {
         [PreserveSig]
         int Next(int celt, ref IntPtr rgelt, out int pceltFetched);
         void Skip(int celt);
         void Reset();
         void Clone(ref ThumbnailCreator.IEnumIDList ppenum);
      }

      [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("BB2E617C-0920-11d1-9A0B-00C04FC2D6C1")]
      private interface IExtractImage
      {
         void GetLocation([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszPathBuffer, int cch, ref int pdwPriority, ref ThumbnailCreator.SIZE prgSize, int dwRecClrDepth, ref int pdwFlags);
         void Extract(out IntPtr phBmpThumbnail);
      }

      [ComImport, Guid("00000002-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
      private interface IMalloc
      {
         [PreserveSig]
         IntPtr Alloc(int cb);
         [PreserveSig]
         IntPtr Realloc(IntPtr pv, int cb);
         [PreserveSig]
         void Free(IntPtr pv);
         [PreserveSig]
         int GetSize(IntPtr pv);
         [PreserveSig]
         int DidAlloc(IntPtr pv);
         [PreserveSig]
         void HeapMinimize();
      }

      [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("000214E6-0000-0000-C000-000000000046")]
      private interface IShellFolder
      {
         void ParseDisplayName(IntPtr hwndOwner, IntPtr pbcReserved, [MarshalAs(UnmanagedType.LPWStr)] string lpszDisplayName, out int pchEaten, out IntPtr ppidl, out int pdwAttributes);
         void EnumObjects(IntPtr hwndOwner, [MarshalAs(UnmanagedType.U4)] ThumbnailCreator.ESHCONTF grfFlags, ref ThumbnailCreator.IEnumIDList ppenumIDList);
         void BindToObject(IntPtr pidl, IntPtr pbcReserved, ref Guid riid, ref ThumbnailCreator.IShellFolder ppvOut);
         void BindToStorage(IntPtr pidl, IntPtr pbcReserved, ref Guid riid, IntPtr ppvObj);
         [PreserveSig]
         int CompareIDs(IntPtr lParam, IntPtr pidl1, IntPtr pidl2);
         void CreateViewObject(IntPtr hwndOwner, ref Guid riid, IntPtr ppvOut);
         void GetAttributesOf(int cidl, IntPtr apidl, [MarshalAs(UnmanagedType.U4)] ref ThumbnailCreator.ESFGAO rgfInOut);
         void GetUIObjectOf(IntPtr hwndOwner, int cidl, ref IntPtr apidl, ref Guid riid, out int prgfInOut, ref ThumbnailCreator.IUnknown ppvOut);
         void GetDisplayNameOf(IntPtr pidl, [MarshalAs(UnmanagedType.U4)] ThumbnailCreator.ESHGDN uFlags, ref ThumbnailCreator.STRRET_CSTR lpName);
         void SetNameOf(IntPtr hwndOwner, IntPtr pidl, [MarshalAs(UnmanagedType.LPWStr)] string lpszName, [MarshalAs(UnmanagedType.U4)] ThumbnailCreator.ESHCONTF uFlags, ref IntPtr ppidlOut);
      }

      [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("00000000-0000-0000-C000-000000000046")]
      private interface IUnknown
      {
         [PreserveSig]
         IntPtr QueryInterface(ref Guid riid, out IntPtr pVoid);
         [PreserveSig]
         IntPtr AddRef();
         [PreserveSig]
         IntPtr Release();
      }

      [StructLayout(LayoutKind.Sequential)]
      private struct SIZE
      {
         public int cx;
         public int cy;
      }

      [StructLayout(LayoutKind.Explicit, CharSet = CharSet.Auto)]
      private struct STRRET_ANY
      {
         // Fields
         [FieldOffset(4)]
         public IntPtr pOLEString;
         [FieldOffset(0)]
         public ThumbnailCreator.ESTRRET uType;
      }

      [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto, Pack = 4)]
      private struct STRRET_CSTR
      {
         public ThumbnailCreator.ESTRRET uType;
         [MarshalAs(UnmanagedType.ByValArray, SizeConst = 520)]
         public byte[] cStr;
      }

      private class UnManagedMethods
      {
         // Methods
         [DllImport("gdi32", CharSet = CharSet.Auto)]
         internal static extern int DeleteObject(IntPtr hObject);
         [DllImport("shell32", CharSet = CharSet.Auto)]
         internal static extern int SHGetDesktopFolder(out ThumbnailCreator.IShellFolder ppshf);
         [DllImport("shell32", CharSet = CharSet.Auto)]
         internal static extern int SHGetMalloc(out ThumbnailCreator.IMalloc ppMalloc);
         [DllImport("shell32", CharSet = CharSet.Auto)]
         internal static extern int SHGetPathFromIDList(IntPtr pidl, StringBuilder pszPath);
      }
#endregion
   }
}

Currently Revit doesn’t support multiple selection in Material edition dialog, so when user wants to delete multiple materials, he/she has to select one, delete, then one again and again. It’s really tedious and waste lots of time. This little tool will help you a lot when you want to delete multiple materials.

The link is: http://en.revitapp.com/materialstool.html

Tips: try your best to use  Parameter(BuiltInParameter) when get parameter from element.

Revit as a parametric product, we will frequently use element parameters (retrieve, modify) to achieve our own goals.

There are several ways to get Parameter from an element:
Parameter(BuiltInParameter)
Parameter(Definition)
Parameter(Guid)
Parameter(String)

All almost similar except the last one which has the least performance because it will iterate all parameters in this element until find the parameter with the specified name. However, in most cases BuiltInParameter is more meaningful for us and easy to get, so it’s better for us to use BuiltInParameter to get parameter. In addition, use this can avoid localization problem, so you don’t need to write different code for different languages.

If you do need to use string to retrieve parameter, go ahead. But keep in mind that there is another property ParametersMap. If you want to search several parameters from one element (all use string as keyword), get this map first and use the map to get parameter would be faster.

Chinese Version: 点这里

When you develop your own application based on Revit API, there are many things that you need to know which won’t be included in the API help book or somewhere else. We call them tips. We plan to write down the tips we found or other friends found to help API developers.

Today we want to share one tip:

It’s better to write one Document::regenerate in one transaction just before its commit, and remember to re-get Elements, Views etc. after the regenerate if you put these code in one class/function.

Reason:

When document regeneration, many things may be changed including the memory allocated for the Elements/Views etc. So if you still use the Elements/View etc reference after the regenerate, things may go beyond your expectation. Just keep in mind that, the ElementId in document is stable, but the Element reference is not. Simplify the logic in one transaction will make your code easy to understand and less chance to encounter problems, sometimes one change will not be real until one regenerate. So if you found you change doesn’t work, maybe you need call regenerate.

Chinese Version: 点这里

In the easy family loader project, we use WPF window to show family preview images. We use ListBox to show all the information.

Here is the code in xmal file:

<ListBox Name=”listFileView”  Grid.Row=”2″ Grid.Column=”3″ Grid.ColumnSpan=”3″ ItemsSource=”{Binding}” SelectionMode=”Multiple” ScrollViewer.HorizontalScrollBarVisibility=”Disabled”>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel IsItemsHost=”True” />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<Border Margin=”3″ BorderThickness=”1″ BorderBrush=”SteelBlue” CornerRadius=”2″>
<StackPanel Margin=”3″>
<StackPanel.ToolTip>
<TextBlock Text=”{Binding Path=path}” TextWrapping=”wrap”/>
</StackPanel.ToolTip>
<Image Margin =”3″ HorizontalAlignment=”Center” Source=”{Binding Path=image}” Stretch=”None”></Image>
<TextBlock FontWeight=”Bold” HorizontalAlignment =”Center” Margin =”3″ Text=”{Binding Path=name}”></TextBlock>
</StackPanel>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>

The code for data binding is “ItemsSource=”{Binding}”“, this is a simpler one, of course you can customize it in different ways, and you can also see Source=”{Binding Path=image}” in <DataTemplate>, which will show subitems in the list. Then we need a class to specify the data type for binding if you want to retrieve it sometime (like when user select one, you want to the detail user selected).

This is the class:

public class MyFile
{
public string strName;
public string strPath;
public ImageSource imgSrc;

public MyFile()
{
}

public string name
{
get { return strName; }
set { strName = value; }
}

public string path
{
get { return strPath; }
set { strPath = value; }
}

public ImageSource image
{
get { return imgSrc; }
set { imgSrc = value; }
}
}

Please notice that you have to write properties of the class as the same name in the binding clause (like: Binding Path=image, so we must have “image” property). And when you add items in the list, do like this:

listFileView.DataContext = from file in files
select new MyFile()
{
name = file.name,
path = file.path,
image = file.image,
};

When get data, you can use following code to retrieve information:

foreach (MyFile file in listFileView.SelectItems) { … }

Load family is a frequent action when we use Revit. However, try to find a desire family is frustrated because the folder structure is too complex. This tool is trying to let you find your family easily. It will filter the path name, then the file name, and it’s quite easy to use. Take a look at the demo vedio in following link to see how easy it is:-)

It supports:
1. Filter based on fold name;
2. Filter based on file name;
3. Support family preview.
4. All filters support logic “and” and “or”.

The link: http://en.revitapp.com/easy_family_loader.html

Sometimes we want to use embedded image resource for ribbon button, so we can simplify our deliverables when release the add-ins (don’t need to copy external image files with the assembly). And actually we have the choice, following code will help you know how:

using System;
using System.IO;
using System.Windows.Media;
using System.Windows.Media.Imaging;

using Autodesk.Revit;
using Autodesk.Revit.DB;
using Autodesk.Revit.DB.Events;
using Autodesk.Revit.UI;

namespace myRevitApp
{
   [Autodesk.Revit.Attributes.Transaction(Autodesk.Revit.Attributes.TransactionMode.Automatic)]
   [Autodesk.Revit.Attributes.Regeneration(Autodesk.Revit.Attributes.RegenerationOption.Manual)]
   public class MyApp : IExternalApplication
   {
      // ExternalCommands assembly path
      static string addInPath = typeof(MyApp).Assembly.Location;
      // Button icons directory
      static string buttonIconsFolder = Path.GetDirectoryName(addInPath);
      public Result OnStartup(UIControlledApplication uiControlledApplication)
      {
         System.Reflection.Assembly myAssembly = System.Reflection.Assembly.GetExecutingAssembly();
         // begin to create custom Ribbon panel and command buttons.
         // create a Ribbon panel.
         RibbonPanel myPanel = uiControlledApplication.CreateRibbonPanel(“My Ribbon Panel”);

         // the first button in the DoorSwing panel, use to invoke the InitializeCommand.
         PushButton pushBtn = myPanel.AddItem(new PushButtonData(“Test”,
                                                          “Test”,
                                                          addInPath,
                                                          typeof(TestCommand).FullName))
                                                          as PushButton;
         pushBtn.ToolTip = “TestCommand”;
         pushBtn.LargeImage = GetEmbeddedImage(myAssembly, “Test.png”);
         pushBtn.Image = GetEmbeddedImage(myAssembly, “Test_small.png”);

         return Result.Succeeded;
      }

      private ImageSource GetEmbeddedImage(System.Reflection.Assembly app, string imageName)
      {
         System.IO.Stream file = app.GetManifestResourceStream(“myRevitApp.” + imageName);
         PngBitmapDecoder bd = new PngBitmapDecoder(file, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default);

         return bd.Frames[0];
      }

      public Result OnShutdown(UIControlledApplication application)
      {
         return Result.Succeeded;
      }
   }
}

Notes:

Please make sure add “PresentationCore” and “WindowsBase” .NET references to the project, and add namespace “System.Windows.Media;” and “System.Windows.Media.Imaging;”.

In addition, make sure add the image resources to your project and set their property “Build Action” as “Embedded Resource”.

There is a post to describe the general change for element iterator in http://blog.revitapp.com/2010/06/new-element-iteration-interfaces/.

Here we have a simple example for code change from 2010 to 2011.

First we have 2010 API code:

TypeFilter familyInstanceTypeFilter = app.Create.Filter.NewTypeFilter(typeof(Autodesk.Revit.Elements.FamilyInstance));
CategoryFilter doorCategoryFilter = app.Create.Filter.NewCategoryFilter(BuiltInCategory.OST_Doors);
Filter andFilter = app.Create.Filter.NewLogicalOrFilter(familyInstanceTypeFilter,doorCategoryFilter);
ElementIterator eleIt= doc.get_Elements(andFilter);
while( eleIt.MoveNext())
{
// do something for element
}

Here is the new code in 2011:

FilteredElementCollector collector = new FilteredElementCollector(doc);
ElementClassFilter famInstFilter = new ElementClassFilter(typeof(FamilyInstance));
ElementCategoryFilter doorFilter = new ElementCategoryFilter(BuiltInCategory.OST_Doors);
LogicalOrFilter orFilter = new LogicalOrFilter(famInstFilter, doorFilter);
ICollection<Element> elems = collector.WherePasses(orFilter).ToElements();
foreach (Element e in elems)
{
// do something…
}

We can also simplify the code to:

ICollection<Element> elems = new FilteredElementCollector(doc).OfClass(typeof(FamilyInstance))
.OfCategory(BuiltInCategory.OST_Doors).ToElements();
foreach (Element e in elems)
{
// do something…
}

You can find that the new interface is much better to use and more possibilities.

Powered by WordPress Web Design by SRS Solutions © 2012 Revit APP Blog Design by SRS Solutions