I'm trying to use GDI+ animation in a ToolWindow, whilst keeping the animation flicker-free. When I set the ControlStyles.AllPaintingInWmPaint to true the application crashes when the toolwindow is opened.
I create my toolwindow with the following code:
SetWindowLong(Handle, win32.GWL.__EXSTYLE, win32.WS._EX_TOOLWINDOW)
Where win32 is a class containing all the WM constants from PInvoke. GWL and WS are enums containing the integer values for the respective constants. The SetWindowLong is sourced from a dll import:
<DllImport("user32.dll",
EntryPoint:="SetWindowLong")>
Private Shared Function SetWindowLong(ByVal hWnd As IntPtr,
ByVal nIndex As Integer,
ByVal dwNewLong As Integer) As Integer
End Function
The ToolWindow code has worked wonderfully for me so far, but I haven't tried to use any GDI animation so far.
Basically, I would appreciate a method to reduce flicker/lag during GDI animation in my toolwindow type form, or a way to use the aforementioned controlstyle. I'm already using these styles:
SetStyle(ControlStyles.DoubleBuffer, True)
SetStyle(ControlStyles.UserPaint, True)
which reduce the flickering by a bit, but not enough.
I think I found the problem. In my Form.Paint event I tried to save on typing:
Dim g as Graphics = e.Graphics
g.DrawImage(bmp)
After changing this back to the original code;
e.graphics.DrawImage(bmp)
the program worked flawlessly! A lack of understanding on my part regarding variables resulted in many hours wasted.
Related
I need to prevent the user of my console program from resizing the window, only allowing it to be changed programmatically. If the user changes the width, everything messes up. Also, I want to disable the maximise button. Are either of these possible in Console?
This answer neatly covers how to disable resizing a form in WinForms, but it won't work for Console.
I came up with a solution that prevents re-sizing of a console window application (either by dragging the corner border or by clicking on the maximize or minimize buttons). The following code is written in the form of a complete VB.Net console application (i.e., a Module):
Module Module1
Private Const MF_BYCOMMAND As Integer = &H0
Public Const SC_CLOSE As Integer = &HF060
Public Const SC_MINIMIZE As Integer = &HF020
Public Const SC_MAXIMIZE As Integer = &HF030
Public Const SC_SIZE As Integer = &HF000
Friend Declare Function DeleteMenu Lib "user32.dll" (ByVal hMenu As IntPtr, ByVal nPosition As Integer, ByVal wFlags As Integer) As Integer
Friend Declare Function GetSystemMenu Lib "user32.dll" (hWnd As IntPtr, bRevert As Boolean) As IntPtr
Sub Main()
Dim handle As IntPtr
handle = Process.GetCurrentProcess.MainWindowHandle ' Get the handle to the console window
Dim sysMenu As IntPtr
sysMenu = GetSystemMenu(handle, False) ' Get the handle to the system menu of the console window
If handle <> IntPtr.Zero Then
DeleteMenu(sysMenu, SC_CLOSE, MF_BYCOMMAND) ' To prevent user from closing console window
DeleteMenu(sysMenu, SC_MINIMIZE, MF_BYCOMMAND) 'To prevent user from minimizing console window
DeleteMenu(sysMenu, SC_MAXIMIZE, MF_BYCOMMAND) 'To prevent user from maximizing console window
DeleteMenu(sysMenu, SC_SIZE, MF_BYCOMMAND) 'To prevent the use from re-sizing console window
End If
Do Until (Console.ReadKey.Key = ConsoleKey.Escape)
'This loop keeps the console window open until you press escape
Loop
End Sub
End Module
I based this answer off of the Stack Overflow question/answer: "Disable the maximize and minimize buttons of a c# console [closed]"
Please let me know if this doesn't work for you. Good luck!
I wanted to comment on the accepted answer, but I lack reputation...
To prevent the console window from resizing when you snap it to a corner, you can use
Console.SetBufferSize(80, 24) ' or whatever size you're using...
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
You guys have been a great help getting me back up to speed after a 7-year retirement from programming.
I am trying to change the font in a sub that draws text in a graphic object. Here's my code:
Private Sub AddLine(ByVal text As String, ByVal Font_name As String, ByVal Font_Size As Integer, ByRef yPosition As Integer, ByVal Alignment As Integer, ByVal G As Graphics, ByVal B As SolidBrush, ByVal ScreenWidth As Integer)
Try
Dim F As New Font(Font_name, Font_Size)
The error shows when I hover over "F" in the dim statement at runtime. (There's a breakpoint on the next line).
Ironically, the program works in spite of the error, but I like to keep my code clean. I've spent the day trying various suggestions on the forum, but nothing works. any ideas of how I should code this New Font line would be appreciated.
As said here, when you declare a new Font, you should use the Font Size as Single and not as Integer, thus, you get an error although the code works fine. Try to change the parameter Font_Size from Integer to Single.
I have a COM component written in C++ that has a Print function. This print function takes the Printer hDC as a parameter that includes all settings to use for the print. Previously, this was called from VB6 code, and Printer.hdc would work here after setting everything on the Printer object.
The code was converted from VB6 to VB.NET, and I have figured out most of things that I need to do. The old Printer object is available through the Microsoft.VisualBasic.PowerPacks.Printing.Compability.VB6.Printer class, but the old hdc property is not supported here.
Can anyone tell me how to get this hdc?
Is this hdc the same as GetHdevmode() on a System.Drawing.Printing.PrinterSettings object?
You can get one out of the Graphics object returned by PrinterSettings.CreateMeasurementGraphics(). Use the Graphics.GetHdc() method. Don't forget ReleaseHdc() after printing.
Hdc is not the same as getdevmode, but you can do everything in .net without using hdc. If it saves time using the old code, you can get the hdc from the graphics object and use it as in nobugz's answer. But if you have a graphics object for the printer, it might be simpler to draw directly to the graphics object and skip the hdc altogether.
Here's a similar approach to the one suggested by Hans but it uses a form control. If you are using a form control anyway, this might be a cleaner approach.
Place a PrintDocument from the Windows Forms toolbox to your form.
Then add the following code to handle the print page (as an example):
Private Sub PrintDocument1_PrintPage(ByVal sender As Object, ByVal e As System.Drawing.Printing.PrintPageEventArgs) Handles PrintDocument1.PrintPage
Dim printerhdc As IntPtr = e.Graphics.GetHdc()
' Do whatever you need to do to get the right image
XYZ.Load file(currentpagenumber)
XYZ.Render(printerhdc.ToInt64, 25, 25, Width, Height)
CurrentPageNumber += 1
If CurrentPageNumber < TotalPageCount Then
e.HasMorePages = True
Else
e.HasMorePages = False
End If
e.Graphics.ReleaseHdc(printerhdc)
End Sub
...
'Gather all the files you need and put their names in an arraylist.
'Then issue the print command
PrintDocument1.Print
' You've just printed your files
source: http://www.vbforums.com/showthread.php?247493-Good-ol-Printer-hDC
(source: http://www.vbforums.com/showthread.php?247493-Good-ol-Printer-hDC)
Preface: I know this is an unusual/improper way to do this. I can do this with a "real" ShowDialog(), background worker/thread, and so on. I'm not looking for help doing it that way; I am trying to do specifically what I describe here, even if it is ugly. If this is impossible for X reason, please let me know though.
I have created a fancy progress dialog for some of our long running operations. I need to have this dialog shown on a new thread while having processing continue on the calling (UI in most cases) thread.
This has 3 real requirements:
Prevent user interaction with the calling form (similar to ShowDialog(this))
Keep the progress dialog above the main window (it can fall behind now)
Allow the main thread to continue processing
What I have looks like this (and works just fine so far, as far as running goes, except for those issues above):
Using ... ShowNewProgressDialogOnNewThread() ...
Logic
UpdateProgress() //static
Logic
UpdateProgress() //static, uses Invoke() to call dialog
...
End Using // destroys the form, etc
I have tried a few ways to do this:
ShowDialog() on BackgroundWorker / Thread
Action.BeginInvoke() which calls a function
ProgressForm.BeginInvoke(... method that calls ShowDialog... )
Wrapping main form in a class that implements IWin32Window so it can be called cross-threaded and passed to ShowDialog() - this one failed somewhere later one, but at least causes ShowDialog() to not barf immediately.
Any clues or wisdom on how to make this work?
Solution (For Now)
The call to EnableWindow is what did what I was looking for.
I do not experience any crashes at all
Changed to use ManualResetEvent
I set TopMost, because I couldn't always guarantee the form would end up on top otherwise. Perhaps there is a better way.
My progress form is like a splash screen (no sizing, no toolbar, etc), perhaps that accounts for the lack of crashes (mentioned in answer)
Here is another thread on the EnableWindow topic (didn't reference for this fix, tho)
Getting the progress window consistently displayed on top of the (dead) form is the difficult requirement. This is normally handled by using the Form.Show(owner) overload. It causes trouble in your case, WF isn't going to appreciate the owner form belonging to another thread. That can be worked around by P/Invoking SetWindowLong() to set the owner.
But now a new problem emerges, the progress window goes belly-up as soon as it tries to send a message to its owner. Somewhat surprisingly, this problem kinda disappears when you use Invoke() instead of BeginInvoke() to update progress. Kinda, you can still trip the problem by moving the mouse over the border of the disabled owner. Realistically, you'll have to use TopMost to nail down the Z-order. More realistically, Windows just doesn't support what you are trying to do. You know the real fix, it is at the top of your question.
Here's some code to experiment with. It assumes you progress form is called dlgProgress:
Imports System.Threading
Public Class ShowProgress
Implements IDisposable
Private Delegate Sub UpdateProgressDelegate(ByVal pct As Integer)
Private mOwnerHandle As IntPtr
Private mOwnerRect As Rectangle
Private mProgress As dlgProgress
Private mInterlock As ManualResetEvent
Public Sub New(ByVal owner As Form)
Debug.Assert(owner.Created)
mOwnerHandle = owner.Handle
mOwnerRect = owner.Bounds
mInterlock = New ManualResetEvent(False)
Dim t As Thread = New Thread(AddressOf dlgStart)
t.SetApartmentState(ApartmentState.STA)
t.Start()
mInterlock.WaitOne()
End Sub
Public Sub Dispose() Implements IDisposable.Dispose
mProgress.BeginInvoke(New MethodInvoker(AddressOf dlgClose))
End Sub
Public Sub UpdateProgress(ByVal pct As Integer)
mProgress.Invoke(New UpdateProgressDelegate(AddressOf dlgUpdate), pct)
End Sub
Private Sub dlgStart()
mProgress = New dlgProgress
mProgress.StartPosition = FormStartPosition.Manual
mProgress.ShowInTaskbar = False
AddHandler mProgress.Load, AddressOf dlgLoad
AddHandler mProgress.FormClosing, AddressOf dlgClosing
EnableWindow(mOwnerHandle, False)
SetWindowLong(mProgress.Handle, -8, mOwnerHandle)
Application.Run(mProgress)
End Sub
Private Sub dlgLoad(ByVal sender As Object, ByVal e As EventArgs)
mProgress.Location = New Point( _
mOwnerRect.Left + (mOwnerRect.Width - mProgress.Width) \ 2, _
mOwnerRect.Top + (mOwnerRect.Height - mProgress.Height) \ 2)
mInterlock.Set()
End Sub
Private Sub dlgUpdate(ByVal pct As Integer)
mProgress.ProgressBar1.Value = pct
End Sub
Private Sub dlgClosing(ByVal sender As Object, ByVal e As FormClosingEventArgs)
EnableWindow(mOwnerHandle, True)
End Sub
Private Sub dlgClose()
mProgress.Close()
mProgress = Nothing
End Sub
'--- P/Invoke
Public Shared Function SetWindowLong(ByVal hWnd As IntPtr, ByVal nIndex As Integer, ByVal dwNewLong As IntPtr) As IntPtr
If IntPtr.Size = 4 Then
Return SetWindowLongPtr32(hWnd, nIndex, dwNewLong)
Else
Return SetWindowLongPtr64(hWnd, nIndex, dwNewLong)
End If
End Function
Private Declare Function EnableWindow Lib "user32.dll" (ByVal hWnd As IntPtr, ByVal enabled As Boolean) As Boolean
Private Declare Function SetWindowLongPtr32 Lib "user32.dll" Alias "SetWindowLongW" (ByVal hWnd As IntPtr, ByVal nIndex As Integer, ByVal dwNewLong As IntPtr) As IntPtr
Private Declare Function SetWindowLongPtr64 Lib "user32.dll" Alias "SetWindowLongW" (ByVal hWnd As IntPtr, ByVal nIndex As Integer, ByVal dwNewLong As IntPtr) As IntPtr
End Class
Sample usage:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Using dlg As New ShowProgress(Me)
For ix As Integer = 1 To 100
dlg.UpdateProgress(ix)
System.Threading.Thread.Sleep(50)
Next
End Using
End Sub
I know it's a bit dirty but can't you just do the work in the dialog??
I mean something like
Dialog.MyShowDialog(callback);
and do all the work in callback as well as the UI update.
That way you'll retain the ShowDialog behaivour while allowing different code to be called.
I wrote a blog post on this topic a while ago (dealing with splash forms, but the idea is the same). The code is in C#, but I will try to convert it an post it here (coming...).