Aero Effects on User-drawn TabControl - vb.net

In my app, I would like to have my tabcontrol have an aero effect on the header. This works with the normal winforms tabcontrol using this code:
Imports System.Runtime.InteropServices
<DllImport("dwmapi.dll")> _
Private Shared Function DwmExtendFrameIntoClientArea(ByVal hwnd As IntPtr, ByRef pMarInset As MARGINS) As Integer
End Function
<StructLayout(LayoutKind.Sequential)> _
Private Structure MARGINS
Public cxLeftWidth As Integer
Public cxRightWidth As Integer
Public cyTopHeight As Integer
Public cyBottomHeight As Integer
End Structure
Problem is, I have a custom tabcontrol class (the actual one is much larger and has a much more complicated OnPaint() override than below)
Public Class CustomTab
Inherits TabControl
Public Sub New()
SetStyle(ControlStyles.UserPaint or ControlStyles.ResizeRedraw,True)
End Sub
Protected Overrides Sub OnPaint(e as PaintEventArgs)
Dim g as Graphics = e.Graphics
Dim r as Rectangle
For i=0 to TabCount - 1
If i = SelectedIndex
r = GetTabRect(i)
g.FillRectangle(New SolidBrush(My.Settings.TabColor),r)
Else
r = GetTabRect(i)
g.FillRectangle(New SolidBrush(SystemColors.Control),r)
End If
Next
End Sub
End Class
When I try the glass effects with this, it does not show in the header like normal.
Does anyone know why the Userdrawn tabcontrol will not have the transparent header?
Working (Normal tabcontrol):
Not working (my tabcontrol):

Never Mind- Found the Answer. In the OnPaint() Sub, write
g.Clear(Color.Black)
this paints the background black, so the glass effects show

Related

wordwrap does not work on datagridtextboxcolumn in VB.net

