Animating form resize in VB.NET - vb.net

I have a form positioned slightly above the taskbar/system tray in the bottom right corner of my desktop window. It's sort of like a pop-up notification. The notification itself looks great, but there's a button on it which resizes the form up, animating the sizing in increments of 5px whilst keeping it's position relative to the bottom-right corner of the screen.
The problem with this is that it doesn't look very smooth. Adjusting Me.Width resizes from the left so you have to then move the form to the left with Me.Left to compensate. Me.SetBounds just seems to be a wrapper for setting those properties anyway.
Is there anything I can do to have the form smoothly (or at least appear to) resize outwards from the left of the form?

ETA: You can actually do this using SetBounds, as SetBounds will delegate to SetBoundsCore which, in turn will invoke SetWindowPos. So SetBounds will internally really set all bounds at once and the window manager will redraw the window only after all properties are set.
Another option would be to import the MoveWindow function and use that instead. It produces a smooth animation here, as it can set size and position simultaneously before telling the window manager to redraw the window.
My test code looked like this (converted from C# via IL to VB with help from Reflector):
Private button1 As Button
Private components As IContainer = Nothing
Private tm As Timer = New Timer
Private Sub Form1_Load(ByVal sender As Object, ByVal e As EventArgs)
MyBase.Left = (Screen.PrimaryScreen.WorkingArea.Width - MyBase.Width)
MyBase.Top = (Screen.PrimaryScreen.WorkingArea.Height - MyBase.Height)
Me.tm.Enabled = False
Me.tm.Interval = 20
AddHandler Me.tm.Tick, Function
If (MyBase.Width < 500) Then
Form1.MoveWindow(MyBase.Handle, (MyBase.Left - 5), (MyBase.Top - 5), (MyBase.Width + 5), (MyBase.Height + 5), True)
Else
Me.tm.Enabled = False
End If
End Function
End Sub
<DllImport("user32.dll", SetLastError:=True)> _
Friend Shared Function MoveWindow(ByVal hWnd As IntPtr, ByVal X As Integer, ByVal Y As Integer, ByVal nWidth As Integer, ByVal nHeight As Integer, ByVal bRepaint As Boolean) As Boolean
End Function

I never tried this, but have you tried setting the DoubleBuffer property of the form to True? This should smooth the drawing.

You might try reducing the pixel steps it's incrementing down to 1-2 as well, 5px seems like a lot to be adjusting size, I could see how this could appear choppy.

Related

How to make a Control move left/right only using the mouse pointer?

I'm currently making a game, it contains a paddle (called base) that must move from left to right only.
I found a piece of code that allowed the platform to move, however it moves in all directions and isn't synced with my mouse pointer properly:
Private Sub Form1_MouseMove(ByVal sender As System.Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles MyBase.MouseMove
base.Location = MousePosition
End Sub
What do I need to change or add in order for the paddle to only move horizontally?
Assume your paddle (I'm naming the Control paddle here, base is not a good name) is placed near the bottom of the Form, its Height ~25 pixels and its bottom distance from the Form's bottom side ~10-20 pixels.
You can clip the Cursor to a narrow band right above it when the Mouse enters the Form.
You can then move the Cursor without intersecting other Controls in the Form, which could interfere with the generation of MouseMove events.
You can also hide the Cursor, so the arrow pointer doesn't become visually obnoxious (unless it's required for something else, of course).
When the Cursor is moved, the movement is translated to the middle of the paddle Control, which is moved only to the left or right, in relation to the current Cursor offset:
(PointToClient(Cursor.Position).X - (paddle.Width \ 2))
When the Form closes, restore the Cursor and the clipping region.
Paste this code inside the Form that contains the paddle (and rename base to paddle):
Protected Overrides Sub OnMouseEnter(e As EventArgs)
MyBase.OnMouseEnter(e)
ClipCursor()
End Sub
Protected Overrides Sub OnMouseMove(e As MouseEventArgs)
MyBase.OnMouseMove(e)
paddle.Left = PointToClient(Cursor.Position).X - (paddle.Width \ 2)
End Sub
Protected Overrides Sub OnFormClosing(e As FormClosingEventArgs)
ShowCursor()
MyBase.OnFormClosing(e)
End Sub
Private Sub ClipCursor()
Dim bandLocation = New Point(
Left + 8 + (paddle.Width \ 2),
Bottom - paddle.Height * 2 - Cursor.Size.Height)
Dim bandSize = New Size(ClientSize.Width - paddle.Width, 20)
Cursor.Clip = New Rectangle(bandLocation, bandSize)
Cursor.Hide()
End Sub
Private Sub ShowCursor()
Cursor.Clip = Rectangle.Empty
Cursor.Show()
End Sub
You can accomplish this by only assigning the X coordinate to the location property:
base.Location = New Point(Cursor.Position.X, Button1.Location.Y)
This will ignore the Y coordinate, resulting only in horizontal movement. Also, be aware that depending on your situation, you may have to translate the mouse pointer coordinates relative to the window. So, in case the result is distorted, do it like this:
base.Location = New Point(PointToClient(Cursor.Position).X, Button1.Location.Y)
This will translate the mouse coordinates (from e.g. Cursor.Position property) into window-relative coordinates.

Button in TextBox disappears when selecting text while Windows media player plays music

This is my first question, please be nice. Oh, and I'm not a native english speaker. :)
I've discovered some weird bug in my application. I've created a TextBox control with a button in it (code below). Following these steps will make the button disappear.
Start windows media player with some music (or video)
Start the application
Click in TextBox control and hold down left mouse button
Move your cursor around like crazy
Woosh... Button disappears.
This will not happen when you closed or paused your windows media player. I was able to reproduce this bug on a different system (Windows 7 and Windows 10). This is totally weird, because it doesn't seem logical. Windows is doing crazy stuff with the windows media player...
I'm not sure if there's a workaround. Can anybody help me with this or should I ask on Microsoft forums? I've tried to call "UpdateButton" while selection changed, but I wasn't successful.
Public Class TextBoxEx
Inherits TextBox
Const BUTTON_WIDTH As Integer = 18
Const EM_SETMARGINS As Integer = &HD3
Const EC_RIGHTMARGIN As Integer = &H2
<Runtime.InteropServices.DllImport("user32.dll", SetLastError:=True)> _
Private Shared Function SendMessage(ByVal hWnd As IntPtr, ByVal Msg As UInteger, ByVal wParam As Integer, ByVal lParam As Integer) As IntPtr
End Function
Private btnCommand As Button
Public Sub New()
btnCommand = New Button
btnCommand.Cursor = Cursors.Default
btnCommand.Image = My.Resources.iconCancel
Me.Controls.Add(btnCommand)
Call UpdateButton()
End Sub
Private Sub UpdateButton()
Dim rightMargin As Integer = (BUTTON_WIDTH + 1) << 16
btnCommand.Size = New Size(BUTTON_WIDTH, Me.ClientSize.Height)
btnCommand.Location = New Point(Me.ClientSize.Width - BUTTON_WIDTH, 0)
Call SendMessage(Me.Handle, EM_SETMARGINS, EC_RIGHTMARGIN, rightMargin)
End Sub
Protected Overrides Sub OnResize(e As System.EventArgs)
MyBase.OnResize(e)
Call UpdateButton()
End Sub
End Class

VB.Net CheckedListBox automatically wrap text

This is more a design than a programming question (well, maybe not). I have 4 CheckedListBoxes which are filled with Data from an SQLite-database (Visual Studio 2010), and some of the entries exceed the width of the Box. I know that I can include a horizontal scrollbar in a CheckedListBox but everybody hates horizontal scrollbars (very ugly), so I tried to find an option to automatically wrap the text that doesn't fit.
So if there is any solution to have the text wrapped when the width of the box is too small it would be awesome.
I could extend the window size but it is already over 1000px in width and some of the users use computers made of wood with 1024x768 solution, so that's not really an option.
Datagrid would be another option but I thought there must be an easier solution. Any hints?
Edit: Sorry, it's Windows Forms.
You can write your own CheckedListBox pretty easily using a panel with some actual CheckBoxes on it so you can do the other things you'd expect such as disable certain ones, fix the way it cuts off drop characters, iterate them and so forth.
The problem with wrap, is a) determining the Text Extent of long text so you know how tall to make each checkbox, and b) having to keep a cumulative items height so you know where to add the next one. Of course, once you support wrap, you have to be able to adjust them all which involves moving them when a text change causes one in the middle to grow/shrink.
The panel's AutoScroll handles all the scrolling for you, which includes adding a HSCroll as needed, which is not always desirable. One way to overcome this, which might work for the actual CheckedListBox you are using, is to eat the HScroll instead.
<DllImport("user32.dll")> _
Private Shared Function ShowScrollBar(hWnd As IntPtr,
wBar As Integer,
bShow As Boolean) As <MarshalAs(UnmanagedType.Bool)> Boolean
End Function
Then in form load or perhaps after you have populated it:
ShowScrollBar(myControl.Handle, ScrollBarDirection.SB_HORZ, False)
You can also just subclass the existing CheckedListBox to eat the scrollbar in OnClientSizeChanged
Public Class CheckedListBox2
Inherits CheckedListBox
' optionally remove the scroll bar
Public Property VerticalScrollOnly As Boolean
' PInvokes
<DllImport("user32.dll", SetLastError:=True)>
Public Shared Function GetWindowLong(ByVal hWnd As IntPtr,
ByVal nIndex As Integer) As Integer
End Function
<DllImport("user32.dll")>
Private Shared Function ShowScrollBar(hWnd As IntPtr,
wBar As Integer,
bShow As Boolean) _
As <MarshalAs(UnmanagedType.Bool)> Boolean
End Function
'// window style constants for scrollbars
Private Const WS_VSCROLL As Integer = &H200000
Private Const WS_HSCROLL As Integer = &H100000
Private Const GWL_STYLE As Integer = -16
Private Enum ScrollBarDirection
SB_HORZ = 0
SB_VERT = 1
SB_CTL = 2
SB_BOTH = 3
End Enum
' eat the HScroll when it shows up
Protected Overrides Sub OnClientSizeChanged(e As EventArgs)
Dim HScrollVis As Boolean
HScrollVis = IsHScrollVisible(Me)
If VerticalScrollOnly AndAlso HScrollVis Then
ShowScrollBar(MyBase.Handle, ScrollBarDirection.SB_HORZ, False)
End If
MyBase.OnClientSizeChanged(e)
End Sub
Friend Shared Function IsHScrollVisible(ByVal ctl As Control) As Boolean
Dim wndStyle As Integer = GetWindowLong(ctl.Handle, GWL_STYLE)
Return ((wndStyle And WS_HSCROLL) <> 0)
End Function
End Class

