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
:)
Related
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 have a very basic question on vb.NET, so basic that I didn't find any answer elsewhere.
I've written with VB express a code that is a simple form proposing various choices through checkboxes.
These choices have to be registered in an array, which I convert later into a textfile to be Perl-processed.
I'm searching a way to zeroise this big array with loops before use, but in fact I don't know how to execute instructions which wouldn't be triggered by events in my main form.
The frame looks like that :
Public Class Form1
'Variables declaration...
'Several boxes like that :
Private Sub CheckBox1_CheckedChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles CheckBox1.CheckedChanged
'Instructions...
End Sub
End Class
To launch this application, I simply click on the associated .exe file.
Basically, my question is :
is there a way to execute instructions that wouldn't be launched by an user event, but immediatly launched when the main Form1 is shown ?
Sorry for being retarded, and thank you in advance if you can help me.
The Form.Load event for instance gets launched as soon as the form is about to be shown :)
The Form will raise a Shown event when it is shown simply handle that:
Private Sub Form_Shown(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Me.Shown
'Instructions...
End Sub
It can be better to do initialisation in the constructor right after the Winforms designer adds the comment: 'Add any initialization after the InitializeComponent() call
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'm just starting to learn VB and Visual Studio and I've run across a problem. I've spent the best part of a day trying to find the answer and I have a horrible feeling that it's going to be something very simple that I've over looked.
I'm working on a WPF in Visual Studio 2010 and am trying to dynamically create a button on the main window when a button is clicked (I know, everything I've read tells me this is pretty basic!) Here's an edited snippet of the code I've written:
Imports System.Data.OleDb
Imports System.Windows.Forms
Imports Excel = Microsoft.Office.Interop.Excel
Class MainWindow
Private Sub Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles edit.Click
...
Dim newButton As New Button
newButton.Text = "New Button"
newButton.Top = 200
newButton.Left = 20
Me.Controls.Add(newButton)
...
End Sub
To my eyes, this looks perfectly simple and correct, but I'm getting an error:
"'Controls' is not a member of 'myApp.MainWindow'."
Has anybody come across this before or know what the problem is? Apologies if this does turn out to be a simple fix :)
The error you are getting is telling you that Controls does not exist within MainWindow. Basically, there is not property by that name accessible from your event handler. If you are working with WPF and MainWindow inherits Window, then you need to set something within the Content property.
The best way to go about this, would be to have some form of container control as the content of the window. You can define this in XAML or in code (via code you should set the Window.Content property). Then, you can add more controls to that container. Suggested containers are Grid, Canvas and StackPanel, etc.
I would suggest something like this:
XAML
<MainWindow ...>
<StackPanel x:Name="ControlContainer">
<Button Content="Click me to create buttons!" Click="CreateButton_Click" />
</StackPanel>
</MainWindow>
Code Behind
Private Sub CreateButton_Click(ByVal sender As Object, ByVal e As EventArgs)
Dim button As New Button()
' Initialize the button
' ...
' Add the button to the stack panel
Me.ControlContainer.Children.Add(button)
End Sub
well MainWindow is not a form. create a new project and copy the same code in a button it will work.
you must find out what is the problem with your form control.
It looks like you are mixing WinForms and WPF coding - these are two different technologies.
This link may help you to add a button at runtime in WPF
I'm pondering taking the plunge to WPF from WinForms for some of my apps, currently I'm working on the combined barcode-reader/text-entry program (healthcare patient forms).
To be able to process the barcode characters, I rely on the Keypreview property in WinForms (because barcodes can be scanned regardless of what control has the focus).
But I cannot seem to find a KeyPreview property in neither VS2008 or VS2010, for a WPF app.
Is there an alternative approach/solution to handle my barcode characters in WPF?
Rgrds Henry
use the override in your own UserControls or Controls (this is an override from UIElement)
protected override void OnPreviewKeyDown(System.Windows.Input.KeyEventArgs e) {
base.OnPreviewKeyDown(e);
}
if you want to preview the key down on any element which you dont create you can do this:
Label label = new Label();
label.PreviewKeyDown += new KeyEventHandler(label_PreviewKeyDown);
and then have a handler like so :-
void label_PreviewKeyDown(object sender, KeyEventArgs e) {
}
if you mark the event as handled (e.Handled = true;) this will stop the KeyDown event being raised.
Thanks got it working! Only problem was I'm coding in VB not C#, but the basic idea holds. Neat to create a label out of thin air and use it to insert yourself in the event stream.
If someone else is interested of the same solution but in VB for WPF, here's my test program, it manages to toss all 'a' characters typed, no matter what control has the focus:
Class MainWindow
Dim WithEvents labelFromThinAir As Label
Private Sub Window_Loaded(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles MyBase.Loaded
AddHandler MainWindow.PreviewKeyDown, AddressOf labelFromThinAir_PreviewKeyDown
End Sub
Private Sub labelFromThinAir_PreviewKeyDown(ByVal sender As Object, ByVal e As KeyEventArgs)
TextBox1.Text = e.Key ' watch 'em coming
If (44 = e.Key) Then e.Handled = True
End Sub
End Class
P.S. This was my first post on stackoverflow, really a useful site. Perhaps I'll be able to answer some questions in here myself later on :-)
WPF uses event bubbling and tunneling. In other words the events travel down and up the visual element tree. Some events will have a corresponding Preview event. So MouseDown will have a PreviewMouseDown that you can respond to. Check out this link and scroll down to the WPF Input Events section.