Author Topic: A Simple Dropdown List for Blocks  (Read 13705 times)

0 Members and 1 Guest are viewing this topic.

zoltan

  • Guest
A Simple Dropdown List for Blocks
« on: June 30, 2012, 04:19:32 PM »
Here is an example of a simple dropdown list to display blocks.  You have to make sure that the BlockTableRecord has a preview icon either by opening the Design Center or using the BLOCKICON command.

This is in no way complete and not a good library component, but it is a quick example of what can be done.

huiz

  • Swamp Rat
  • Posts: 913
  • Certified Prof C3D
Re: A Simple Dropdown List for Blocks
« Reply #1 on: June 30, 2012, 04:52:29 PM »
Great example of the use of DrawString  8)

Very usable idea, thank you zoltan for sharing!
The conclusion is justified that the initialization of the development of critical subsystem optimizes the probability of success to the development of the technical behavior over a given period.

TheMaster

  • Guest
Re: A Simple Dropdown List for Blocks
« Reply #2 on: June 30, 2012, 07:10:48 PM »
Here is an example of a simple dropdown list to display blocks.  You have to make sure that the BlockTableRecord has a preview icon either by opening the Design Center or using the BLOCKICON command.

This is in no way complete and not a good library component, but it is a quick example of what can be done.

Your code works in a modal dialog but will probably crash AutoCAD or fail in some other way if you tried to use it in a modeless context (e.g., palette or a modeless form). You can't store BlockTableRecords in a control's items collections if you want the control to be usable in a modeless context.

I also don't like the idea of dedicated controls for things like block icons. They are just images. For example, my own controls are not dedicated to very specific AutoCAD objects, because there's quite a few cases where similar control functionality is needed (image and text), and I would not want to have to write a dedicated control for each specific use, so I use a generic combo box with support for an image/icon, which is easy to adapt to specific uses like block icons, linetype images, hatch patterns, etc. You can find lots of them on CodeProject.com.

« Last Edit: June 30, 2012, 07:18:53 PM by TheMaster »

zoltan

  • Guest
Re: A Simple Dropdown List for Blocks
« Reply #3 on: June 30, 2012, 07:21:13 PM »

Your code works in a modal dialog but will probably crash AutoCAD or fail in some other way if you tried to use it in a modeless context (e.g., palette or a modeless form). You can't store BlockTableRecords in a control's items collections if you want the control to be usable in a modeless context.

That's why this is a simple example.

zoltan

  • Guest
Re: A Simple Dropdown List for Blocks
« Reply #4 on: June 30, 2012, 07:23:04 PM »
Here is a SIMPLE layer list.  I would like to be able to make the controls inside the dropdown list be clickable and raise events, but that has proven to be pretty difficult in WinForms.  A piece-of-cake in WPF, from what I've read.


