Cyotek Development Bloghttps://devblog.cyotek.com/tag/combobox/atom.xml2014-10-11T07:30:31ZAdding Double Click support to the ComboBox controlurn:uuid:ea23b0c5-bc2a-40ba-9fb2-ca1533294f222014-10-11T07:30:31Z2014-10-11T07:30:31Z<p>I was recently using a <code>ComboBox</code> control with the
<code>DropDownStyle</code> set to <code>Simple</code>, effectively turning into a
combined text box and list box.</p>
<p>However, when I wanted an action to occur on double clicking an
item in the list I found that the control doesn't actually offer
double click support. I suppose I should have just ripped out
the combo box at that point and went with dedicated controls but
instead I decided to extend <code>ComboBox</code> to support double clicks.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/combobox-double-click.png" class="gallery" title="Double click events from a simple mode ComboBox control" ><img src="https://images.cyotek.com/image/thumbnail/devblog/combobox-double-click.png" alt="Double click events from a simple mode ComboBox control" decoding="async" loading="lazy" /></a><figcaption>Double click events from a simple mode ComboBox control</figcaption></figure><h2 id="hmm-no-wm_lbuttondblclk-message">Hmm, no WM_LBUTTONDBLCLK message?</h2>
<p>I had assumed I could simply get the handle of the list
component, set the <code>CS_DBLCLKS</code> style, and start receiving
<code>WM_LBUTTONDBLCLK</code> messages. Unfortunately I couldn't get this
to work. Something to revisit another day perhaps.</p>
<h2 id="fine-lets-fake-it-with-wm_lbuttonup-instead">Fine, lets fake it with WM_LBUTTONUP instead</h2>
<p>So plan A was a bust. Not to worry, I had another idea. In a
<a href="/post/getting-the-hwnd-of-the-edit-component-within-a-combobox-control">previous post</a> I described how to use the <code>GetComboBoxInfo</code>
Win32 API call to obtain the handles to the integrated controls.
We'll use this along with a <code>NativeWindow</code> to watch for
<code>WM_LBUTTONUP</code> messages and handle our double clicks that way.</p>
<h2 id="what-is-nativewindow">What is NativeWindow?</h2>
<p>I haven't described <code>NativeWindow</code> in any previous post, so I'll
briefly cover it now. <code>NativeWindow</code> is a managed wrapper around
a Win32 window handle, and allows you to easily hook into it's
window procedure (WndProc) in order to capture and process
messages sent to the window. Very tidy. The most important class
members are</p>
<ul>
<li><code>AssignHandle</code> - attaches the class to a window</li>
<li><code>ReleaseHandle</code> - detaches the handle once you're finished
with it</li>
<li><code>WndProc</code> - allows you to process messages, otherwise there's
not really much point in using the class!</li>
</ul>
<p>One final point, in most cases you're probably going to want to
subclass <code>NativeWindow</code> as <code>WndProc</code> is protected. And that's
what we'll do here, using a new <code>ListBoxNativeWindow</code> class.</p>
<h2 id="attaching-the-handle">Attaching the handle</h2>
<p>As I mentioned above, you have to explicitly attached your
<code>NativeWindow</code> implementation to a window. For this
demonstration control we'll do it when the control handle is
created, and when the drop down list style is changed. I'll also
add a <code>AllowDoubleClick</code> property to control the new behaviour,
so we'll also set it from there.</p>
<blockquote>
<p><code>NativeWindow</code> doesn't implement <code>IDisposable</code> so for best
practice you should make sure you manually clean up by calling
<code>ReleaseHandle</code> when you are done.</p>
</blockquote>
<p>As I've previously covered the <code>COMBOBOXINFO</code> structure and
<code>GetComboBoxInfo</code> call I won't go over these again - please
refer to my <a href="/post/getting-the-hwnd-of-the-edit-component-within-a-combobox-control">previous post</a> if you need more info.</p>
<p>Assuming we successfully obtain the combo box information, we
instantiate a new instance of our <code>ListBoxNativeWindow</code> and
attach it to the handle of the list box.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> ListBoxNativeWindow _listBoxWindow<span class="symbol">;</span>