Vertical ScrollBar does not repaint using EnableScrollBar api

I am trying to disable a VScrollBar control using the EnableScrollBar api. When I call the api it returns as if no problems ocurred but the VScrollBar is not repainted.
To reproduce the problem create a Vb.Net windows forms project, drop a VScrollBar control and a button to the form and paste the following code:
<DllImport("user32.dll", CharSet:=CharSet.Auto, SetLastError:=True, ExactSpelling:=True)>
Public Shared Function EnableScrollBar(ByVal hWnd As IntPtr, ByVal nBar As Integer, ByVal value As Integer) As Boolean
End Function
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim wSBflags As UInteger = 3UI 'SB_VERT
Dim wArrows As UInteger = 3UI 'ESB_DISABLE_BOTH
Dim result As Boolean = EnableScrollBar(Me.VScrollBar1.Handle, wSBflags, wArrows)
End Sub
I tried using SendMessage to send a redraw (WM_REDRAW) and a paint (WM_PAINT) but cant get it to work. Any ideias?
Ps: If you drop a multiline textbox and use the same code it works....
SB_VERT is for the vertical scrollbar as part of the non-client area of a window. For a scroll bar control, use the SB_CTL constant.

How can I get Aero Glass on a Windows Form without Borders?

I'm trying to have Aero Glass look in my forms in VB.NET 2010 app with DWM API, but as function call suggests, it extends look of Frame to the client area, and if form has no border, nothing will happen and form will become invisible. So, can I get Aero glass in a form without any border.... ??
As you've said, DwmExtendFrameIntoClientArea literally extends the transparent glass effect of the window's frame into its client area, which means that if your form's FormBorderStyle is set to "None", your window will effectively be invisible.
Instead, you need to use the DwmEnableBlurBehindWindow API, which enables the glassy blur effect on a window without requiring it to have a frame/border. It takes two parameters. The first (hWnd) is the handle to the form that you wish to apply the blur behind effect to. The second (pBlurBehind) is a structure passed by reference that contains data or parameters for the effect.
Therefore, you also have to define the DWM_BLURBEHIND structure, which itself contains four members. The first (dwFlags) is a bitwise combination of constant values that indicate which members of this structure have been set. The second (fEnable) indicates whether you want to enable or disable the blur effect. The third (hRgnBlur) allows you to specify a particular region within the client area that the blur effect will be applied to; setting this to Nothing indicates that the entire client area will have the blur effect. The fourth (fTransitionOnMaximized) allows you to specify whether or not the form's colorization should transition to match the maximized windows.
Here are the final API declarations that you have to include in your code in order to use this function:
<StructLayout(LayoutKind.Sequential)> _
Private Structure DWM_BLURBEHIND
Public dwFlags As Integer
Public fEnable As Boolean
Public hRgnBlur As IntPtr
Public fTransitionOnMaximized As Boolean
End Structure
Private Const DWM_BB_ENABLE As Integer = &H1
Private Const DWM_BB_BLURREGION As Integer = &H2
Private Const DWM_BB_TRANSITIONONMAXIMIZED As Integer = &H4
<DllImport("dwmapi.dll", PreserveSig:=False)> _
Private Shared Sub DwmEnableBlurBehindWindow(ByVal hWnd As IntPtr, ByRef pBlurBehind As DWM_BLURBEHIND)
End Sub
And then here's a simple example of how you would call this function on a particular form:
Protected Overrides Sub OnLoad(ByVal e As System.EventArgs)
MyBase.OnLoad(e)
''#Set the form's border style to None
Me.FormBorderStyle = FormBorderStyle.None
''#Whatever region that you fill with black will become the glassy region
''# (in this example, the entire form becomes transparent)
Me.BackColor = Color.Black
''#Create and populate the blur-behind structure
Dim bb As DWM_BLURBEHIND
bb.dwFlags = DWM_BB_ENABLE
bb.fEnable = True
bb.hRgnBlur = Nothing
''#Enable the blur-behind effect
DwmEnableBlurBehindWindow(Me.Handle, bb)
End Sub
If instead, you only want to apply the blur behind effect to a particular subregion of the form, you will need to supply a valid region for the hRgnBlur member, and add the DWM_BB_BLURREGION flag to the dwFlags member.
You can use the Region.GetHrgn method to get a handle to the region you want to specify as the hRgnBlur member. For example, instead of the above code, you can use the following:
Protected Overrides Sub OnLoad(ByVal e As System.EventArgs)
MyBase.OnLoad(e)
''#Set the form's border style to None
Me.FormBorderStyle = FormBorderStyle.None
''#Fill the entire form with black to make it appear transparent
Me.BackColor = Color.Black
''#Create a region corresponding to the area of the form you want to render as glass
Using g As Graphics = Me.CreateGraphics
Dim glassRect As New Rectangle(0, 0, 100, 150)
Using rgn As New Region(glassRect)
''#Create and populate the blur-behind structure
Dim bb As DWM_BLURBEHIND
bb.dwFlags = DWM_BB_ENABLE Or DWM_BB_BLURREGION
bb.fEnable = True
bb.hRgnBlur = rgn.GetHrgn(g)
''#Enable blur-behind effect
DwmEnableBlurBehindWindow(Me.Handle, bb)
End Using
End Using
End Sub
Notice how, even when specifying a particular subregion to apply the blur-behind effect to, I still set the entire form's background color to black? This will cause the region we specified to render with a glassy blur-behind effect, and the rest of the form to appear transparent. Of course, you can set the rest of the form's background color to any color that you want (although make sure to fill the rectangle that you want to appear as glass with the color black, as before), but it will appear as partially transparent, just without the glassy blur-behind effect. MSDN explains why this is the case:
When you apply the blur-behind effect
to a subregion of the window, the
alpha channel of the window is used
for the nonblurred area. This can
cause an unexpected transparency in
the nonblurred region of a window.
Therefore, be careful when you apply a
blur effect to a subregion.
As far as I'm concerned, that makes applying this effect to only a subregion of the form's window relatively worthless. The only time it seems to me like it might make sense is if you want to render an arbitrary non-rectangular shape as glassy, with the rest of the form remaining transparent.