|
Introduction
Sometimes programming is like doing a jigsaw puzzle with a missing
corner.
I work at Red Gate Software, the
company that produces SQL Compare, which is written in C#. Although C# and
the .NET framework enable us to write good code quickly, the combination has its
drawbacks. More often than not I find that even though .NET covers 95
percent of the native Windows API, the five percent that’s missing is the five
percent I need. This article describes one of these situations.
C# is very good at handling international character support. This means
that SQL Compare can handle databases that use diverse character sets. The
problem comes when we need to get the T-SQL scripts out of SQL Compare and into
other third-party tools.
Tangled web of encoding
There are a number of different ways of saving Unicode data to disk. ASCII
encoding doesn’t handle international characters at all – it will only cope with
seven-bit values from 0 to 127. UTF-8 encoding is the same as ANSI
encoding for seven-bit characters such as A, B, C and D. Other, more
exotic, characters are stored as two-, three- or four-byte combinations.
Unicode, or UTF-16, encoding normally stores each character as two bytes,
although it can occasionally use four bytes. In addition, each encoding
type allows a preamble. This consists of two (for UTF-16) or three (UTF-8)
bytes written at the start of a file to indicate the encoding of the file.
Not all tools support all encodings. SQL Query Analyzer, for example,
supports UTF-16, but not UTF-8 encoded files; if you ask it to open a UTF-8 file
it will treat it as ASCII and treat a two-byte international character as two
separate characters. If, however, you save files as UTF-16, then editors
that are not Unicode-aware will display each two-byte character as two single
characters, l i k e t h i s.
Simple solution?
The lack of universal support means that we need to let our users choose the
type of encoding they want when saving files to disk. This is where the
tricky part starts. .NET provides a SaveFileDialog class that
you can normally use to get a filename. Unfortunately, this dialog does
not provide a way of asking the user for an encoding. I thought adding
this functionality would be a simple matter of inheriting from the
SaveFileDialog class and then adding a couple of controls to the
dialog. This class is not, however, a simple Windows form. Instead,
it uses the Windows GetSaveFileName function to display the save
file dialog.
This made things a bit harder. I looked at the MSDN help for the
SaveFileDialog class and noticed that there is a protected
HookProc method. This lets you hook into the dialog creation
process. You should be able to inherit from the class, intercept the
appropriate windows messages, and then add the controls onto the form.
Unfortunately, the SaveFileDialog class is sealed. This means
that you cannot inherit from the class and override the HookProc
method.
At this point I did a search through MSDN and the newsgroups. I found a
couple of interesting articles by Dino Esposito and some newsgroup postings by
Nicholas Paladino that suggested a good approach.
I decided to use the GetSaveFileName from the Windows API.
This takes an OPENFILENAME struct. One of the fields of this
struct is a hook that expects a function pointer. One of the neat things
about .NET interop is the way you can use a managed C# delegate in place of an
unmanaged C++ function pointer:
.
.
private delegate int OFNHookProcDelegate(int hdlg, int msg,
int wParam, int lParam);
.
.
ofn.lpfnHook = new OFNHookProcDelegate(HookProc);
.
.
GetSaveFileName(ref ofn)
.
.
private int HookProc(int hdlg, int msg, int wParam, int lParam)
{
}
.
.
When GetSaveFileName is called, the HookProc
receives notification messages from the save file dialog box.
I needed to hook into the WM_INITDIALOG message. In the hook I found
the location of the standard Save as type: label and combobox and
added a new label and combo box for the encoding choices using
CreateWindowEx:
int fileTypeWindow=GetDlgItem(parent, 0x441);
RECT aboveRect = new RECT();
GetWindowRect(fileTypeWindow, ref aboveRect);
POINT point=new POINT();
point.X=aboveRect.Left;
point.Y=aboveRect.Bottom;
ScreenToClient(parent, ref point);
int labelHandle=CreateWindowEx(0, "STATIC", "mylabel",
WS_VISIBLE | WS_CHILD | WS_TABSTOP,
point.X, point.Y + 12, 200, 100, parent,
0, 0, 0);
SetWindowText(labelHandle, "&Encoding:");
In addition to intercepting the WM_INITDIALOG message, I needed
to intercept the WM_DESTROY and WM_NOTIFY messages to
destroy the label and combo box, and to intercept the selection of a file to
work out the chosen encoding.
The final dialog box looks like this:

Download the source code
You can download the complete source code for this sample by following the
link at the top of this article. Although the sample doesn’t provide all the
functionality of the SaveFileDialog class, it should provide a good
starting point for you to implement the functionality you need.
| You must Sign In to use this message board. |
|
| | Msgs 1 to 25 of 26 (Total in Forum: 26) (Refresh) | FirstPrevNext |
|
|
 |
|
|
I need to place the textbox infront of the filename combo and for that i need to get the pos which is in a binary format, how can i do that
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
This is the best thing ever. That savefiledialog has been pissing me off with multi-threaded applications.
This has solved that. Thank you.
Have you made any extentions to this?
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
I extended the save dialog with a button, that opens a new form by clicking on it. In this new form the user can generate a filename with a few clicks on some checkboxes (e.g. date, date of file, name of file and so on). When the user than exits the form the generated filename should appear in the filename control of the save dialog. The problem is to write a string via wm_settext in the filename control. I would be glad if you can give me a hint how to do this. with kind regards - zardoz
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Are there any special resource usage considerations? I've taken ideas from this code and put them into a component. Next, I want to create a new Dispose method that cleans up resources for the SaveFileDialog, so I can use it in a using clause like I do my other dialogs. What would you suggest I do in the Dispose method?
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
|
How to make the extended save file dialog resizable.? I noticed that the save file dialog created with GetSaveFileName win API is not resizable. Any thought? Thanks.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
|
 |
