Results 1 to 4 of 4

Thread: Customising Common Dialogues

  1. #1

    Thread Starter
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    111,221

    Customising Common Dialogues

    C# version here.

    Someone posted a question recently asking how to set the initial location of a ColorDialog. I originally posted that it wasn't possible and you'd have to use the Windows API to get the window's handle and move it yourself. I recently discovered that that's not strictly true. You do have to work with unmanaged code but you can do it within a class derived from your common dialogue of choice. Here's an example that answers the aforementioned question:
    VB.NET Code:
    1. Imports System.Runtime.InteropServices
    2.  
    3. ''' <summary>
    4. ''' Extends the ColorDialog class and allows the initial location of the window to be specified.
    5. ''' </summary>
    6. Public Class ColorDialogEx
    7.     Inherits ColorDialog
    8.  
    9. #Region " Types "
    10.  
    11.     ''' <summary>
    12.     ''' Defines values that can be assigned to the Flags field of a WindowPos object.
    13.     ''' </summary>
    14.     <Flags()> _
    15.     Private Enum WindowPosFlags As UInteger
    16.         NoResize = &H1
    17.         NoMove = &H2
    18.         NoZOrder = &H4
    19.         NoRedraw = &H8
    20.         NoActivate = &H10
    21.         FrameChanged = &H20
    22.         ShowWindow = &H40
    23.         HideWindow = &H80
    24.         NoCopyBits = &H100
    25.         NoOwnerZOrder = &H200
    26.         NoSendChanging = &H400
    27.         DeferErase = &H2000
    28.         AsyncWindowPos = &H4000
    29.     End Enum
    30.  
    31.     ''' <summary>
    32.     ''' Received as the lParam value when a WM_WINDOWPOSCHANGING message is received.
    33.     ''' </summary>
    34.     Private Structure WindowPos
    35.         Public HWnd As IntPtr
    36.         Public HWndInsertAfter As IntPtr
    37.         Public X As Integer
    38.         Public Y As Integer
    39.         Public Cx As Integer
    40.         Public Cy As Integer
    41.         Public Flags As WindowPosFlags
    42.     End Structure
    43.  
    44. #End Region 'Types
    45.  
    46. #Region " Constants "
    47.  
    48.     ''' <summary>
    49.     ''' The message received when the window position is changing.
    50.     ''' </summary>
    51.     Private Const WM_WINDOWPOSCHANGING As Integer = &H46
    52.  
    53. #End Region 'Constants
    54.  
    55. #Region " Variables "
    56.  
    57.     ''' <summary>
    58.     ''' The location at which the window will be initially displayed.
    59.     ''' </summary>
    60.     ''' <remarks>
    61.     ''' If no value is assigned the window is displayed at the default location.
    62.     ''' </remarks>
    63.     Private initialLocation As Nullable(Of Point) = Nothing
    64.  
    65. #End Region 'Variables
    66.  
    67. #Region " Constructors "
    68.  
    69.     ''' <summary>
    70.     ''' Creates a new instance of the ColorDialogEx class.
    71.     ''' </summary>
    72.     Public Sub New()
    73.         MyBase.New()
    74.     End Sub
    75.  
    76.     ''' <summary>
    77.     ''' Creates a new instance of the ColorDialogEx class.
    78.     ''' </summary>
    79.     ''' <param name="initialLocation">
    80.     ''' The location at which the window will be initially dispalyed.
    81.     ''' </param>
    82.     Public Sub New(ByVal initialLocation As Point)
    83.         Me.New()
    84.         Me.initialLocation = initialLocation
    85.     End Sub
    86.  
    87. #End Region 'Constructors
    88.  
    89. #Region " Methods "
    90.  
    91.     ''' <summary>
    92.     ''' Hooks into the common dialog's message queue.
    93.     ''' </summary>
    94.     ''' <param name="hWnd">
    95.     ''' The handle to the native window.
    96.     ''' </param>
    97.     ''' <param name="msg">
    98.     ''' The message received from the operating system.
    99.     ''' </param>
    100.     ''' <param name="wparam">
    101.     ''' Message-specific data.
    102.     ''' </param>
    103.     ''' <param name="lparam">
    104.     ''' Message-specific data.
    105.     ''' </param>
    106.     ''' <returns>
    107.     ''' A zero value if the default dialog box procedure processes the message; a nonzero value if the default dialog box procedure ignores the message.
    108.     ''' </returns>
    109.     Protected Overrides Function HookProc(ByVal hWnd As System.IntPtr, _
    110.                                           ByVal msg As Integer, _
    111.                                           ByVal wparam As System.IntPtr, _
    112.                                           ByVal lparam As System.IntPtr) As System.IntPtr
    113.         'Trap the WM_WINDOWPOSCHANGING message if an initial location has been specified.
    114.         If msg = WM_WINDOWPOSCHANGING AndAlso Me.initialLocation.HasValue Then
    115.             'Get the data provided by the OS.
    116.             Dim pos As WindowPos = DirectCast(Marshal.PtrToStructure(lparam, _
    117.                                                                      GetType(WindowPos)),  _
    118.                                               WindowPos)
    119.  
    120.             'Look for the specific combination of flags that indicates that
    121.             'this is the appropriate message on which to set the location.
    122.             If pos.Flags = (WindowPosFlags.NoResize Or _
    123.                             WindowPosFlags.NoZOrder Or _
    124.                             WindowPosFlags.NoActivate) Then
    125.                 With Me.initialLocation.Value
    126.                     pos.X = .X
    127.                     pos.Y = .Y
    128.                 End With
    129.  
    130.                 'Make the new data available to unmanaged code.
    131.                 Marshal.StructureToPtr(pos, lparam, True)
    132.             End If
    133.         End If
    134.  
    135.         'Pass on the message.
    136.         Return MyBase.HookProc(hWnd, msg, wparam, lparam)
    137.     End Function
    138.  
    139. #End Region 'Methods
    140.  
    141. End Class
    Note that there were six WM_WINDOWPOSCHANGING messages received before the window was fully loaded and I found the correct combination of flags by trial and error. That combination is received on the second of those six messages. If you try to set the location on any of the others it doesn't work. Note that this will also not prevent you moving the dialogue by dragging the title bar.

    Adapting this idea you can trap any Windows message you want and then use the window's handle to manipulate it using unmanaged code. The HookProc method is a member of the CommonDialog class, so all classes derived from CommonDialog provide this functionality.

    One final point to note here is that i've used a Nullable(Of Point) to store the initial location. If it has no value then the default location is used. In .NET 1.x you don't have Nullable types so you'd have to use two variables: one Point and one Boolean.
    Last edited by jmcilhinney; Oct 29th, 2008 at 07:42 PM.
    Why is my data not saved to my database? | MSDN Data Walkthroughs
    VBForums Database Development FAQ
    My CodeBank Submissions: VB | C#
    My Blog: Data Among Multiple Forms (3 parts)
    Beginner Tutorials: VB | C# | SQL

  2. #2

    Thread Starter
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    111,221

    Re: Customising Common Dialogues

    I should also point out, for those who don't realise, that you don't ever explicitly call the HookProc method. It will get invoked implicitly each time the common dialogue receives a message from the OS. All you have to do is create the object and specify the location, e.g.
    vb.net Code:
    1. Using dialogue As New ColorDialogEx(New Point(10, 20))
    2.     If dialogue.ShowDialog() = Windows.Forms.DialogResult.OK Then
    3.         Dim selectedColour As Color = dialogue.Color
    4.  
    5.         'Use selectedColour here.
    6.     End If
    7. End Using
    Why is my data not saved to my database? | MSDN Data Walkthroughs
    VBForums Database Development FAQ
    My CodeBank Submissions: VB | C#
    My Blog: Data Among Multiple Forms (3 parts)
    Beginner Tutorials: VB | C# | SQL

  3. #3
    New Member
    Join Date
    Nov 2008
    Posts
    2

    Re: Customising Common Dialogues

    Excellent sample.
    Is there any way to apply this technique to OpenFileDialog, which is non-inheritable, or must I revert to Win32?

  4. #4

    Thread Starter
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    111,221

    Re: Customising Common Dialogues

    Quote Originally Posted by ljohnway
    Excellent sample.
    Is there any way to apply this technique to OpenFileDialog, which is non-inheritable, or must I revert to Win32?
    Hmmm... I didn't realise that OpenFileDialog was NotInheritable. The only thing that comes to mind is inheriting the FileDialog class and reproducing the functionality of the OpenFileDialog yourself. I'm not sure how easy or practical that is though.
    Why is my data not saved to my database? | MSDN Data Walkthroughs
    VBForums Database Development FAQ
    My CodeBank Submissions: VB | C#
    My Blog: Data Among Multiple Forms (3 parts)
    Beginner Tutorials: VB | C# | SQL

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width