<span class="keyword">private</span> <span class="keyword">void</span> AttachHandle<span class="symbol">(</span><span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>ReleaseHandle<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>IsHandleCreated <span class="symbol">&amp;&amp;</span> <span class="keyword">this</span><span class="symbol">.</span>AllowDoubleClick <span class="symbol">&amp;&amp;</span> <span class="keyword">this</span><span class="symbol">.</span>DropDownStyle <span class="symbol">==</span> ComboBoxStyle<span class="symbol">.</span>Simple<span class="symbol">)</span>
 <span class="symbol">{</span>
 COMBOBOXINFO info<span class="symbol">;</span>

 info <span class="symbol">=</span> <span class="keyword">new</span> COMBOBOXINFO<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 info<span class="symbol">.</span>cbSize <span class="symbol">=</span> Marshal<span class="symbol">.</span>SizeOf<span class="symbol">(</span>info<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>GetComboBoxInfo<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Handle<span class="symbol">,</span> <span class="keyword">ref</span> info<span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 IntPtr hWnd<span class="symbol">;</span>

 hWnd <span class="symbol">=</span> info<span class="symbol">.</span>hwndList<span class="symbol">;</span>

 _listBoxWindow <span class="symbol">=</span> <span class="keyword">new</span> ListBoxNativeWindow<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">)</span><span class="symbol">;</span>
 _listBoxWindow<span class="symbol">.</span>AssignHandle<span class="symbol">(</span>hWnd<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>Our new class is also storing a reference to the owner
<code>ComboBox</code> control so that we can raise events as appropriate
later on.</p>
<p>As we should clean up behind ourselves, there's a helper method
to release any existing handles which we will call when
assigning a new handle, or when disposing of the control.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">void</span> ReleaseHandle<span class="symbol">(</span><span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span>_listBoxWindow <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 _listBoxWindow<span class="symbol">.</span>ReleaseHandle<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 _listBoxWindow <span class="symbol">=</span> <span class="keyword">null</span><span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>Now it's time to watch for some messages.</p>
<h2 id="intercepting-messages">Intercepting messages</h2>
<p>Intercepting messages in a <code>NativeWindow</code> is no different to
that of a normal control - just override <code>WndProc</code> and wait for
something interesting.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">const</span> <span class="keyword">int</span> WM_LBUTTONUP <span class="symbol">=</span> <span class="number">0x0202</span><span class="symbol">;</span>

<span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> WndProc<span class="symbol">(</span><span class="keyword">ref</span> Message m<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span>m<span class="symbol">.</span>Msg <span class="symbol">==</span> WM_LBUTTONUP<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="comment">// do stuff!</span>
 <span class="symbol">}</span>

 <span class="keyword">base</span><span class="symbol">.</span>WndProc<span class="symbol">(</span><span class="keyword">ref</span> m<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="double-clicks">Double clicks</h2>
<p>A double click is a pretty simple thing - it is the second click
to occur within a defined interval and with the cursor within
the region of the first click. These system values are
configurable by the end user so we shouldn't hard code our own
values.</p>
<p>The <code>DoubleClickSize</code> and <code>DoubleClickTime</code> properties of the
<code>SystemInformation</code> class provide managed access to these system
values, and so we can now populate our <code>WndProc</code> template with
some real code.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">if</span> <span class="symbol">(</span>m<span class="symbol">.</span>Msg <span class="symbol">==</span> NativeMethods<span class="symbol">.</span>WM_LBUTTONUP<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">long</span> previousMessageTime<span class="symbol">;</span>
 <span class="keyword">long</span> currentMessageTime<span class="symbol">;</span>
 Point currentLocation<span class="symbol">;</span>

 previousMessageTime <span class="symbol">=</span> _lastMessageTime<span class="symbol">;</span>
 currentMessageTime <span class="symbol">=</span> DateTime<span class="symbol">.</span>Now<span class="symbol">.</span>Ticks<span class="symbol">;</span>
 currentLocation <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>GetPoint<span class="symbol">(</span>m<span class="symbol">.</span>LParam<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>_lastMessageTime <span class="symbol">&gt;</span> <span class="number">0</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 Rectangle doubleClickBounds<span class="symbol">;</span>
 Size doubleClickSize<span class="symbol">;</span>

 doubleClickSize <span class="symbol">=</span> SystemInformation<span class="symbol">.</span>DoubleClickSize<span class="symbol">;</span>
 doubleClickBounds <span class="symbol">=</span> <span class="keyword">new</span> Rectangle<span class="symbol">(</span>_lastMousePosition<span class="symbol">.</span>X <span class="symbol">-</span> <span class="symbol">(</span>doubleClickSize<span class="symbol">.</span>Width <span class="symbol">/</span> <span class="number">2</span><span class="symbol">)</span><span class="symbol">,</span> _lastMousePosition<span class="symbol">.</span>Y <span class="symbol">-</span> <span class="symbol">(</span>doubleClickSize<span class="symbol">.</span>Height <span class="symbol">/</span> <span class="number">2</span><span class="symbol">)</span><span class="symbol">,</span> doubleClickSize<span class="symbol">.</span>Width<span class="symbol">,</span> doubleClickSize<span class="symbol">.</span>Height<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>previousMessageTime <span class="symbol">+</span> <span class="symbol">(</span>SystemInformation<span class="symbol">.</span>DoubleClickTime <span class="symbol">*</span> TimeSpan<span class="symbol">.</span>TicksPerMillisecond<span class="symbol">)</span> <span class="symbol">&gt;</span> currentMessageTime <span class="symbol">&amp;&amp;</span> doubleClickBounds<span class="symbol">.</span>Contains<span class="symbol">(</span>currentLocation<span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 MouseEventArgs e<span class="symbol">;</span>

 e <span class="symbol">=</span> <span class="keyword">new</span> MouseEventArgs<span class="symbol">(</span>MouseButtons<span class="symbol">.</span>Left<span class="symbol">,</span> <span class="number">2</span><span class="symbol">,</span> currentLocation<span class="symbol">.</span>X<span class="symbol">,</span> currentLocation<span class="symbol">.</span>Y<span class="symbol">,</span> <span class="number">0</span><span class="symbol">)</span><span class="symbol">;</span>

 _owner<span class="symbol">.</span>RaiseDoubleClick<span class="symbol">(</span>e<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>

 _lastMessageTime <span class="symbol">=</span> currentMessageTime<span class="symbol">;</span>
 _lastMousePosition <span class="symbol">=</span> currentLocation<span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>Although it might look a little complicated at first glance, it
should be straight forward.</p>
<ul>
<li>The very first time you click with the left mouse button, we
record the current time and the cursor location</li>
<li>Each subsequent click then
<ul>
<li>Compares the current cursor position against a rectangle
centered on the previous position</li>
<li>Compares the previous click time with the current time
subtracted from the interval</li>
<li>If both the interval since the last click has not elapsed
and the cursor is in the same general area, then we have our
double click</li>
<li>Regards of if an event is to be raised or not, we then
update the time and position for the next click</li>
</ul>
</li>
</ul>
<h2 id="raising-the-event">Raising the event</h2>
<p>Although I'd like to do the &quot;right thing&quot; and trigger a
<code>WM_LBUTTONDBLCLK </code> message, the control doesn't support it and
there's not really much point in adding it when it's not going
to have any real value. So we'll manually do it.</p>
<p>I start by adding an internal method to our <code>ComboBox</code> control -
I tend to avoid internals where possible but I don't really see
a need to expose this publicly.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">internal</span> <span class="keyword">void</span> RaiseDoubleClick<span class="symbol">(</span>MouseEventArgs e<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>OnDoubleClick<span class="symbol">(</span>EventArgs<span class="symbol">.</span>Empty<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>OnMouseDoubleClick<span class="symbol">(</span>e<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>Short and to the point, it simply raises the two different
events .NET controls have for double clicks.</p>
<p>And back in our <code>WndProc</code>, we construct a new <code>MouseEventArgs</code>
object and then call the new method.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
MouseEventArgs e<span class="symbol">;</span>

e <span class="symbol">=</span> <span class="keyword">new</span> MouseEventArgs<span class="symbol">(</span>MouseButtons<span class="symbol">.</span>Left<span class="symbol">,</span> <span class="number">2</span><span class="symbol">,</span> currentLocation<span class="symbol">.</span>X<span class="symbol">,</span> currentLocation<span class="symbol">.</span>Y<span class="symbol">,</span> <span class="number">0</span><span class="symbol">)</span><span class="symbol">;</span>

_owner<span class="symbol">.</span>RaiseDoubleClick<span class="symbol">(</span>e<span class="symbol">)</span><span class="symbol">;</span>
</pre>
</figure>
<p>It's worth pointing out the fudge in this - the magic number <code>2</code>
which represents the number of times the button was clicked. The
<code>0</code>, while still magic, represents a mouse wheel delta which is
not appropriate for this event.</p>
<p>And with that code in place, this slightly long winded article
has gotten to the point and you now have fully working events.</p>
<h2 id="really-i-cant-see-them">Really? I can't see them</h2>
<p>Oh of course. As the <code>ComboBox</code> control doesn't support the
<code>DoubleClick</code> and <code>MouseDoubleClick</code> events, the <code>DoubleClick</code>
event has been hidden (but not <code>MouseDoubleClick</code> for some
reason). Easy enough to bring it back - just redefine
<code>DoubleClick</code> with the <code>new</code> keyword set the <code>EditorBrowsable</code>
and <code>Browsable</code> attributes so it will appear in designers.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="symbol">[</span>EditorBrowsable<span class="symbol">(</span>EditorBrowsableState<span class="symbol">.</span>Always<span class="symbol">)</span><span class="symbol">]</span>
<span class="symbol">[</span>Browsable<span class="symbol">(</span><span class="keyword">true</span><span class="symbol">)</span><span class="symbol">]</span>
<span class="keyword">public</span> <span class="keyword">new</span> <span class="keyword">event</span> EventHandler DoubleClick
<span class="symbol">{</span>
 add <span class="symbol">{</span> <span class="keyword">base</span><span class="symbol">.</span>DoubleClick <span class="symbol">+=</span> value<span class="symbol">;</span> <span class="symbol">}</span>
 remove <span class="symbol">{</span> <span class="keyword">base</span><span class="symbol">.</span>DoubleClick <span class="symbol">-=</span> value<span class="symbol">;</span> <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="always-a-catch">Always a catch</h2>
<p>This was yet another blog post that was written in a hurry after
writing some code in a hurry. I'm positive there must be a
better way using normal window styles and messages rather than
the manual approach I've taken.</p>
<p>There's also a flaw in the code - if you triple click (or more)
then you'll get two (or more) double click events. I don't know
of too many people who spam double clicks so I'm going to ignore
this for now. Possibly at some point I'll be bored enough to
take another look at this and see where I went wrong with the
pure API approach.</p>
<p>Finally, given the hurry with which both of these items were
written, it hasn't had any robust testing, and so may be a
flawed piece of work.</p>
<p>As always, a demonstration project accompanies this article.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2014-10-11 - First published</li>
<li>2020-11-21 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/adding-double-click-support-to-the-combobox-control .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comCreating a WYSIWYG font ComboBox using C#urn:uuid:33ca6c15-e6c6-46e9-a7bb-3dfb282339b82011-02-19T20:05:31Z2011-02-19T20:05:31Z<p>This article shows how to use the built in ownerdraw
functionality of a standard Windows Forms ComboBox control to
display a WYSIWYG font list.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/font-combobox.png" class="gallery" title="The FontComboBox control in a sample application" ><img src="https://images.cyotek.com/image/thumbnail/devblog/font-combobox.png" alt="The FontComboBox control in a sample application" decoding="async" loading="lazy" /></a><figcaption>The FontComboBox control in a sample application</figcaption></figure><h2 id="setting-up-the-control">Setting up the control</h2>
<p>To start, we'll create a new class, and inherit this from the
<code>ComboBox</code> control.</p>
<p>We are going to use variable ownerdraw for this sample, as it
gives us a little more flexibility without having to mess around
with the <code>ItemHeight</code> property. We'll add a constructor, and set
the ownerdraw mode here. Also, we'll add a new version of the
<code>DrawMode</code> property, which we'll both hide and disable the value
persistence. We always want the font list to be sorted, so for
now we'll do the same with the <code>Sorted</code> property.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> FontComboBox<span class="symbol">(</span><span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>DrawMode <span class="symbol">=</span> DrawMode<span class="symbol">.</span>OwnerDrawVariable<span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>Sorted <span class="symbol">=</span> <span class="keyword">true</span><span class="symbol">;</span>
<span class="symbol">}</span>

<span class="symbol">[</span>Browsable<span class="symbol">(</span><span class="keyword">false</span><span class="symbol">)</span><span class="symbol">,</span> DesignerSerializationVisibility<span class="symbol">(</span>DesignerSerializationVisibility<span class="symbol">.</span>Hidden<span class="symbol">)</span><span class="symbol">,</span> EditorBrowsable<span class="symbol">(</span>EditorBrowsableState<span class="symbol">.</span>Never<span class="symbol">)</span><span class="symbol">]</span>
<span class="keyword">public</span> <span class="keyword">new</span> DrawMode DrawMode
<span class="symbol">{</span>
 <span class="keyword">get</span> <span class="symbol">{</span> <span class="keyword">return</span> <span class="keyword">base</span><span class="symbol">.</span>DrawMode<span class="symbol">;</span> <span class="symbol">}</span>
 <span class="keyword">set</span> <span class="symbol">{</span> <span class="keyword">base</span><span class="symbol">.</span>DrawMode <span class="symbol">=</span> value<span class="symbol">;</span> <span class="symbol">}</span>
<span class="symbol">}</span>

<span class="symbol">[</span>Browsable<span class="symbol">(</span><span class="keyword">false</span><span class="symbol">)</span><span class="symbol">,</span> DesignerSerializationVisibility<span class="symbol">(</span>DesignerSerializationVisibility<span class="symbol">.</span>Hidden<span class="symbol">)</span><span class="symbol">,</span> EditorBrowsable<span class="symbol">(</span>EditorBrowsableState<span class="symbol">.</span>Never<span class="symbol">)</span><span class="symbol">]</span>
<span class="keyword">public</span> <span class="keyword">new</span> <span class="keyword">bool</span> Sorted
<span class="symbol">{</span>
 <span class="keyword">get</span> <span class="symbol">{</span> <span class="keyword">return</span> <span class="keyword">base</span><span class="symbol">.</span>Sorted<span class="symbol">;</span> <span class="symbol">}</span>
 <span class="keyword">set</span> <span class="symbol">{</span> <span class="keyword">base</span><span class="symbol">.</span>Sorted <span class="symbol">=</span> value<span class="symbol">;</span> <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="caching-font-objects">Caching Font objects</h2>
<p>In order to avoid continuously creating and destroying font
objects, we'll create a internal cache of fonts. When it's time
to draw the control, the cache will be queried - if the
requested font exists, it will be returned, otherwise the font
will be created and added to the cache. This will be done via
the <code>GetFont</code> method below.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">protected</span> <span class="keyword">virtual</span> Font GetFont<span class="symbol">(</span><span class="keyword">string</span> fontFamilyName<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">lock</span> <span class="symbol">(</span>_fontCache<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span><span class="symbol">!</span>_fontCache<span class="symbol">.</span>ContainsKey<span class="symbol">(</span>fontFamilyName<span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 Font font<span class="symbol">;</span>

 font <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>GetFont<span class="symbol">(</span>fontFamilyName<span class="symbol">,</span> FontStyle<span class="symbol">.</span>Regular<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">if</span> <span class="symbol">(</span>font <span class="symbol">==</span> <span class="keyword">null</span><span class="symbol">)</span>
 font <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>GetFont<span class="symbol">(</span>fontFamilyName<span class="symbol">,</span> FontStyle<span class="symbol">.</span>Bold<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">if</span> <span class="symbol">(</span>font <span class="symbol">==</span> <span class="keyword">null</span><span class="symbol">)</span>
 font <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>GetFont<span class="symbol">(</span>fontFamilyName<span class="symbol">,</span> FontStyle<span class="symbol">.</span>Italic<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">if</span> <span class="symbol">(</span>font <span class="symbol">==</span> <span class="keyword">null</span><span class="symbol">)</span>
 font <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>GetFont<span class="symbol">(</span>fontFamilyName<span class="symbol">,</span> FontStyle<span class="symbol">.</span>Bold <span class="symbol">|</span> FontStyle<span class="symbol">.</span>Italic<span class="symbol">)</span><span class="symbol">;</span>

 _fontCache<span class="symbol">.</span>Add<span class="symbol">(</span>fontFamilyName<span class="symbol">,</span> font<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>

 <span class="keyword">return</span> _fontCache<span class="symbol">[</span>fontFamilyName<span class="symbol">]</span><span class="symbol">;</span>
<span class="symbol">}</span>

<span class="keyword">protected</span> <span class="keyword">virtual</span> Font GetFont<span class="symbol">(</span><span class="keyword">string</span> fontFamilyName<span class="symbol">,</span> FontStyle fontStyle<span class="symbol">)</span>
<span class="symbol">{</span>
 Font font<span class="symbol">;</span>

 <span class="keyword">try</span>
 <span class="symbol">{</span>
 font <span class="symbol">=</span> <span class="keyword">new</span> Font<span class="symbol">(</span>fontFamilyName<span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>PreviewFontSize<span class="symbol">,</span> fontStyle<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="keyword">catch</span>
 <span class="symbol">{</span>
 font <span class="symbol">=</span> <span class="keyword">null</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">return</span> font<span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<blockquote>
<p>Note: Whilst testing the control, I discovered that some of
the fonts installed on the development system only had bold or
italic styles. The original version of this method, which
always attempts to get the normal style would cause a crash.</p>
</blockquote>
<p>Due to this, I changed the method to try and access the normal
style, and if that failed, to try the other styles. Perhaps
there is a better way of doing this, but I leave that as an
exercise for the future.</p>
<p>As we don't want the font size of the dropdown list to
necessarily match that of the display/edit portion, we'll add a
new property named `PreviewFontSize*.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">event</span> EventHandler PreviewFontSizeChanged<span class="symbol">;</span>

<span class="symbol">[</span>Category<span class="symbol">(</span><span class="string">&quot;Appearance&quot;</span><span class="symbol">)</span><span class="symbol">,</span> DefaultValue<span class="symbol">(</span><span class="number">12</span><span class="symbol">)</span><span class="symbol">]</span>
<span class="keyword">public</span> <span class="keyword">int</span> PreviewFontSize
<span class="symbol">{</span>
 <span class="keyword">get</span> <span class="symbol">{</span> <span class="keyword">return</span> _previewFontSize<span class="symbol">;</span> <span class="symbol">}</span>
 <span class="keyword">set</span>
 <span class="symbol">{</span>
 _previewFontSize <span class="symbol">=</span> value<span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>OnPreviewFontSizeChanged<span class="symbol">(</span>EventArgs<span class="symbol">.</span>Empty<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>

<span class="keyword">protected</span> <span class="keyword">virtual</span> <span class="keyword">void</span> OnPreviewFontSizeChanged<span class="symbol">(</span>EventArgs e<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span>PreviewFontSizeChanged <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
 PreviewFontSizeChanged<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">,</span> e<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>CalculateLayout<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>When certain actions occur, such as this property changing, we
want to calculate the height of items in the dropdown list.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">void</span> CalculateLayout<span class="symbol">(</span><span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>ClearFontCache<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">using</span> <span class="symbol">(</span>Font font <span class="symbol">=</span> <span class="keyword">new</span> Font<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Font<span class="symbol">.</span>FontFamily<span class="symbol">,</span> <span class="symbol">(</span><span class="keyword">float</span><span class="symbol">)</span><span class="keyword">this</span><span class="symbol">.</span>PreviewFontSize<span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 Size textSize<span class="symbol">;</span>

 textSize <span class="symbol">=</span> TextRenderer<span class="symbol">.</span>MeasureText<span class="symbol">(</span><span class="string">&quot;yY&quot;</span><span class="symbol">,</span> font<span class="symbol">)</span><span class="symbol">;</span>
 _itemHeight <span class="symbol">=</span> textSize<span class="symbol">.</span>Height <span class="symbol">+</span> <span class="number">2</span><span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="loading-the-list-of-font-families">Loading the list of font families</h2>
<p>In order to avoid slowing the control down without reason, we'll
delay loading the list of font families until there is a reason,
either when the control's text has changed, or when the control
gets focus.</p>
<p>This will be done by creating a <code>LoadFontFamilies</code> method which
will be called by overriding <code>OnGotFocus</code> and <code>OnTextChanged</code>.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">virtual</span> <span class="keyword">void</span> LoadFontFamilies<span class="symbol">(</span><span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Items<span class="symbol">.</span>Count <span class="symbol">==</span> <span class="number">0</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 Cursor<span class="symbol">.</span>Current <span class="symbol">=</span> Cursors<span class="symbol">.</span>WaitCursor<span class="symbol">;</span>

 <span class="keyword">foreach</span> <span class="symbol">(</span>FontFamily fontFamily <span class="keyword">in</span> FontFamily<span class="symbol">.</span>Families<span class="symbol">)</span>
 <span class="keyword">this</span><span class="symbol">.</span>Items<span class="symbol">.</span>Add<span class="symbol">(</span>fontFamily<span class="symbol">.</span>Name<span class="symbol">)</span><span class="symbol">;</span>

 Cursor<span class="symbol">.</span>Current <span class="symbol">=</span> Cursors<span class="symbol">.</span>Default<span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>

<span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> OnGotFocus<span class="symbol">(</span>EventArgs e<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>LoadFontFamilies<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">base</span><span class="symbol">.</span>OnGotFocus<span class="symbol">(</span>e<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>

<span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> OnTextChanged<span class="symbol">(</span>EventArgs e<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">base</span><span class="symbol">.</span>OnTextChanged<span class="symbol">(</span>e<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Items<span class="symbol">.</span>Count <span class="symbol">==</span> <span class="number">0</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">int</span> selectedIndex<span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>LoadFontFamilies<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 selectedIndex <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>FindStringExact<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Text<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">if</span> <span class="symbol">(</span>selectedIndex <span class="symbol">!=</span> <span class="symbol">-</span><span class="number">1</span><span class="symbol">)</span>
 <span class="keyword">this</span><span class="symbol">.</span>SelectedIndex <span class="symbol">=</span> selectedIndex<span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="drawing-the-items">Drawing the items</h2>
<p>Drawing an overdraw ComboBox is done by overriding the
<code>OnDrawItem</code> method. However, as we have told the control we are
doing variable sized ownerdraw, we also need to override
<code>OnMeasureItem</code>. This method allows us to define the size for
each item, or in the case of this control to set the height of
each item to match the pixel height calculated for the value of
the <code>PreviewFontSize</code> property.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> OnMeasureItem<span class="symbol">(</span>MeasureItemEventArgs e<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">base</span><span class="symbol">.</span>OnMeasureItem<span class="symbol">(</span>e<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>e<span class="symbol">.</span>Index <span class="symbol">&gt;</span> <span class="symbol">-</span><span class="number">1</span> <span class="symbol">&amp;&amp;</span> e<span class="symbol">.</span>Index <span class="symbol">&lt;</span> <span class="keyword">this</span><span class="symbol">.</span>Items<span class="symbol">.</span>Count<span class="symbol">)</span>
 <span class="symbol">{</span>
 e<span class="symbol">.</span>ItemHeight <span class="symbol">=</span> _itemHeight<span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>

<span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> OnDrawItem<span class="symbol">(</span>DrawItemEventArgs e<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">base</span><span class="symbol">.</span>OnDrawItem<span class="symbol">(</span>e<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>e<span class="symbol">.</span>Index <span class="symbol">&gt;</span> <span class="symbol">-</span><span class="number">1</span> <span class="symbol">&amp;&amp;</span> e<span class="symbol">.</span>Index <span class="symbol">&lt;</span> <span class="keyword">this</span><span class="symbol">.</span>Items<span class="symbol">.</span>Count<span class="symbol">)</span>
 <span class="symbol">{</span>
 e<span class="symbol">.</span>DrawBackground<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span><span class="symbol">(</span>e<span class="symbol">.</span>State <span class="symbol">&amp;</span> DrawItemState<span class="symbol">.</span>Focus<span class="symbol">)</span> <span class="symbol">==</span> DrawItemState<span class="symbol">.</span>Focus<span class="symbol">)</span>
 e<span class="symbol">.</span>DrawFocusRectangle<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">using</span> <span class="symbol">(</span>SolidBrush textBrush <span class="symbol">=</span> <span class="keyword">new</span> SolidBrush<span class="symbol">(</span>e<span class="symbol">.</span>ForeColor<span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">string</span> fontFamilyName<span class="symbol">;</span>

 fontFamilyName <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>Items<span class="symbol">[</span>e<span class="symbol">.</span>Index<span class="symbol">]</span><span class="symbol">.</span>ToString<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 e<span class="symbol">.</span>Graphics<span class="symbol">.</span>DrawString<span class="symbol">(</span>fontFamilyName<span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>GetFont<span class="symbol">(</span>fontFamilyName<span class="symbol">)</span><span class="symbol">,</span> textBrush<span class="symbol">,</span> e<span class="symbol">.</span>Bounds<span class="symbol">,</span> _stringFormat<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>The actual drawing is very simple - we use the built in drawing
for the background and focus rectangle, and then use the
<code>Graphics</code> object to draw the text using the <code>GetFont</code> method
explained above.</p>
<p>You might notice that the above code is referencing a previously
defined <code>StringFormat</code> object. This is created using the below
method.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">protected</span> <span class="keyword">virtual</span> <span class="keyword">void</span> CreateStringFormat<span class="symbol">(</span><span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span>_stringFormat <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
 _stringFormat<span class="symbol">.</span>Dispose<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 _stringFormat <span class="symbol">=</span> <span class="keyword">new</span> StringFormat<span class="symbol">(</span>StringFormatFlags<span class="symbol">.</span>NoWrap<span class="symbol">)</span><span class="symbol">;</span>
 _stringFormat<span class="symbol">.</span>Trimming <span class="symbol">=</span> StringTrimming<span class="symbol">.</span>EllipsisCharacter<span class="symbol">;</span>
 _stringFormat<span class="symbol">.</span>HotkeyPrefix <span class="symbol">=</span> HotkeyPrefix<span class="symbol">.</span>None<span class="symbol">;</span>
 _stringFormat<span class="symbol">.</span>Alignment <span class="symbol">=</span> StringAlignment<span class="symbol">.</span>Near<span class="symbol">;</span>
 _stringFormat<span class="symbol">.</span>LineAlignment <span class="symbol">=</span> StringAlignment<span class="symbol">.</span>Center<span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>IsUsingRTL<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">)</span><span class="symbol">)</span>
 _stringFormat<span class="symbol">.</span>FormatFlags <span class="symbol">|=</span> StringFormatFlags<span class="symbol">.</span>DirectionRightToLeft<span class="symbol">;</span>
<span class="symbol">}</span>

<span class="keyword">private</span> <span class="keyword">bool</span> IsUsingRTL<span class="symbol">(</span>Control control<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">bool</span> result<span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>control<span class="symbol">.</span>RightToLeft <span class="symbol">==</span> RightToLeft<span class="symbol">.</span>Yes<span class="symbol">)</span>
 result <span class="symbol">=</span> <span class="keyword">true</span><span class="symbol">;</span>
 <span class="keyword">else</span> <span class="keyword">if</span> <span class="symbol">(</span>control<span class="symbol">.</span>RightToLeft <span class="symbol">==</span> RightToLeft<span class="symbol">.</span>Inherit <span class="symbol">&amp;&amp;</span> control<span class="symbol">.</span>Parent <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
 result <span class="symbol">=</span> IsUsingRTL<span class="symbol">(</span>control<span class="symbol">.</span>Parent<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">else</span>
 result <span class="symbol">=</span> <span class="keyword">false</span><span class="symbol">;</span>

 <span class="keyword">return</span> result<span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="cleaning-up">Cleaning up</h2>
<p>As we are creating a large number of objects, we need to clean
these up in the controls <code>Dispose</code> method.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> Dispose<span class="symbol">(</span><span class="keyword">bool</span> disposing<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>ClearFontCache<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>_stringFormat <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
 _stringFormat<span class="symbol">.</span>Dispose<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">base</span><span class="symbol">.</span>Dispose<span class="symbol">(</span>disposing<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>

<span class="keyword">protected</span> <span class="keyword">virtual</span> <span class="keyword">void</span> ClearFontCache<span class="symbol">(</span><span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span>_fontCache <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">foreach</span> <span class="symbol">(</span><span class="keyword">string</span> key <span class="keyword">in</span> _fontCache<span class="symbol">.</span>Keys<span class="symbol">)</span>
 _fontCache<span class="symbol">[</span>key<span class="symbol">]</span><span class="symbol">.</span>Dispose<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 _fontCache<span class="symbol">.</span>Clear<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<h3 id="suggestions-for-improvement">Suggestions for improvement</h3>
<p>The control as it stands is a basic example, and depending on
your application's needs, it could be further expanded. For
example:</p>
<ul>
<li>Currently each instance of the control will use its own font
cache. By making the cache and access methods static, a single
cache could be used by all instances</li>
<li>When you select a font in Word, this is added to a kind of
&quot;recently used&quot; list at the top of Word's own font picker. The
same sort of functionality could be quite easily added to this
control.</li>
<li>Currently the font text is displayed on a single line. If the
control isn't wide enough, the text is trimmed and therefore
it may not be always possible to tell the full name of a font.
Either tooltip support or drawing across multiple lines could
help with this, or by resizing the dropdown component to be
the minimum width required to display all font names without
trimming.</li>
</ul>
<h3 id="full-source">Full source</h3>
<p>The full source of the class is below.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">using</span> System<span class="symbol">;</span>
<span class="keyword">using</span> System<span class="symbol">.</span>Collections<span class="symbol">.</span>Generic<span class="symbol">;</span>
<span class="keyword">using</span> System<span class="symbol">.</span>ComponentModel<span class="symbol">;</span>
<span class="keyword">using</span> System<span class="symbol">.</span>Drawing<span class="symbol">;</span>
<span class="keyword">using</span> System<span class="symbol">.</span>Drawing<span class="symbol">.</span>Text<span class="symbol">;</span>
<span class="keyword">using</span> System<span class="symbol">.</span>Windows<span class="symbol">.</span>Forms<span class="symbol">;</span>

<span class="keyword">namespace</span> Cyotek<span class="symbol">.</span>Windows<span class="symbol">.</span>Forms
<span class="symbol">{</span>
 <span class="keyword">public</span> <span class="keyword">class</span> FontComboBox <span class="symbol">:</span> ComboBox
 <span class="symbol">{</span>
 <span class="keyword">#region</span>&#160;&#160;Private&#160;Member&#160;Declarations&#160;&#160;

 <span class="keyword">private</span> Dictionary<span class="symbol">&lt;</span><span class="keyword">string</span><span class="symbol">,</span> Font<span class="symbol">&gt;</span> _fontCache<span class="symbol">;</span>
 <span class="keyword">private</span> <span class="keyword">int</span> _itemHeight<span class="symbol">;</span>
 <span class="keyword">private</span> <span class="keyword">int</span> _previewFontSize<span class="symbol">;</span>
 <span class="keyword">private</span> StringFormat _stringFormat<span class="symbol">;</span>

 <span class="keyword">public</span> FontComboBox<span class="symbol">(</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 _fontCache <span class="symbol">=</span> <span class="keyword">new</span> Dictionary<span class="symbol">&lt;</span><span class="keyword">string</span><span class="symbol">,</span> Font<span class="symbol">&gt;</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>DrawMode <span class="symbol">=</span> DrawMode<span class="symbol">.</span>OwnerDrawVariable<span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>Sorted <span class="symbol">=</span> <span class="keyword">true</span><span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>PreviewFontSize <span class="symbol">=</span> <span class="number">12</span><span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>CalculateLayout<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>CreateStringFormat<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">public</span> <span class="keyword">event</span> EventHandler PreviewFontSizeChanged<span class="symbol">;</span>

 <span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> Dispose<span class="symbol">(</span><span class="keyword">bool</span> disposing<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>ClearFontCache<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>_stringFormat <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
 _stringFormat<span class="symbol">.</span>Dispose<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">base</span><span class="symbol">.</span>Dispose<span class="symbol">(</span>disposing<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> OnDrawItem<span class="symbol">(</span>DrawItemEventArgs e<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">base</span><span class="symbol">.</span>OnDrawItem<span class="symbol">(</span>e<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>e<span class="symbol">.</span>Index <span class="symbol">&gt;</span> <span class="symbol">-</span><span class="number">1</span> <span class="symbol">&amp;&amp;</span> e<span class="symbol">.</span>Index <span class="symbol">&lt;</span> <span class="keyword">this</span><span class="symbol">.</span>Items<span class="symbol">.</span>Count<span class="symbol">)</span>
 <span class="symbol">{</span>
 e<span class="symbol">.</span>DrawBackground<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span><span class="symbol">(</span>e<span class="symbol">.</span>State <span class="symbol">&amp;</span> DrawItemState<span class="symbol">.</span>Focus<span class="symbol">)</span> <span class="symbol">==</span> DrawItemState<span class="symbol">.</span>Focus<span class="symbol">)</span>
 e<span class="symbol">.</span>DrawFocusRectangle<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">using</span> <span class="symbol">(</span>SolidBrush textBrush <span class="symbol">=</span> <span class="keyword">new</span> SolidBrush<span class="symbol">(</span>e<span class="symbol">.</span>ForeColor<span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">string</span> fontFamilyName<span class="symbol">;</span>

 fontFamilyName <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>Items<span class="symbol">[</span>e<span class="symbol">.</span>Index<span class="symbol">]</span><span class="symbol">.</span>ToString<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 e<span class="symbol">.</span>Graphics<span class="symbol">.</span>DrawString<span class="symbol">(</span>fontFamilyName<span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>GetFont<span class="symbol">(</span>fontFamilyName<span class="symbol">)</span><span class="symbol">,</span> textBrush<span class="symbol">,</span> e<span class="symbol">.</span>Bounds<span class="symbol">,</span> _stringFormat<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>

 <span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> OnFontChanged<span class="symbol">(</span>EventArgs e<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">base</span><span class="symbol">.</span>OnFontChanged<span class="symbol">(</span>e<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>CalculateLayout<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> OnGotFocus<span class="symbol">(</span>EventArgs e<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>LoadFontFamilies<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">base</span><span class="symbol">.</span>OnGotFocus<span class="symbol">(</span>e<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> OnMeasureItem<span class="symbol">(</span>MeasureItemEventArgs e<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">base</span><span class="symbol">.</span>OnMeasureItem<span class="symbol">(</span>e<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>e<span class="symbol">.</span>Index <span class="symbol">&gt;</span> <span class="symbol">-</span><span class="number">1</span> <span class="symbol">&amp;&amp;</span> e<span class="symbol">.</span>Index <span class="symbol">&lt;</span> <span class="keyword">this</span><span class="symbol">.</span>Items<span class="symbol">.</span>Count<span class="symbol">)</span>
 <span class="symbol">{</span>
 e<span class="symbol">.</span>ItemHeight <span class="symbol">=</span> _itemHeight<span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>

 <span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> OnRightToLeftChanged<span class="symbol">(</span>EventArgs e<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">base</span><span class="symbol">.</span>OnRightToLeftChanged<span class="symbol">(</span>e<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>CreateStringFormat<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> OnTextChanged<span class="symbol">(</span>EventArgs e<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">base</span><span class="symbol">.</span>OnTextChanged<span class="symbol">(</span>e<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Items<span class="symbol">.</span>Count <span class="symbol">==</span> <span class="number">0</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">int</span> selectedIndex<span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>LoadFontFamilies<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 selectedIndex <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>FindStringExact<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Text<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">if</span> <span class="symbol">(</span>selectedIndex <span class="symbol">!=</span> <span class="symbol">-</span><span class="number">1</span><span class="symbol">)</span>
 <span class="keyword">this</span><span class="symbol">.</span>SelectedIndex <span class="symbol">=</span> selectedIndex<span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>

 <span class="keyword">public</span> <span class="keyword">virtual</span> <span class="keyword">void</span> LoadFontFamilies<span class="symbol">(</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Items<span class="symbol">.</span>Count <span class="symbol">==</span> <span class="number">0</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 Cursor<span class="symbol">.</span>Current <span class="symbol">=</span> Cursors<span class="symbol">.</span>WaitCursor<span class="symbol">;</span>

 <span class="keyword">foreach</span> <span class="symbol">(</span>FontFamily fontFamily <span class="keyword">in</span> FontFamily<span class="symbol">.</span>Families<span class="symbol">)</span>
 <span class="keyword">this</span><span class="symbol">.</span>Items<span class="symbol">.</span>Add<span class="symbol">(</span>fontFamily<span class="symbol">.</span>Name<span class="symbol">)</span><span class="symbol">;</span>

 Cursor<span class="symbol">.</span>Current <span class="symbol">=</span> Cursors<span class="symbol">.</span>Default<span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>

 <span class="symbol">[</span>Browsable<span class="symbol">(</span><span class="keyword">false</span><span class="symbol">)</span><span class="symbol">,</span> DesignerSerializationVisibility<span class="symbol">(</span>DesignerSerializationVisibility<span class="symbol">.</span>Hidden<span class="symbol">)</span><span class="symbol">,</span> EditorBrowsable<span class="symbol">(</span>EditorBrowsableState<span class="symbol">.</span>Never<span class="symbol">)</span><span class="symbol">]</span>
 <span class="keyword">public</span> <span class="keyword">new</span> DrawMode DrawMode
 <span class="symbol">{</span>
 <span class="keyword">get</span> <span class="symbol">{</span> <span class="keyword">return</span> <span class="keyword">base</span><span class="symbol">.</span>DrawMode<span class="symbol">;</span> <span class="symbol">}</span>
 <span class="keyword">set</span> <span class="symbol">{</span> <span class="keyword">base</span><span class="symbol">.</span>DrawMode <span class="symbol">=</span> value<span class="symbol">;</span> <span class="symbol">}</span>
 <span class="symbol">}</span>

 <span class="symbol">[</span>Category<span class="symbol">(</span><span class="string">&quot;Appearance&quot;</span><span class="symbol">)</span><span class="symbol">,</span> DefaultValue<span class="symbol">(</span><span class="number">12</span><span class="symbol">)</span><span class="symbol">]</span>
 <span class="keyword">public</span> <span class="keyword">int</span> PreviewFontSize
 <span class="symbol">{</span>
 <span class="keyword">get</span> <span class="symbol">{</span> <span class="keyword">return</span> _previewFontSize<span class="symbol">;</span> <span class="symbol">}</span>
 <span class="keyword">set</span>
 <span class="symbol">{</span>
 _previewFontSize <span class="symbol">=</span> value<span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>OnPreviewFontSizeChanged<span class="symbol">(</span>EventArgs<span class="symbol">.</span>Empty<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>

 <span class="symbol">[</span>Browsable<span class="symbol">(</span><span class="keyword">false</span><span class="symbol">)</span><span class="symbol">,</span> DesignerSerializationVisibility<span class="symbol">(</span>DesignerSerializationVisibility<span class="symbol">.</span>Hidden<span class="symbol">)</span><span class="symbol">,</span> EditorBrowsable<span class="symbol">(</span>EditorBrowsableState<span class="symbol">.</span>Never<span class="symbol">)</span><span class="symbol">]</span>
 <span class="keyword">public</span> <span class="keyword">new</span> <span class="keyword">bool</span> Sorted
 <span class="symbol">{</span>
 <span class="keyword">get</span> <span class="symbol">{</span> <span class="keyword">return</span> <span class="keyword">base</span><span class="symbol">.</span>Sorted<span class="symbol">;</span> <span class="symbol">}</span>
 <span class="keyword">set</span> <span class="symbol">{</span> <span class="keyword">base</span><span class="symbol">.</span>Sorted <span class="symbol">=</span> value<span class="symbol">;</span> <span class="symbol">}</span>
 <span class="symbol">}</span>

 <span class="keyword">private</span> <span class="keyword">void</span> CalculateLayout<span class="symbol">(</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>ClearFontCache<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">using</span> <span class="symbol">(</span>Font font <span class="symbol">=</span> <span class="keyword">new</span> Font<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Font<span class="symbol">.</span>FontFamily<span class="symbol">,</span> <span class="symbol">(</span><span class="keyword">float</span><span class="symbol">)</span><span class="keyword">this</span><span class="symbol">.</span>PreviewFontSize<span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 Size textSize<span class="symbol">;</span>

 textSize <span class="symbol">=</span> TextRenderer<span class="symbol">.</span>MeasureText<span class="symbol">(</span><span class="string">&quot;yY&quot;</span><span class="symbol">,</span> font<span class="symbol">)</span><span class="symbol">;</span>
 _itemHeight <span class="symbol">=</span> textSize<span class="symbol">.</span>Height <span class="symbol">+</span> <span class="number">2</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>

 <span class="keyword">private</span> <span class="keyword">bool</span> IsUsingRTL<span class="symbol">(</span>Control control<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">bool</span> result<span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>control<span class="symbol">.</span>RightToLeft <span class="symbol">==</span> RightToLeft<span class="symbol">.</span>Yes<span class="symbol">)</span>
 result <span class="symbol">=</span> <span class="keyword">true</span><span class="symbol">;</span>
 <span class="keyword">else</span> <span class="keyword">if</span> <span class="symbol">(</span>control<span class="symbol">.</span>RightToLeft <span class="symbol">==</span> RightToLeft<span class="symbol">.</span>Inherit <span class="symbol">&amp;&amp;</span> control<span class="symbol">.</span>Parent <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
 result <span class="symbol">=</span> IsUsingRTL<span class="symbol">(</span>control<span class="symbol">.</span>Parent<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">else</span>
 result <span class="symbol">=</span> <span class="keyword">false</span><span class="symbol">;</span>

 <span class="keyword">return</span> result<span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">protected</span> <span class="keyword">virtual</span> <span class="keyword">void</span> ClearFontCache<span class="symbol">(</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span>_fontCache <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">foreach</span> <span class="symbol">(</span><span class="keyword">string</span> key <span class="keyword">in</span> _fontCache<span class="symbol">.</span>Keys<span class="symbol">)</span>
 _fontCache<span class="symbol">[</span>key<span class="symbol">]</span><span class="symbol">.</span>Dispose<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 _fontCache<span class="symbol">.</span>Clear<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>

 <span class="keyword">protected</span> <span class="keyword">virtual</span> <span class="keyword">void</span> CreateStringFormat<span class="symbol">(</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span>_stringFormat <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
 _stringFormat<span class="symbol">.</span>Dispose<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 _stringFormat <span class="symbol">=</span> <span class="keyword">new</span> StringFormat<span class="symbol">(</span>StringFormatFlags<span class="symbol">.</span>NoWrap<span class="symbol">)</span><span class="symbol">;</span>
 _stringFormat<span class="symbol">.</span>Trimming <span class="symbol">=</span> StringTrimming<span class="symbol">.</span>EllipsisCharacter<span class="symbol">;</span>
 _stringFormat<span class="symbol">.</span>HotkeyPrefix <span class="symbol">=</span> HotkeyPrefix<span class="symbol">.</span>None<span class="symbol">;</span>
 _stringFormat<span class="symbol">.</span>Alignment <span class="symbol">=</span> StringAlignment<span class="symbol">.</span>Near<span class="symbol">;</span>
 _stringFormat<span class="symbol">.</span>LineAlignment <span class="symbol">=</span> StringAlignment<span class="symbol">.</span>Center<span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>IsUsingRTL<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">)</span><span class="symbol">)</span>
 _stringFormat<span class="symbol">.</span>FormatFlags <span class="symbol">|=</span> StringFormatFlags<span class="symbol">.</span>DirectionRightToLeft<span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">protected</span> <span class="keyword">virtual</span> Font GetFont<span class="symbol">(</span><span class="keyword">string</span> fontFamilyName<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">lock</span> <span class="symbol">(</span>_fontCache<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span><span class="symbol">!</span>_fontCache<span class="symbol">.</span>ContainsKey<span class="symbol">(</span>fontFamilyName<span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 Font font<span class="symbol">;</span>

 font <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>GetFont<span class="symbol">(</span>fontFamilyName<span class="symbol">,</span> FontStyle<span class="symbol">.</span>Regular<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">if</span> <span class="symbol">(</span>font <span class="symbol">==</span> <span class="keyword">null</span><span class="symbol">)</span>
 font <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>GetFont<span class="symbol">(</span>fontFamilyName<span class="symbol">,</span> FontStyle<span class="symbol">.</span>Bold<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">if</span> <span class="symbol">(</span>font <span class="symbol">==</span> <span class="keyword">null</span><span class="symbol">)</span>
 font <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>GetFont<span class="symbol">(</span>fontFamilyName<span class="symbol">,</span> FontStyle<span class="symbol">.</span>Italic<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">if</span> <span class="symbol">(</span>font <span class="symbol">==</span> <span class="keyword">null</span><span class="symbol">)</span>
 font <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>GetFont<span class="symbol">(</span>fontFamilyName<span class="symbol">,</span> FontStyle<span class="symbol">.</span>Bold <span class="symbol">|</span> FontStyle<span class="symbol">.</span>Italic<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">if</span> <span class="symbol">(</span>font <span class="symbol">==</span> <span class="keyword">null</span><span class="symbol">)</span>
 font <span class="symbol">=</span> <span class="symbol">(</span>Font<span class="symbol">)</span><span class="keyword">this</span><span class="symbol">.</span>Font<span class="symbol">.</span>Clone<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 _fontCache<span class="symbol">.</span>Add<span class="symbol">(</span>fontFamilyName<span class="symbol">,</span> font<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>

 <span class="keyword">return</span> _fontCache<span class="symbol">[</span>fontFamilyName<span class="symbol">]</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">protected</span> <span class="keyword">virtual</span> Font GetFont<span class="symbol">(</span><span class="keyword">string</span> fontFamilyName<span class="symbol">,</span> FontStyle fontStyle<span class="symbol">)</span>
 <span class="symbol">{</span>
 Font font<span class="symbol">;</span>

 <span class="keyword">try</span>
 <span class="symbol">{</span>
 font <span class="symbol">=</span> <span class="keyword">new</span> Font<span class="symbol">(</span>fontFamilyName<span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>PreviewFontSize<span class="symbol">,</span> fontStyle<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="keyword">catch</span>
 <span class="symbol">{</span>
 font <span class="symbol">=</span> <span class="keyword">null</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">return</span> font<span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">protected</span> <span class="keyword">virtual</span> <span class="keyword">void</span> OnPreviewFontSizeChanged<span class="symbol">(</span>EventArgs e<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span>PreviewFontSizeChanged <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
 PreviewFontSizeChanged<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">,</span> e<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>CalculateLayout<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="update-history">Update History</h2>
<ul>
<li>2011-02-19 - First published</li>
<li>2020-11-21 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/creating-a-wysiwyg-font-combobox-using-csharp .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comAdding a horizontal scrollbar to a ComboBox using C#urn:uuid:15bcc036-d651-472f-8556-48a77261dae62010-07-13T17:29:48Z2010-07-13T17:29:48Z<p>In our <a href="https://cyotek.com/cyotek-webcopy">WebCopy</a> application we decided to
update the User Agent configuration to allow selection from a
predefined list of common agents, but still allow the user to
enter their own custom agent if required.</p>
<p>Rather than use two separate fields, we choose to use a
<code>ComboBox</code> in <strong>simple</strong> mode, which is both a <code>TextBox</code> and a
<code>ListBox</code> in a single control. This mode seems somewhat out of
fashion, I think the only place I see it used is in the Font
common dialog, virtually unchanged since Windows 3.1.</p>
<p>The problem was immediately apparent however on firing up
WebCopy and going to select a user agent - the agent strings can
be very long, far longer than the width of the control.</p>
<p>Unfortunately however, the .NET ComboBox doesn't allow you to
directly enable horizontal scrolling. So we'll do it the old
fashioned way using the Windows API.</p>
<p>In order for a window to support horizontal scrolling, it needs
to have the <code>WS_HSCROLL</code> style applied to it. And to setup the
horizontal scrollbar, we need to call the <code>SendMessage</code> API with
the <code>CB_SETHORIZONTALEXTENT</code> message.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/cbhscroll.png" class="gallery" title="A sample project showing a horizontal scrollbar attached to each display type of a ComboBox." ><img src="https://images.cyotek.com/image/thumbnail/devblog/cbhscroll.png" alt="A sample project showing a horizontal scrollbar attached to each display type of a ComboBox." decoding="async" loading="lazy" /></a><figcaption>A sample project showing a horizontal scrollbar attached to each display type of a ComboBox.</figcaption></figure>
<p>As usual, we'll be starting off by creating a new <em>Component</em>,
which we'll inherit from <code>ComboBox</code>.</p>
<p>Traditionally, you would call <code>GetWindowLong</code> and
<code>SetWindowLong</code> API's with the <code>GWL_STYLE</code> or <code>GWL_EXSTYLE</code>
flags. However, we can more simply override the <code>CreateParams</code>
property of our component and set the new style when the control
is created.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">protected</span> <span class="keyword">override</span> CreateParams CreateParams
<span class="symbol">{</span>
 <span class="keyword">get</span>
 <span class="symbol">{</span>
 CreateParams createParams<span class="symbol">;</span>

 createParams <span class="symbol">=</span> <span class="keyword">base</span><span class="symbol">.</span>CreateParams<span class="symbol">;</span>
 createParams<span class="symbol">.</span>Style <span class="symbol">|=</span> WS_HSCROLL<span class="symbol">;</span>

 <span class="keyword">return</span> createParams<span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>With that done, we can now inform Windows of the size of the
horizontal scroll area, and it will automatically add the
scrollbar if required. To do this, I'll add two new methods to
the component. The first will set the horizontal extent to a
given value. The second will calculate the length of the longest
piece of text in the control and then set the extent to match.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">void</span> SetHorizontalExtent<span class="symbol">(</span><span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">int</span> maxWith<span class="symbol">;</span>

 maxWith <span class="symbol">=</span> <span class="number">0</span><span class="symbol">;</span>
 <span class="keyword">foreach</span> <span class="symbol">(</span><span class="keyword">object</span> item <span class="keyword">in</span> <span class="keyword">this</span><span class="symbol">.</span>Items<span class="symbol">)</span>
 <span class="symbol">{</span>
 Size textSize<span class="symbol">;</span>

 textSize <span class="symbol">=</span> TextRenderer<span class="symbol">.</span>MeasureText<span class="symbol">(</span>item<span class="symbol">.</span>ToString<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>Font<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">if</span> <span class="symbol">(</span>textSize<span class="symbol">.</span>Width <span class="symbol">&gt;</span> maxWith<span class="symbol">)</span>
 maxWith <span class="symbol">=</span> textSize<span class="symbol">.</span>Width<span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">this</span><span class="symbol">.</span>SetHorizontalExtent<span class="symbol">(</span>maxWith<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>

<span class="keyword">public</span> <span class="keyword">void</span> SetHorizontalExtent<span class="symbol">(</span><span class="keyword">int</span> width<span class="symbol">)</span>
<span class="symbol">{</span>
 SendMessage<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Handle<span class="symbol">,</span> CB_SETHORIZONTALEXTENT<span class="symbol">,</span> <span class="keyword">new</span> IntPtr<span class="symbol">(</span>width<span class="symbol">)</span><span class="symbol">,</span> IntPtr<span class="symbol">.</span>Zero<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>The first overload of <code>SetHorizontalExtent</code> iterates through all
the items in the control and uses the <code>TextRenderer</code> object to
measure the size of the text. Once it has found the largest
piece of text, it calls the second overload with the size.</p>
<p>The second overload does the actual work of notifying Windows
using the <code>SendMessage</code> call, <code>CB_SETHORIZONTALEXTENT</code> message
and the given width. <code>SendMessage</code> takes two configuration
parameters per message, but <code>CB_SETHORIZONTALEXTENT</code> only
requires one, and so we send <code>0</code> for the second.</p>
<p>The above function works with all display modes of the ComboBox.</p>
<p>For completeness, here are the API declarations we are using:</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">const</span> <span class="keyword">int</span> WS_HSCROLL <span class="symbol">=</span> <span class="number">0x100000</span><span class="symbol">;</span>
<span class="keyword">private</span> <span class="keyword">const</span> <span class="keyword">int</span> CB_SETHORIZONTALEXTENT <span class="symbol">=</span> <span class="number">0x015E</span><span class="symbol">;</span>

<span class="symbol">[</span>DllImport<span class="symbol">(</span><span class="string">&quot;user32.dll&quot;</span><span class="symbol">)</span><span class="symbol">]</span>
<span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">extern</span> IntPtr SendMessage<span class="symbol">(</span>IntPtr hWnd<span class="symbol">,</span> UInt<span class="number">32</span> msg<span class="symbol">,</span> IntPtr wParam<span class="symbol">,</span> IntPtr lParam<span class="symbol">)</span><span class="symbol">;</span>
</pre>
</figure>
<p>As usual, a demonstration project is available from the link
below.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2010-07-13 - First published</li>
<li>2020-11-21 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/adding-a-horizontal-scrollbar-to-a-combobox-using-csharp .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.com