|
|
2.1 How do you prevent serialization of certain child controls in your Composite Control?
|
 |
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 |
2.2 How do I get hold of the currently focused Control?
|
 |
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:
|
public class MyForm : Form
|
[DllImport("user32.dll", CharSet=CharSet.Auto, CallingConvention=CallingConvention.Winapi)]
|
internal static extern 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);
|
' Declare the GetFocused method here:
|
Public Shared Function GetFocus() As IntPtr
|
Private Function GetFocusedControl() As Control
|
Dim focusedControl As Control = Nothing
|
' To get hold of the focused control:
|
Dim focusedHandle As IntPtr = GetFocus()
|
If IntPtr.Zero.Equals(focusedHandle) Then
|
' Note that if the focused Control is not a .Net control, then this will return null.
|
focusedControl = Control.FromHandle(focusedHandle)
|
2.3 Why do calling Focus() on a control not set focus on it?
|
 |
We have a small sample that shows how to do this. Download PropTab.zip. After you download and unzip the sample, build the project. Both the control assembly and a small driver assembly get built. After that add the control to your toolbox using 'Customise toolbox...'. Then drag and drop an instance of the added control onto the form in the driver winforms sub-project. When you select this control the properties window should have a tab with a bitmap 'T' as shown below.
These are the steps involved. Before following these steps please download and take a look at the sample. That will greatly help when following these steps.
Control related steps
Assume that you have a control that has two sets of properties, one set that you wish to have displayed in the main property tab and another set that you wish to have displayed in the second tab.
- Mark those properties that you wish to display in the first tab with the BrowsableAttribute to true.
- Mark those properties that you wish to display in the second tab with the BrowsableAttribute set to false. Create another attribute. Name it anything you want and give it a single boolean property. Initialize this property to true.
Other steps
- Derive a class from System.Windows.Forms.PropertyGridInternal.PropertiesTab. You have to override a few methods. The most important of these is GetProperties. We override GetProperties as shown below to use our CustomAttribute as the filtering attribute instead of the BrowsableAttribute that the PropertyGrid uses by default.
|
public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object component, Attribute[] attrs)
|
return TypeDescriptor.GetProperties(component, new Attribute[]
|
{new BrowsableAttribute(false), new CustomTabDisplayAttribute(true)});
|
Create a embedded resource bitmap with the same name as the derived tab's type. This bitmap had to be 16x16.
A brief note of explanation on the sample. The sample shows a user control that displays its own tab for some properties. These properties are marked with the filtering attribute, CustomTabDisplayAttribute. The rest of the properties are just displayed in the normal property tab and need no special attention.
To get a deeper understanding of how this works, please refer to these FAQs on TypeConverters and TypeDescriptors.
|
2.4 How do I listen to windows messages in my Control?
|
 |
In a derived class you should override WndProc as follows (listening to the WM_KEYUP message, for example):
|
public class MyCombo : ComboBox
|
private const int WM_KEYUP = 0x101;
|
protected override void WndProc(ref System.Windows.Forms.Message m)
|
return; //ignore the keyup
|
Private WM_KEYUP As Integer = &H101
|
Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
|
2.5 How do I programatically change the color of a control?
|
 |
Use the properties BackColor and ForeColor.
|
button1.BackColor = Color.White;
|
button1.ForeColor = Color.Blue;
|
2.6 How can I change the Border color of my control?
|
 |
Override the OnPaint. Here is some code for a derived Button.
|
public class MyButton : Button
|
protected override void OnPaint(PaintEventArgs e)
|
Color borderColor = Color.Blue;
|
ControlPaint.DrawBorder(e.Graphics, e.ClipRectangle, borderColor,
|
borderWidth, ButtonBorderStyle.Solid, borderColor, borderWidth,
|
ButtonBorderStyle.Solid, borderColor, borderWidth, ButtonBorderStyle.Solid,
|
borderColor, borderWidth, ButtonBorderStyle.Solid);
|
Protected Overrides Sub OnPaint(e As PaintEventArgs)
|
Dim borderWidth As Integer = 1
|
Dim borderColor As Color = Color.Blue
|
ControlPaint.DrawBorder(e.Graphics, e.ClipRectangle, borderColor, borderWidth, ButtonBorderStyle.Solid, borderColor, borderWidth, ButtonBorderStyle.Solid, borderColor, borderWidth, ButtonBorderStyle.Solid, borderColor, borderWidth, ButtonBorderStyle.Solid)
|
2.7 Why should I provide a Non-Client border to my Control derived class?
|
 |
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. |
2.8 How do I provide a 2 pixel 3d border in the Non-Client area of my Control derived class?
|
 |
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.
|
protected override CreateParams CreateParams
|
cparams = base.CreateParams;
|
cparams.Style &= ~8388608 /*WS_BORDER*/;
|
cparams.ExStyle = cparams.ExStyle | 512 /*WS_EX_DLGFRAME*/;
|
2.9 How do I provide a 1 pixel border in the NonClient area of my Control?
|
 |
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:
|
protected override CreateParams CreateParams
|
System.Windows.Forms.CreateParams cp = base.CreateParams;
|
cparams.ExStyle &= ~512 /*WS_EX_CLIENTEDGE*/;
|
cparams.Style &= ~8388608 /*WS_BORDER*/;
|
cp.Style |= 0x800000; // WS_BORDER
|
protected override void WndProc(ref Message m)
|
if(m.Msg == 133/*WM_NCPAINT*/)
|
this.DrawFlatNCBorder(ref m);
|
private void DrawFlatNCBorder(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*/);
|
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
|
IntPtr hRgn2 = NativeMethods.CreateRectRgn(bounds.Left, bounds.Top, bounds.Right, bounds.Bottom);
|
// Provide a new clipping region.
|
msg.WParam = (IntPtr) hRgn2;
|
// combine with existing clipping region.
|
NativeMethods.CombineRgn(hRgn1, hRgn1, hRgn2, NativeMethods.RGN_AND);
|
NativeMethods.DeleteObject(hRgn2);
|
NativeMethods.ReleaseDC(msg.HWnd, hdc);
|
2.10 How do I invalidate a control including it's NonClient area?
|
 |
