October 2003 - Posts
Many a time do I find Design-Time support in Visual Studio.NET to be a no-man's land, be it because no one in my surroundings knows how to do something I have to do and I have to resort to an hour or more of internet research, at times in vain, or be it because I have to look into and touch Microsoft's internal/private classes.
This time I encountered a problem which required me to create a UITypeEditor that is on an Extender Provider's provided property. Easy enough, but it doesn't stop at that. I had to use reflection to scan the component this provided property was applied to, to find all public boolean properties with get/set accessors. Now we're in that no-man's land I was talking about earlier.
Point of Interest: If you want to apply Design-Time support attributes to properties supplied by an Extender Provider, you must apply them to both the Get and Set methods you create.
The following is the solution to this problem. I tried documenting it as best I can:
using System;
using System.ComponentModel;
using System.Globalization;
using System.Collections;
using System.Drawing.Design;
using System.Windows.Forms;
using System.Windows.Forms.Design;
using System.Reflection;
/// <summary>
/// Written by Omer van Kloeten. All rights reserved.
/// </summary>
namespace HowTo
{
/// <summary>
/// Provides a user interface for selecting a state property.
/// </summary>
public class ConsciousTypeEditor : UITypeEditor
{
// This class is the list that will pop up when we click
// on the down arrow of the mock-combo box in the designer.
private class PropertiesList : ListBox
{
public PropertiesList(Component component)
{
// Go over all properties, filtering out the ones we need (public/get/set/boolean).
// None is a reserved type for a case where no property is selected.
foreach (PropertyInfo info in component.GetType().GetProperties())
{
if (info.GetGetMethod(false) != null &&
info.GetSetMethod(false) != null &&
info.PropertyType == typeof(bool) &&
info.Name != "None")
{
this.Items.Add(info.Name);
}
}
this.Items.Add("None");
this.Sorted = true;
// Not setting the border to none just doesn't look good.
this.BorderStyle = BorderStyle.None;
}
}
/// <summary>
/// Displays a list of available values for the specified component than sets the value.
/// </summary>
/// <param name="context">An ITypeDescriptorContext that can be used to gain additional context information.</param>
/// <param name="provider">A service provider object through which editing services may be obtained.</param>
/// <param name="value">An instance of the value being edited.</param>
/// <returns>The new value of the object. If the value of the object hasn't changed, this method should return the same object it was passed.</returns>
public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
{
if (provider != null)
{
// This service is in charge of popping our ListBox.
IWindowsFormsEditorService service1 = ((IWindowsFormsEditorService) provider.GetService(typeof(IWindowsFormsEditorService)));
if (service1 != null)
{
// This is an internal Microsoft class representing the PropertyGrid entry for our component.
if (provider.GetType().FullName == "System.Windows.Forms.PropertyGridInternal.PropertyDescriptorGridEntry")
{
// Get the component we're working on via reflection.
Component comp = ((Component)(provider.GetType().GetProperty("Component").GetGetMethod().Invoke(provider, new object[0])));
PropertiesList list = new PropertiesList(comp);
// Drop the list control.
service1.DropDownControl(list);
if (list.SelectedIndices.Count == 1)
{
value = list.SelectedItem.ToString();
}
// Close the list control after selection.
service1.CloseDropDown();
}
}
}
return value;
}
/// <summary>
/// Gets the editing style of the <see cref="EditValue"/> method.
/// </summary>
/// <param name="context">An ITypeDescriptorContext that can be used to gain additional context information.</param>
/// <returns>Returns the DropDown style, since this editor uses a drop down list.</returns>
public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
{
// We're using a drop down style UITypeEditor.
return UITypeEditorEditStyle.DropDown;
}
}
}
| Hack Value: | [ | X | X | X | X | X | ] |
| Cool Value: | [ | X | X | X | X | - | ] |
| Usable Value: | [ | X | X | X | X | - | ] |
Lots of people who are at the PDC are going "Oooh", "Ahhh" and other syllables at Longhorn right now.
Me, I'm not that excited.
"So what if you're not excited? You might be an M$-basher for all we know," you might say.
So I have to say that no, I'm no M$-basher. I love most of what Microsoft is doing right now and I think they're going the right way. I haven't said an ill word of them in a long long time (without counting the amount of times I have yelled with frustration after finding out a class I need is internal to the FCL).
"Ok, so why aren't you excited about Longhorn? It's nothing like any past Windows OS and has better ****ing, *****ness, *****es and blah blah blah…"
That's one reason I'm not excited. I don't understand what you want from me. It's not like you've come forth and given me an example of how I can reduce the amount of code I use in an application. You haven't come and given me a cool new design pattern to use in my C# applications. You haven't. So I know nothing, actually.
Another thing is that this OS is going to be out when? 2005? 2006? It's not like we're going to have a Hebrew enabled version until 6 or so months afterwards, like every other Windows OS up until now (yes, that is a subtle hint towards Microsoft), so it's going to be quite a while before I have to try and program something for it or even try and learn it.
Lastly, this system is in beta, and a rather early one at that. I am not going to try and spare 3.5 GiB of space for something that might crash and take down the rest of my hard disk with it (and don't tell me it won't; you don't know).
Give me a copy of the OS 6 months before it's out. By than it'll be stable enough for me to work with and at that point you will see me excited.
A Type Initializer is first called when referencing a static member or creating an instance of the class.
This might cause problems to some developers as the Type Initializers are not called upon loading the AppDomain, loading the Assembly or using Reflection on the Type.
This is solved by using System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(RuntimeTypeHandle), which invokes the Type Initializer.
Personally, I tried using it once and stopped since it's just too hackish.
| Hack Value: | [ | X | X | X | X | - | ] |
| Cool Value: | [ | X | X | X | X | - | ] |
| Usable Value: | [ | X | X | - | - | - | ] |
Over my time as a programmer with .NET and C#, I have come to see some things that should be but are not a part of .NET, C#, Everett, etc.
This is the first in my list of ideas:
- DataSetAdapter, a collection of DataAdapters for a single DataSet.
The lack of this is quite an annoyance when using a DataSet that is not filled by a DAAB of sorts.
How about a complete DataBase/TableSpace to Typed DataSet wizard which will cause the Typed DataSet to contain all of its DataAdapters? Maybe even a few hard-coded methods for filling/updating the DataSet?
- The SQL syntax has the DISTINCT keyword, allowing the SELECT statements to filter reoccurring results of a column.
How about implementing one in the DataTable?
- SQL support in the System.Data classes (DataSet, DataTable, DataView, etc.) would be great.
(I know it's going to be a pain to implement this...)
- Why can't I cancel a Command in OnRowUpdating, but only change it?
Changing it to a command that does nothing still has the overhead of a database call, as far as I can see.
- Executing aggregate functions on a DataColumn in a DataTable such as AVG, MAX, MIN, SUM, etc. would be great.
- When inheriting from a base class, having the ability to automatically implement all of its members, like it is done today in Everett with interfaces.
Why post this here? Who knows, maybe someone from the C# team or the like thought one of the ideas brought up here is nice/cool/stupid/weird/already exists in a form and decided to comment. Maybe then the snowball would start to roll.
For starters, I'd like to thank Yosi Taguri for hooking me up and Scott Watermasysk for setting me up with the weblog.
You guys plain out rule.
This weblog is going to be a dedicated .NET developer's log with tips, tricks, experiences and some of the pointy edges to this slick framework and environment.
RSS Nyaw.
More Posts