New Thread for Loading animated Gif - wpf

I have read countless pages on how this can be achieved but none seem to be working for me.. I wonder if it is because I am using WPF??
I have an image control on my WPF page that holds an animated gif. Its initial Visibility property is set to hidden - the idea being it will become visible when the user logs in and then disappears once login has been achieved.
So, my first thought was to make the image visible on button click - carry out the grunt work then hide it again. However the image never gets displayed until after all the processing has completed - rendering the idea a useless one.
So, after a bit of digging it seems people have achieved this by processing a new thread and using the Background Worker object. I have tried this but continually get the error:
The calling thread cannot access this object because a different
thread owns it.
My code is as follows:
Global Variable for the thread object:
Friend g_thLoading As Thread
On button click for login on I have:
Private Sub btnLoginOk_Click(sender As Object, e As RoutedEventArgs) Handles btnLoginOk.Click
g_thLoading = New Thread(AddressOf LoadingImage)
g_thLoading.IsBackground = True
g_thLoading.Start()
Try
... Heavy Processing that takes a while ...
Catch liEX As UnauthorizedAccessException
txbResults.Text = liEX.Message()
Catch ex As Exception
MsgBox(ex.Message)
End Try
g_thLoading.Abort()
End Sub
Then the method for making the image visible:
Public Sub LoadingImage()
imgLoading.Visibility = Visibility.Visible
End Sub
I have read some posts about the error message stating to Freeze the object (cannot see how this works) and also look at using a Dispatcher - this is a little lost on me though as every example I have tried does not work.

Related

How to update a property on the viewmodel before calling a long running process?

I have the following code:
Public Sub ExecuteMyCommand
ShowProgressBar=True
CallLongRunningProcess
ShowProgressBar=False
End Sub
To get the progressbar in the UI to actually show before the long running process starts this code has to be rewritten to:
Public Async Sub ExecuteMyCommand
ShowProgressBar=True
Await Task.Run(Sub() CallLongRunningProcess)
ShowProgressBar=False
End Sub
Now the problem:
In some methods the long running process involves creating WPF UserControls. These must be created on a STA thread. I have found how to do this, but then I get an InvalidOperationException because the user control is owned by non UI thread. So, I am looking for a way to update the ShowProgressBar property before calling the long running process on the UI thread (yeah, ugly, I know).
Did you try Dispatcher.Invoke? It keeps the tasks on the UI thread.
You'd have to move the ShowProgressBar=False into the delegate, of course.
Public Sub ExecuteMyCommand
ShowProgressBar=True
Me.Dispatcher.Invoke(DispatcherPriority.Normal, New LongRunningProcessDelegate())
End Sub
I'd also separate UI modifying logic from the computing task, i.e. the task should call back into the UI thread when its done, and modification of UI elements would be left to the callback.

Exception: The Calling thread cannot access this object because a different thread owns it

I'm converting a UI from windows forms to WPF. I'm getting the following exception "The Calling thread cannot access this object because a different thread owns it" whenever I try to call anything on this new WPF window I created.
I referred stack overflow and many websites to find out that I should use Dispatcher.CheckAccess() or somethings similar to dispatcher and check access. I tried many such things
This is one of the things that I have used
Private Delegate Sub ShowSkinInvoked()
If (Dispatcher.CheckAccess()) Then
Me.Show()
Else
Dim d As ShowSkinInvoked = New ShowSkinInvoked(AddressOf ShowSkin)
Dispatcher.Invoke(d)
End If
This has removed the exception and while debugging the error is gone but it freezes the application and I cannot do anything other than terminate it. It doesn't even show the window after "Me.Show".
Also, if I compile the program and then make the calling module use this compiled exe by specifying path to exe then for some reason it works perfect.
If this sounds confusing then what I mean is, I have multiple forms. If I call the code in module A to load and display module B then it gives me the exception but if I call the code in module A to run the compiled exe of module B then it runs perfectly.
Any suggestions?
When WPF creates a user interface it created a thread that is responsible for handling all the user interaction events and scheduling the rendering. This is called the dispatcher thread. Many of the objects that it creates are sub classes of DispatcherObject.
You can't call methods on a DispatcherObject from threads other then the Dispatcher thread that created them. The reasons why are complicated but relate to COM interop.
When you are in a Buttons click event you are running on dispatcher thread.
If you are coming from another thread you must get your work to be performed on the dispatcher thread. It can typically be found by accessing the static current dispatcher Dispatcher.CurrentDispatcher, unless your creating multiple dispatcher threads.
However I would suggest explaining your problem in terms of what work your trying to do with regards to having one form show ui on another. There are multiple ways like an EventAggregator to communicate between ui that might be more appropriate.

Unable To Stop Individual SQL Dependencies On Form Close

