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:
publicForm1(){
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.
The attached EditableList UserControl implements an editable listbox with an in-place TextBox and Button allowing users to directly edit the contents of the list box.
When the user clicks on a selected item, a textbox and a button is shown over the selected item and the user can directly edit the selected item text. The button can be programmed to show for example a OpenFileDialog to allow user to select a file (useful while implementing a Files list).
You cannot place controls into a StatusBar control in the designer. However, you can add any no. of Controls to the StatusBar programatically through it’s Controls property. After adding the Controls, set their Visible, Location and Bounds property appropriately.
You could then create a status bar that looks like this, for example:
One solution is to use a panel that has a picturebox placed on it with DockStyle.Fill. This will make the picturebox assume the size of the panel. In addition, set the DockPadding.All property to the width of the desired border. Then in the Panel’s OnPaint method, call the baseclass and then paint the desired borders.
Here are both VB and C# projects that illustrate how you might go about this. The derived PicturePanel class has properties that allow you to set the bordersize and color as well as the image that is to be displayed. This sample retrieves the image from an embedded
Note that when you call this method the control should be visible, otherwise the focus will not be set. Hence calling this in say Form_Load on a control in the form will be ineffective. You should instead consider give that control an appropriate TabIndex, so that it will be the first focused control.
Looks like the ImageList editor loses the transparency when it does some internal copy/clone of the images. However, it seems that it does work when you add the images in code to the ImageList.
One workaround (not so tidy) is to add the images to the ImageList in the design time (so that your design-time will be closer to the runtime) and then clear that ImageList and refill it with the images again, in code.
Say textBox1 and cancelButton and the control names, then this is how you could do this:
[C#]
// Handler to the Validating event of the TextBox.privatevoidTextBox_Validating(object sender, System.ComponentModel.CancelEventArgs e)
{
if (!this.cancelButton.Focused)
{
// Do this only when the cancel button is not clicked.if(invalidState)
e.Cancel = true;
}
}
[VB.Net]
’ Handler to the Validating event of the TextBox.
Private Sub TextBox_Validating(ByVal sender As Object, ByVal e As System.ComponentModel.CancelEventArgs)
If Not Me.cancelButton.Focused Then
’ Do this only when the cancel button is not clicked.
If invalidState Then
e.Cancel = True
End If
End If
End Sub
Providing a border in the non-client region of your control rather than in the ClientRectangle has very many advantages:
When you include a scrollbar in your control, the scrollbar will appear inside the border, rather than to the outside if you drew the border in the client area.
When you allow custom painting of the control, your user will not draw over the NC border.
Your own client painting code will be simplified in that you will not have to bother about taking the border into account while painting the client area.
The next faq will tell you how to include a non-client border.
Clicking Advanced… under this DynamicProperties option in the control properties displays certain control properties that can be set through an XML app.config file that is added to your project. This file stores the dynamic property values and is automatically read during the initialization process on the form at design time. You can manually edit this file to change the properties and next when you run the application, controls will pick up the new values. This file is used strictly for design time initializations.
You can do this in different ways explained below. In all the cases the bitmap or icon should follow these rules:
The bitmap or icon dimension should be 16X16 with 16 colors.
The left-bottom pixel-color will be assumed to be the transparent color.
Technique 1: Use a bitmap (not an icon, in the embedded resource) file implicitly without specifying the ToolboxBitmapAttribute for the type:
Say, you have a custom control MyControl in the namespace MyNamespace, create a bmp file MyControl.bmp following the above rules. Add this file to your project at the top-level and make it an embedded resource. The project’s default namespace should be MyNamespace.
If the control’s namespace and the project’s default namespace don’t match then move the bitmap to appropriate subfolders so that they match. If this is not possible, typically when the namespaces are not related at all then you cannot use this technique, use instead one of the techniques below using the ToolboxBitmap attribute.
Create the assembly and the next time you add it to the toolbox the custom image in MyControl.bmp should be available in the toolbox.
This is the easiest technique to implement as it doesn’t require you to use the ToolboxBitmapAttribute in your type definition.
Technique 2: Use ToolboxBitmap attribute.
Example 1:
Use a bitmap (not icon) in the embedded resource with the same name as the type.
In the above scenario the runtime will look for a embedded bmp file of name MyCustomType.bmp in the project’s root directory. Note that the default namespace and the type’s namespace match.
Example 2:
If you want your icons in sub-directories then change the attribute like this:
where the bmp or ico file (yap, now, when you explicitly specify the resource, you can use an ico file) is in a sub-directory called ‘ToolboxIcons’.
Example 3:
Sometimes your type’s namespace and the default assembly namespace may be unrelated, in which case you have to use a different type that has the same namespace as the default assembly namespace to scope the embedded image file.
Default namespace: ‘MyAssemblyNamespace’
namespaceMyAssemblyNamespace
{
publicclassSomeType
{...}
}
namespaceDifferentNamespace
{
// Using SomeType which has the same namespace as the default assembly namespace to scope the embedded resource.
[ToolboxBitmap(typeof(SomeType), 'MyCustomType.ico')]
publicclassMyCustomType
{...}
}
In this case the runtime will look for the above resource at the top-most directory. If your icons were in a subdirectory named ‘ToolboxIcons’ then the attribute would look like this:
TopLevel controls (like Form) cannot be added into Controls list using Controls.Add(). For example, if you are trying to add a form into a Panel’s control list, it will lead you to an exception ‘Top-level control cannot be added to a control’. To avoid this exception, you need to set the form’s TopLevel property to false.
Form form2 = new Form();
form2.TopLevel = false;
this.panel1.Controls.Add(form2);
When you click on a button on a Toolbar the click is sent to the Toolbar, so you need to check the ToolBarButtonClickEventArgs’s button property to determine the button on the Toolbar that was clicked.
[C#]
privatevoidtoolBar1_ButtonClick(object sender, System.Windows.Forms.ToolBarButtonClickEventArgs e)
{
//check if toolBarButton1 was clickedif (e.Button == toolBarButton1)
{
MessageBox.Show('Button 1 clicked');
}
}
[VB.NET]
Private Sub toolBar1_ButtonClick(ByVal sender As Object, ByVal e As System.Windows.Forms.ToolBarButtonClickEventArgs)
’check if toolBarButton1 was clicked
If e.Button = toolBarButton1 Then
MessageBox.Show('Button 1 clicked')
End If
End Sub
In a derived class you should override WndProc as follows (listening to the WM_KEYUP message, for example):
[C#]
publicclassMyCombo : ComboBox
{
privateconstint WM_KEYUP = 0x101;
protectedoverridevoidWndProc(ref System.Windows.Forms.Message m)
{
if(m.Msg == WM_KEYUP)
{
return; //ignore the keyup
}
base.WndProc(ref m);
}
}
[VB.NET]
Public Class MyTextBox
Inherits TextBox
Private WM_KEYUP As Integer = &H101
Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
If m.Msg = WM_KEYUP Then
Return ’ignore the keyup
End If
MyBase.WndProc(m)
End Sub ’WndProc
End Class ’MyTextBox
Note that it’s only the ImageList class that can draw an Icon with alpha channel properly. So, instead of using the Graphics.DrawImage method, first associate this icon to a ImageList and then use the ImageList.Draw method to draw the icon. Also, note that this is possible only in XP with XP Themes enabled for that application.
Make sure that you include the manifest file that will enable XP themes support for you application. Then the icons with alpha channel will draw semi-transparently.
The best place to ensure a particular height/width for you control is in the SetBoundsCore override of your Control, as follows:
protectedoverridevoidSetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified)
{
int prefHeight = this.GetPreferredHeight();
// Ensure that the height is atleast as big as prefHeightif(height < prefHeight)
height = prefHeight;
base.SetBoundsCore(x, y, width, height, specified);
}
Protected Overrides Sub SetBoundsCore(ByVal x As Integer, ByVal y As Integer, ByVal width As Integer, ByVal height As Integer, ByVal specified As BoundsSpecified)
Dim prefHeight As Integer = Me.GetPreferredHeight()
’ Ensure that the height is atleast as big as prefHeight
If height < prefHeight Then
height = prefHeight
End If
MyBase.SetBoundsCore(x, y, width, height, specified)
End Sub
You will have to first provide some space in the NC area by setting the WS_BORDER flag in CreateParams and then draw the border yourself by listening to the WM_NCPAINT message in your Control, as follows:
protectedoverride CreateParams CreateParams
{
get
{
System.Windows.Forms.CreateParams cp = base.CreateParams;
if(this.needFlatBorder)
{
cparams.ExStyle &= ~512/*WS_EX_CLIENTEDGE*/;
cparams.Style &= ~8388608/*WS_BORDER*/;
cp.Style |= 0x800000; // WS_BORDER
}
}
}
protectedoverridevoidWndProc(ref Message m)
{
if(m.Msg == 133/*WM_NCPAINT*/)
{
this.DrawFlatNCBorder(ref m);
}
base.WndProc(ref m);
}
privatevoidDrawFlatNCBorder(ref Message msg)
{
IntPtr hRgn1 = (IntPtr) msg.WParam;
// The update region is clipped to the window frame. When wParam is 1, the entire window frame needs to be updated.
IntPtr hdc = NativeMethods.GetDCEx(msg.HWnd, hRgn1, 1/*DCX_WINDOW*/|0x0020/*DCX_PARENTCLIP*/);
if (hdc != IntPtr.Zero)
{
using (Graphics g = Graphics.FromHdc(hdc))
{
Rectangle bounds = new Rectangle(0,0,this.Width,this.Height);
ControlPaint.DrawBorder(g,bounds,this.borderColor,ButtonBorderStyle.Solid);
// create a clipping region for remaining parts to be drawn excluding// the border we did just drew
bounds.Inflate(-1, -1);
IntPtr hRgn2 = NativeMethods.CreateRectRgn(bounds.Left, bounds.Top, bounds.Right, bounds.Bottom);
if(hRgn2 == (IntPtr)1)
{
// Provide a new clipping region.
msg.WParam = (IntPtr) hRgn2;
}
else
{
// combine with existing clipping region.
NativeMethods.CombineRgn(hRgn1, hRgn1, hRgn2, NativeMethods.RGN_AND);
NativeMethods.DeleteObject(hRgn2);
}
}
msg.Result = (IntPtr) 1;
NativeMethods.ReleaseDC(msg.HWnd, hdc);
}
Invalidate();
}
You can do so as follows by overriding the CreateParams property in your Control. The advantage with this approach is that drawing is handled by the system as soon as you set the flag below.
The .Net framework libraries does not provide you an API to query for the focused Control. You have to invoke a windows API to do so:
[C#]
publicclassMyForm : Form
{
[DllImport('user32.dll', CharSet=CharSet.Auto, CallingConvention=CallingConvention.Winapi)]
internalstaticextern IntPtr GetFocus();
private Control GetFocusedControl()
{
Control focusedControl = null;
// To get hold of the focused control:
IntPtr focusedHandle = GetFocus();
if(focusedHandle != IntPtr.Zero)
// Note that if the focused Control is not a .Net control, then this will return null.
focusedControl = Control.FromHandle(focusedHandle);
return focusedControl;
}
}
[VB.Net]
Public Class Form1
’ Declare the GetFocused method here:
_
PublicSharedFunction GetFocus() As IntPtr
EndFunctionPrivateFunction GetFocusedControl() As Control
Dim focusedControl As Control = Nothing
’ Toget hold of the focused control:
Dim focusedHandle As IntPtr = GetFocus()
If IntPtr.Zero.Equals(focusedHandle) Then
’ Note that if the focused Control isnot a .Net control, then this will return null.
focusedControl = Control.FromHandle(focusedHandle)
EndIfReturn focusedControl
EndFunctionEndClass
One generic way for all Controls is to get the difference between the ClientRectangle’s width and the Control.Bounds’ width. That should give the border width (and in general the NC area width); similarly for height.
When custom initialization is to be done during runtime on certain Controls, the best way is to implement the ISupportInitialize interface in that Control. Then the BeginInit method will be called as soon as the Control gets created and EndInit will be called after the design-time initialization of that control.
You can do this using the bitwise operators &, | and ^ ( And, Or and Xor (or &, Or, ^) in VB.Net). Here is code that will toggle label1 being anchored on the left.
[C#]
privatevoidbutton1_Click(object sender, System.EventArgs e)
{
if ((label1.Anchor & AnchorStyles.Left) == 0)
{ //add it
label1.Anchor = label1.Anchor | AnchorStyles.Left;
}
elseif ((label1.Anchor & AnchorStyles.Left) != 0)
{ //remove
label1.Anchor = label1.Anchor ^ AnchorStyles.Left;
}
}
[VB.NET]
Private Sub button1_Click(sender As Object, e As System.EventArgs)
If(label1.Anchor And AnchorStyles.Left) = 0 Then
’add it
label1.Anchor = label1.Anchor Or AnchorStyles.Left
ElseIf(label1.Anchor And AnchorStyles.Left) <> 0 Then
’remove
label1.Anchor = label1.Anchor Xor AnchorStyles.Left
End If
End Sub ’button1_Click
3. Add the control to the Form’s Controls collection
In general, if you need help on exactly what code you need to add, just look at the code generated by the designer when you add the control at design time. You can generally use the same code at runtime.
Here are code snippets that create a textBox at runtime.
[C#]
//step 1
TextBox tb = new TextBox();
//step2
tb.Location = new Point( 10, 10);
tb.Size = new Size(100, 20);
tb.Text = 'I was created at runtime';
//step3this.Controls.Add(tb);
[VB.NET]
’step 1
Dim tb as TextBox = New TextBox()
’step2
tb.Location = New Point(10, 10)
tb.Size = New Size(100, 20)
tb.Text = 'I was created at runtime'
’step3
Me.Controls.Add(tb)
You use System Reflection to dynamically load a control. If the DLL is named ‘SpecControls.DLL’ and the class you want is ‘SpecControls.ColorControl’, then use this code.
[C#]
// load the assembly
System.Reflection.Assembly assembly = Assembly.LoadFrom('SpecControls.DLL');
// get the type
Type t = assembly.GetType('SpecControls.ColorControl');
// create an instanceandadd it.
//
Control c = (Control)Activator.CreateInstance(t);
parent.Controls.Add(c);
[VB.NET]
’ load the assembly
Dim assembly1 As System.Reflection.Assembly = Assembly.LoadFrom('SpecControls.DLL')
’ get the type
Dim t AsType = assembly1.GetType('SpecControls.ColorControl')
’ create an instanceandadd it.
’
Dim c As Control = CType(Activator.CreateInstance(t), Control)
parent.Controls.Add(c)
Controls with a ‘Transparent’ color actually render their parent’s background, so you’re seeing the White background of the Form, not the PictureBox. Three easy ways to deal with this:
Use a Panel with it’s ‘BackgroundImage’ property set instead of a PictureBox, and parent the LinkLabels to the panel (PictureBoxes generally don’t have children)
Set the BackgroundImage of the Form to the image (basically the same as 1 above, but avoids the extra control)
In code, set the Parent of the LinkLabel to be the PictureBox. You’ll need to update the LinkLabel’s position to match the new origin of the parent if the PictureBox isn’t at (0,0)
(Shawn Burke on microsoft.public.dotnet.framework.windowsforms newsgroup)
A control’s Visible property will also depend on it’s parent control’s (if any) visible property. If the parent control is not visible, the control will also return false.
One way is to add code to your Validating handler and only execute the validation routine if the mouse is in the client area. This will avoid the click on the title bar and its system menu. You might also want to add special handling for the tab key so your validation is hit independent of the mouse location when you tab off the control.
You can do this by creating the editor yourself rather than allowing TypeDescriptor to do it:
1) Shadow the property you care about in your designer...
protectedoverridevoidPreFilterProperties(IDictionaryProperties props)
{
PropertyDescriptor basePD = props['MyProperty'];
props['MyProperty'] = new EditorPropertyDescriptor(basePD);
}
2) Create a property descriptor that 'wraps' the original descriptor
privateclassEditorPropertyDescriptor : PropertyDescriptor
{
private PropertyDescriptor basePD;
publicEditorPropertyDescriptor(PropertyDescriptor base)
{
this.basePD = base;
}
// now, for each property and method, just delegate to the base...publicoverride TypeConverter Converter
{
get { return basePD.Converter; }
}
publicoverrideboolCanResetValue(object comp)
{
return basePD.CanResetValue(comp);
}
// and finally, implement GetEditor to create your special one...3) create your editor by hand when it’s asked forpublicoverrideobjectGetEditor(Type editorBaseType)
{
if (editorBaseType == typeof(UITypeEditor))
{
returnnew MyEditor(Param1, param2, param3);
}
return basePD.GetEditor(editorBaseType);
}
}
(from sburke_online@microsoft..nospam..com on microsoft.public.dotnet.framework.windowsforms)
By default, the arrow keys are not handled by a control’s key processing code, but instead are filtered out for focus management. Hence, the control’s KeyDown, KeyUp and KeyPressed events are not hit when you press an arrow. If you want your control to handle these keyboard events, you tell the framework by overriding your control’s IsInputKey method.
protectedoverrideboolIsInputKey(Keys key)
{
switch(key)
{
case Keys.Up:
case Keys.Down:
case Keys.Right:
case Keys.Left:
returntrue;
}
returnbase.IsInputKey(key);
}