|
|
For some strange reason the C# code is converting | (124) to char 92, instead of 0 (unless the C# to VB.net converter I used did that - I had to fix various things by hand anyway)
Microsoft MVP J# 2004-2007 Borland "Spirit of Delphi" QuickTime, ActiveX, VCL, .NET, Robotics http://www.kagi.com/birbilis http://birbilis.spaces.live.com
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
'20070325: George Birbilis (birbilis@kagi.com) 'Converted sample at http://www.codeproject.com/csharp/GetSaveFileName.asp from C# to VB.net 'Added "Encoding" property 'Added more "EncodingTypes" and respective dropdown strings 'Fixed filter to use "Chr" instead of "ChrW" and convert "|" [Chr(124)] to Chr(0) instead of ChrW(92)
' Example of use: ' ' Dim ofd As SaveFileDialogWithEncoding = New SaveFileDialogWithEncoding ' ofd.DefaultExt = "sql" ' ofd.EncodingType = EncodingTypes.UTF8 ' ofd.Filter = "SQL files (*.sql)|*.sql|All files (*.*)|*.*" ' If (ofd.ShowDialog = DialogResult.OK) Then ' MessageBox.Show(String.Format("Name={0}, Encoding={1}", ofd.FileName, ofd.EncodingType)) ' End If
Imports System.Text Imports System.Text.Encoding Imports System.Drawing Imports System.Collections Imports System.ComponentModel Imports System.Windows.Forms Imports System.Data Imports System.Runtime.InteropServices
Public Class SaveFileDialogWithEncoding Inherits Component
#Region "Enums"
'note the order of these is important Public Enum EncodingTypes ANSI = 0 UTF8 UTF16 UTF7 UTF32 UNKNOWN End Enum
#End Region
#Region "Fields"
Protected Encodings As System.Text.Encoding() = New System.Text.Encoding() {ASCII, UTF8, Unicode, UTF7, UTF32}
Private m_LabelHandle As Integer = 0 Private m_ComboHandle As Integer = 0 Private m_Filter As String = "" Private m_DefaultExt As String = "" Private m_FileName As String = "" Private m_EncodingType As EncodingTypes Private m_ActiveScreen As Screen
#End Region
#Region "Constants"
Private Const OFN_ENABLEHOOK As Integer = 32 Private Const OFN_EXPLORER As Integer = 524288 Private Const OFN_FILEMUSTEXIST As Integer = 4096 Private Const OFN_HIDEREADONLY As Integer = 4 Private Const OFN_CREATEPROMPT As Integer = 8192 Private Const OFN_NOTESTFILECREATE As Integer = 65536 Private Const OFN_OVERWRITEPROMPT As Integer = 2 Private Const OFN_PATHMUSTEXIST As Integer = 2048 Private Const SWP_NOSIZE As Integer = 1 Private Const SWP_NOMOVE As Integer = 2 Private Const SWP_NOZORDER As Integer = 4 Private Const WM_INITDIALOG As Integer = 272 Private Const WM_DESTROY As Integer = 2 Private Const WM_SETFONT As Integer = 48 Private Const WM_GETFONT As Integer = 49 Private Const CBS_DROPDOWNLIST As Integer = 3 Private Const CBS_HASSTRINGS As Integer = 512 Private Const CB_ADDSTRING As Integer = 323 Private Const CB_SETCURSEL As Integer = 334 Private Const CB_GETCURSEL As Integer = 327 Private Const WS_VISIBLE As UInteger = 268435456 Private Const WS_CHILD As UInteger = 1073741824 Private Const WS_TABSTOP As UInteger = 65536 Private Const CDN_FILEOK As Integer = -606 Private Const WM_NOTIFY As Integer = 78
#End Region
#Region "Properties"
Public Property DefaultExt() As String Get Return m_DefaultExt End Get Set(ByVal value As String) m_DefaultExt = value End Set End Property
Public Property Filter() As String Get Return m_Filter End Get Set(ByVal value As String) m_Filter = value End Set End Property
Public Property FileName() As String Get Return m_FileName End Get Set(ByVal value As String) m_FileName = value End Set End Property
Public Property EncodingType() As EncodingTypes Get Return m_EncodingType End Get Set(ByVal value As EncodingTypes) m_EncodingType = value End Set End Property
Public Property Encoding() As Encoding Get Return Encodings(m_EncodingType) End Get Set(ByVal value As Encoding) Dim i As Integer = 0 For Each enc As Encoding In Encodings If enc.Equals(value) Then m_EncodingType = CType(i, EncodingTypes) Exit Property End If Next enc m_EncodingType = EncodingTypes.UNKNOWN End Set End Property
#End Region
#Region "Methods"
<DllImport("Comdlg32.dll", CharSet:=CharSet.Auto, SetLastError:=True)> _ Private Shared Function GetSaveFileName(ByRef lpofn As OPENFILENAME) As Boolean End Function
<DllImport("Comdlg32.dll")> _ Private Shared Function CommDlgExtendedError() As Integer End Function
<DllImport("user32.dll")> _ Private Shared Function SetWindowPos(ByVal hWnd As Integer, ByVal hWndInsertAfter As Integer, ByVal X As Integer, ByVal Y As Integer, ByVal cx As Integer, ByVal cy As Integer, ByVal uFlags As UInteger) As Boolean End Function
<DllImport("user32.dll")> _ Private Shared Function GetWindowRect(ByVal hWnd As Integer, ByRef lpRect As RECT) As Boolean End Function
<DllImport("user32.dll")> _ Private Shared Function GetParent(ByVal hWnd As Integer) As Integer End Function
<DllImport("user32.dll", CharSet:=CharSet.Auto)> _ Private Shared Function SetWindowText(ByVal hWnd As Integer, ByVal lpString As String) As Boolean End Function
<DllImport("user32.dll")> _ Private Overloads Shared Function SendMessage(ByVal hWnd As Integer, ByVal Msg As Integer, ByVal wParam As Integer, ByVal lParam As Integer) As Integer End Function
<DllImport("user32.dll", CharSet:=CharSet.Auto)> _ Private Overloads Shared Function SendMessage(ByVal hWnd As Integer, ByVal Msg As Integer, ByVal wParam As Integer, ByVal lParam As String) As Integer End Function
<DllImport("user32.dll")> _ Private Shared Function DestroyWindow(ByVal hwnd As Integer) As Boolean End Function
<DllImport("user32.dll", CharSet:=CharSet.Auto)> _ Private Shared Function GetDlgItem(ByVal hDlg As Integer, ByVal nIDDlgItem As Integer) As Integer End Function
<DllImport("user32.dll", CharSet:=CharSet.Auto)> _ Private Shared Function CreateWindowEx(ByVal dwExStyle As Integer, ByVal lpClassName As String, ByVal lpWindowName As String, ByVal dwStyle As UInteger, ByVal x As Integer, ByVal y As Integer, ByVal nWidth As Integer, ByVal nHeight As Integer, ByVal hWndParent As Integer, ByVal hMenu As Integer, ByVal hInstance As Integer, ByVal lpParam As Integer) As Integer End Function
<DllImport("user32.dll")> _ Private Shared Function ScreenToClient(ByVal hWnd As Integer, ByRef lpPoint As POINT) As Boolean End Function
Private Function HookProc(ByVal hdlg As Integer, ByVal msg As Integer, ByVal wParam As Integer, ByVal lParam As Integer) As Integer Select Case (msg) Case WM_INITDIALOG 'we need to centre the dialog Dim sr As Rectangle = m_ActiveScreen.Bounds Dim cr As RECT = New RECT Dim parent As Integer = GetParent(hdlg) GetWindowRect(parent, cr) Dim x As Integer = CInt((sr.Right + (sr.Left - (cr.Right - cr.Left))) / 2) Dim y As Integer = CInt((sr.Bottom + (sr.Top - (cr.Bottom - cr.Top))) / 2) SetWindowPos(parent, 0, x, y, (cr.Right - cr.Left), ((cr.Bottom - cr.Top) + 32), SWP_NOZORDER) 'we need to find the label to position our new label under Dim fileTypeWindow As Integer = GetDlgItem(parent, 1089) Dim aboveRect As RECT = New RECT GetWindowRect(fileTypeWindow, aboveRect) 'now convert the label's screen co-ordinates to client co-ordinates Dim point As POINT = New POINT point.X = aboveRect.Left point.Y = aboveRect.Bottom ScreenToClient(parent, point) 'create the label Dim labelHandle As Integer = CreateWindowEx(0, "STATIC", "mylabel", (WS_VISIBLE _ Or (WS_CHILD Or WS_TABSTOP)), point.X, (point.Y + 12), 200, 100, parent, 0, 0, 0) SetWindowText(labelHandle, "&Encoding:") 'should show with same encoding as system dialog or not show label at all Dim fontHandle As Integer = SendMessage(fileTypeWindow, WM_GETFONT, 0, 0) SendMessage(labelHandle, WM_SETFONT, fontHandle, 0) 'we now need to find the combo-box to position the new combo-box under Dim fileComboWindow As Integer = GetDlgItem(parent, 1136) aboveRect = New RECT GetWindowRect(fileComboWindow, aboveRect) point = New POINT point.X = aboveRect.Left point.Y = aboveRect.Bottom ScreenToClient(parent, point) Dim rightPoint As POINT = New POINT rightPoint.X = aboveRect.Right rightPoint.Y = aboveRect.Top ScreenToClient(parent, rightPoint) 'we create the new combobox Dim comboHandle As Integer = CreateWindowEx(0, "ComboBox", "mycombobox", (WS_VISIBLE _ Or (WS_CHILD _ Or (CBS_HASSTRINGS _ Or (CBS_DROPDOWNLIST Or WS_TABSTOP)))), point.X, (point.Y + 8), (rightPoint.X - point.X), 100, parent, 0, 0, 0) SendMessage(comboHandle, WM_SETFONT, fontHandle, 0) 'and add the encodings we want to offer SendMessage(comboHandle, CB_ADDSTRING, 0, "ANSI") SendMessage(comboHandle, CB_ADDSTRING, 0, "Unicode (UTF-8)") SendMessage(comboHandle, CB_ADDSTRING, 0, "Unicode (UTF-16)") SendMessage(comboHandle, CB_ADDSTRING, 0, "Unicode (UTF-7)") SendMessage(comboHandle, CB_ADDSTRING, 0, "Unicode (UTF-32)") SendMessage(comboHandle, CB_SETCURSEL, CType(m_EncodingType, Integer), 0) 'remember the handles of the controls we have created so we can destroy them after m_LabelHandle = labelHandle m_ComboHandle = comboHandle Case WM_DESTROY 'destroy the handles we have created If (m_ComboHandle <> 0) Then DestroyWindow(m_ComboHandle) End If If (m_LabelHandle <> 0) Then DestroyWindow(m_LabelHandle) End If Case WM_NOTIFY 'we need to intercept the CDN_FILEOK message 'which is sent when the user selects a filename Dim nmhdr As NMHDR = CType(Marshal.PtrToStructure(New IntPtr(lParam), GetType(NMHDR)), NMHDR) If (nmhdr.Code = CDN_FILEOK) Then 'a file has been selected 'we need to get the encoding m_EncodingType = CType(SendMessage(m_ComboHandle, CB_GETCURSEL, 0, 0), EncodingTypes) End If End Select Return 0 End Function
Public Function ShowDialog() As DialogResult 'set up the struct and populate it Dim ofn As OPENFILENAME = New OPENFILENAME ofn.lStructSize = Marshal.SizeOf(ofn) ofn.lpstrFilter = (m_Filter.Replace("|", Microsoft.VisualBasic.Chr(0)) + Microsoft.VisualBasic.Chr(0)) ofn.lpstrFile = (m_FileName + New String(Microsoft.VisualBasic.Chr(32), 512)) ofn.nMaxFile = ofn.lpstrFile.Length ofn.lpstrFileTitle = (System.IO.Path.GetFileName(m_FileName) + New String(Microsoft.VisualBasic.Chr(32), 512)) ofn.nMaxFileTitle = ofn.lpstrFileTitle.Length ofn.lpstrTitle = "Save file as" ofn.lpstrDefExt = m_DefaultExt 'position the dialog above the active window ofn.hwndOwner = Form.ActiveForm.Handle 'we need to find out the active screen so the dialog box is 'centred on the correct display m_ActiveScreen = Screen.FromControl(Form.ActiveForm) 'set up some sensible flags ofn.Flags = (OFN_EXPLORER _ Or (OFN_PATHMUSTEXIST _ Or (OFN_NOTESTFILECREATE _ Or (OFN_ENABLEHOOK _ Or (OFN_HIDEREADONLY Or OFN_OVERWRITEPROMPT))))) 'this is where the hook is set. Note that we can use a C# delegate in place of a C function pointer ofn.lpfnHook = New OFNHookProcDelegate(AddressOf HookProc) 'if we're running on Windows 98/ME then the struct is smaller If (System.Environment.OSVersion.Platform <> PlatformID.Win32NT) Then ofn.lStructSize = (ofn.lStructSize - 12) End If 'show the dialog If Not GetSaveFileName(ofn) Then Dim ret As Integer = CommDlgExtendedError() If (ret <> 0) Then Throw New ApplicationException(("Couldn't show file open dialog - " + ret.ToString)) End If Return DialogResult.Cancel End If
'Birb-start Dim oldFilename As String = m_FileName m_FileName = ofn.lpstrFile Dim cancelCheck As New CancelEventArgs() RaiseEvent FileOK(Me, cancelCheck) If cancelCheck.Cancel Then m_FileName = oldFilename 'restore filename since dialog was canceled Return DialogResult.Cancel Else Return DialogResult.OK End If 'Birb-end End Function
Public Delegate Function OFNHookProcDelegate(ByVal hdlg As Integer, ByVal msg As Integer, ByVal wParam As Integer, ByVal lParam As Integer) As Integer
#End Region
#Region "Structures"
<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Auto)> _ Private Structure OPENFILENAME Public lStructSize As Integer Public hwndOwner As IntPtr Public hInstance As Integer <MarshalAs(UnmanagedType.LPTStr)> _ Public lpstrFilter As String <MarshalAs(UnmanagedType.LPTStr)> _ Public lpstrCustomFilter As String Public nMaxCustFilter As Integer Public nFilterIndex As Integer <MarshalAs(UnmanagedType.LPTStr)> _ Public lpstrFile As String Public nMaxFile As Integer <MarshalAs(UnmanagedType.LPTStr)> _ Public lpstrFileTitle As String Public nMaxFileTitle As Integer <MarshalAs(UnmanagedType.LPTStr)> _ Public lpstrInitialDir As String <MarshalAs(UnmanagedType.LPTStr)> _ Public lpstrTitle As String Public Flags As Integer Public nFileOffset As Short Public nFileExtension As Short <MarshalAs(UnmanagedType.LPTStr)> _ Public lpstrDefExt As String Public lCustData As Integer Public lpfnHook As OFNHookProcDelegate <MarshalAs(UnmanagedType.LPTStr)> _ Public lpTemplateName As String 'only if on nt 5.0 or higher Public pvReserved As Integer Public dwReserved As Integer Public FlagsEx As Integer End Structure
Private Structure RECT Public Left As Integer Public Top As Integer Public Right As Integer Public Bottom As Integer End Structure
Private Structure POINT Public X As Integer Public Y As Integer End Structure
Private Structure NMHDR Public HwndFrom As Integer Public IdFrom As Integer Public Code As Integer End Structure
#End Region
#Region "Events"
Public Event FileOK(ByVal sender As System.Object, ByVal e As System.ComponentModel.CancelEventArgs) 'Birb
#End Region
End Class
http://www.kagi.com/birbilis
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
The SaveFileDialogWithEncoding example is great. However, the look and feel are "old style" -- in other words, the buttons and controls don't have the new "XP look" (i.e. rounded buttons, etc.). It's probably a flag setting in one of the fields in the OPENFILENAME structure and I'm looking into that. I was just wondering if you (or anyone else) had any insight to solving that problem.
Thanks for a great article and sample.
Regards,
Roy
_____________ Roy H. Berger roybrew@att.net
|
| Sign In·View Thread·PermaLink | 1.00/5 (1 vote) |
|
|
|
 |
