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

0 Members and 1 Guest are viewing this topic.

zoltan

  • Newt
  • Posts: 188
Re: A Simple Dropdown List for Blocks
« Reply #15 on: July 03, 2012, 07:28:41 AM »
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.

Yes, That was my idea too.  I have tried it in a number of exhaustive ways.  It turns out that the mouse events are only raised for the control when it is rolled up and there are no mouse events when the list is dropped down.

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.

Yes.  Very good point.  I overlooked that in haste.
« Last Edit: July 03, 2012, 07:32:13 AM by zoltan »

TheMaster

  • Guest
Re: A Simple Dropdown List for Blocks
« Reply #16 on: July 03, 2012, 01:10:27 PM »
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.

Yes, That was my idea too.  I have tried it in a number of exhaustive ways.  It turns out that the mouse events are only raised for the control when it is rolled up and there are no mouse events when the list is dropped down.


Are you sure?  From looking at the framework code, it would appear that OnMouseDown(), OnMouseMove(), etc. are being called.



zoltan

  • Newt
  • Posts: 188
Re: A Simple Dropdown List for Blocks
« Reply #17 on: July 03, 2012, 01:43:44 PM »
Are you sure?  From looking at the framework code, it would appear that OnMouseDown(), OnMouseMove(), etc. are being called.

Where are you looking in the framework?

I put this in the control:
Code - C#: [Select]
  1. protected override void OnMouseDown(MouseEventArgs e)
  2. {
  3.    Debug.WriteLine(e.Location);
  4.   base.OnMouseDown(e);
  5. }
  6.  

It does not return a point when clicking on a dropdown list item.  Are you seeing different behavior?

Jeff H

  • Needs a day job
  • Posts: 6103
Re: A Simple Dropdown List for Blocks
« Reply #18 on: July 03, 2012, 02:42:08 PM »
Am I missing something which is usually the case but for a combo box would'nt you just use a SelectionChange Event index or value?
 
 

Jeff H

  • Needs a day job
  • Posts: 6103
Re: A Simple Dropdown List for Blocks
« Reply #19 on: July 03, 2012, 03:00:16 PM »
Am I missing something which is usually the case but for a combo box would'nt you just use a SelectionChange Event index or value?
I see what your talking about.
 
 

TheMaster

  • Guest
Re: A Simple Dropdown List for Blocks
« Reply #20 on: July 04, 2012, 01:31:19 AM »
Are you sure?  From looking at the framework code, it would appear that OnMouseDown(), OnMouseMove(), etc. are being called.

Where are you looking in the framework?

I put this in the control:
Code - C#: [Select]
  1. protected override void OnMouseDown(MouseEventArgs e)
  2. {
  3.    Debug.WriteLine(e.Location);
  4.   base.OnMouseDown(e);
  5. }
  6.  

It does not return a point when clicking on a dropdown list item.  Are you seeing different behavior?

Are you trying to get notified when you click on an item in the dropdown list, or in the control's edit box?

Cloning AutoCAD's layer combo is not trivial, and you're not going to do it by simply subclassing a combobox and handling a few events. The default behavior for a ComboBox is to close the dropdown when the user clicks on an item in it.  The dropdown on AutoCAD's layer combo doesn't close when the user clicks an item, and you're not going to emulate that behavior easily.

I don't recall ever needing a layer combo box, but I have used a ListView to display layers and their states using icons in the same style as the layer combo.


MexicanCustard

  • Swamp Rat
  • Posts: 705
Re: A Simple Dropdown List for Blocks
« Reply #21 on: July 05, 2012, 08:07:14 AM »
Just want to point out that now might be a good time for you to look into WPF.  All the problems you're having are easily addressed in WPF including cloning the behavior of AutoCADs dropdown listbox for layers, although I haven't tried it yet.  A WPF listbox has both a Click event and a SelectionChanged event you could hook into either depending on how you want to handle user actions.
Revit 2019, AMEP 2019 64bit Win 10

Jeff H

  • Needs a day job
  • Posts: 6103
Re: A Simple Dropdown List for Blocks
« Reply #22 on: July 08, 2012, 11:06:59 AM »
Thanks for sharing Zoltan.
 