I have looked at previous answers on this blog which dont seem to help me.
I have a datagrid and add columns that are datagridtextboxcolumn . When I click on a cell on datagrid - if the line is too big for the width of cell , it will display on the following line, but when I am not clicked on the cell, the end of the line of text will not be displayed on the next line - and is therefore not displayed.
I derived a new class from datagridtextboxcolumn and attempted to override the paint and painttext methods and this appeared to have no effect - the text is still displayed in the column and only 1 line is displayed.
My code is
Here is the derived class: -
( below I have overridden the paint and painttext class in order to see which method has some effect on the display in the datagrid - but there is no effect occuring through this process of overriding.
enter code here
Imports Microsoft.VisualBasic
Imports System.ComponentModel
Imports System.Data
Imports System.Data.Common
Imports System.Data.OleDb
Imports System.Drawing
Imports System.Windows.Forms
Namespace DataGridRichTextBox
Public Class DataGridRichTextBoxColumn
Inherits DataGridTextBoxColumn
Private _source As CurrencyManager
Private _rowNum As Integer
Private _isEditing As Boolean
Public Sub New()
_source = Nothing
_isEditing = False
End Sub 'New
Protected Overloads Sub PaintText(ByRef g As Graphics, ByVal bounds As System.Drawing.Rectangle, ByRef Text As String, ByVal alligntoright As Boolean)
End Sub
Protected Overloads Sub PaintText(ByRef g As Graphics, ByVal bounds As System.Drawing.Rectangle, ByRef Text As String, ByRef s1 As System.Drawing.Brush, ByRef s2 As System.Drawing.Brush, ByVal alligntoright As Boolean)
End Sub
Protected Overloads Overrides Sub Paint(ByVal g As Graphics, ByVal bounds As System.Drawing.Rectangle, ByVal _source As CurrencyManager, ByVal num As Integer)
End Sub
Protected Overloads Overrides Sub SetColumnValueatrow(ByVal _source As CurrencyManager, ByVal num As Integer, ByVal obj As Object)
End Sub
End Class 'DataGridComboBoxColumn
End Namespace
'Here is where I add the derived class as an object to the datagrid : -
Imports System.Windows.Forms
Imports System.Data.SqlClient
Imports System.Drawing
Public Class DataGridMine
Inherits DataGrid
Public r_counter, column_num, x1 As Integer
Public x13 As Integer
#Region " Windows Form Designer generated code "
Public Sub New()
MyBase.New()
'This call is required by the Windows Form Designer.
InitializeComponent()
End Sub
'Form overrides dispose to clean up the component list.
Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
If disposing Then
If Not (components Is Nothing) Then
components.Dispose()
End If
End If
MyBase.Dispose(disposing)
End Sub
'Required by the Windows Form Designer
Private components As System.ComponentModel.IContainer
Friend WithEvents SqlSelectCommand2 As New SqlCommand
Friend WithEvents DataSet51 As New tasks_trial2.DataSet5
Public WithEvents DataGridTableStyle1 As New DataGridTableStyle
Public WithEvents task_name_col, parent_col As New DataGridTextBoxColumn
Public WithEvents description_col As New DataGridRichTextBox.DataGridRichTextBoxColumn
Friend WithEvents SqlDataAdapter3 As New SqlDataAdapter
Friend WithEvents SqlDataAdapter2 As New SqlDataAdapter
Friend WithEvents SqlSelectCommand3 As New SqlCommand
<System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
Dim resources As System.Resources.ResourceManager = New System.Resources.ResourceManager(GetType(Form1))
CType(Priority_code_table1, System.ComponentModel.ISupportInitialize).BeginInit()
SuspendLayout()
Me.DataMember = "tasks"
Me.DataSource = DataSet51
Me.Location = New Point(8, 230)
Me.Size = New Size(1117, 384)
Me.TabIndex = 0
Me.TableStyles.AddRange(New DataGridTableStyle() {DataGridTableStyle1})
SqlDataAdapter2.SelectCommand = SqlSelectCommand2
SqlDataAdapter2.TableMappings.AddRange(New System.Data.Common.DataTableMapping() {New System.Data.Common.DataTableMapping("Table", "tasks")})
SqlSelectCommand2.CommandText = ""
SqlSelectCommand2.Connection = SqlConnection10
DataGridTableStyle1.DataGrid = Me
DataGridTableStyle1.AllowSorting = False
column_num = 3
DataGridTableStyle1.HeaderForeColor = SystemColors.ControlText
DataGridTableStyle1.MappingName = "tasks"
DataGridTableStyle1.SelectionBackColor = Color.Aquamarine
DataGridTableStyle1.SelectionForeColor = Color.Black
DataGridTableStyle1.PreferredRowHeight = 10
DataGridTableStyle1.PreferredColumnWidth = 75
description_col.HeaderText = "Description"
description_col.MappingName = "description"
description_col.Width = 260
'.....................
' where column is description_col.
Public Sub add_columns(ByRef dgrid1 As DataGridMine, ByVal column As Object)
dgrid1.DataGridTableStyle1.GridColumnStyles.AddRange(New DataGridColumnStyle() {column})
End Sub
you don't have to do all that if you use a DataGridViewTextBoxColumn
just have to set some option via code or properties menu
go to your Columns listing and select the Column you want the text to be wrapped up and not cutt off.
go to DefaulCellStyle and set
WrapMode = True
additionally you could set this on the DGV Property Menu
AutoSizeColumnsMode to Fill
and
AutoSizeRowsMode to AllCells
that should do the Trick
this is all set in Properties Menu but you can do this in Code too so your choice

Highlighting around textboxes

I am trying to draw a highlighted border around a custom textbox control so that I can reuse the highlighting feature for each new program I create. My approach so far has been to override the paint event in the control library (dll) after the custom property I have created is set. The code for the control is below.
Imports System.Windows.Forms
Imports System.ComponentModel
Imports System.Drawing
Imports System.ComponentModel.Design
<ToolboxBitmap(GetType(Button))>
Public Class Textbox_Custom
Inherits System.Windows.Forms.TextBox
Public Event OnEnterKeyPress()
Public Event MissingInfo_Change As EventHandler
Dim iMissing_Info As Boolean
Dim iCharacterInput As Cinput
Public Property CharacterInput As Cinput
'<Browsable(True), DefaultValue("AllowAll")>
Get
Return Me.iCharacterInput
End Get
Set(ByVal value As Cinput)
Me.iCharacterInput = value
End Set
End Property
Public Property Missing_Info As Boolean
'<Browsable(True), DefaultValue(True)>
Get
Return iMissing_Info
End Get
Set(value As Boolean)
iMissing_Info = value
**MyBase.Refresh()**
End Set
End Property
Protected Overrides Sub OnKeyPress(e As KeyPressEventArgs)
MyBase.OnKeyPress(e)
If Asc(e.KeyChar) = 13 Then
RaiseEvent OnEnterKeyPress()
End If
Select Case Me.iCharacterInput
Case Cinput.CharactersOnly
If IsNumeric(e.KeyChar) Then
e.Handled = True
End If
Case Cinput.NumericOnly
If Not IsNumeric(e.KeyChar) And Asc(e.KeyChar) <> 8 Then
e.Handled = True
End If
End Select
End Sub
Protected Overrides Sub OnPaint(e As PaintEventArgs)
MyBase.OnPaint(e)
**If iMissing_Info = True Then**
Dim rect As New Rectangle(New Point(0, 0), New Size(Me.Size.Width + 2, Me.Size.Height + 2))
Dim pen As New Pen(Brushes.OrangeRed, 2)
e.Graphics.DrawRectangle(pen, rect)
e.Dispose()
End If
End Sub
End Class
Public Enum Cinput
AllowAll
NumericOnly
CharactersOnly
End Enum
While debugging I have set a breakpoint in the OnPaint override (lines **), but it never hits it. I then put a breakpoint in the Set section of the Missing_Info property where I am trying to invalidate the control to redraw. It does hit the MyBase.Refresh breakpoint so I don't understand what I've missed.
I realize there have been several other posts on this topic, but from what I can tell they seem to require putting panels behind the control. I feel like I should be able to include this action in a custom control and not have to code a new highlighting section for each new project. Thanks for any help in advance.
In the end I decided to just go with changing the control background to a semi-transparent red color which should be obvious enough for what I'm doing.

vb.net gamepad support, partially working

I found the following class code on a forum. It works great for the gamepad (up, down, left, right) however all the code for the buttons is missing. Can anyone fill in the blanks?
This works:
Private Sub joystick1_Up() Handles joystick1.Up
moveUp()
End Sub
This does not:
Private Sub joystick1_buttonPressed() Handles joystick1.buttonPressed
MsgBox(joystick1.btnValue)
End Sub
because there is no "buttonPressed" event and I have no idea how to write it.
And here's the class:
Imports System.ComponentModel
Imports System.Runtime.InteropServices
Public Class joystick
Inherits NativeWindow
Private parent As Form
Private Const MM_JOY1MOVE As Integer = &H3A0
' Public Event Move(ByVal joystickPosition As Point)
Public btnValue As String
Public Event Up()
Public Event Down()
Public Event Left()
Public Event Right()
<StructLayout(LayoutKind.Explicit)> _
Private Structure JoyPosition
<FieldOffset(0)> _
Public Raw As IntPtr
<FieldOffset(0)> _
Public XPos As UShort
<FieldOffset(2)> _
Public YPos As UShort
End Structure
Private Class NativeMethods
Private Sub New()
End Sub
' This is a "Stub" function - it has no code in its body.
' There is a similarly named function inside a dll that comes with windows called
' winmm.dll.
' The .Net framework will route calls to this function, through to the dll file.
<DllImport("winmm", CallingConvention:=CallingConvention.Winapi, EntryPoint:="joySetCapture", SetLastError:=True)> _
Public Shared Function JoySetCapture(ByVal hwnd As IntPtr, ByVal uJoyID As Integer, ByVal uPeriod As Integer, <MarshalAs(UnmanagedType.Bool)> ByVal changed As Boolean) As Integer
End Function
End Class
Public Sub New(ByVal parent As Form, ByVal joyId As Integer)
AddHandler parent.HandleCreated, AddressOf Me.OnHandleCreated
AddHandler parent.HandleDestroyed, AddressOf Me.OnHandleDestroyed
AssignHandle(parent.Handle)
Me.parent = parent
Dim result As Integer = NativeMethods.JoySetCapture(Me.Handle, joyId, 100, True)
End Sub
Private Sub OnHandleCreated(ByVal sender As Object, ByVal e As EventArgs)
AssignHandle(DirectCast(sender, Form).Handle)
End Sub
Private Sub OnHandleDestroyed(ByVal sender As Object, ByVal e As EventArgs)
ReleaseHandle()
End Sub
Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
If m.Msg = MM_JOY1MOVE Then
' Joystick co-ords.
' (0,0) (32768,0) (65535, 0)
'
'
'
' (0, 32768) (32768, 32768) (65535, 32768)
'
'
'
'
' (0, 65535) (32768, 65535) (65535, 65535)
'
Dim p As JoyPosition
p.Raw = m.LParam
' RaiseEvent Move(New Point(p.XPos, p.YPos))
If p.XPos > 16384 AndAlso p.XPos < 49152 Then
' X is near the centre line.
If p.YPos < 6000 Then
' Y is near the top.
RaiseEvent Up()
ElseIf p.YPos > 59536 Then
' Y is near the bottom.
RaiseEvent Down()
End If
Else
If p.YPos > 16384 AndAlso p.YPos < 49152 Then
' Y is near the centre line
If p.XPos < 6000 Then
' X is near the left.
RaiseEvent Left()
ElseIf p.XPos > 59536 Then
' X is near the right
RaiseEvent Right()
End If
End If
End If
End If
If btnValue <> m.WParam.ToString Then
btnValue = m.WParam.ToString
End If
MyBase.WndProc(m)
End Sub
End Class
Instead of using the older winmm, I would use XInput instead (or if you can, use XNA).
There are a couple of ways you could go to do this. One step up is to use the XInput dlls directly as outlined by this question on the MSDN forums. That's still fairly ugly though. Probably the "easier" way to do this is using a wrapper library that exists out there like SlimDX or SharpDX.
One of the advantages of using XInput via SlimDX or SharpDX is that it will also work within a Windows Store app for Windows 8 :).
Here's a snippet from the GamePad sample in SharpDX:
C#
var controllers = new[] { new Controller(UserIndex.One), new Controller(UserIndex.Two), new Controller(UserIndex.Three), new Controller(UserIndex.Four) };
// Get 1st controller available
Controller controller = null;
foreach (var selectControler in controllers)
{
if (selectControler.IsConnected)
{
controller = selectControler;
break;
}
}
VB
Dim controllers As New List(Of Controller)
controllers.Add(New Controller(UserIndex.One))
controllers.Add(New Controller(UserIndex.Two))
controllers.Add(New Controller(UserIndex.Three))
controllers.Add(New Controller(UserIndex.Four))
Dim controller as Controller = Nothing;
For Each selectController In controllers
If selectController.IsConnected Then
controller = selectController
Exit For
End If
Next
Then you can get the state to work with using:
var state = controller.GetState();
You will notice that XInput uses more of a polling model, so you will need to occasionally check for a button press to detect this. If you need to poll continuously, you can probably spin up a new Task to do this on.

