HotDog's Blog

Hotdog (Robert Verpalen) about C# and vb.net

This blog hosted by:
http://blogs.vbcity.com      
  Home :: Syndication  :: Login

FebMarch 2006Apr
SMTWTFS
2627281234
567891011
12131415161718
19202122232425
2627282930311
2345678

Articles

Archives

Topics

CONTACT

Fun but useful linkies

General

VS 2005

Wolfenstein ET

Friday, March 31, 2006 #

Often, mostly for designers, I want to get a list of available types. Mostly specific types and from all Assemblies. Had multiple code functions for that and that multiple times, because for different projects I tended to rewrite them (argh? yes argh ). But... now I decided to just storm ahead and create some default functionality.

Why, that's easy, I can almost hear you say. Just use Assembly.GetTypes.... Right....Some issues there: I want all referenced assemblies as well and, it should work in design time. And off course some filtering, assignable to other (interface) types.

The design mode is the difficult part. Most seemed to be 'straight forward' enough: use an IServiceProvider  to obtain an IDesignerHost. The IServiceProvider could normally be obtained from the ISite of a component or Control. And if none was provider it could be obtained from LicenseManager.CurrentContext. Then the IDesignerhost exposed a RootComponentClassName property which could lead to the type and thus to the assembly from which the design time. For some scenarios, eg when using an UITypeEditor, the LicenseManager.CurrentContext will not be set, but then it can be specifically set inside the editor.
But... this would only work the first time when the component was added to the form. After that, the RootComponentClassName would contain only the name of the form, not the full namespace.
After some fiddling around and playing the guitar very badly, because I don't know how, it turned out that the IDesignerHost would contain the full name after loading had completed. Knowing that, it was a matter of working around, but the code should continue as well... So a wrapper control would be best while wrapping anyway.

The result is in the code below. There is one difference still between design time and runtime: runtime uses the EntryAssembly (the main executable) and design time the Assembly in which the design is taking place. This is intented: when assigning a type to a property, the assembly must know the type for the code to compile. In runtime, the appdomain has the main assembly loaded and the types can be used throughout the entire appdomain.

The classes included:
-The TypeEnumerator itself is the main component. It can be used as a component, just as an instance to get types, or as a DataSource.
-The TypeEnumeratorSettings holds properties for the filtering. It can be exchanged with the other classes.
-The TypeSelector is a listbox holding control which uses the TypeEnumerator to display the types and enable the use to select them.
-The TypeEditor is an UITypeEditor which can extend a designer for properties with the 'Type' type, or the 'Type[]' type. The TypeSelector and TypeEnumerator for example use it for setting their FilterType(s)