I thought had something laying around kinda similar, but nothing great or special, and not a good implementation for reuse, but could use it to kinda mimic the layer combobox on the properties palette.
 
One of the issues with it is that one of properties the of the object that you bind it to needs to be a System.Drawing.Color that you set the ComboBox.ValueMember to or all the boxes will be black.
 

 
I could not find it but off of memory for binding it
Code - C#: [Select]
  1.             comboBoxColor.DataSource = colors;
  2.             comboBoxColor.ValueMember = "Color";
  3.             comboBoxColor.DisplayMember = "Name";
  4.  

 
Then basically a hard-coded version of OnDrawItem
Code - C#: [Select]
  1.         protected override void OnDrawItem(DrawItemEventArgs e)
  2.         {
  3.             e.DrawBackground();
  4.             if (e.Index > -1)
  5.             {
  6.                 object obj = this.Items[e.Index];
  7.                 object objValue = obj.GetType().GetProperty(this.ValueMember).GetGetMethod().Invoke(obj, null);
  8.                 string dispalyMember = obj.GetType().GetProperty(this.DisplayMember).GetValue(obj, null).ToString();
  9.  
  10.                 Color fillColor = objValue is Color ? (Color)objValue : Color.Black;
  11.                 Rectangle colorRectangle = new Rectangle(e.Bounds.X  + 1, e.Bounds.Y +  2, 9, 9);
  12.  
  13.                 using (Brush brush = new SolidBrush(fillColor))
  14.                 {
  15.                     e.Graphics.FillRectangle(brush, colorRectangle);
  16.                     e.Graphics.DrawRectangle(Pens.Black, colorRectangle);
  17.                 }
  18.  
  19.                 using (Brush brush = new SolidBrush(e.ForeColor))
  20.                 {
  21.                     e.Graphics.DrawString(dispalyMember, e.Font, brush, colorRectangle.Right  + 2, e.Bounds.Top);
  22.                 }
  23.  
  24.                 e.DrawFocusRectangle();
  25.             }
  26.         }
  27.  

When I first did I tried something like this where I added some public properties so set box height and width, text offset, etc..... in VS designer.
Code - C#: [Select]
  1.   public partial class ColorComboBox : ComboBox
  2.     {
  3.         private System.ComponentModel.IContainer components = null;
  4.         public Pen RectangleBorderColor { get; set; }
  5.         public Color RectangleFillColor { get; set; }
  6.         public int RectangleWidth { get; set; }
  7.         public int RectangleHeight { get; set; }
  8.         public int RectangleXOffset { get; set; }
  9.         public int RectangleYOffset { get; set; }
  10.         public int TextXOffset { get; set; }
  11.         public int TextYOffset { get; set; }
  12.         public ColorComboBox()
  13.         {
  14.             InitializeComponent();
  15.             RectangleBorderColor = Pens.Black;
  16.             RectangleFillColor = Color.White;
  17.             RectangleHeight = this.Height / 2;
  18.             RectangleWidth = RectangleHeight;
  19.             RectangleXOffset = this.Width % RectangleWidth;
  20.             RectangleYOffset = this.Height % RectangleHeight;
  21.             TextXOffset = RectangleWidth / 2;
  22.             TextYOffset = 0;
  23.         }
  24.        
  25.               /// <summary>
  26.         /// Clean up any resources being used.
  27.         /// </summary>
  28.         /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
  29.         protected override void Dispose(bool disposing)
  30.         {
  31.             if (disposing && (components != null))
  32.             {
  33.                 components.Dispose();
  34.             }
  35.             base.Dispose(disposing);
  36.         }  
  37.         /// <summary>
  38.         /// Required method for Designer support - do not modify
  39.         /// the contents of this method with the code editor.
  40.         /// </summary>
  41.         private void InitializeComponent()
  42.         {
  43.             components = new System.ComponentModel.Container();
  44.         }
  45.         protected override void OnCreateControl()
  46.         {
  47.             base.OnCreateControl();
  48.             this.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawFixed;
  49.             this.DropDownStyle = ComboBoxStyle.DropDownList;
  50.         }
  51.         protected override void OnDrawItem(DrawItemEventArgs e)
  52.         {
  53.             e.DrawBackground();
  54.             if (e.Index > -1)
  55.             {
  56.                 object obj = this.Items[e.Index];
  57.                 object objValue = obj.GetType().GetProperty(this.ValueMember).GetGetMethod().Invoke(obj, null);
  58.                 string dispalyMember = obj.GetType().GetProperty(this.DisplayMember).GetValue(obj, null).ToString();
  59.                 Color fillColor = objValue is Color ? (Color)objValue : RectangleFillColor;
  60.  
  61.                 int width = RectangleWidth > e.Bounds.Width ? e.Bounds.Width : RectangleWidth;
  62.                 int height = RectangleHeight > e.Bounds.Height ? e.Bounds.Height : RectangleHeight;
  63.  
  64.                 int xOffset = RectangleXOffset > e.Bounds.Width - width ? e.Bounds.Width - width : RectangleXOffset;
  65.                 int yOffset = RectangleYOffset > e.Bounds.Height - height ? e.Bounds.Height - height : RectangleYOffset;
  66.  
  67.                 Rectangle colorRectangle = new Rectangle(e.Bounds.X + xOffset, e.Bounds.Y +  yOffset, width, height);
  68.  
  69.                 using (Brush brush = new SolidBrush(fillColor))
  70.                 {
  71.                     e.Graphics.FillRectangle(brush, colorRectangle);
  72.                     e.Graphics.DrawRectangle(Pens.Black, colorRectangle);
  73.                 }
  74.  
  75.                 using (Brush brush = new SolidBrush(e.ForeColor))
  76.                 {
  77.                     e.Graphics.DrawString(dispalyMember, e.Font, brush, colorRectangle.Right  + TextXOffset, e.Bounds.Top  + TextYOffset);
  78.                 }
  79.  
  80.                 e.DrawFocusRectangle();
  81.             }
  82.         }
  83.     }
  84.  

 
 
 
 