custom textboxes in VB.Net

tldr - Made a subclass of Textbox, text looks screwy when it has focus. What's the proper way to handle it?
For my company's VB.Net application, I've been asked to make our textboxes behave like Google's textboxes, ie they need to have a blue-ish border around them when they have focus and a gray-ish border when they do not. I can already accomplish this by setting a textbox's BorderStyle to 'None', then drawing the appropriate rectangle within a form's Paint event. However, I have to do this for each and every single textbox that I use. And our application has quite a few of them. Needless to say, this is a pain and I'd rather have one piece of code that I can call upon.
So I figured that I have two options; I can either make a user control that contains a single textbox which uses the above method, or I can write my own class that inherits from the TextBox class and makes this behavior standard. I have elected to use the latter approach, and via overriding the OnPaint method I have achieved the desired behavior. But now I'm encountering some new pitfalls.
The main problem that I'm having is that text within the textbox is not rendered correctly when the textbox has focus. The text takes on a different font, appears bold, and highlighting looks wonky. If the textbox loses focus, the text looks correct. I suspect that I need to handle drawing for highlighted text differently, but I'm not sure what I need to do. Do I handle it in the OnPaint method or do I need to catch it somewhere else? Do I need to abandon this approach altogether and just make a user control?
Bonus question: for anyone with experience making custom textboxes, are there any tips or gotchas that I need to know about? This is my first time making a custom control, so I don't really know what all to expect.
edit: forgot to mention that I'm able to override OnPaint because I set the UserPaint flag to true. I'm guessing this was obvious, but I just want to be thorough.
edit2: Here's the class in its entirety.
Imports System.Drawing
Public Class MyCustomTextBox
Inherits TextBox
Public Sub New()
MyBase.New()
Me.BorderStyle = BorderStyle.None
SetStyle(ControlStyles.UserPaint, True)
End Sub
Protected Overrides Sub OnGotFocus(ByVal e As System.EventArgs)
'I want these textboxes to highlight all text by default
Me.SelectAll()
MyBase.OnGotFocus(e)
End Sub
Protected Overrides Sub OnLostFocus(ByVal e As System.EventArgs)
Me.SelectionLength = 0
MyBase.OnLostFocus(e)
End Sub
Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)
Dim p As Pen = Nothing
'MyBase.OnPaint(e)
e.Graphics.FillRectangle(Brushes.White, Me.ClientRectangle)
If Me.Focused Then
p = New Pen(Brushes.CornflowerBlue)
Else
p = New Pen(Brushes.Gainsboro)
End If
e.Graphics.DrawRectangle(p, 0, 0, Me.ClientSize.Width - 1, Me.ClientSize.Height - 1)
e.Graphics.DrawString(Me.Text, Me.Font, New SolidBrush(Me.ForeColor), Me.ClientRectangle)
End Sub
End Class
As Hans mentioned, the TextBox doesn't even use the OnPaint method when it draws its text.
One way to do it is paint over the 3D border of the control in the WM_NCPAINT message. I won't claim it's completely flicker free:
Imports System.Runtime.InteropServices
Public Class TextBoxWithBorder
Inherits TextBox
Public Const WM_NCPAINT As Integer = &H85
<Flags()> _
Private Enum RedrawWindowFlags As UInteger
Invalidate = &H1
InternalPaint = &H2
[Erase] = &H4
Validate = &H8
NoInternalPaint = &H10
NoErase = &H20
NoChildren = &H40
AllChildren = &H80
UpdateNow = &H100
EraseNow = &H200
Frame = &H400
NoFrame = &H800
End Enum
<DllImport("User32.dll")> _
Public Shared Function GetWindowDC(ByVal hWnd As IntPtr) As IntPtr
End Function
<DllImport("user32.dll")> _
Private Shared Function ReleaseDC(ByVal hWnd As IntPtr, ByVal hDC As IntPtr) As Boolean
End Function
<DllImport("user32.dll")> _
Private Shared Function RedrawWindow(hWnd As IntPtr, lprcUpdate As IntPtr, hrgnUpdate As IntPtr, flags As RedrawWindowFlags) As Boolean
End Function
Public Sub New()
MyBase.BorderStyle = Windows.Forms.BorderStyle.Fixed3D
End Sub
Protected Overrides Sub OnResize(e As System.EventArgs)
MyBase.OnResize(e)
RedrawWindow(Me.Handle, IntPtr.Zero, IntPtr.Zero, RedrawWindowFlags.Frame Or RedrawWindowFlags.UpdateNow Or RedrawWindowFlags.Invalidate)
End Sub
Protected Overrides Sub WndProc(ByRef m As Message)
MyBase.WndProc(m)
If m.Msg = WM_NCPAINT Then
Dim hDC As IntPtr = GetWindowDC(m.HWnd)
Using g As Graphics = Graphics.FromHdc(hDC)
If Me.Focused Then
g.DrawRectangle(Pens.CornflowerBlue, New Rectangle(0, 0, Me.Width - 1, Me.Height - 1))
Else
g.DrawRectangle(Pens.Gainsboro, New Rectangle(0, 0, Me.Width - 1, Me.Height - 1))
End If
g.DrawRectangle(SystemPens.Window, New Rectangle(1, 1, Me.Width - 3, Me.Height - 3))
End Using
ReleaseDC(m.HWnd, hDC)
End If
End Sub
End Class
I override the OnResize event to send the RedrawWindow message, which basically makes the control invalidate it's nonclient area.
Refactor as needed.