I am very new to SQL Dependencies and found this is a great way to handle data updating within my application. Previously I was constantly querying my Database every minute or so to check for changes, whereas now I receive update notifications on change events as they occur.
When my application first launches, it starts the SQL Dependency to the database with: SqlDependency.Start(ConnectionString) and in turn Stops it once I exit.
I then setup about 3 or 4 dependency queries to receive updates for different data within my application - all works great and exactly how I need.
However, my issue is this:
I have a second form of which I call multiple instances of. This form also has about 3 different dependencies attached to update different data again. However, when I close the instance of the form, I am finding that the dependency continues to run and thus throws an error in my application due to the form now being closed.
My question is: Is there a way I can stop the individual SQL Dependencies once I close the form? The dependencies on the Main form should continue to run, but every time I open and close an instance of the second form, I need to stop and start those queries from running.
Is this possible?
I can provide sample code snippets if required.
Any help appreciated. Thankyou
There is a Stop method in the SqlDependency class that you can invoke when you are done listening. Is there some issue with this?
ADDED
According to the docs, "Stops a listener for a connection specified in a previous Start call."
So, thhough I have not ever done exactly this, it should work if you are using a different connections in the main and child forms.
Okay, I ended up resolving this issue myself after a little bit of thought and researching.
Previously, I was declaring the SQLDependency within the same Sub Routine as the Handler like so:
Private Sub RefreshRegisterData()
...
Dim Register_dep As SqlDependency
Register_dep = New SqlDependency(cmd)
AddHandler Register_dep.OnChange, AddressOf Registers_OnChange
...
End Sub
However, to resolve my issue I basically moved the scope of the SQLDependency outside of the Sub Routine and declared it globally within the Class:
Public Class frmStorePage
Private Register_dep As SqlDependency
...
Private Sub RefreshRegisterData()
...
Register_dep = New SqlDependency(cmd)
AddHandler Register_dep.OnChange, AddressOf Registers_OnChange
...
End Sub
End Class
Then, when exiting my form, I simply implemented a RemoveHandler call to stop the message queue and destroy the existing handler:
Private Sub frmStorePage_FormClosed(sender As Object, e As FormClosedEventArgs) Handles Me.FormClosed
RemoveHandler Register_dep.OnChange, AddressOf Registers_OnChange
...
End Sub
Now, not sure if this is the preferred method of solving this issue, however, I am yet to witness any implications by this code and it solves my problem.

How to send message/activate a SysTray application?

We are trying to setup a SysTray application which can be activated from elsewhere. To be more specific the activation will come from a third party app which we cannot modify but allows us to activate our own app via its path (plus a parameter/argument).
When it gets activated we want to put up a BalloonText, there are to be no forms involved.
Therefore we have two problems to solve:
Make our SysTray application single instance (since it's no good generating multiple instances).
Allow this other app to activate our application with arguments
Lots of help out there to help learners create simple SysTray applications (and indeed we've done it ourselves as part of a solution to an unconnected project).
However we've never tried to make it single instance before.
Lots of help out there to help learners create single instance Winforms applications (again we've done this as part of other projects) but always simple applications with conventional forms (not SysTray). We use the VisualBasic WindowsFormsApplicationBase method.
Can't seem to combine these two approaches into a single solution.
Update:
Hans answer below nails it (and especially his comment):
This is already taken care of with a NotifyIcon, drop it on the form.
And the "Make single instance application" checkbox. And the
StartupNextInstance event. You'll need to stop assuming there's
anything special about this
As far as your first question about checking for other instances this seems to work. I used the CodeProject example as a baseline. In your Sub Main routine you can check for other instances by using the GetProcessesByName Method of the Process class. Something like this:
Public Sub Main()
'Turn visual styles back on
Application.EnableVisualStyles()
'Run the application using AppContext
Dim p() As Process
p = Process.GetProcessesByName("TrayApp") 'Your application name here
If UBound(p) >= 0 Then
End
End If
Application.Run(New AppContext)
End Sub
For the second question if your SysTray application is already running you might want to give this article on .Net Interprocess Communication a try. Otherwise parse the CommandLine arguments in yourSub Main as it is created.
From above article:
The XDMessaging library provides an easy-to-use, zero-configuration solution to same-box cross-AppDomain communications. It provides a simple API for sending and receiving targeted string messages across application boundaries. The library allows the use of user-defined pseudo 'channels' through which messages may be sent and received. Any application can send a message to any channel, but it must register as a listener with the channel in order to receive. In this way, developers can quickly and programmatically devise how best their applications can communicate with each other and work in harmony.
Everything becomes trivial when you actually do use a form. It is simple to put your app together with the designer, simple to get your app to terminate, simple to avoid a ghost icon in the tray, simple to create a context menu, simple to add popups if you ever need them.
The only un-simple thing is getting the form to not display. Paste this code in the form's class:
Protected Overrides Sub SetVisibleCore(ByVal value As Boolean)
If Not Me.IsHandleCreated Then
Me.CreateHandle()
value = False
End If
MyBase.SetVisibleCore(value)
End Sub
The "Exit" command in the context menu is now simply:
Private Sub ExitToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ExitToolStripMenuItem.Click
Me.Close()
End Sub

How to create a form on its own thread and keep it open throughout application lifetime

I am creating a little testing component and am running into a problem
Basically the component is a decorator on a class that controls all access to the database, it creates a form with a two buttons on it: "Simulate Lost Connection" and "Reconnect". Press the button, and instead of letting function calls pass through the wrapper starts throwing NoConnectionException()s nice and simple, and real helpful for testing.
The problem is that this particular application when it detects a lost connection raises a modal dialog box "connection lost!" that sits there until the connection is regained. Because it is modal I cannot press my nifty button to simulate regained connectivity.
What I need to do therefore is to run my little testing form in a different thread. I'm not absolutely sure how to do that. I tried
new Thread(
new ThreadStart(
(Action)delegate {_form.Start();}
)
).Start();
But the thread closes as soon as the method returns so the form never shows up except for an instant.
Any idea how I go about achieving what I want?
You will need to start a message loop on the newly created thread. You can do that by calling Application.Run(form).
Try
new Thread(
new ThreadStart((Action)delegate {
_form.Start();
System.Windows.Forms.Application.Run();
}
)
).Start();
It sounds like you aren't holding on to your thread. It's an object, like everything else, so if it is scoped to your method, it will fall out of scope once your method exits. Try making it an instance variable.
showdialog :
System.Threading.Thread t = new System.Threading.Thread(new form1().ShowDialog());
t.Start();

Resources