« Last Edit: July 08, 2012, 11:19:54 AM by Jeff H »

zoltan

  • Newt
  • Posts: 188
Re: A Simple Dropdown List for Blocks
« Reply #23 on: July 08, 2012, 12:27:09 PM »
That is very cool, Jeff.  I was about to make a ColorComboBox as well, but I got off track.

If you bind it to Autodesk.AutoCAD.Colors.Color, your ValueMember would be ColorValue and the DisplayMember property would be ColorName.  It should work for ACI colors, True Color, and Color Book colors.
« Last Edit: July 08, 2012, 12:32:28 PM by zoltan »

Jeff H

  • Needs a day job
  • Posts: 6103
Re: A Simple Dropdown List for Blocks
« Reply #24 on: July 08, 2012, 01:02:27 PM »
A wrote a little class for testing it quickly and knew it useless and was wasting my time and was not really needed but you just made me realize how really useless and how much I really wasted my time writing it. :ugly:
 
 
This website looks familiar and I think I picked up the basic idea from here
http://alinberce.wordpress.com/2009/05/14/custom-combo-box-with-colors-and-text-part-ii/
 
Another issue I noticed is if another control event, or just changing the color value of the object in the bound collection that is the SelectedValue or current item in combobox the color would update 1, 2, 3, or sometimes 4 seconds later but probably is a method that can be used to update it immediately.

Jeff H

  • Needs a day job
  • Posts: 6103
Re: A Simple Dropdown List for Blocks
« Reply #25 on: July 08, 2012, 01:58:03 PM »
That is very cool, Jeff.  I was about to make a ColorComboBox as well, but I got off track.

If you bind it to Autodesk.AutoCAD.Colors.Color, your ValueMember would be ColorValue and the DisplayMember property would be ColorName.  It should work for ACI colors, True Color, and Color Book colors.

Not sure the difference and might not work in other situations but little testing I did looks like Color.ColorNameForDisplay should be used.

Jeff H

  • Needs a day job
  • Posts: 6103
Re: A Simple Dropdown List for Blocks
« Reply #26 on: July 08, 2012, 05:45:37 PM »
Another issue I noticed is if another control event, or just changing the color value of the object in the bound collection that is the SelectedValue or current item in combobox the color would update 1, 2, 3, or sometimes 4 seconds later but probably is a method that can be used to update it immediately.
Not sure if best way but ComboBox.Refresh() seems to work