How can I catch the autosize double-click event on a listview in VB.NET?

I am using Visual Studio 2008 and VB.NET. I've got a listview control on my form and I've added columns using the windows forms designer. As you know, if you double-click on the sizer or divider or whatever you want to call it between two columns, the column on the left will autosize (unless you disable that). How can I catch this specific event? The ColumnWidthChanged event and the DoubleClick event are likely candidates, but in the ColumnWidthChanged event, there's no way I can see to determine if it was an autosize. Similarly, there's no simple way to catch what was clicked exactly with the DoubleClick event. Does anyone have any ideas how I can catch this specific event type?
Detecting events on a listview's header is quite tricky.
You will need to create your own header to replace the one that it normally uses, and then listen to the appropriate messages. There aren't any specific ones for column resize handles, as far as I know.
The following class subclasses ListView and adds a handler that detects a double-click between columns. That is as close as it gets, I think.
I hope it will help you out somewhat.
Class MyListView
Inherits ListView
Protected Overrides Sub CreateHandle()
MyBase.CreateHandle()
New HeaderControl(Me)
End Sub
Private Class HeaderControl
Inherits NativeWindow
Private _parent As ListView = Nothing
<DllImport("User32.dll", CharSet := CharSet.Auto, SetLastError := True)> _
Public Shared Function SendMessage(hWnd As IntPtr, msg As Integer, wParam As IntPtr, lParam As IntPtr) As IntPtr
End Function
Public Sub New(parent As ListView)
_parent = parent
Dim header As IntPtr = SendMessage(parent.Handle, (&H1000 + 31), IntPtr.Zero, IntPtr.Zero)
Me.AssignHandle(header)
End Sub
Protected Overrides Sub WndProc(ByRef message As Message)
Const WM_LBUTTONDBLCLK As Integer = &H203
Select Case message.Msg
Case WM_LBUTTONDBLCLK
Dim position As Point = Control.MousePosition
Dim relative As Point = _parent.PointToClient(position)
Dim rightBorder As Integer = 0
For Each c As ColumnHeader In _parent.Columns
rightBorder += c.Width
If relative.X > (rightBorder - 6) AndAlso relative.X < (rightBorder + 6) Then
MessageBox.Show([String].Format("Double-click after column '{0}'", c.Text))
End If
Next
Exit Select
End Select
MyBase.WndProc(message)
End Sub
End Class
End Class
You will need to include a using System.Runtime.InteropServices; statement for this to work.