How can I persist a collection of items into code
The CollectionEditor allows adding and removing items from a collection at design time. If the items in this collection implement IComponent or if they are derived from Component, the items in your collection can be persisted in code. Download collectioneditorsample.zip for a complete sample project. Here are some steps you should follow: 1) Make sure your item is derived from Component or implements Icomponent. For example: public class SomeItem : Component { private string label = ””; public SomeItem() { } public SomeItem(string label) { this.label = label; } public string Label { get { return label; } set { label = value; } } public override string ToString() { return String.Format(”SomeItem: ( Label = ’{0}’ )”, label); } } 2) Next implement your Collection. You have to implement the Ilist interface. The CollectionEditor will determine the type of instances to be added to your collection using reflection inspecting the return type of the Item property (Indexer). [ Description(”The set of properties to be edited with CollectionEditor.”), DesignerSerializationVisibility(DesignerSerializationVisibility.Content), Editor(typeof(System.ComponentModel.Design.CollectionEditor), typeof(System.Drawing.Design.UITypeEditor)) ] public SomeItemCollection SomeItems { get { if (someItemCollection == null) { someItemCollection = CreateItemCollection(); } return someItemCollection; } } protected SomeItemCollection CreateItemCollection() { return new SomeItemCollection(this); } public class SomeItemCollection : IList { // Fields private SomeItemDisplayer owner; public event EventHandler Changed; // Constructors public SomeItemCollection(SomeItemDisplayer owner) { this.owner = owner; } internal ArrayList InnerArray { get { return owner.someItems; } } public void OnChanged() { if (this.Changed != null) this.Changed(this, EventArgs.Empty); } /// /// The CollectionEditor will determine the type of objects to be created by /// looking at the property type of the following method. CollectionEditor /// internally uses reflection to get the PropertyInfo for the ”Item” property. /// This method must be public. /// public SomeItem this[int index] { set { if (value == null) throw new ArgumentNullException(”value”); if (index < 0 || index >= this.InnerArray.Count) throw new ArgumentOutOfRangeException(String.Format(”Invalid Argument {0}: {1}”, ”index”, index.ToString())); this.InnerArray[index] = value; OnChanged(); } get { if (index < 0 || index >= this.InnerArray.Count) throw new ArgumentOutOfRangeException(”Invalid Argument {0}: {1}”, ”index”, index.ToString()); return (SomeItem) this.InnerArray[index]; } } public void AddRange(object[] items) { InnerArray.AddRange(items); OnChanged(); } /// /// This implementation for the Item property for the IList interface. It’s /// property type is object. /// object IList.this[int index] { set { // forward call to public indexer this[index] = (SomeItem) value; } get { // forward call to public indexer return this[index]; } } public /*IEnumerable*/ IEnumerator GetEnumerator() { return InnerArray.GetEnumerator(); } public /*ICollection*/ int Count { get { return InnerArray.Count; } } public /*IList*/ void RemoveAt(int index) { if (index < 0 || index >= this.InnerArray.Count) throw new ArgumentOutOfRangeException(String.Format(”Invalid Argument {0}: {1}”, ”index”, index.ToString())); this.InnerArray.RemoveAt(index); OnChanged(); } public /*IList*/ void Remove(object value) { int n = this.InnerArray.IndexOf(value,0); if (n != -1) this.RemoveAt(n); } public /*IList*/ void Insert(int index, object item) { if (item == null) throw new ArgumentNullException(”item”); if (index < 0 || index > this.InnerArray.Count) throw new ArgumentOutOfRangeException(String.Format(”Invalid Argument {0}: {1}”,”index”, index.ToString())); this.InnerArray.Insert(index,item); OnChanged(); } public /*IList*/ int IndexOf(object value) { if (value == null) throw new ArgumentNullException(String.Format(”Invalid Argument {0}: {1}”,”value”, ”null”)); return this.InnerArray.IndexOf(value,0); } public /*IList*/ bool IsReadOnly { get { return false; } } public /*IList*/ void Clear() { InnerArray.Clear(); this.owner.Invalidate(); } public /*IList*/ bool Contains(object value) { return this.IndexOf(value) != -1; } void System.Collections.ICollection.CopyTo(Array dest, int index) { int count = this.InnerArray.Count; for (int n1 = 0; n1 < count; n1++) dest.SetValue(this.InnerArray[n1], (n1 + index)); } int System.Collections.IList.Add(object item) { int n = this.InnerArray.Add(item); OnChanged(); return n; } bool System.Collections.IList.IsFixedSize { get { return false; } } bool System.Collections.ICollection.IsSynchronized { get { return false; } } object System.Collections.ICollection.SyncRoot { get { return this; } } } 3) Reference this collection in your control or component that should be designable. You need to supply a DesignerSerializationVisibility and an Editor attribute: private SomeItemCollection someItemCollection = null; ArrayList someItems = new ArrayList(); [ Description(”The set of properties to be edited with CollectionEditor.”), DesignerSerializationVisibility(DesignerSerializationVisibility.Content), Editor(typeof(System.ComponentModel.Design.CollectionEditor), typeof(System.Drawing.Design.UITypeEditor)) ] public SomeItemCollection SomeItems { get { if (someItemCollection == null) { someItemCollection = CreateItemCollection(); } return someItemCollection; } } protected SomeItemCollection CreateItemCollection() { return new SomeItemCollection(this); }
How can I make my controls transparent so the form’s background image can show through them
By default, a control’s background color will be the same as the container’s background. In the picture below, the form’s background was set to Color.Red. In design mode, you can see the four controls have the red background, but since the form’s Image was set to a bitmap, you cannot see the form’s red background. To make the controls’ background transparent, you can set the alpha blend value of their background to zero. In code, you could use: public Form1() { InitializeComponent(); checkBox1.BackColor = Color.FromArgb(0, checkBox1.BackColor); button1.BackColor = Color.FromArgb(0, button1.BackColor); linkLabel1.BackColor = Color.FromArgb(0, linkLabel1.BackColor); label1.BackColor = Color.FromArgb(0, label1.BackColor); // Or use the System.Drawing.Color.Transparent color. } In design mode, you can set the alpha blend value to zero by typing the four component in the property grid. So, for each control’s BackColor property, you would type 0,255,0,0 to make it a transparent red. Here is a VB project using this idea.
I want to do custom handling of special keys such as the Tab key or F2 in the TextBox of a column in the DataGrid. How do I subclass this TextBox to get at it virtual members
The TextBox property of the DataGridTextBoxColumn is ReadOnly, so you just cannot set a new derived TextBox into it. One solution is to derive a TextBox, and then have it use the derived TextBox in the DataGridTextBoxColumn instead of the ‘generic’ TextBox that is there. This is the same technique used in our combobox in a column sample. In that sample, the generic textbox was ‘replaced’ with a combobox. Here, we replace it with a derived TextBox where we can easily override virtual members. A reasonable question is why not just use the event mechanism of the existing TextBox to modify behavior. Events like KeyPress would allow us to do some things PROVIDED they get hit. Due to the key processing architecture of the FrameWork, for some special keys, these key events are not always fired. And if they are fired, sometimes it is impossible to avoid the default processing if this is the intent you have. Overriding a virtual function, doing something special, and then NOT calling the baseclass is a standard way of avoiding default processing. In this sample (both VB and CS), we override PreProcessMessage and avoid processing the Keys.Tab key. You can modify the code to not process Keys.F2 as well. DataGridTextBoxColumn is subclassed so it can use our derived TextBox.
What mechanisms does .Net provide to convert one type to another
Type conversion is usually possible in one of the following ways: 1) Using explicit methods in source type like: public class SizeF { … public Size ToSize(); } There is usually a corresponding explicit or implicit operator that does the same conversion. The operators are however not available in VB.Net. 2) Using FromXXX static methods exposed in the destination type: class Image { … public static Image FromStream(Stream); } 3) Using implicit or explicit operators defined in the source or destination type. This will allow you to perform implicit or explicit casts from the source type to the destination type. SizeF sizeF = size; // Possible because type Size has an implicit type conversion operator that converts a Size to SizeF PointF pointF = (PointF)sizeF; // Possible because type SizeF has an explicit type conversion operator that converts a SizeF to PointF. There is usually a corresponding ToXXX method that does the same conversion. You can use either this or the ToXXX methods in C#. 4) Using TypeConverters. Some types come with TypeConverters that allow you to convert to or convert from another type. This conversion is usually not documented and can be discovered only by writing some test code. For example, the System.Drawing.Icon type’s TypeConverter converts the Icon into a byte[]. You can use this functionality in your code as follows: TypeConverter tc = TypeDescriptor.GetConverter(typeof(System.Drawing.Icon)); byte[] blob = tc.ConvertTo(myIcon, typeof(byte[])); It’s usually time consuming to figure out whether a TypeConverter’s ConvertTo or ConvertFrom method can perform the necessary conversion. The attached TypeConverterLookup tool lets you figure that out easily on any type declared in any assembly available in the GAC or available in the same directory as the tool’s exe. If you have types in custom assemblies, then just copy over that assembly to the same directory as the tool, you can then specify the type in the tool.
How do I color a individual cell depending upon its value or some external method
We give three different methods for doing this. The first one overrides Paint in a derived columnstyle and sets the backcolor there. The second uses a delegate to set the color in the Paint override. The third method adds an event to the derived column style to allow you to set the color in an event handler. Method 1 You can do this by deriving from DataGridTextBoxColumn and overriding the Paint method to conditionally set the backColor and foreColor. The sample code below colors any cell that starts with a letter higher than ’F’. You can download a project (C#, VB) using this class. [C#] public class DataGridColoredTextBoxColumn : DataGridTextBoxColumn { protected override void Paint(System.Drawing.Graphics g, System.Drawing.Rectangle bounds, System.Windows.Forms.CurrencyManager source, int rowNum, System.Drawing.Brush backBrush, System.Drawing.Brush foreBrush, bool alignToRight) { // the idea is to conditionally set the foreBrush and/or backbrush // depending upon some criteria on the cell value // Here, we color anything that begins with a letter higher than ’F’ try{ object o = this.GetColumnValueAtRow(source, rowNum); if( o!= null) { char c = ((string)o)[0]; if( c > ’F’) { // could be as simple as // backBrush = new SolidBrush(Color.Pink); // or something fancier… backBrush = new LinearGradientBrush(bounds, Color.FromArgb(255, 200, 200), Color.FromArgb(128, 20, 20), LinearGradientMode.BackwardDiagonal); foreBrush = new SolidBrush(Color.White); } } } catch(Exception ex){ /* empty catch */ } finally{ // make sure the base class gets called to do the drawing with // the possibly changed brushes base.Paint(g, bounds, source, rowNum, backBrush, foreBrush, alignToRight); } } } [VB.NET} Public Class DataGridColoredTextBoxColumn Inherits DataGridTextBoxColumn Public Sub New() End Sub Protected Overloads Overrides Sub Paint(ByVal g As Graphics, ByVal bounds As Rectangle, ByVal source As CurrencyManager, ByVal rowNum As Integer, ByVal backBrush As Brush, ByVal foreBrush As Brush, ByVal alignToRight As Boolean) ’ the idea is to conditionally set the foreBrush and/or backbrush ’ depending upon some crireria on the cell value ’ Here, we color anything that begins with a letter higher than ’F’ Try Dim o As Object o = Me.GetColumnValueAtRow(source, rowNum) If (Not (o) Is Nothing) Then Dim c As Char c = CType(o, String).Substring(0, 1) If (c > ”F”) Then ’ could be as simple as ’ backBrush = new SolidBrush(Color.Pink); ’ or something fancier… backBrush = New LinearGradientBrush(bounds, Color.FromArgb(255, 200, 200), Color.FromArgb(128, 20, 20), LinearGradientMode.BackwardDiagonal) foreBrush = New SolidBrush(Color.White) End If End If Catch ex As Exception ’ empty catch Finally ’ make sure the base class gets called to do the drawing with ’ the possibly changed brushes MyBase.Paint(g, bounds, source, rowNum, backBrush, foreBrush, alignToRight) End Try End Sub End Class Method 2 To use some method to provide the cell color, you can use a similar technique as discussed above. But instead of setting the color based on cell value, call a delegate method instead. This delegate can be passed in using the constructor for the custom column style. You can download a sample that shows how this can be done. Method 3 If you want a more flexible solution, you could expose an event as part of your derived columnstyle that fires in the Paint override. This would allow the handler of the event to set the color value depending upon the row and column parameters that are passed as part of the event args. You can download a sample (C#, VB) that implements this technique. The sample actually handles allowing a cell to be editable on a cell by cell basis through an event. This sample colors the back ground of the disabled cell gray. You could modify the eventargs to include a backcolor, and use this event to color cells based on row and column values removing the event call in the Edit override.