|
|
Never mind -- figured it out. Before you instantiate the form object you need to call Application.EnableVisualStyles() like so:
[STAThread] static void Main() { Application.EnableVisualStyles(); Application.Run(new Form1()); }
Regards,
Roy
_____________ Roy H. Berger roybrew@att.net
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Hi This is chandu.Really this article is very cool.Can i know how to add some more controls like check box,text box etc..,to the dialog box
Thanks in advance
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Where are the control IDs defined? I'm trying to find the control id to aid in extending the print dialog.
http://www.thesimpsonsquotes.com/
|
| Sign In·View Thread·PermaLink | 2.00/5 (1 vote) |
|
|
|
 |
|
|
you could use WinSight maybe or similar tool
Microsoft MVP J# 2004-2007 Borland "Spirit of Delphi" QuickTime, ActiveX, VCL, .NET, Robotics http://www.kagi.com/birbilis http://birbilis.spaces.live.com
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
|
Yes,
Declare: [DllImport("user32.dll")] static extern int SetParent(int HWNDChild, int HWNDParent);
In WM_INITDIALOG, comment out code to CreateWindowEx for COMBO, add following:
//we create the new combobox _box = new ComboBox(); SetParent(_box.Handle.ToInt32(), parent); _box.Location = new Point(point.X, point.Y+8); _box.Size = new Size(aboveRect.Right - aboveRect.Left, 100); _box.Items.AddRange(new object[] { "UTF-8", "UTF-8 with preamble", "Unicode", "ANSI"}); _box.DropDownStyle = ComboBoxStyle.DropDownList; _box.SelectedIndex = 0;
In WM_DESTROY:
_box.Dispose();
In WM_NOTIFY:
m_EncodingType = (EncodingType)_box.SelectedIndex;
_________________ Stuart Carnie
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
I want to add a textbox rather than a combo, so I was excited when I saw Stuart's comment. For some reason, it won't draw my textbox until I mouse over the 87 pixels of the dialog where the TextBox should appear. After that, it does draw. I can tell it is the first 87 pixels because I made the TextBox longer and saw the dialog drew the control that extended beyond the first 87 pixels.
I tried adding _TextBox.Refresh to the WM_NOTIFY section. This immediately painted the client area of the TextBox, but not the border. The border was only drawn after I moused over the TextBox.
Here is the code I'm using to create the TextBox. Should there be any additional code that was not needed for the ComboBox?
private void _CreateTextBox( int parent, RECT aboveRect, POINT point ) { _TextBox = new TextBox(); SetParent( _TextBox.Handle.ToInt32(), parent );
_TextBox.Location = new Point( point.X, point.Y + 8 ); _TextBox.Size = new Size( aboveRect.Right - ( aboveRect.Left - 50 ), 100 ); }
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Ah! I figured it out. Stewarts code is fine, but the label added by the original code is too long. I cut the width of the label down from 200 to 90 and my TextBox works fine. I wonder if there is some kind of z-order we can set to draw the WinForms control above the label ...
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
|
 |
|
|
 |
|
|
as I created a personal folder for my project, and I want to access that folder more quickly by adding an item(or replace an existing one) on the left hand side NavBar, just wondering if there is any approach to achieve that, thanks
|
| Sign In·View Thread·PermaLink | 1.00/5 (1 vote) |
|
|
|
 |
|
|
 |
|
|
If I want to add controls to the top of the dialog box (instead of the bottom), do I need to use SetWindowPos() to shift the rest of the controls down (after of course resizing the dialog box accordingly)?
|
| Sign In·View Thread·PermaLink | 2.33/5 (3 votes) |
|
|
|
 |
|
|
General 
|