You can do so as follows in your Control:
|
private void InvalidateWindow()
|
NativeMethods.RedrawWindow(this.Handle, IntPtr.Zero, IntPtr.Zero,
|
0x0400/*RDW_FRAME*/ | 0x0100/*RDW_UPDATENOW*/
|
| 0x0001/*RDW_INVALIDATE*/);
|
2.11 How can I implement a scrollable picture box?
|
 |
Normally, the context menu will be shown at the center of the control when the keyboard is used to invoke the context menu of a control (Shift+F10, for example). You can customize the location as follows.
- Override WndProc in the grid and check if the Message.Msg is
WM_CONTEXTMENU (0x007b)
- If so, check if the Message.LParam is -1, which means this was due to a
keyboard message as opposed to the user right-clicking the mouse.
- Now, call the associated ContextMenu's Show method explicitly at the
selected row position.
- Make sure NOT to call the base class after showing the menu explicitly.
|
protected override void WndProc(ref Message m)
|
if(m.Msg == 0x007B /*WM_CONTEXTMENU*/)
|
// LParam == -1 means that this is due to a keyboard message
|
Protected Overrides Sub WndProc(ByRef m As Message)
|
If m.Msg = 0x007B Then 'WM_CONTEXTMENU
|
' LParam == -1 means that this is due to a keyboard message
|
If (CType(m.LParam,Integer)) = -1 Then
|
2.12 How can I put Controls, a ProgressBar for example, into a StatusBar?
|
 |
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:
|
2.13 How do I implement an ownerdrawn statusbar so I can put a progressbar in it?
|
 |
Check out the code posted originally in VB by Jacob Grass, and translated to C# by Jason Lavigne on the dotnet.discussion newsgroup at develop.com.
|
2.14 How would I change the icon that appears on the toolbox for a custom control?
|
 |
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.
Default Assembly Namespace: "MyAssemblyNamespace"
|
namespace MyAssemblyNamespace
|
[ToolboxBitmap(typeof(MyCustomType))]
|
public class MyCustomType : Component
|
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:
|
[ToolboxAttribute(typeof(MyCustomType), "ToolboxIcons.MyCustomType.bmp")]
|
[ToolboxAttribute(typeof(MyCustomType), "ToolboxIcons.MyCustomType.ico")]
|
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"
|
namespace MyAssemblyNamespace
|
namespace DifferentNamespace
|
// Using SomeType which has the same namespace as the default assembly namespace to scope the embedded resource.
|
[ToolboxBitmap(typeof(SomeType), "MyCustomType.ico")]
|
public class MyCustomType
|
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:
|
[ToolboxBitmap(typeof(SomeType), "ToolboxIcons.MyCustomType.ico")]
|
2.15 A control's Validating event is hit even when the user clicks on the Close box. How can I avoid this behavior?
|
 |
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.
|
private bool tabKeyPressed = false;
|
private void textBox1_Validating(object sender, System.ComponentModel.CancelEventArgs e)
|
this.ClientRectangle.Contains(this.PointToClient(Cursor.Position)))
|
if(boolNotOKValues) //do your validating
|
e.Cancel = true; //failed
|
protected override bool ProcessDialogKey(Keys keyData)
|
tabKeyPressed = keyData == Keys.Tab;
|
return base.ProcessDialogKey(keyData);
|
2.16 I would like to prevent validation in my textbox when the user clicks on my Cancel button, how do I do this?
|
 |
Say textBox1 and cancelButton and the control names, then this is how you could do this:
|
// Handler to the Validating event of the TextBox.
|
private void TextBox_Validating(object sender, System.ComponentModel.CancelEventArgs e)
|
if (!this.cancelButton.Focused)
|
// Do this only when the cancel button is not clicked.
|
' 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.
|
2.17 Why does adding images to an ImageList in the designer cause them to lose their alpha channel?
|
 |
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.
Take a look at this faq on how to add images to your project and retrieve them programatically during runtime.
Adding image files to a project as an embedded resource and retrieving them programatically.
|
2.18 Why do the XP Icons when drawn using Graphics.DrawImage not draw transparently?
|
 |
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.
|
2.19 Why do the XP Icons that have alpha channel not draw properly when associated with controls like ListView?
|
 |
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. |
2.20 How do I prevent resizing of my Controls by the user, via Docking or anchoring or manual resizing during design-time?
|
 |
The best place to ensure a particular height/width for you control is in the SetBoundsCore override of your Control, as follows:
|
protected override void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified)
|
int prefHeight = this.GetPreferredHeight();
|
// Ensure that the height is atleast as big as 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
|
MyBase.SetBoundsCore(x, y, width, height, specified)
|
| |