Unload SplashScreen Image - vb.net

I have an application that loads a spashscreen which loads a picture and centers this for a set length of time.
The issue I have is that the file used by splashscreen is locked and I can not make any modifications or replace it etc. until application is unloaded.
I am using the default VB net Spashscreen method and the code I use for the image is below
Dim Advert As System.Drawing.Image = Image.FromFile("Y:\Test\TestPic.jpg")
Dim width As Integer = Advert.Width
Dim height As Integer = Advert.Height
Me.BackgroundImage = Advert
Me.BackgroundImageLayout = ImageLayout.Stretch
Me.Size = New Size(width, height)
Me.CenterToScreen()
and then this code in the application events to overide the display time
Protected Overrides Function OnInitialize(ByVal commandLineArgs As
System.Collections.ObjectModel.ReadOnlyCollection(Of String)) As Boolean
Me.MinimumSplashScreenDisplayTime = 7000
Return MyBase.OnInitialize(commandLineArgs)
End Function
Is there a way to release the image file when the splash screen closes.

Related

How to get DPI for monitor where a modal dialog is displayed

I have the following code to check for the DPI of a screen where a form is being displayed. It works correctly for regular (modeless) windows, but returns the DPI of the screen where the main window is displayed if the dialog is displayed modally. The apparent fault is with the call Process.GetCurrentProcess().MainWindowHandle, which seems to return the process ID of the main window (since presumably, the modal window runs under the calling window's process). Unfortunately, this causes layout issues when the DPI must be detected in the modal dialog and elements rearranged when it's dragged from one monitor to another.
How do I get the DPI of the window the modal dialog is being displayed on?
Thanks!
-Pete
Calling format:
Dim factor As Single = GetDpiWindowMonitor(Me) / 96.0
Code being called:
'Get DPI of monitor containing this window by GetDpiForMonitor.
Public Function GetDpiWindowMonitor(ByVal curForm As Form) As Single
'Get handle to this window.
Process.GetCurrentProcess().Refresh()
Dim handleWindow As Integer = Process.GetCurrentProcess().MainWindowHandle
' ^^^ This seems to return the handle of the main window, not the dialog
'Get handle to monitor.
Dim handleMonitor As Integer = W32.MonitorFromWindow(handleWindow, W32.MONITOR_DEFAULTTONEAREST)
'Get DPI.
Dim curDPI As Integer = GetDpiSpecifiedMonitor(curForm, handleMonitor)
Return curDPI
End Function
'Get DPI of a specified monitor by GetDpiForMonitor.
Public Function GetDpiSpecifiedMonitor(ByVal curForm As Form,
ByVal handleMonitor As Integer) As Single
'Check if GetDpiForMonitor function is available.
If Not IsEightOneOrNewer() Then
If curForm.CurrentAutoScaleDimensions.Width = 0 Then
If FrmRegisteredTo.CurrentAutoScaleDimensions.Width > 0 Then
Return FrmRegisteredTo.CurrentAutoScaleDimensions.Width
Else
Return 96
End If
Else
Return curForm.CurrentAutoScaleDimensions.Width
End If
End If
'Get DPI.
Dim dpiX As UInteger
Dim dpiY As UInteger
Dim result As Integer = W32.GetDpiForMonitor(handleMonitor, W32.Monitor_DPI_Type.MDT_Default, dpiX, dpiY)
If (result <> 0) Then 'If not S_OK (= 0)
Throw New Exception("Failed to get DPI of monitor containing this window.")
End If
Return Convert.ToSingle(dpiX)
End Function

Adding images into next available picturebox

I'm doing a deck builder project through a card database and so far when I click a row (using datagridview), the value contained in the "image_url" column is printed into an invisible textbox, which is then used to download that image and show it in a picturebox.
Now that all works fine, but decks go up to 60 cards so I'm going to use 60 pictureboxes to print the user's selected cards. What I'm been trying to do is set up like a picturecount and when they select a column the number is increased by one like this:
picturebox(picturecount) = textbox4.text
but I've run into too many errors. Would you know a way to display the user's selected card in the next available picturebox? For example if they select "Dark Magician" three times, then the image of the "Dark Magician" is printed in the first available three pictureboxes
VB.NET:
Private Async Sub PictureLoader()
Dim imageURL As String
If TextBox4.Text = "" Then
imageURL = dataSet.Tables("YGO cards").Rows(row_count).Item(7)
Else
imageURL = TextBox4.Text
End If
Dim client As Net.WebClient = New Net.WebClient()
Dim ms As MemoryStream = New MemoryStream(Await client.DownloadDataTaskAsync(New Uri(imageURL)))
Using image As Image = Image.FromStream(ms)
PictureBox1.Image?.Dispose()
PictureBox1.Image = DirectCast(image.Clone(), Image)
End Using
ms.Dispose()
client.Dispose()
End Sub
and this is the event when a column is selected in the datagrid!
Dim index As Integer
index = e.RowIndex
Dim selectedrow As DataGridViewRow
selectedrow = DataGridView1.Rows(index)
' selectedrow.Cells(1) is the image_Url column
TextBox4.Text = selectedrow.Cells(1).Value.ToString()
If TextBox4.Text = "" Then
PictureBox1.Image = Nothing
' imageURL = dataSet.Tables("YGO cards").Rows(row_count).Item(7)
Else
PictureLoader()
End If
Ignore for a moment the specifics of your particular problem and break this down into a generic statement. What you're saying is that you have a collection and that the size of the collection can grow or shrink based on user input. This is an ideal case for a List(Of T) where you declare the List by specifying the data type of the items it will hold and then add items as needed. Because you are storing the URL of the card, you would create a new List(Of String):
Dim cards As List(Of String) = New List(Of String)
Now whenever you needed to add URLs to your list you would call the Add method if it is a single URL or AddRange if it is multiple URLs:
cards.Add(TextBox1.Text)
'Or
cards.AddRange({TextBox1.Text, TextBox2.Text, TextBox3.Text})
As far as displaying the image in the PictureBox, there's really no need to create a MemoryStream and clone an Image considering that the PictureBox class has the Load and LoadAsync (which it looks like you want asynchronous capabilities) methods. But if you wanted to create a PictureBox for every item in your collection, you will need to iterate through the collection, create a new PictureBox, call the Load or Load Async method on the currently iterated URL, and then add it to the Form (or a container in general). This can be done using a traditional For/Each loop:
'Create a placeholder variable
Dim cardPictureBox As PictureBox
'Loop through every selected card URL
For Each url As String In Cards
'Create a new PictureBox
cardPictureBox = New PictureBox() With {
.Size = New Size(100, 100)
.SizeMode = PictureBoxSizeMode.CenterImage
.WaitOnLoad = False
}
'Add the PictureBox to the Form
Me.Controls.Add(cardPictureBox)
'Load the image asynchronously
cardPictureBox.LoadAsync(url)
Next

Vb.Net Application Screen Capture and Save it as a pdf

I have this small application which capture the screen and save it as a pdf. I am running this application on my PC which has 1080P primary display and 1080P TV as an extended display. When i put the application on the extended TV display (full screen mode) it doesn't capture entire screen. It capture only 1/4 of the screen. But i want to capture the entire screen. How do i fix this. Please help
Here is the code;
Private Sub SaveForm_shift1()
Dim yesterday As String = DateTime.Now.ToString("yyyy-MM-dd")
Dim filePath As String = "C:\Autodesk" + yesterday + ".jpg"
Dim bmpScreenshot As Bitmap = New Bitmap(Width, Height, PixelFormat.Format32bppArgb)
Dim gfxScreenshot As Graphics = Graphics.FromImage(bmpScreenshot)
gfxScreenshot.CopyFromScreen(Me.Location.X, Me.Location.Y, 0, 0, Me.Size, CopyPixelOperation.SourceCopy)
Try
gfxScreenshot.Dispose()
' Save the screenshot
bmpScreenshot.Save(filePath)
saveToPdf_shift1(filePath)
deleteImg(filePath)
Catch ex As Exception
MessageBox.Show(ex.ToString)
End Try
End Sub
Use this function to get a Bitmap with all the screens
Public Function GetScreenShot() As Bitmap
Dim bmp As Bitmap = New Bitmap(Screen.AllScreens.Sum(Function(s As Screen) s.Bounds.Width),
Screen.AllScreens.Max(Function(s As Screen) s.Bounds.Height))
Dim gfx As Graphics = Graphics.FromImage(bmp)
gfx.CopyFromScreen(New Point(0, 0), New Point(0, 0), bmp.Size)
Return bmp
End Function
If you have strange staggered screen positions, the empty areas will be black in the resulting bitmap.

VB Scale form for screen capture

I am trying to do a screen capture using the code below
Dim area As Rectangle = FormPrintArea.Bounds
Dim areaWidth As Integer = CInt(area.Width * ScaleFactor)
Dim areaHeight As Integer = CInt(area.Height * ScaleFactor)
Dim capture As Bitmap = New Bitmap(areaWidth, areaHeight, PixelFormat.Format32bppArgb)
Dim graph As Graphics = Graphics.FromImage(capture)
Dim ScaledSize As New Size With {
.Height = areaHeight,
.Width = areaWidth
}
graph.CopyFromScreen(area.X, area.Y, 0, 0, ScaledSize, CopyPixelOperation.SourceCopy)
PictureBox1.Image = capture
``
The problem is I can't find ScaleFactor, Everything works perfect as long as "Change the size of text, apps and the other items: 100%" in Windows 10 Display Settings but if it is set differently like 125% (recommended), I lose about 20% of the image. It looks like there is a ScaleFactor in the LanguageFont class but I can't seem to access it from VB (or C#).
The application has a VB Form (FormPrintArea) that the user uses to define the print area. If I set ScaleFactor to 1.25 on systems set to 125% (recommended), then everything works. Is there any way to get the value from Windows through an API?
I needed to declare my App DPIaware as #Hans Passant said, the easiest way is to add the lines below to the Partial Class startup form (Form1).
<DllImport("User32.dll")>
Private Shared Sub SetProcessDPIAware()
End Sub
Then change the New Sub at the end of the startup form to call SetProcessDPIAware
Public Sub New()
SetProcessDPIAware()
InitializeComponent()
End Sub
Once you do that you can get the ScaleFactor in Form1.Load as follows
DPIScalingX = Me.CreateGraphics().DpiX / 96
DPIScalingY = Me.CreateGraphics().DpiY / 96

Disposing dynamically created picturebox to release file lock?

I am trying to release the file lock on some images so i can move them into an archive folder.
The program loops through the images and adds pictureboxes to a flowlayout panel. After the operation is completed i dispose of the flowlayout panel, and then archive the files.
It is my understanding that disposing the panel will dispose of the picture boxes inside of it, however when i try the move operation i get a IOException Access Denied.
Code:
Dim ImagesInFolder As New List(Of Image)()
For Each JPEGImages As String In Directory.GetFiles(ExportDir.FullName, "*.jpg")
ImagesInFolder.Add(Image.FromFile(JPEGImages))
Next
Dim x As Integer = 0
Dim y As Integer = 0
For i As Integer = 0 To ImagesInFolder.Count - 1
Dim _image As New PictureBox()
_image.Location = New Point(x, y)
x += 50
_image.Image = ImagesInFolder(i)
_image.Size = New Size(50, 50)
_image.SizeMode = PictureBoxSizeMode.StretchImage
FlowLayoutPanel1.Controls.Add(_image)
Next
Later in the application:
FlowLayoutPanel1.Dispose()
Directory.Move(CurrentFolder, ArchiveFolder)
The problem is Because of loading image using Image.FromFile(file).
When you load image using Image.FromFile(file) the file will be locked.
To avoid locking file you can load your images using Image.FromStream.
Code:
Dim filePath = "path to your image file"
Dim contentBytes = File.ReadAllBytes(filePath)
Dim memoryStream As New MemoryStream(contentBytes)
Dim image= Image.FromStream(memoryStream)
YourPictureBox.Image = image