Cannot use Window control simultaneously in two threads - wpf

From the Page_load event of Window1 I'm calling a function of a public class and passing parameter as the same Window1. After the function is called, a thread is started. The thread is called on Page_Loaded event of Window1. The code is like this:
Private Sub StartScreen_Loaded(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles Me.Loaded
Try
TmHeartbeat.Stop()
TmHeartbeat.Interval = 600000
TmHeartbeat.Start()
ResetVariables()
FormLoadSetting(Me)
SetButtonProperty(btnEnglish, "\Images\ButtonBgBig.png", GetFormLabel("Start Screen", "Common", "Button English"))
SetButtonProperty(btnSpanish, "\Images\ButtonBgBig.png", GetFormLabel("Start Screen", "Common", "Button Spanish"))
SetDisplayTimer(DGT, False, Me, 1800000)
MediaElement1.Source = New Uri(GetPath("Images\EnglishVideo\" & GetFormLabel("Start Screen", "Common", "Video")))
If GetFormLabel("Start Screen", "Common", "Audio") <> "" Then
PlayAudio(APlay, GetFormLabel("Start Screen", "Common", "Audio"))
End If
Dim AudioPlay As New System.Media.SoundPlayer
Dim sc As New Screenshot
sc.TakeScreenshot(Me)
Catch ex As Exception
AliLogFileEntry(TransactionType.ErrorOnForm, "Error In Function: StartScreen_Loaded: " & Me.Title & ", ErrorMessage: " & ex.Message)
End Try
End Sub
The function TakeScreenshot(Me) which is in class Screenshot is called. Along with the Screenshot class, I also have another class named GetScreenshot and a function TakeScreenshot1. The code for the class file is like this:
Imports System.IO
Imports System.Threading
Public Class Screenshot
Public Sub TakeScreenshot(ByVal formname As Window)
Dim GT As New GetScreenshot
GT.source = formname
Dim newThread As New Thread(AddressOf GT.TakeScreenshot1)
newThread.Start()
End Sub
End Class
Public Class GetScreenshot
Public source As Window
Public Function TakeScreenshot1()
Thread.Sleep(2000)
If OG.GetValue("TakeScreenshot") <> "0" Then
Try
AliLogFileEntry(TransactionType.System, "In Function: TakeScreenshot")
Dim scale As Double = OG.GetValue("Screenshot_Scale") / 100
AliLogFileEntry(TransactionType.System, "In Function: GetJpgImage")
Dim renderHeight As Double = source.RenderSize.Height * scale
Dim renderWidth As Double = source.RenderSize.Width * scale
Dim renderTarget As New RenderTargetBitmap(CInt(Math.Truncate(renderWidth)), CInt(Math.Truncate(renderHeight)), 96, 96, PixelFormats.Pbgra32)
Dim sourceBrush As New VisualBrush(source)
Dim drawingVisual As New DrawingVisual()
Dim drawingContext As DrawingContext = drawingVisual.RenderOpen()
Using drawingContext
drawingContext.PushTransform(New ScaleTransform(scale, scale))
drawingContext.DrawRectangle(sourceBrush, Nothing, New Rect(New Point(0, 0), New Point(source.RenderSize.Width, source.RenderSize.Height)))
End Using
renderTarget.Render(drawingVisual)
Dim jpgEncoder As New JpegBitmapEncoder()
jpgEncoder.QualityLevel = 100
jpgEncoder.Frames.Add(BitmapFrame.Create(renderTarget))
Dim _imageArray As [Byte]()
Using outputStream As New MemoryStream()
jpgEncoder.Save(outputStream)
_imageArray = outputStream.ToArray()
End Using
Dim screenshot As Byte() = _imageArray
Dim dir As DirectoryInfo = New DirectoryInfo("Screenshots")
If Not dir.Exists Then dir = Directory.CreateDirectory(dir.FullName)
Dim path As String = AppDomain.CurrentDomain.BaseDirectory
Dim fileStream As New IO.FileStream("Screenshots\" & source.Title & ".jpg", FileMode.Create, FileAccess.ReadWrite)
Dim binaryWriter As New IO.BinaryWriter(fileStream)
binaryWriter.Write(screenshot)
binaryWriter.Close()
Catch ex As Exception
AliLogFileEntry(TransactionType.ErrorOnForm, "Error In Function: TakeScreenshot , ErrorMessage: " & ex.Message)
End Try
End If
End Function
End Class
When I debug this file, I get this error:
And the error is generated on line
Dim sourceBrush As New VisualBrush(source)
Pleas help

You are using source from a different thread than it was created on. In order to use a windows control on a different thread you need to call back to the original thread.
See How to: Make Thread-Safe Calls to Windows Forms Controls for more.

Related

saved image file showing twice when I refresh combobox list

So I am working with blobs in visual basic, and I'm saving image files to a database. When I save them I have their name.jpg showing up in a combobox, but when I save another image and it refreshes the list, the name of the previous image shows twice. I'm not sure how I managed that. I am new to this so don't look down on me too much!
When the button on my form is clicked:
Private Sub btnSaveBlob_Click(sender As Object, e As EventArgs) Handles btnSaveBlob.Click
SaveBlobToDatabase()
refreshBlobList()
End Sub
My methods:
Private Sub SaveBlobToDatabase()
GetCompleteFilePath()
Dim BLOB() As Byte
Dim FileStream As New IO.FileStream _
(CompleteFilePath, IO.FileMode.Open, IO.FileAccess.Read)
Dim reader As New IO.BinaryReader(FileStream)
BLOB = reader.ReadBytes(CInt(My.Computer.FileSystem.GetFileInfo(CompleteFilePath).Length))
FileStream.Close()
reader.Close()
Dim SaveDocCommand As New SqlCommand
SaveDocCommand.Connection = conn
SaveDocCommand.CommandText = "INSERT INTO DocumentStorage" &
"(FileName, DocumentFile)" &
"VALUES (#FileName, #DocumentFile)"
Dim FileNameParameter As New SqlParameter("#FileName", SqlDbType.NChar)
Dim DocumentFileParameter As New SqlParameter("#DocumentFile", SqlDbType.Binary)
SaveDocCommand.Parameters.Add(FileNameParameter)
SaveDocCommand.Parameters.Add(DocumentFileParameter)
FileNameParameter.Value =
CompleteFilePath.Substring(CompleteFilePath.LastIndexOf("\") + 1)
DocumentFileParameter.Value = BLOB
Try
SaveDocCommand.Connection.Open()
SaveDocCommand.ExecuteNonQuery()
MessageBox.Show(FileNameParameter.Value.ToString &
"saved to database.", "BLOB Saved!", MessageBoxButtons.OK,
MessageBoxIcon.Information)
Catch ex As Exception
MessageBox.Show(ex.Message, "Save Failed",
MessageBoxButtons.OK, MessageBoxIcon.Error)
Finally
SaveDocCommand.Connection.Close()
End Try
End Sub
Private Sub refreshBlobList()
Dim GetBlobListCommand As New SqlCommand("SELECT FileName FROM DocumentStorage", conn)
Dim reader As SqlDataReader
GetBlobListCommand.Connection.Open()
reader = GetBlobListCommand.ExecuteReader
While reader.Read
lstBlob.Items.Add(reader(0))
End While
reader.Close()
GetBlobListCommand.Connection.Close()
If lstBlob.Items.Count > 0 Then
lstBlob.SelectedIndex = 0
End If
End Sub
Objects in .net that show you a .Dispose method should be disposed. They have unmanaged code and need to properly release things. Connections, Commands, Streams, and Readers fall into this group. Luckily .net has provided Using...End Using blocks that handle this for us even if there is an error. For this reason, these objects should be kept local to the methods where they are used.
As much as possible, methods should perform a single task. I pulled out the code to create the byte array to a separate Function. I also split the data access code from the user interace code. You may want to put the data access code in a separate class. This would come in handy if you want to change to a web app, for example.
In the data access code:
You can pass the connection string directly to the constructor of the connection. Likewise, pass the CommandText and Connection directly to the constructor of the command. The .Add method of the .Parameters collection will create a new parameter from the name and datatype passed to it. You can also set the value on the same line.
Private ConStr As String = "Your connection string"
Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click
Dim RowsAdded As Integer
Try
RowsAdded = SaveBlobToDatabase()
Catch ex As Exception
MessageBox.Show(ex.Message, "Save Failed", MessageBoxButtons.OK, MessageBoxIcon.Error)
End Try
If RowsAdded = 1 Then
MessageBox.Show("Saved to database.", "BLOB Saved!", MessageBoxButtons.OK, MessageBoxIcon.Information)
End If
End Sub
Private Function SaveBlobToDatabase() As Integer
Dim RowsAdded As Integer
Dim CompleteFilePath As String = GetCompleteFilePath()
Using conn As New SqlConnection(ConStr),
SaveCommand As New SqlCommand("INSERT INTO DocumentStorage(FileName, DocumentFile) VALUES(#FileName, #DocumentFile);", conn)
SaveCommand.Parameters.Add("#FileName", SqlDbType.NChar).Value = Path.GetFileName(CompleteFilePath) 'Requires Imports System.IO
SaveCommand.Parameters.Add("#DocumentFile", SqlDbType.Binary).Value = GetByteArrayFromFile(CompleteFilePath)
conn.Open()
RowsAdded = SaveCommand.ExecuteNonQuery()
End Using
Return RowsAdded
End Function
Private Function GetByteArrayFromFile(FullPath As String) As Byte()
Dim Blob() As Byte
Using FileStream As New IO.FileStream(FullPath, IO.FileMode.Open, IO.FileAccess.Read),
reader As New IO.BinaryReader(FileStream)
Blob = reader.ReadBytes(CInt(My.Computer.FileSystem.GetFileInfo(FullPath).Length))
End Using
Return Blob
End Function
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
lstBlob.Items.Clear()
lstBlob.DataSource = refreshBlobList()
lstBlob.DisplayMember = "FileName"
If lstBlob.Items.Count > 0 Then
lstBlob.SelectedIndex = 0
End If
End Sub
Private Function refreshBlobList() As DataTable
Dim dt As New DataTable
Using conn As New SqlConnection(ConStr),
GetBlobListCommand As New SqlCommand("SELECT FileName FROM DocumentStorage", conn)
conn.Open()
dt.Load(GetBlobListCommand.ExecuteReader)
End Using
Return dt
End Function

Importing Microsoft.Office.Interop.Word breaks New Frame()

The problem is importing Microsoft.Office.Interop.Word (which I need elsewhere in this class) breaks "New Frame()". The error is
'New' cannot be used on an interface
My guess is that Interop.Word redefines "frame". How do I fix this?
My code:
Imports C1.WPF
Imports Microsoft.Office.Interop.Word
End Sub
Private Sub btn_SendQuote_Click(sender As Object, e As RoutedEventArgs) Handles btn_SendQuote.Click
Dim tab_SendQuote As New C1TabItem()
Dim frame_SendQuote As New Frame()
Dim scroller_SendQuote As New ScrollViewer()
Dim str_Name As String = "Send Quote"
Dim str_NavigationLink As String = "PM_SendQuote.xaml"
createNewTab(tab_SendQuote, frame_SendQuote, scroller_SendQuote, str_Name, str_NavigationLink)
End Sub
Private Sub createNewTab(tab As C1TabItem, frame As Frame, scroller As ScrollViewer, str_TabName As String, str_NavigationLink As String)
'Function to be used for adding tabs
'Add and name new tab
tab.Header = tabcontrol.Items.Count + 1 & ". " & str_TabName
tab.CanUserClose = True
tabcontrol.Items.Add(tab)
'Add frame to the tab and include new job subform page
With frame
.NavigationService.Navigate(New Uri(str_NavigationLink, UriKind.Relative))
.HorizontalAlignment = HorizontalAlignment.Stretch
.VerticalAlignment = VerticalAlignment.Top
.Margin = New Thickness(0, 0, 0, 0)
End With
With scroller
.CanContentScroll = vbTrue
.VerticalScrollBarVisibility = ScrollBarVisibility.Auto
.HorizontalScrollBarVisibility = ScrollBarVisibility.Auto
.Content = frame
End With
tab.Content = scroller
' Set new tab as active tab
tabcontrol.SelectedIndex = tabcontrol.Items.IndexOf(tab)
End Sub

How to crop and resize image using vb.net and SQL Server

I'm using vb.net and sql server 2008 and a picturebox for uploading photos.
This is what I want to do.
1.The image can be anything. It can be bitmap,jpg,png, anything
2. upload image from my computer
3. Crop the image and resize it to fit in my picture box.
4. Save it to the SQL Server database
This is my working code for uploading and saving images in the database.
Public Class Form1
Dim a As New OpenFileDialog
Private Sub PictureBox1_DoubleClick(sender As Object, e As EventArgs) Handles PictureBox1.DoubleClick
Dim picl as string
a.filter = nothing
picl = a.filename
a.showdialog()
picturebox1.image = image.fromfile(a.filename)
End Sub
Private Sub btnSave_Click(sender As Object, e As EventArgs) Handles btnSave.Click
cn.Open()
Using cmd As New SqlClient.SqlCommand("dbo.uspAdd", cn)
cmd.CommandType = CommandType.StoredProcedure
cmd.Parameters.Add(New SqlClient.SqlParameter("#customerPic", SqlDbType.Image)).Value = IO.File.ReadAllBytes(a.FileName)
cmd.ExecuteNonQuery()
MsgBox("Save Record New record Successfully")
End Using
cn.close
End Sub
I have read some reference here but it does not work for me and now I'm stuck for almost an hour.
This is what i have tried.
Imports System.Drawing.Drawing2D
Public Class Form1
Dim a As New OpenFileDialog
Private Sub PictureBox1_DoubleClick(sender As Object, e As EventArgs) Handles PictureBox1.DoubleClick
Dim picl As String
a.Filter = Nothing
picl = a.FileName
a.ShowDialog()
PictureBox1.Image = Image.FromFile(a.FileName)
Dim OriginalImage = Image.FromFile(a.FileName)
Dim CropRect As New Rectangle(100, 0, 100, 100)
Dim CropImage = New Bitmap(CropRect.Width, CropRect.Height)
Using grp = Graphics.FromImage(CropImage)
grp.DrawImage(OriginalImage, New Rectangle(0, 0, CropRect.Width, CropRect.Height), CropRect, GraphicsUnit.Pixel)
OriginalImage.Dispose()
End Using
End Sub
Can someone please help me to fix my code. Any help would be very much appreciated. Thanks
you can crop image and designate the path and the name of cropped image with the following code sample , anyway ur sql fucntion needs image file path
Dim LocX = 100 'x cord. of where crop starts
Dim LocY = 0 'y cord. of where crop starts
Dim CropW = 100 'Crop width
Dim CropH = 100 'Crop height
Dim CropRect As New Rectangle(LocX, LocY, CropW, CropH)
Dim OriginalImage = PictureBox1.Image
Dim CropImage = New Bitmap(CropRect.Width, CropRect.Height)
Using grp = Graphics.FromImage(CropImage)
grp.DrawImage(OriginalImage, New Rectangle(0, 0, CropRect.Width, CropRect.Height), CropRect, GraphicsUnit.Pixel)
CropImage.Save("cropped file path and name here")
End Using

VB.NET - create an object from this example code (learning object orientated programming techniques)

I'm going back through some code in a vb.net I wrote and re factoring, I'm trying to make it more object orientated as I'm trying to teach myself better coding practice. I have one particular section of code that gets repeated a lot throughout the app, the basic structure doesn't really change other than sometimes is may have more or less parameters (the code inside the function):
Public Function EditTest(ByVal test_id As Integer) As DataTable
Dim con As OleDbConnection = New OleDbConnection(FileLocations.connectionStringNewDb)
Dim cmd As New OleDbCommand
cmd.CommandType = CommandType.StoredProcedure
cmd.CommandText = "Q_EDIT_TEST"
cmd.Parameters.Add("#Parameter1", OleDbType.Integer).Value = test_id
cmd.Connection = con
con.Open()
Dim da As OleDbDataAdapter = New OleDbDataAdapter(cmd)
Dim ds As DataSet = New DataSet()
da.Fill(ds, "editTest")
Dim rtable_ As DataTable = ds.Tables("editTest")
con.Close()
Return rtable_
End Function
I'd like to be introduce object orientation into this by just creating a new object, based on a class? "DatabaseData" lets call the class, with it's creation parameters as (in this example) the name of the query I'm going to run (Q_EDIT_TEST) and the parameters I'm going to add to it (this is where I'm finding it tricky). The example above I have 1 parameter, what if I wanted to create an object with 2 parameters? 3 parameters? etc. Can anyone show me an example of how this could be achieved? Cheers!
Ok bear with me here :-) still learning here:
Public Class DatabaseData
Property queryName As String
Dim con As OleDbConnection = New OleDbConnection(FileLocations.connectionStringNewDb)
Dim cmd As OleDbCommand
cmd.CommandType = CommandType.StoredProcedure
cmd.CommandText = queryName
End Class
cmd.CommandType throws an error:
"Error 1 Declaration expected. "
Ok this is what I have so far, it doesn't work I know but am I on the right track here? or am I being insane and is there a much better way of doing this...
Imports System.Data.OleDb
Imports System.Data.SqlClient
Module PureClassClasses
Public Class Params
Property myParams As New List(Of customParam)
Class customParam
Property Name As String
Property Value As OleDbType
End Class
End Class
Public Class DataGetter
Private queryName As String
Private listOfParams As List
Private rtable As DataTable
Public Sub New(ByVal Qn As String, ByVal paramList As List)
queryName = Qn
listOfParams = paramList
End Sub
ReadOnly Property ReturnedData(ByVal queryName As String, ByVal listOfParams) As DataTable
Get
Dim conn As OleDbConnection = New OleDbConnection(FileLocations.connectionStringNewDb)
Dim cmd As New OleDbCommand
cmd.CommandType = CommandType.StoredProcedure
cmd.CommandText = queryName
'cmd.Parameters.Add("#Parameter1", OleDbType.Integer).Value = test_id <-- HELP?
'cmd.Parameters.Add("#Parameter2", OleDbType.Integer).Value = test_id <-- HELP?
cmd.Connection = conn
conn.Open()
Dim da As OleDbDataAdapter = New OleDbDataAdapter(cmd)
Dim ds As DataSet = New DataSet()
da.Fill(ds, "returnedData")
Dim rtable_ As DataTable = ds.Tables("returnedData")
conn.Close()
Return rtable
End Get
End Property
End Class
End Module
I want to be able to call it somthing along the lines of...
Dim p As New Params
Dim cp As New Params.customParam
cp = new customParam
cp.name = "My First Param"
cp.type = OleDbType.Integer
cp.value = 5
p.myparams.add(cp)
Dim myDataTable As New DataTable
myData = new DataGetter
myDataTable = myData.ReturnData("Q_A_QUERY", listofparams)
Is this making any sense... :S
This not really looking at the proper properties of db params, but to give you an idea about the structure.
Class Params
property myParams as new list(of customParam)
class customParam
property name as string
property value as OleDbType
end class
end class
Usage:
dim p as new params
dim cp as new params.customParam
cp = new customParam
cp.name = "My First Param"
cp.value = OleDbType.Integer
p.myparams.add(cp)
cp = new customParam
cp.name = "My 2ndParam"
cp.value = OleDbType.Integer
p.myparams.add(cp)
Then, pass p as an object into your routine, and go and get all the custom obects (params), it contains.
Your Code, simplified:
Public Function EditTest(ByVal test_id As Integer, p as params) As DataTable
for each cp in p
cmd.Parameters.Add(cp.name, cp.value)
next
end function
You will need co construct your classes with the right properties. This code is only to show you how OO would be used here..
for database access, we have a full scale data access layer that suits all of our needs and it's fully object oriented. it's too large for me to post here, but if you would like a copy send me a message.

WPF Image Path Blues

I have a number of images in the resources directory and access them like this...
Public Function ReturnToolBarImage(ByVal ImageName As String) As Image
Dim UpdateImage As New Image
With UpdateImage
End With
Dim UpdateBitmap As New BitmapImage
With UpdateBitmap
.BeginInit()
.UriSource = New Uri("pack://application:,,,/HOA_Manager_Client_04;component/Resources/" & ImageName, UriKind.Absolute)
.EndInit()
.DecodePixelHeight = 32
End With
UpdateImage.Source = UpdateBitmap
Return UpdateImage
End Function
I have control that insists upon using an Image Path not an Image - I can access it like this
.ImageSource = "//application:,,,/HOA_Manager_Client_04;component/Resources/Customers.png"
But the image is, or course, far too large.. Soooooo I added a function to save the resized image to a Temp folder (that is working) and send the path back - but for some reason that just doesn't work (no errors, just no image). Any ideas?
Public Function ReturnToolBarImageAsString(ByVal ImageName As String) As String
Dim UpdateImage As New Image
With UpdateImage
End With
Dim UpdateBitmap As New BitmapImage
With UpdateBitmap
.BeginInit()
.UriSource = New Uri("pack://application:,,,/HOA_Manager_Client_04;component/Resources/" & ImageName, UriKind.Absolute)
.EndInit()
.DecodePixelHeight = 32
End With
Using FS As New IO.FileStream("../../Pages/Temp/" & ImageName, IO.FileMode.Create)
Dim vEncoder As New PngBitmapEncoder
vEncoder.Frames.Add(BitmapFrame.Create(UpdateBitmap))
vEncoder.Save(FS)
End Using
'Return "../Temp/" & ImageName
Return "//application:,,,/HOA_Manager_Client_04;component/Pages/Temp/" & ImageName
End Function
By creating a directory at run time in the Bin directory seems to have cured the issue
Public Function ReturnToolBarImageAsString(ByVal ImageName As String) As String
Dim UpdateImage As New Image
With UpdateImage
End With
Dim UpdateBitmap As New BitmapImage
With UpdateBitmap
.BeginInit()
.DecodePixelHeight = 32
.UriSource = New Uri("pack://application:,,,/HOA_Manager_Client_04;component/Resources/" & ImageName, UriKind.Absolute)
.EndInit()
End With
Dim vPath As String = My.Application.Info.DirectoryPath
If Not IO.Directory.Exists(vPath & "\Temp_Images") Then
IO.Directory.CreateDirectory(vPath & "\Temp_Images")
End If
Using FS As New IO.FileStream(vPath & "\Temp_Images\" & ImageName, IO.FileMode.Create)
Dim vEncoder As New PngBitmapEncoder
vEncoder.Frames.Add(BitmapFrame.Create(UpdateBitmap))
vEncoder.Save(FS)
End Using
Return vPath & "\Temp_Images\" & ImageName
End Function

Resources