Code CopyHideScrollFull
namespace ReflectionWrappers
{
//references needed:
//-System.windows.forms

//-System.Drawing

using
System;
using
System.ComponentModel;
using
System.ComponentModel.Design;
using
System.Drawing.Design;
using
System.Reflection;
using
System.Collections.Generic;
using
System.Windows.Forms;
using
System.Windows.Forms.Design;
///
<summary>
///
Class to enemurate available types. This class can be used directly, as a component, and as a DataSource
///
</summary>
public
class TypeEnumerator : Component, IListSource, ISupportInitialize
{
public static Assembly GetTopAssembly()
{
return GetTopAssembly(null);
}
public
static Assembly GetTopAssembly(IServiceProvider provider)
{
IDesignerHost host = null;
return
GetTopAssembly(ref provider, ref host, false);
}
static
Assembly GetTopAssembly(ref IServiceProvider provider, ref IDesignerHost id, bool SupressErrors)
{
id = null;
Assembly
a;
if
(LicenseManager.UsageMode == LicenseUsageMode.Designtime
|| (a = Assembly.GetEntryAssembly()) == null)
{
if (id == null)
{
if (provider == null)
provider = LicenseManager.CurrentContext;
id = (IDesignerHost)provider.GetService(typeof(IDesignerHost));
}
try

{
if (id == null || id.RootComponentClassName == null)
{
throw new Exception("An assembly was tried to be obtained in design time, but no designer was (fully) loaded yet. When using this functionality in a control or component, please call it after intialization is completed. The TypeSelector control contains functionality on how to go about");
}
//id.RootComponent.GetType() would return the base class being edited,

//not the actual form or component. RootComponentClassName should contain the

//proper namespace

Type
type = Type.GetType(id.RootComponentClassName, false);
if
(type == null)
{
if (id.Loading)
{
throw new Exception("The proper type could not be obtained in design time. The designer was still loading, please call this function after loading is completed. Assign to the Load event to be able to actually use the new values");
}
throw
new Exception("An assembly was tried to be obtained in design time, but the proper type could not be determined: " + id.RootComponentClassName + ".\nMake sure the current project has been build if you just added or changed a reference. If this error persists, you might have to reload the solution (aka restart visual studio)");
}
a = type.Assembly;
}
catch

{
if (SupressErrors)
return null;
throw;
}
}
return
a;
}

public TypeEnumerator()
: this(new TypeEnumeratorSettings())
{
//Source : http://blogs.vbcity.com/hotdog/archive/2006/03/31/5943.aspx
}
public
TypeEnumerator(TypeEnumeratorSettings Settings)
{
this.Settings = Settings;
}
public
TypeEnumerator(Assembly InAssembly)
: this(InAssembly, null)
{
}
public
TypeEnumerator(params Type[] FilterTypes)
: this(null, FilterTypes)
{
}
public
TypeEnumerator(Assembly InAssembly, params Type[] FilterTypes)
: this()
{
settings.Filter.FilterTypes = FilterTypes;
}
public TypeEnumerator(IContainer container)
: this()
{
if (container != null)
container.Add(this);
}
///
<summary>
///
Used for designer instances
///
</summary>
///
<param name="ServiceProvider"></param>
public
TypeEnumerator(IServiceProvider ServiceProvider)
: this()
{
this.ServiceProvider = ServiceProvider;
}

private IServiceProvider provider;
///
<summary>
///
For design time only: the service provider that can give the current designer (IDesignerHost)
///
If this value is not specified, the <see cref="Site"/> property is used, but in some cases (eg an <see cref="UITypeEditor"/>),
///
this component itself will not be in designmode, and the Site will not be available. In those cases
///
the proper provider can be set (or if possible, the assembly itself ;-) )
///
</summary>
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public
IServiceProvider ServiceProvider
{
get
{
if (provider != null)
return provider;
if (Site != null)
return Site;
if (Container != null)
{
if (Container is ISite)
return ((ISite)Container);
}
return
null;
}
set
{ provider = value; }
}
Type
[] types;
bool
listchanged=true;
protected
void OnListChanged(bool ClearList)
{
listchanged |= ClearList;
if
(suspendlistchange || Initializing)
return;
if (ListChanged != null)
ListChanged(this, EventArgs.Empty);
}
public
event EventHandler ListChanged;


/// <summary>
///
Returns all non system types out of the toplevel assembly.
///
To specify further settings, use an instance of the TypeEnumerator
///
</summary>
///
<returns></returns>
public
static IEnumerable<Type> GetAllTypes()
{
return new TypeEnumerator().GetTypes();
}

IEnumerable<Type> GetTypes()
{
foreach (Assembly a in GetAssemblies())
{
foreach (Type t in GetTypes(a))
{
yield return t;
}
}
}
private
TypeEnumeratorSettings settings;
[Description("The settings on which the obtaining of specified types is based")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public
TypeEnumeratorSettings Settings
{
get { return settings; }
set

{
if (value == null)
{
if (DesignMode)
value = new TypeEnumeratorSettings();
else
throw new ArgumentNullException();
}
if
(settings == value) return;
PropertyChangedEventHandler
handler = new PropertyChangedEventHandler(settings_PropertyChanged);
if
(settings != null)
settings.PropertyChanged -= handler;
settings = value;
settings.PropertyChanged += handler;
OnListChanged(true);
}
}
void
settings_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
OnListChanged(true);
}
bool
suspendlistchange;
public
Type[] GetList()
{
if (listchanged && !Initializing)
{
Cursor cur = Cursor.Current;
Cursor
.Current = Cursors.WaitCursor;
suspendlistchange = true;
try

{
List<Type> list = new List<Type>();
foreach
(Type t in GetTypes())
{
list.Add(t);
}
types = list.ToArray();
listchanged = false;
}
finally

{
suspendlistchange = false;
Cursor
.Current = cur;
}
}
return
types;
}
public
Type[] Types
{
get { return GetList(); }
}
///
<summary>
///
Fires when the component is ready for use. Will only fire if the types are asked for (with <see cref="GetList"/> or <see cref="Types"/>)
///
or if BeginEdit and EndEdit is used.
///
If the component is used in a designer, beginedit and endedit are used automatically in which case
///
Load will fire as well.
///
</summary>
public
event EventHandler Load;
bool
InitTopAssembly()
{
if (settings.TopAssembly != null)
return true;
provider = ServiceProvider;
IDesignerHost
host = null;
try

{
suspendlistchange = true;
settings.TopAssembly = GetTopAssembly(ref provider, ref host, false);
}
catch

{
if (host != null && host.Loading)
{
host.LoadComplete += new EventHandler(host_LoadComplete);
return
false;
}
throw
;
}
finally

{
suspendlistchange = false;
}
if
(Load != null)
Load(this, EventArgs.Empty);
return true;
}
///
<summary>
///
Gets all assemblies according to the specified settings.
///
</summary>
///
<returns></returns>
public
IEnumerable<Assembly> GetAssemblies()
{
if (!InitTopAssembly())
yield break;
yield return settings.TopAssembly;
if
(settings.IncludeReferencedAssemblies)
{
//bool designtime = LicenseManager.UsageMode == LicenseUsageMode.Designtime;
foreach
(AssemblyName a in settings.TopAssembly.GetReferencedAssemblies())
{
if (settings.IncludeSystemAssemblies
||
(!a.Name.StartsWith("System.") && a.Name != "mscorlib" && a.Name != "System"))
{
yield return Assembly.Load(a);
}
}
}
}
void
host_LoadComplete(object sender, EventArgs e)
{
InitTopAssembly();
OnListChanged(false);
}
///
<summary>
///
Checks if the specified type should be included according to the current settings
///
</summary>
///
<param name="t"></param>
///
<returns></returns>
public
virtual bool CheckType(Type t)
{
if (settings.HasFilter &&
!settings.Filter.ValidateType(t, false))
return
false;
if (settings.HasExlusionRules
&& settings.Exclude.ValidateType(t, true))
return
false;
return true;
}
IEnumerable<Type> GetTypes(Assembly a)
{
foreach (Type t in a.GetTypes())
{
if (CheckType(t))
yield return t;
}
}
#region
IListSource Members
public
bool ContainsListCollection
{
get { return true; }
}
System.Collections.IList IListSource.GetList()
{
return GetList();
}
#endregion
#region
ISupportInitialize Members
public
bool Initializing
{
get { return initcount > 0; }
}
int
initcount = 0;
public
void BeginInit()
{
initcount++;
}
public
void EndInit()
{
if (--initcount <= 0)
{
initcount = 0;
InitTopAssembly();
if
(listchanged)
OnListChanged(true);
}
}
#endregion
}
[TypeConverter(typeof(ComponentConverter))]
public
class TypeEnumeratorSettings : INotifyPropertyChanged
{
private Assembly topAssembly;
[Description("The Assembly in which to (begin the) search. If this value is null, the top assembly where this value is called is used")]
[DesignerSerializationVisibility( DesignerSerializationVisibility.Hidden)]
public
Assembly TopAssembly
{
get { return topAssembly; }
set

{
if (topAssembly == value) return;
topAssembly = value;
OnPropertyChanged("TopAssembly");
}
}
private bool includeReferencedAssemblies = true;
[DefaultValue(true)]
[Description("")]
public
bool IncludeReferencedAssemblies
{
get { return includeReferencedAssemblies; }
set

{
if (includeReferencedAssemblies == value) return;
includeReferencedAssemblies = value;
OnPropertyChanged("IncludeReferencedAssemblies");
}
}
private
bool includeSystemAssemblies = false;
[DefaultValue(false)]
[Description("If you want to include types in the referenced system assemblies, set this value to true. By default only non-framework-system code is included")]
public
bool IncludeSystemAssemblies
{
get { return includeSystemAssemblies; }
set

{
if (includeSystemAssemblies == value) return;
includeSystemAssemblies = value;
OnPropertyChanged("IncludeSystemAssemblies");
}
}
#region
Filter
private
TypeFilter filtersettings,exclude;
[DesignerSerializationVisibility( DesignerSerializationVisibility.Content)]
[Description("Only types that correspond with the settings included in this property are included in the list.")]
public
TypeFilter Filter
{
get
{
if(filtersettings==null)
filtersettings = new TypeFilter(this,"Filter");
return filtersettings;
}
}
[DefaultValue(false)]
public
bool HasFilter
{
get { return filtersettings != null && !filtersettings.AllowAll; }
}
[DefaultValue(null)]
[Description("The type(s) to which the types in this list must be assignable")]
[Editor(typeof(TypeEditor), typeof(UITypeEditor))]
public
Type[] FilterTypes
{
get {
if (filtersettings == null) return null;
return
filtersettings.FilterTypes;
}
set

{
Filter.FilterTypes = value;
OnPropertyChanged("FilterTypes");
}
}
[DefaultValue(true)]
public
bool AllowAbstract
{
get { return exclude == null || !exclude.FilterAbstract; }
set

{
if (value != AllowAbstract)
Exclude.FilterAbstract = !value;
}
}
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
[Description("Types that correspond with the settings in this property are excluded from the selection")]
public
TypeFilter Exclude
{
get
{
if (exclude == null)
exclude = new TypeFilter(this,"Excluded");
return exclude;
}
}
[DefaultValue(false)]
public
bool HasExlusionRules
{
get { return exclude != null && !exclude.AllowAll; }
}
[TypeConverter(typeof(ComponentConverter))]
public
class TypeFilter:INotifyPropertyChanged
{
public readonly TypeEnumeratorSettings Owner;
public
readonly string AssignedProperty;
internal
TypeFilter(TypeEnumeratorSettings owner,string MyPropery)
{
this.Owner = owner;
AssignedProperty = MyPropery;
}

private Type[] filterTypes;
[DefaultValue(null)]
[Description("The type(s) to which the types in this list must be assignable")]
[Editor(typeof(TypeEditor), typeof(UITypeEditor))]
public
Type[] FilterTypes
{
get { return filterTypes; }
set

{
if (value != null && value.Length == 0) value = null;
if
(filterTypes == value) return;
filterTypes = value;
OnPropertyChanged("FilterTypes");
}
}
/// <summary>
///
Checks if the specified type validates against this filter
///
</summary>
///
<param name="t"></param>
///
<param name="IsExclusion"></param>
///
<returns></returns>
public
bool ValidateType(Type t,bool IsExclusion)
{
//check filter types
if
(filterTypes != null)
{
foreach (Type ft in filterTypes)
{
if (ft.IsAssignableFrom(t)==IsExclusion) return IsExclusion;
}
}
//check flags
if
(typeattr != DefaultTypeAttributes)
{
TypeAttributes res = typeattr & t.Attributes;
if
(IsExclusion)
return res > 0;
else
return res == typeattr;
}
return
true;
}
#region INotifyPropertyChanged Members
public
event PropertyChangedEventHandler PropertyChanged;
void
OnPropertyChanged(string name)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
Owner.OnPropertyChanged(AssignedProperty);
}
#endregion
public const TypeAttributes DefaultTypeAttributes = (TypeAttributes)0;
private
TypeAttributes typeattr = DefaultTypeAttributes;
///
<summary>
///
The <see cref="System.Reflection.TypeAttributes"/> to check the Type against
///
</summary>
[DefaultValue(DefaultTypeAttributes)]
[Browsable(false)]
[DesignerSerializationVisibility( DesignerSerializationVisibility.Hidden)]
[Description("Attributes on which to filter. Flagwise so multiple properties can be combined")]
public
TypeAttributes FilterAttributes
{
get { return typeattr; }
set

{
typeattr = value;                    
}
}


   
[DefaultValue(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public
bool AllowAll
{
get
{
return
filterTypes == null
&& typeattr == DefaultTypeAttributes
;
}
set

{
if (value)
{
filterTypes = null;
typeattr = DefaultTypeAttributes;
}
}
}
public bool FilterOnAttribute(TypeAttributes ta)
{
return typeattr != DefaultTypeAttributes && (typeattr & ta) > 0;
}
public
bool SetAttribute(TypeAttributes ta, bool On)
{
if (FilterOnAttribute(ta) == On) return false;
if
(On)
typeattr |= ta;
else
typeattr ^= ta;
OnPropertyChanged("Filter" + ta.ToString());
return
true;
}
[DefaultValue(false)]
public
bool FilterAbstract
{
get
{
return FilterOnAttribute(TypeAttributes.Abstract);
}
set

{
SetAttribute(TypeAttributes.Abstract, value);
}
}


[DefaultValue(false)]
public
bool FilterPublic
{
get
{
return FilterOnAttribute(TypeAttributes.Public);
}
set

{
SetAttribute(TypeAttributes.Public, value);
}
}

[DefaultValue(false)]
public
bool FilterNestedPublic
{
get
{
return FilterOnAttribute(TypeAttributes.NestedPublic);
}
set

{
SetAttribute(TypeAttributes.NestedPublic, value);
}
}
public override string ToString()
{
if (AllowAll) return null;
return
"Filter set";
}
}
#endregion
#region INotifyPropertyChanged Members
public
event PropertyChangedEventHandler PropertyChanged;
void
OnPropertyChanged(string name)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
#endregion
}
/// <summary>
///
Listbox style control to select types
///
</summary>
[DefaultEvent("SelectionFinal")]
public
class TypeSelector : UserControl,ISupportInitialize
{
ListBox lb = new ListBox();
Panel
pnlControls = new Panel();
CheckBox

chkIncludeSystem = new CheckBox(),
chkIncludeReferencedAssemblies = new CheckBox();
ToolTip
tt = new ToolTip();
public
TypeSelector()
: this(new TypeEnumerator())
{
}
public
TypeSelector(TypeEnumerator Enumerator)
{
if (Enumerator == null)
throw new ArgumentNullException();
SuspendLayout();
te = Enumerator;
te.BeginInit();
if
(te.ServiceProvider == null)
te.ServiceProvider = Site;
te.Load += new EventHandler(te_ListChanged);
te.ListChanged += new EventHandler(te_ListChanged);
te.EndInit();
lb.Dock = DockStyle.Fill;
lb.DisplayMember = "Name";
lb.SelectedIndexChanged += new EventHandler(lb_SelectedIndexChanged);
lb.Click += new EventHandler(lb_Click);
lb.DoubleClick += new EventHandler(lb_DoubleClick);
lb.DataSourceChanged += new EventHandler(lb_DataSourceChanged);
lb.BorderStyle = BorderStyle.None;
lb.MouseMove += new MouseEventHandler(lb_MouseMove);
lb.MouseLeave += new EventHandler(lb_MouseLeave);
Controls.Add(lb);
//pnlControls.Height = 20;

pnlControls.AutoSize = true;
pnlControls.Dock = DockStyle.Bottom;
pnlControls.BorderStyle = BorderStyle.None;
Controls.Add(pnlControls);
chkIncludeReferencedAssemblies.DataBindings.Add("Checked", te.Settings, "IncludeReferencedAssemblies");
AddControl(chkIncludeReferencedAssemblies);
chkIncludeSystem.DataBindings.Add("Checked", te.Settings, "IncludeSystemAssemblies");
AddControl(chkIncludeSystem);
ShortNotation = false;
tt.ShowAlways = true;
tt.InitialDelay = 200;
tt.ReshowDelay = 500;
tt.SetToolTip(chkIncludeReferencedAssemblies, "If this value is false, only the types in the main assembly will be shown");
tt.SetToolTip(chkIncludeSystem, "Determines if System/framework types should be included");
selectionmode = GetDefaultSelectionMode();
base
.BorderStyle = BorderStyle.FixedSingle;
Padding = new Padding(1);
ResumeLayout();
}
void
AddControl(Control c)
{
c.Dock = DockStyle.Bottom;
c.Height = 16;
c.Font = new System.Drawing.Font(c.Font.FontFamily, 7);
pnlControls.Controls.Add(c);
}
void
AddControl(CheckBox c)
{
AddControl((Control)c);
c.TabStop = false;
c.AutoEllipsis = true;
c.DataBindings[0].DataSourceUpdateMode = DataSourceUpdateMode.OnPropertyChanged;
}
void
lb_SelectedIndexChanged(object sender, EventArgs e)
{
if (SelectedItemChanged != null)
SelectedItemChanged(this, e);
}
void
lb_Click(object sender, EventArgs e)
{
OnSelectionFinal(FinalSelectionMode.SingleClick, e);
}
int
lastmouseindex = -1;
void
lb_MouseMove(object sender, MouseEventArgs e)
{
SetLBToolTip();
}
void
lb_MouseLeave(object sender, EventArgs e)
{
SetLBToolTip();
}
void
SetLBToolTip()
{
System.Drawing.Point p = lb.PointToClient(MousePosition);
int
i = lb.IndexFromPoint(p);
if
(i == lastmouseindex) return;
lastmouseindex = i;
if
(i != -1)
tt.Show(lb.Items[i].ToString(), lb, p);
else
tt.Hide(lb);
}

public event EventHandler SelectedItemChanged;
void
lb_DoubleClick(object sender, EventArgs e)
{
OnSelectionFinal(FinalSelectionMode.DoubleClick, e);
}
private
bool allowusersettings = true;
[DefaultValue(true)]
[Description("true if the user can adapt the criteria (default), otherwise false")]
public
bool AllowUserSettings
{
get { return allowusersettings; }
set

{
pnlControls.Visible = allowusersettings = value;
}
}
[DefaultValue(BorderStyle.FixedSingle)]
public
new BorderStyle BorderStyle
{
get { return base.BorderStyle; }
set
{ base.BorderStyle = value; }
}
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public
Type SelectedType
{
get { return lb.SelectedItem as Type; }
set

{
if (value == null)
lb.SelectedItems.Clear();
else
lb.SelectedItem = value;
}
}
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public
Type[] SelectedTypes
{
get
{
if (lb.SelectedItems.Count == 0) return null;
Type
[] types = new Type[lb.SelectedItems.Count];
for
(int i = 0; i < types.Length; i++)
{
types[i] = (Type)lb.SelectedItems[i];
}
return
types;
}
set

{
lb.SelectedItems.Clear();
if
(value == null) return;
foreach
(Type t in value)
{
lb.SelectedItems.Add(t);
}
}
}
[Editor(typeof(TypeEditor), typeof(UITypeEditor))]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[DefaultValue(null)]
public
Type[] FilterTypes
{
get { return te.Settings.Filter.FilterTypes; }
set

{
te.Settings.Filter.FilterTypes = value;
}
}

[DefaultValue(false)]
public
bool MultiSelect
{
get { return lb.SelectionMode == System.Windows.Forms.SelectionMode.MultiExtended; }
set

{
lb.SelectionMode =
value

? System.Windows.Forms.SelectionMode.MultiExtended
: System.Windows.Forms.SelectionMode.One;
//only reset selectionmode if no manual change was made

if
(!selectionmanualset)
ResetSelectionMode();
}
}
private
bool shortnotation;
[DefaultValue(false)]
[Description("If true, the descriptions will be shown in shorter format")]
public
bool ShortNotation
{
get { return shortnotation; }
set

{
shortnotation = value;
chkIncludeSystem.Text = value ? "System" : "Include system assemblies";
chkIncludeReferencedAssemblies.Text = value ? "References" : "Include referenced assemblies";
if
(llClear != null)
llClear.Text = value ? "Clear" : "Clear selection";
}
}

Button btnOK;
///
<summary>
///
Used for designers: indicates if an ok button should be shown
///
</summary>
[DefaultValue(false)]
public
bool ShowOk
{
get { return btnOK != null; }
set

{
if (ShowOk == value) return;
if
(value)
{
btnOK = new Button();
btnOK.Text = "OK";
btnOK.AutoSize = true;
AddControl(btnOK);
btnOK.Click += new EventHandler(btnOK_Click);
}
else

{
btnOK.Dispose();
btnOK = null;
}
}
}
LinkLabel
llClear;
[Description("Show a label to clear the selection..")]
[DefaultValue(false)]
public
bool ShowClear
{
get { return llClear != null; }
set

{
if (ShowClear == value) return;
if
(value)
{
llClear = new LinkLabel();
llClear.Click += new EventHandler(llClear_Click);
ShortNotation = shortnotation;
AddControl(llClear);
llClear.BringToFront();
}
else

{
llClear.Dispose();
llClear = null;
}
}
}
void
llClear_Click(object sender, EventArgs e)
{
SelectedType = null;
OnSelectionFinal(FinalSelectionMode.ClearSelection, e);
}
///
<summary>
///
Fires when the selection is final.
///

///
How the selection is made, can be set by using <see cref="SelectionMode"/>
///
Default settings:
///
A doubleclick on an item is always considered final. A single click is considered final
///
when not using multiselect.
///
If <see cref="ShowOk"/> is used, the ok click is considered final as well
///
</summary>
public
event EventHandler SelectionFinal;
[Flags]
public
enum FinalSelectionMode
{
Default = 0,
SingleClick = 1,
DoubleClick = 2,
///
<summary>
///
If showOK is used.
///
</summary>
OK = 4,
ClearSelection = 8
}
private
FinalSelectionMode selectionmode;
bool
selectionmanualset;
public
FinalSelectionMode SelectionMode
{
get { return selectionmode; }
set

{
//for the default value, fill in the actual default value
if
(value == FinalSelectionMode.Default)
value = GetDefaultSelectionMode();
selectionmode = value;
selectionmanualset = true;
}
}
bool
ShouldSerializeSelectionMode()
{
return selectionmode != GetDefaultSelectionMode();
}
public
void ResetSelectionMode()
{
selectionmode = GetDefaultSelectionMode();
}
public
FinalSelectionMode GetDefaultSelectionMode()
{
return GetDefaultSelectionMode(MultiSelect);
}
public
FinalSelectionMode GetDefaultSelectionMode(bool Multiselect)
{
FinalSelectionMode fs = FinalSelectionMode.DoubleClick | FinalSelectionMode.OK;
if
(!MultiSelect)
fs |= FinalSelectionMode.SingleClick;
return fs;
}
void
OnSelectionFinal(FinalSelectionMode mode, EventArgs e)
{
if (SelectionFinal != null
&& (mode & selectionmode) > 0)
{
SelectionFinal(this, e);
}
}
void
btnOK_Click(object sender, EventArgs e)
{
OnSelectionFinal(FinalSelectionMode.OK, e);
}
void
te_ListChanged(object sender, EventArgs e)
{
this.Cursor = Cursors.WaitCursor;
try

{
lb.Items.Clear();
lb.Items.AddRange(te.Types);
}
finally

{
this.Cursor = null;
}
}
void
lb_DataSourceChanged(object sender, EventArgs e)
{
//remember selected items
//looks silly, but it works ;-)

//the selecteditemcollection is updated and the listbox knows it. uhuh.

SelectedTypes = SelectedTypes;
//clear/set tooltip

SetLBToolTip();
}

private TypeEnumerator te;
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public
TypeEnumeratorSettings Settings
{
get
{
return te.Settings;
}
}
[Browsable(false)]
public
TypeEnumerator Enumerator
{
get { return te; }
}
protected
override System.Drawing.Size DefaultSize
{
get
{
return new System.Drawing.Size(100, 150);
}
}
#region ISupportInitialize Members
public void BeginInit()
{
te.BeginInit();
}
public void EndInit()
{
te.EndInit();
}
#endregion
}
///
<summary>
///
An <see cref="UITypeEditor"/> for selecting Type or Types. If the target property is an array, multiselect is used, otherwise single select
///
</summary>
public
class TypeEditor : UITypeEditor
{
private TypeSelector Selector;
public
TypeEnumeratorSettings Settings
{
get
{
return Selector.Settings;
}
}
public
TypeEditor()
{
}
protected
virtual TypeSelector CreateSelector(IServiceProvider provider)
{
TypeEnumerator te = new TypeEnumerator(provider);
return
new TypeSelector(te);
}
public
override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
{
return UITypeEditorEditStyle.DropDown;
}
IWindowsFormsEditorService
iw;
public
override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
{
if (Selector == null)
{
Selector = CreateSelector(provider);
Selector.SelectionFinal += new EventHandler(Selector_SelectionFinal);
Selector.ShortNotation = true;
}
bool
multiselect = context.PropertyDescriptor.PropertyType.IsArray;
Selector.MultiSelect = multiselect;
Selector.ShowOk = multiselect;
Selector.ShowClear = true;
//set current value

if
(value == null)
Selector.SelectedType = null;
else if (multiselect)
Selector.SelectedTypes = (Type[])value;
else
Selector.SelectedType = (Type)value;
//show dropdown
iw = (IWindowsFormsEditorService)provider.GetService(typeof(IWindowsFormsEditorService));
iw.DropDownControl(Selector);
//return value

return
multiselect ? (object)Selector.SelectedTypes : Selector.SelectedType;
}
void
Selector_SelectionFinal(object sender, EventArgs e)
{
iw.CloseDropDown();
}
}
}
. . .
posted @ 6:49 AM