I'm trying to create an Adorner that will follow the mouse during a drag & drop operation. It needs to do so even when the mouse is dragging over an element that has AllowDrop set to False.
Problems:
Normal mouse events (e.g. MouseMove) don't fire during Drag & Drop
Drag & Drop events (e.g. DragOver, GiveFeedback) only fire on valid drop targets (elements with AllowDrop set to true)
I need to track:
Where the mouse is
When the mouse moves
Without any of the above events, there's no easy way I can find to do this.
I've solved #1 by using the native GetCursorPos method. This can reliably get the position of the mouse whenever I want.
My remaining problem is getting notified when the mouse moves. Is there any way I can get mouse movement notifications during a drag & drop operation, even when dragging over elements with AllowDrop set to false?
Note: I don't want to use a timer and just continuously refresh the position (if I can help it), I'd much rather use actual mouse input.
Wow, I didn't expect this one to be so tough.
My first attempt was to try and bypass WPF and go straight to the native window message pump. But it turns out that even the standard WM_MOUSEMOVE message doesn't come through during a drag and drop operation. Digging deeper (through the ole2.dll source code), I discovered that a separate, invisible window gets created during the drag which eats up all the normal messages and instead interfaces with the drop targets directly (which is probably why the normal WPF mouse events don't fire in the first place).
I was worried that might be the end of it, until I discovered hooks, which let you get a hold of messages before they're consumed by the active window. Using the WH_MOUSE hook I was able to intercept the WM_MOUSEMOVE message and place my Adorner accordingly.
I'm not going to post all the code for the Adorner here, but I will give you the P/Invoke code I used to track the mouse:
Module NativeMethods
<DllImport("user32.dll")>
Public Function SetWindowsHookEx(ByVal idHook As HookType, ByVal lpfn As [Delegate], ByVal hInstance As IntPtr, ByVal threadId As Integer) As IntPtr
End Function
<DllImport("user32.dll")>
Public Function CallNextHookEx(ByVal hhk As IntPtr, ByVal nCode As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As IntPtr
End Function
<DllImport("user32.dll")>
Public Function UnhookWindowsHookEx(ByVal hhk As IntPtr) As Boolean
End Function
<StructLayout(LayoutKind.Sequential)>
Friend Structure Win32Point
Public X As Int32
Public Y As Int32
Public Shared Widening Operator CType(Point As Win32Point) As Drawing.Point
Return New Drawing.Point(Point.X, Point.Y)
End Operator
Public Shared Widening Operator CType(Point As Win32Point) As Windows.Point
Return New Windows.Point(Point.X, Point.Y)
End Operator
End Structure
Const WM_MOUSEMOVE As Integer = 512
Enum HookType As Integer
WH_JOURNALRECORD = 0
WH_JOURNALPLAYBACK = 1
WH_KEYBOARD = 2
WH_GETMESSAGE = 3
WH_CALLWNDPROC = 4
WH_CBT = 5
WH_SYSMSGFILTER = 6
WH_MOUSE = 7
WH_HARDWARE = 8
WH_DEBUG = 9
WH_SHELL = 10
WH_FOREGROUNDIDLE = 11
WH_CALLWNDPROCRET = 12
WH_KEYBOARD_LL = 13
WH_MOUSE_LL = 14
End Enum
Public Delegate Function HookProc(ByVal code As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As Integer
<StructLayout(LayoutKind.Sequential)>
Structure MOUSEHOOKSTRUCT
Public pt As Win32Point
Public hwnd As IntPtr
Public wHitTestCode As UInteger
Public dwExtraInfo As IntPtr
End Structure
End Module
Class MouseTracker
Private HookHandle As IntPtr
Private HookDelegate As New HookProc(AddressOf NativeHook)
Private Sub AddNativeHook()
#Disable Warning BC40000 ' Type or member is obsolete
HookHandle = SetWindowsHookEx(HookType.WH_MOUSE, HookDelegate, IntPtr.Zero, AppDomain.GetCurrentThreadId())
#Enable Warning BC40000 ' Type or member is obsolete
End Sub
Private Sub RemoveNativeHook()
UnhookWindowsHookEx(HookHandle)
End Sub
Private Function NativeHook(code As Integer, wParam As IntPtr, lParam As IntPtr) As Integer
If code >= 0 Then
If wParam = WM_MOUSEMOVE Then
Dim data = Marshal.PtrToStructure(Of MOUSEHOOKSTRUCT)(lParam)
'From here you can use Visual.PointFromScreen(data.pt) to get the coordinates of the mouse relative to any WPF Visual.
'Then you do whatever you want with that!
End If
End If
Return CallNextHookEx(IntPtr.Zero, code, wParam, lParam)
End Function
End Class
If you need more information, I heavily referenced:
pinvoke.net: https://pinvoke.net/default.aspx/user32/SetWindowsHookEx.html
Microsoft docs on hooks: https://learn.microsoft.com/en-us/windows/win32/winmsg/about-hooks
Related
The (deprecated) Microsoft Forms 2.0 controls include a combobox which provides an invaluable property: .TopIndex (documentation here).
It seems that this property is not available with standard comboboxes in forms in Microsoft Access 2019 (according to the documentation), and is not available with standard comboboxes in Windows Forms (.NET) (here, ComboBox inherits from ListControl and does not provide this property, while ListBox also inherits from ListControl, but provides it).
I have a lot of old code which heavily relies on the .TopIndex property, and it's time to move that code to other technologies.
So I'd like to know if I have missed something in the documentation and if there is an equivalent property with another name which I could use to determine which items are visible in the list part of a combobox. I'd like to know this for comboboxes in Access 2019 (I am not that hostile towards this application as many others here are) as well as for comboboxes in Windows Forms.
I am aware that there are a lot of free and commercial controls (including comboboxes) with enhanced functionality for Windows Forms. I will definitely go that way (or write my own) unless I have missed something in the documentation.
However, the situation is completely different when it comes to Access 2019 forms. I could not find a single free third-party ActiveX / COM combobox which I could use on Access forms and which provides this functionality. Theoretically, I probably could write an ActiveX / COM control using .NET and then use it on Access 2019 forms, but this seems quite painful.
As far as .Net WinForm ComboBox goes, you have not missed anything as the functionality of the TopIndex property is not implemented. That said, it is pretty straight forward to extend the base ComboBox control to add this property. The following example control should get you started.
This control attaches a listener to the native ListBox dropdown and updates the TopIndex property on WM_VSCROLL and the LB_SETCARETINDEX (this captures the initial position on opening) messages. Additionally, the base SelectedIndexChange event is used to capture changes due to keyboard actions (pgUp/pgDn, Arrow up/down). The TopIndex property is retained after the dropdown closes and is reset on opening the dropdown. The control also exposes a TopIndexChanged event.
Imports System.Runtime.InteropServices
Public Class ComboBoxEx : Inherits ComboBox
Private listBoxListener As ListBoxNativeWindow
Public Event TopIndexChanged As EventHandler(Of ComboBoxTopIndexArg)
Private _TopIndex As Int32 = -1
Public Sub New()
MyBase.New
listBoxListener = New ListBoxNativeWindow(Me)
End Sub
Public Property TopIndex As Int32
Get
Return _TopIndex
End Get
Private Set(value As Int32)
If value <> _TopIndex Then
_TopIndex = value
RaiseEvent TopIndexChanged(Me, New ComboBoxTopIndexArg(value))
End If
End Set
End Property
Protected Overrides Sub OnDropDown(e As EventArgs)
_TopIndex = -1 ' reset on opening the listbox
MyBase.OnDropDown(e)
End Sub
Private Class ListBoxNativeWindow : Inherits NativeWindow
Private listBoxHandle As IntPtr
Private TopIndex As Int32
Private parent As ComboBoxEx
Public Sub New(ByVal parent As ComboBoxEx)
Me.parent = parent
WireParent()
If parent.IsHandleCreated Then
GetListBoxHandle()
AssignHandle(listBoxHandle)
End If
End Sub
Private Sub WireParent()
AddHandler parent.HandleCreated, AddressOf Me.OnHandleCreated
AddHandler parent.HandleDestroyed, AddressOf Me.OnHandleDestroyed
AddHandler parent.SelectedIndexChanged, AddressOf UpdateTopIndexOnIndexChanged
End Sub
Private Sub OnHandleCreated(ByVal sender As Object, ByVal e As EventArgs)
GetListBoxHandle()
AssignHandle(listBoxHandle)
End Sub
Private Sub OnHandleDestroyed(ByVal sender As Object, ByVal e As EventArgs)
ReleaseHandle()
End Sub
Private Sub UpdateTopIndexOnIndexChanged(sender As Object, e As EventArgs)
SetParentTopIndex()
End Sub
Private Sub GetListBoxHandle()
Const CB_GETCOMBOBOXINFO As Int32 = &H164
Dim info As New ComboBoxInfo
info.cbSize = Marshal.SizeOf(info)
Dim res As Boolean = SendMessage(Me.parent.Handle, CB_GETCOMBOBOXINFO, Nothing, info)
listBoxHandle = info.hwndList
End Sub
Protected Overrides Sub WndProc(ByRef m As Message)
Const WM_VSCROLL As Int32 = &H115
Const LB_SETCARETINDEX As Int32 = &H19E
MyBase.WndProc(m)
If m.Msg = WM_VSCROLL OrElse m.Msg = LB_SETCARETINDEX Then
SetParentTopIndex()
End If
End Sub
Private Sub SetParentTopIndex()
Const LB_GETTOPINDEX As Int32 = &H18E
parent.TopIndex = SendMessage(listBoxHandle, LB_GETTOPINDEX, IntPtr.Zero, IntPtr.Zero)
End Sub
End Class
Public Class ComboBoxTopIndexArg : Inherits EventArgs
Public Sub New(topIndex As Int32)
Me.TopIndex = topIndex
End Sub
Public ReadOnly Property TopIndex As Int32
End Class
#Region "NativeMethods"
<StructLayout(LayoutKind.Sequential)>
Private Structure ComboBoxInfo
Public cbSize As Int32
Public rcItem As RECT
Public rcButton As RECT
Public stateButton As IntPtr
Public hwndCombo As IntPtr
Public hwndEdit As IntPtr
Public hwndList As IntPtr
End Structure
<StructLayout(LayoutKind.Sequential)>
Private Structure RECT
Public Left, Top, Right, Bottom As Int32
End Structure
<DllImport("user32.dll")>
Private Shared Function SendMessage(hWnd As IntPtr, Msg As Int32, wParam As IntPtr, <Out()> ByRef lParam As ComboBoxInfo) As Boolean
End Function
<DllImport("user32.dll")>
Private Shared Function SendMessage(hWnd As IntPtr, Msg As Int32, wParam As IntPtr, lParam As IntPtr) As Int32
End Function
#End Region
End Class
I leave it to you to wrap this in an ActiveX exposed wrapper for use in Access. Doing so is fairly easy using the templates in the Microsoft InteropForms Toolkit 2.1. Just note that those templates are setup using the "Any CPU" platform and you will need to change that to "x86".
This answer shows how to use a cuebanner with a textbox. The content for the label is for each textbox the same. A comment on that answer states that you can use attached properties to increase style re-usability and includes a link to C# code.
I'm able to write/read C# but unfortunately I don't understand what the code does. I also have done some searching for attached properties but I really don't get how I can change the displayed text so it reusable.
Could anyone give me an example how I do this in VB.NET or link me some sites that explain how it's done?
This will give you watermark text on your textbox controls:
Imports:
Imports System.Runtime.InteropServices
Global Declarations in your main class:
Private Const EM_SETCUEBANNER As Integer = &H1501
<DllImport("user32.dll", CharSet:=CharSet.Auto)> _
Private Shared Function SendMessage(ByVal hWnd As IntPtr, ByVal msg As Integer, ByVal wParam As Integer, <MarshalAs(UnmanagedType.LPWStr)> ByVal lParam As String) As Int32
End Function
Functions in your main class:
Private Sub SetCueText(ByVal control As Control, ByVal text As String)
SendMessage(control.Handle, EM_SETCUEBANNER, 0, text)
End Sub
Usage (usually in form_load event):
SetCueText(TextBox1, "blah")
SetCueText(TextBox2, "blahblah")
Is this what you meant?
Hope this helps :)
I'm updating a VB.net project that needs to have cue banners added to text boxes and read-only comboboxes (DropDownStyle=DropDownList). The machine I'm developing on is Windows 7. I'm adding the cue text in a class that extends a combobox and adds a cue text property. This is how the cue text is added to the combobox:
'"Me" refers to a combobox that has been extended to include a Cue Text property
SendMessage(New HandleRef(Me, Me.Handle), CB_SETCUEBANNER, IntPtr.Zero, _cueText)
The above code is from this blog bost: http://www.aaronlerch.com/blog/page/7/, which is in C#; I translated it to VB. I tried other similar variations that I found elsewhere, all with the same result: it works great for text boxes and comboboxes when I run the program in Windows 7; it only works for text boxes in Windows XP.
I've read lots of comments on different forums about making sure visual styles are selected and disabling east Asia languages and complex scripts. I've done all that, but still haven't gotten it to work on XP.
Has anyone gotten cue banners for comboboxes to work on XP?
Using various blog and forum posts, I created a class that extends the ComboBox control and implements a CueText property that works on Windows 7 and XP. I found the most relevant pieces of information here:
How to set cue text in XP: http://www.ageektrapped.com/blog/the-missing-net-1-cue-banners-in-windows-forms-em_setcuebanner-text-prompt/
How to set cue text in Windows 7: http://www.aaronlerch.com/blog/2007/12/01/watermarked-edit-controls/
In a nutshell, Windows 7 and XP set the cue banner text slightly differently, so you need to check which operating system the program is running on and then handle the cue text appropriately. You need to use EM_SETCUEBANNER As Integer = &H1501 for XP and CB_SETCUEBANNER As UInteger = &H1703 for Windows 7. You also need to single out the text part of the combo box if the app is running on XP. You can see the details in the code below. To figure out which OS is running, see MS KB articles 304289 (VB) or 304283 (C#). (I would post the links, but I don't have enough reputation points to post more than two.)
One caveat is that this will not work on XP if the combo boxes are read only (DropDownStyle = DropDownList). Windows 7 seems to work fine either way. If your application needs to run on XP and you need the combo boxes to be read only but still display the cue text, here's what you can do:
Create a combo box and use the default DropDownStyle, "DropDown"
Handle the KeyPress event for the combo box(es) and in that method tell it that the event has been handled like this: e.Handled = True. This will prevent text from being typed.
Create a blank ContextMenuStrip using the Toolbox in the design view, click on the combo box, view its properties, and set its ContextMenuStrip to the one you just created. This will cause nothing to happen when the user right clicks in the combo box so they can't paste text into it.
Here's the VB code for a class that inherits the ComboBox control and adds a CueText property that works for XP and 7. The only thing you'll have to do is figure out which OS is running:
Imports System.ComponentModel
Imports System.Runtime.InteropServices
Public Class CueComboBox
Inherits ComboBox
' Occurs when the CueText property value changes.
Public Event CueTextChanged As EventHandler
'Windows XP
Private Shared EM_SETCUEBANNER As Integer = &H1501
'Windows 7
Private Shared CB_SETCUEBANNER As UInteger = &H1703
<DllImport("user32.dll", CharSet:=CharSet.Auto)> _
Private Shared Function SendMessage(ByVal hWnd As IntPtr, ByVal msg As Integer, ByVal wParam As Integer, <MarshalAs(UnmanagedType.LPWStr)> ByVal lParam As String) As Int32
End Function
<DllImport("user32.dll")> _
Private Shared Function GetComboBoxInfo(ByVal hwnd As IntPtr, ByRef pcbi As COMBOBOXINFO) As Boolean
End Function
<StructLayout(LayoutKind.Sequential)> _
Private Structure COMBOBOXINFO
Public cbSize As Integer
Public rcItem As RECT
Public rcButton As RECT
Public stateButton As IntPtr
Public hwndCombo As IntPtr
Public hwndItem As IntPtr
Public hwndList As IntPtr
End Structure
<StructLayout(LayoutKind.Sequential)> _
Private Structure RECT
Public left As Integer
Public top As Integer
Public right As Integer
Public bottom As Integer
End Structure
Private Shared Function GetComboBoxInfo(ByVal control As Control) As COMBOBOXINFO
Dim info As New COMBOBOXINFO()
'a combobox is made up of three controls, a button, a list and textbox;
'we want the textbox
info.cbSize = Marshal.SizeOf(info)
GetComboBoxInfo(control.Handle, info)
Return info
End Function
Private _cueText As String = [String].Empty
' Gets or sets the text that will display as a cue to the user.
<Description("The text value to be displayed as a cue to the user.")> _
<Category("Appearance")> <DefaultValue("")> <Localizable(True)> _
Public Property CueText() As String
Get
Return _cueText
End Get
Set(ByVal value As String)
If value Is Nothing Then
value = [String].Empty
End If
If Not _cueText.Equals(value, StringComparison.CurrentCulture) Then
_cueText = value
UpdateCue()
OnCueTextChanged(EventArgs.Empty)
End If
End Set
End Property
<EditorBrowsable(EditorBrowsableState.Advanced)> _
Protected Overridable Sub OnCueTextChanged(ByVal e As EventArgs)
RaiseEvent CueTextChanged(Me, e)
End Sub
Protected Overrides Sub OnHandleCreated(ByVal e As EventArgs)
UpdateCue()
MyBase.OnHandleCreated(e)
End Sub
Private Sub UpdateCue()
' If the handle isn't yet created, this will be called when it is created
If Me.IsHandleCreated Then
' Windows XP sets the cue banner differently than Windows 7
If Form1.OPERATING_SYSTEM = "Windows XP" Then
Dim info As COMBOBOXINFO = GetComboBoxInfo(Me)
SendMessage(info.hwndItem, EM_SETCUEBANNER, 0, _cueText)
Else
SendMessage(New HandleRef(Me, Me.Handle), CB_SETCUEBANNER, IntPtr.Zero, _cueText)
End If
End If
End Sub
End Class
I need only to show a custom control (a clock with rotating hands) and with this to replace the mouse cursor, if I use a file .cur or .ani to replace the mouse cursor
Me.CUrsor = New Cursor("absolute path of the .ani file")
there is no problem: I can change the cursor during a procedure: but the quality of the animation is very bad, and, also for other reasons, I'd prefer to use my little user-control. The problem is that if I write:
Me.gridScreen.Visibility = Visibility.Visible
' some operations that takes about 1 second
Me.gridScreen.Visibility = Visibility.Hidden
(gridScreen is the grid that contains the user-control)
Obviously I can see nothing, because the update of the UI happens at the end of the procedure. I have tried Me.UpdateLayout(), but it doesn't work.
I have tryed to use the dispacker in many way but none that works :-(
This is my lost attempt:
(uCurClock is the usercontrol, gridScreen a Grid placed at the top-level in the window, with trasparent background, that contains the usercontrol)
Private Sub showClock()G
Dim thread = New System.Threading.Thread(AddressOf showClockIntermediate)
thread.Start()
End Sub
Private Sub hideClock()
Dim thread = New System.Threading.Thread(AddressOf hideClockIntermediate)
thread.Start()
End Sub
Private Sub showClockIntermediate()
Me.Dispatcher.BeginInvoke(DispatcherPriority.Normal, New Action(AddressOf showClockFinale))
End Sub
Private Sub hideClockIntermediate()
Me.Dispatcher.BeginInvoke(DispatcherPriority.Normal, New Action(AddressOf hideClockFinale))
End Sub
Private Sub showClockFinale()
Dim pt As Point = Mouse.GetPosition(Nothing)
Me.uCurClock.Margin = New Thickness(pt.X - 9, pt.Y - 9, 0, 0)
Me.gridScreen.Visibility = Visibility.Visible
Me.Cursor = Cursors.None
Me.UpdateLayout()
End Sub
Private Sub hideClockFinale()
Me.gridScreen.Visibility = Visibility.Hidden
Me.Cursor = Cursors.Arrow
Me.UpdateLayout()
End Sub
Private Sub u_MouseMove(ByVal sender As System.Object, ByVal e As MouseEventArgs) Handles gridScreen.MouseMove
Dim pt As Point = e.GetPosition(Nothing)
Me.uCurClock.Margin = New Thickness(pt.X - 9, pt.Y - 9, 0, 0)
e.Handled = True
End Sub
Private Sub u_MouseEnter(ByVal sender As System.Object, ByVal e As MouseEventArgs) Handles gridScreen.MouseEnter
Me.uCurClock.Visibility = Visibility.Visible
e.Handled = True
End Sub
Private Sub u_MouseLeave(ByVal sender As System.Object, ByVal e As MouseEventArgs) Handles gridScreen.MouseLeave
Me.uCurClock.Visibility = Visibility.Hidden
e.Handled = True
End Sub
PIleggi
While the following code will do what you ask for, I suspect it won't actually help you, since you've mentioned animation. You're going to need to use multiple threads. However, just to demonstrate why that is, here's something that answers the question you've asked:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles Button1.Click
uc1.Visibility = Visibility.Visible
Cursor = Cursors.Wait
' Push visibility changes now.
' (Sort of like DoEvents - and a horrible idea for exactly the same
' reasons that DoEvents was a total train wreck. Never actually do
' this - use a background thread instead.)
Dim df As New DispatcherFrame(True)
Dispatcher.BeginInvoke(Sub() df.Continue = False, DispatcherPriority.ContextIdle)
Dispatcher.PushFrame(df)
Thread.Sleep(1000)
ClearValue(CursorProperty)
uc1.Visibility = Visibility.Hidden
End Sub
Assuming you have some usercontrol called uc1 on the page, this will force it to be visible while your slow procedure runs.
But no animations will run. The problem is, if you're doing something slow on the UI thread, the UI thread can't do anything else - it can't run animations, it can't respond to user input. Basically the UI is frozen out. The only reason the code shown here even makes the user control visible is that it basically says "do any outstanding UI thread work now", which has the side effect of processing your change to the Visible property.
But animations happen on the UI thread too.
If you want to do this properly, you need to do the work on a background thread, possibly by using the BackgroundWorker, or by writing your own threading code.
reference DispatcherFrame Class Reference
good ole DoEvents for WPF!!!
Public Sub DoEvents()
Dim frame As New DispatcherFrame()
Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background, New DispatcherOperationCallback(AddressOf ExitFrame), frame)
Dispatcher.PushFrame(frame)
End Sub
Public Function ExitFrame(ByVal f As Object) As Object
CType(f, DispatcherFrame).Continue = False
Return Nothing
End Function
Maybe I'm missing something, but...
The ListView control in Windows 7 displays a highlight around selected items that looks like a 3D blue translucent rectangle (I'm not talking about the selection rectangle, but the rectangle around the actual selected items). It even shows a lighter rectangle when hovering over items.
However, when I use the ListView in WinForms (even when double-buffered), the selected items just have a plain blue background (and no hover background) which looks much less professional than, say, the list in Explorer.
Does anyone know what secret API function I should call to make the .NET ListView look in line with the rest of the OS?
For example, here is one of my applications written in C++, using a standard ListView control in Windows 7: (notice the highlight and hover rectangle)
And here is a rewrite of that application in C# with WinForms: (notice the crude highlight and no hover)
OK, I totally figured it out, and this may help others who are bothered by this issue.
I began by noticing that the ListView control in C++Builder looks "correct" under Windows 7, so I looked in the source code for the VCL to see what kind of magic they're doing to make the ListView look like the list control in Windows Explorer. I stumbled on one line of code that looked promising:
SetWindowTheme(Handle, 'explorer', nil);
From the SDK documentation, this function "Causes a window to use a different set of visual style information than its class normally uses."
So, I tried invoking this function on my WinForms ListView control:
[DllImport("uxtheme.dll", CharSet = CharSet.Unicode)]
public static extern int SetWindowTheme(IntPtr hWnd, String pszSubAppName, String pszSubIdList);
SetWindowTheme(myListView.Handle, "explorer", null);
...and, by god, it worked! The ListView finally looks like it belongs with the rest of the OS! Thanks, Borland Inprise Embarcadero! You really are good for something!
Imports System.Runtime.InteropServices
Public Class Form1
<DllImport("uxtheme", CharSet:=CharSet.Unicode)> _
Public Shared Function SetWindowTheme(ByVal hWnd As IntPtr, ByVal textSubAppName As String, ByVal textSubIdList As String) As Integer
End Function
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
SetWindowTheme(lst.Handle, "explorer", Nothing)
End Sub
End Class
The above code will work like a champ...
edit: now it is working for me too, the exact signature is:
<DllImport("uxtheme.dll",
BestFitMapping:=False,
CharSet:=CharSet.Unicode,
EntryPoint:="#136",
CallingConvention:=CallingConvention.Winapi)>
Private Shared Function SetWindowsTheme(ByVal handle As IntPtr, ByVal app As String, ByVal id As String) As Integer
' Leave function empty - DLLImport attribute forwards calls to the right function
End Function
Public Shared Sub MakeControlLookBeautiful(ByVal c As Windows.Forms.Control)
SetWindowsTheme(c.Handle, "explorer", Nothing)
End Sub
:)