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)
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();
elsethrow 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;
elsereturn 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);
}
#endregionpublic
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();
CheckBoxchkIncludeSystem =
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
{
}
[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();
} } } . . .