(Yes, the icons are crap.  I found them on the 'net. Feel free to replace them.)
« Last Edit: June 30, 2012, 07:51:10 PM by zoltan »

zoltan

  • Guest
Re: A Simple Dropdown List for Blocks
« Reply #5 on: June 30, 2012, 07:54:47 PM »

I also don't like the idea of dedicated controls for things like block icons. They are just images. For example, my own controls are not dedicated to very specific AutoCAD objects, because there's quite a few cases where similar control functionality is needed (image and text), and I would not want to have to write a dedicated control for each specific use, so I use a generic combo box with support for an image/icon, which is easy to adapt to specific uses like block icons, linetype images, hatch patterns, etc. You can find lots of them on CodeProject.com.

Why would you not want to have a dedicated control for this?  I'm sure there are generic controls out there that you can put images and text into them in all sorts of flexible ways, but would it not be easier to have a control that is designed to draw the Block, Linetype, or Layer exactly the way it should be?  Where do you put the implementation to gather up all of the images and text for the Symbol Table Record and give it to your generic control in just the right formatting?



TheMaster

  • Guest
Re: A Simple Dropdown List for Blocks
« Reply #6 on: July 01, 2012, 12:58:55 AM »
Why would you not want to have a dedicated control for this? 
Because a dedicated control is not necessary, and doesn't promote separation of UI from business logic.

Today I need a combo box.  Tomorrow I may need a listbox, next week I may need a listview, treeview, etc..

Am I supposed to write (how many?) dedicated controls of different types for each circumstance?  Or, should I use generic controls, and put my business logic in a separate layer that can be easily reused by many different types of generic controls, and thereby avoid replication of the business logic.

Your example violates one of the most fundamental principles of good design, which is that it fails to separate business logic from the UI layer, and I'm not going to explain that in detail, because you can find plenty of discussion and explanation about that all over the internet.

So, it is not just one 'dedicated control', it is potentially many dedicated controls of different types.

If you hold a myopic view of the problem, you will only find yourself throwing code out and rewriting again many times over (or worse, copying and pasting it here and there) because you didn't anticipate future or varying requirements.

Quote
I'm sure there are generic controls out there that you can put images and text into them in all sorts of flexible ways, but would it not be easier to have a control that is designed to draw the Block, Linetype, or Layer exactly the way it should be? 

I can draw those things exactly the way they should drawn using well-designed generic controls. They're just bitmap images, nothing more special about them in that regards.

Quote
Where do you put the implementation to gather up all of the images and text for the Symbol Table Record and give it to your generic control in just the right formatting?

That question would seem to suggest that you've yet to discover data binding.

I'd suggest you research the concept of data binding, because it eliminates the need for dedicated code to 'gather up all the images and text', and also eliminates the need to manually load items into list-type controls as well, and makes the entire process generic. When you use data binding, the control itself gets the items to display in the control, and the data that is displayed for each item, with no code required on your part.

When you use data binding, you just give the control a data source that exposes a collection of the objects whose data will be displayed in each item in the control, and you give the control the names of the properties which the control will use to get the data from each item (e.g., the Name and BlockIcon properties of the BlockTableRecord would be the ones that would be used if one is directly binding to BlockTableRecord, which is not wise for reasons I cited earlier). 

The reason why data binding is preferred for providing data to the UI is because all types of controls support data binding to data sources in a consistent way, and that is what allows the same business logic to be re-used with any type of control that supports data binding, with no need for special or control-specific code.
« Last Edit: July 01, 2012, 01:35:26 AM by TheMaster »

TheMaster

  • Guest
Re: A Simple Dropdown List for Blocks
« Reply #7 on: July 01, 2012, 01:29:37 AM »

Your code works in a modal dialog but will probably crash AutoCAD or fail in some other way if you tried to use it in a modeless context (e.g., palette or a modeless form). You can't store BlockTableRecords in a control's items collections if you want the control to be usable in a modeless context.

That's why this is a simple example.

I don't think I can agree that designing a control that can't be used on anything but a model form qualifies as 'simple'.



zoltan

  • Guest
Re: A Simple Dropdown List for Blocks
« Reply #8 on: July 01, 2012, 08:36:11 AM »
I don't think I can agree that designing a control that can't be used on anything but a model form qualifies as 'simple'.

Well, simple meaning that you just put it on a modal form, give it some BlockTableRecord or LayerTableRecord objects and it just works.  Creating a control like this that would work in a modeless context would require some complex logic to monitor the database and update the controls as the table records changed or update the list when records were added or removed, or the active document changed.  That implementation would be anything but simple.

I guess we just disagree on the definition of simple.

Maybe I should have used the word easy.  To get something done quickly, using myBlockComboBox.Items.Add(someBlockTableRecord) would be a lot easier than going out to CodeProject.com and finding some complicated control that somebody has designed to be very powerful and flexible, or purchasing a commercial product. Then, reading all about how it works and what it does before creating a code layer for separating the logic of displaying the information on the control.  And finally, creating a data binding between that control and some object which represents the data contained in the table records.

But I guess everybody's definition of easy is different.

TheMaster

  • Guest
Re: A Simple Dropdown List for Blocks
« Reply #9 on: July 01, 2012, 11:10:48 AM »
I don't think I can agree that designing a control that can't be used on anything but a model form qualifies as 'simple'.

Well, simple meaning that you just put it on a modal form, give it some BlockTableRecord or LayerTableRecord objects and it just works.  Creating a control like this that would work in a modeless context would require some complex logic to monitor the database and update the controls as the table records changed or update the list when records were added or removed, or the active document changed.  That implementation would be anything but simple.

I guess we just disagree on the definition of simple.

Maybe I should have used the word easy.  To get something done quickly, using myBlockComboBox.Items.Add(someBlockTableRecord) would be a lot easier than going out to CodeProject.com and finding some complicated control that somebody has designed to be very powerful and flexible, or purchasing a commercial product. Then, reading all about how it works and what it does before creating a code layer for separating the logic of displaying the information on the control.  And finally, creating a data binding between that control and some object which represents the data contained in the table records.

But I guess everybody's definition of easy is different.

If you're not familiar with things like data binding, then you don't know what 'easy' is, because data binding can make things even easier than what you've done, and without the horribly-bad practice of coupling business logic to the UI.

Perhaps after you've had to write more controls of different types that work in any context (modal or modeless), you might understand why doing things in what you consider to be the 'easy' way, but then ending up having to throw it out and redo it again from scratch (possibly more than once), is not the 'easy' way.

Beyond that, I'd prefer not to debate good/bad programming and design practices, as they are commonly-accepted ideas. If you think otherwise, post your code on StackOverflow.com or CodeProject.com and head for the nearest bunker  :laugh:
« Last Edit: July 01, 2012, 11:29:36 AM by TheMaster »

MexicanCustard

  • Swamp Rat
  • Posts: 705
Re: A Simple Dropdown List for Blocks
« Reply #10 on: July 02, 2012, 08:06:45 AM »
I have to agree with Tony on this one.  The "easier" way would be to use WPF and Data Binding your UI and seperating the "Model" in this case the Autocad DB.  I believe Autocad .NET has even exposed a bindable layer for both the events you show above but I have not tried using it.  Check Keans site I think he just wrote about it.

I use WPF for all my Autocad Palettes and Dialog Boxes now.  Usually just binding to an ObservableCollection and having my Business Logic just manipulate the collection.  It ends up that the logic doesnt care whats on the front end and the UI can be any TreeView, ListView, or Form it wants to be.
Revit 2019, AMEP 2019 64bit Win 10

TheMaster

  • Guest
Re: A Simple Dropdown List for Blocks
« Reply #11 on: July 02, 2012, 12:44:48 PM »

I guess we just disagree on the definition of simple.

Maybe I should have used the word easy.  To get something done quickly, using myBlockComboBox.Items.Add(someBlockTableRecord) would be a lot easier than going out to CodeProject.com and finding some complicated control that somebody has designed to be very powerful and flexible, or purchasing a commercial product. Then, reading all about how it works and what it does before creating a code layer for separating the logic of displaying the information on the control.  And finally, creating a data binding between that control and some object which represents the data contained in the table records.

But I guess everybody's definition of easy is different.

Yes, and everyone's definition of 'easy' is constrained by their experience and degree of familiarity with the tools they use.

Perhaps this little exercise might help you understand.

The exercise doesn't address images, but a simple extension of the standard WinForm's ComboBox could address that as well.

1.  Create a copy of the project you posted in this thread.

2.  Open the copy of the project, and delete your BlockComboBox from the form.

3.  Add a standard ComboBox to the form, and give it the name comboBox1

4.  Replace your form's constructor with this:

Code - C#: [Select]
  1.  
  2. public BlockForm(BlockTableRecord[] blocks)
  3. {
  4.     InitializeComponent();
  5.     this.comboBox1.DisplayMember = "Name";
  6.     this.comboBox1.DataSource = blocks;
  7. }
  8.  
  9.  

Run your project, and you'll see that with nothing more than two lines of code, the block names appear in a standard WinForms ComboBox.

The DisplayMember property value tells the combobox what property on the source item contains the text to be displayed in the dropdown list. A custom combobox that displays images along with text can utilize the very same method for getting the image from each item (by adding a property to the control that you can set to the name of the property that exposes the image on the item source object, and using Reflection to get its value from the items), and that would not require any dependence on any AutoCAD API, and could be reused for displaying any kind of items with images, rather than just blocks, and would require only one additional line of code to enable image display.

Once you've done the above, you will know what 'easy' is.

« Last Edit: July 02, 2012, 12:54:48 PM by TheMaster »

zoltan

  • Guest
Re: A Simple Dropdown List for Blocks
« Reply #12 on: July 02, 2012, 03:11:41 PM »
Perhaps this little exercise might help you understand.

Yes. I understand now.  Can you show me how to make it display the preview image?

TheMaster

  • Guest
Re: A Simple Dropdown List for Blocks
« Reply #13 on: July 02, 2012, 05:31:33 PM »
Perhaps this little exercise might help you understand.

Yes. I understand now.  Can you show me how to make it display the preview image?

If you mean how to write a generic combobox with image support that
uses data binding to get the item images, you would add a property to
the custom combobox that you set to the name of the property on the
item source object that exposes the image, and you can access it from
your OnDrawItem() override like this:

Code - C#: [Select]
  1.  
  2. // Assign the name of the property on the item source
  3. // object that holds the Image for the item, which for
  4. // a BlockTableRecord, would be "PreviewIcon":
  5.  
  6. public string ImageMember {get;set;}
  7.  
  8. // Call this to get the image for the item. You could
  9. // (and probably should) cache the image so that it
  10. // doesn't need to be fetched every time you need to
  11. // draw it.  You could use an ImageList to cache the
  12. // images for each item, keyed to the item's name.
  13.  
  14. System.Drawing.Image GetItemImage( object item )
  15. {
  16.   if( string.IsNullOrEmpty( this.ImageMember ) )
  17.     throw new InvalidOperationException("ImageMember property not assigned");
  18.   PropertyInfo pi = item.GetType().GetProperty( this.ImageMember );
  19.   if( pi == null )
  20.     throw new InvalidOperationException("Property not found on item source");
  21.   return pi.GetValue( item, null ) as System.Drawing.Image;
  22. }
  23.  
  24.  

Another thing you need to correct is that your custom combobox control
handles the DrawItem event, which is not how derived types implement
custom drawing. Events are meant to be handled by other consumers but
not by derived types. Derived types should override the corresponding
virtual member, which in this case is the OnDrawItem() method.
« Last Edit: July 02, 2012, 07:23:42 PM by TheMaster »

TheMaster

  • Guest
Re: A Simple Dropdown List for Blocks
« Reply #14 on: July 02, 2012, 07:55:24 PM »
Here is a SIMPLE layer list.  I would like to be able to make the controls inside the dropdown list be clickable and raise events, but that has proven to be pretty difficult in WinForms.  A piece-of-cake in WPF, from what I've read.


(Yes, the icons are crap.  I found them on the 'net. Feel free to replace them.)

Making the items clickable essentially requires that you handle the mouse events and use the location to determine what item the mouse was over, there's not much more to it than that. As long as you know the size of the images, and each one's origin in control coordinates, it shouldn't be hard to figure out which one was clicked.