in vb6, how to capture timer event which the Timer created by code? [duplicate] - timer

How can i achieve event handling for dynamic controls in VB6? Any ideas?

The easiest way is to declare a module-level variable of the same type as the control, and use the WithEvents keyword. For example
Option Explicit
' Declare object variable as CommandButton and handle the events.'
Private WithEvents cmdObject As CommandButton
Private Sub Form_Load()
'Add button control and keep a reference in the WithEvents variable'
Set cmdObject = Form1.Controls.Add("VB.CommandButton", "cmdOne")
cmdObject.Visible = True
cmdObject.Caption = "Dynamic CommandButton"
End Sub
'Handle the events of the dynamically-added control'
Private Sub cmdObject_Click()
Print "This is a dynamically added control"
End Sub
There are more complicated variations if you want to be able to handle events from many different controls, perhaps of different types, through one central routine.

That depends on whether the control is intrinsic or not.
This article explains it.

Related

How do I resolve this apparent Catch22

I have a wpf custom control with (as is common) a shared constructor. This one looks like this;
Shared Sub New()
'This OverrideMetadata call tells the system that this element wants to provide a style that is different than its base class.
'This style is defined in Themes\Generic.xaml
DefaultStyleKeyProperty.OverrideMetadata(GetType(VtlDataNavigator), New FrameworkPropertyMetadata(GetType(VtlDataNavigator)))
ItemsSourceProperty.OverrideMetadata(GetType(VtlDataNavigator), New FrameworkPropertyMetadata(Nothing, AddressOf OnItemsSourceHasChanged))
End Sub
The control inherits ItemsControl and I want to have notification of when the itemsSource changes hence the second line in the constructor. OnItemsSourceHasChanged needs to be a shared sub in order for the line in the constructor to compile. Fine.
In the shared sub I have the following:
Private Shared Function OnItemsSourceHasChanged(d As DependencyObject, e As DependencyPropertyChangedEventArgs) As Object
RecordCount = Items.SourceCollection.Cast(Of Object)().Count()
End Function
which of course fails to compile because you cannot refer to an instance method of a class from within a shared method without creating a specific instance of the class. Even If a create a separate non shared handler for this I will still end up with the same error when trying to call it.
In essence all I want is the total RecordCount from the ItemsSource (hence my need to know when it's changed) so that I can then assign that value to RecordCount. However what I appear to have instead is the proverbial catch 22 of errors.
The answer is probably staring me in the face, but for now it escapes me. Any Ideas?

WPF Custom Control VB.net

I've created a custom control called ecTextBox in VB.NET which uses a control template in Generic.xaml. That works.
In code behind of the custom control I override metadata in the constructor:
Public Sub New()
DefaultStyleKeyProperty.OverrideMetadata(GetType(ecTextBox), New FrameworkPropertyMetadata(GetType(ecTextBox)))
End Sub
In MainWindow.xaml I use the custom control with a simple
<ec:ecTextBox/>
That works fine.
But if I throw a second control or change the propertys of the first ecTextBox in MainWindow.xaml, I get the message "PropertyMetaData is already registered for type ecTextBox".
In StackOverflow I've read, that C# programmers should use the static-Keyword for the constructor. But if I change the constructor to
Shared Sub New
DefaultStyleKeyProperty.OverrideMetadata(GetType(ecTextBox), New FrameworkPropertyMetadata(GetType(ecTextBox)))
End Sub
the second custom control don't uses the control template but appears as a normal TextBox without Border.
What is the correct way to override the metadata for all used ecTextBox controls and prevent the errors?
This is the solution:
Public Sub New()
MyBase.New()
End Sub
Shared Sub New()
DefaultStyleKeyProperty.OverrideMetadata(GetType(ecTextBox), New FrameworkPropertyMetadata(GetType(ecTextBox)))
End Sub

Threaded Function has multiple parameters and returns data

I am working on a WPF .NET 3.5 application that does a few long tasks that I would like to make a seperate thread to the UI thread to process the data and then when completed update some labels in the UI. The problem I am having is that the function I have uses two parameters and I am struggling to work out how to call a function with multiple parameters in a thread and update the UI.
I have been playing around with using a Delegate Sub to call the function (it is located in a seperate Class), and my code was also attempting to return a dataset from the function for the calling thread to update the UI, but I am not sure if this is the best practice to achieve this or wether I should use a dispatcher for the called function to do the UI updating (feedback would be greatly appreciated).
My code is as follows.
Private Delegate Sub WorkHandler(ByVal input1 As String, ByVal input2 As String)
Private Sub Window_Loaded(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
Dim test_helper As New test_global
Dim worker As New WorkHandler(AddressOf test_helper.getWeatherData)
worker.BeginInvoke("IDA00005.dat", "Adelaide", AddressOf weatherCallBack, Nothing)
' The following is what I was using prior to attempting to work with threads, do I continue to update the UI here getting the called function to return a dataset, or do I have the called function do the UI updating?
'Dim ls As DataSet = test_helper.getWeatherData("IDA00005.dat", "Adelaide")
'Dim f_date As String = ls.Tables("weather").Rows(1).Item(3).ToString
End Sub
Public Sub weatherCallBack(ByVal ia As IAsyncResult)
CType(CType(ia, Runtime.Remoting.Messaging.AsyncResult).AsyncDelegate, WorkHandler).EndInvoke(ia)
End Sub
And my function that I am attempting to call is as follows:
Class test_global
Public Sub getWeatherData(ByVal filename As String, ByVal location As String) 'As DataSet
...
End Sub
End Class
My problem is if I was to have the calling thread to update the UI, how do I have the called thread to return a dataset, or if the called thread is to update the UI, how do I go about achieving this?
Update:
Following the recomendations provided, I have impletemented a BackgroundWorker that raises a DoWork and RunWorkerCompleted events to get the data and update the UI, respectively. My updated code is as follows:
Class Weather_test
Implements INotifyPropertyChanged
Private WithEvents worker As System.ComponentModel.BackgroundWorker
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Private Sub NotifyPropertyChanged(ByVal info As String)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(info))
End Sub
Private Sub Window_Loaded(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
Dim test_helper As New test_global
Dim worker = New System.ComponentModel.BackgroundWorker
worker.WorkerReportsProgress = True
worker.WorkerSupportsCancellation = True
Dim str() = New String() {"IDA00005.dat", "Adelaide"}
Try
worker.RunWorkerAsync(str)
Catch ex As Exception
MsgBox(ex.Message)
End Try
End Sub
Private Sub worker_DoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles worker.DoWork
Dim form_Helpder As New test_global
Dim ds As DataSet = form_Helpder.getWeatherData(e.Argument(0), e.Argument(1))
e.Result = ds
End Sub
Private Sub worker_Completed(ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs) Handles worker.RunWorkerCompleted
If e.Error IsNot Nothing Then
MsgBox(e.Error.Message)
Else
...
NotifyPropertyChanged("lbl_minToday")
...
End If
End Sub
End Class
I then have in a seperate class my functions that get and process the data.
I am able to debug the code in Visual Studio 2010 and the form displays but the labels are not updating, and when I put a breakpoint at the RunWorkerAsync line the line is called and the Window_Loaded sub completes but it appears that none of the DoWork or RunWorkerCompleted events are called (well at least the functions are not).
Can anyone provide some assistance on how I can debug the code to see why these functions are not being called?
Also, is the above code the correct method that was recommended in the answers?
Any assistance provided will be greatly appreciated.
Matt
You should use the BackgroundWorker component.
You should call your function in the DoWork handler and set e.Result to the returned DataSet.
You can then update the UI in the RunWorkerCompleted handler.
Use a BackgroundWorker. Implement your long-running method, and pass the arguments to the method in the DoWorkEventArgs parameters of the DoWork event handler. Do not update the UI, either directly or indirectly (i.e. don't update properties of your view model), in this method.
Use progress reporting to update the UI while the method is running: call ReportProgress in the long-running method, passing any information that needs to appear in the UI in the UserState parameter. In the ProgressChanged event handler, get the state from the ProgressChangedEventArgs and update the UI (by, one hopes, updating the appropriate properties of your view model and raising PropertyChanged).
You'll need to implement a class to contain the user state for progress reporting, since UserState is of type object.
Note that you can also update the UI with the results of the long-running method when it's complete. This is done in a similar fashion to progress reporting: implement a class to contain the results, set the Result property of the DoWorkEventArgs to an instance of this class, and the result will be available in the Result property of the WorkCompletedEventArgs when the RunWorkerCompleted event is raised.
Make sure that you handle any exceptions that the long-running method raises by checking the Error property of the WorkCompletedEventArgs.
I don't have much experience with BackgroundWorker (I have only used it once), but it is definitely a solution to your problem. However, the approach I always use is to start a new Thread (not ThreadPool thread via delegates), which acquires a lock and then updates all of the properties. Provided that your class implements INotifyPropertyChanged, you can then use databinding to have the GUI automatically update any time the property changes. I have had very good results with this approach.
As far as passing a Dispatcher to your thread goes, I believe you can do that as well. However, I would tread lightly because I believe I have run into cases with this where the Dispatcher I think I'm using is no longer associated with the main thread. I have a library that needs to call a method that touches GUI elements (even though the dialog might not be displayed), and I solved this problem by using Dispatcher.Invoke. I was able to guarantee that I was using the Dispatcher associated with the main thread because my application uses MEF to Export it.
If you'd like more details about anything I've posted, please comment and I'll do my best to embellish on the topics.

WPF and VB.net: Data Binding to Separate Class created outside of Expression Blend

I have a WPF application with form that has a textbox named "txtStatusWindow". I also have a vb.net class handed to me by a co-worker that needs to be called and executed by the code in my application code-behind. My co-worker insists that I will need to use common .net events to update the textbox on my form.
The separate vb.net class:
Public Class globalclass
Public Event txtStatusWindow(ByVal Text As String)
Public Sub InitializeProgram()
RaiseEvent txtStatusWindow("Updating something.")
System.Threading.Thread.Sleep(2000)
RaiseEvent txtStatusWindow("Updating something else.")
System.Threading.Thread.Sleep(2000)
RaiseEvent txtStatusWindow("Updating something other than else.")
System.Threading.Thread.Sleep(2000)
RaiseEvent txtStatusWindow("Updating something other than the else stuff.")
System.Threading.Thread.Sleep(2000)
End Sub
End Class
I need to be able to call the sub "InitializeProgram()" from my code-behind, and it needs to be able to update "txtStatusWindow.text" as it runs.
I told him that the updating of the text box can be done with data-binding, but I don't know how to integrate a separate class like this into my project, how to call methods in it, or how to cause it to update my text blocks through data binding.
I also suggested that the methods in this class aren't optimal for connecting to the WPF project anyway, but he just wrote it as an example to discover how to connect the two projects.
Eventually, I will need to integrate classes like these that will be running separate threads to update their data from a dynamic source, and cause many controls to update in my application.
So far, the only way we have been able to get this to work from my code-behind is this:
Partial Public Class SplashScreen
Dim NewText as String
Public WithEvents Globals As globalclass = New globalclass
Public Delegate Sub StringDelegate(ByVal Text As String)
Public SplashText As String
Public Sub New()
MyBase.New()
Me.InitializeComponent()
Me.Show()
Globals.InitializeProgram()
End Sub
Public Sub UpdateSplashscreenHandler(ByVal Text As String) Handles Globals.UpdateSplashScreen
StatusWindowText.Text = Text
End Sub
Notwithstanding the fact that the WPF screen "freezes" until the "globalclass InitializeProgram" method completes (txtStatusWindow.Text does not update while sub without using the esoteric "refresh" extension...), I fully believe we are going about this the wrong way.
There are precious few examples out there concerning the integration and then binding to objects in existing code. Thanks for examining our little quandary.
If this status window is in XAML and the status window is a UserControl, then add a StatusText dependency property to the status window. Then, in the XAML you can bind to the value of that property with something like:
<UserControl x:Name="MyStatusWindow" ...>
<TextBlock Text="{Binding Path=StatusText, ElementName=MyStatusWindow}" />
</UserControl>
Then, from your event, just update the value of that StatusText property.
(Is that even close to what you were asking?)
Also, about that freezing: Instead of doing that updating in the constructor of that class, you might want to do it from the Loaded event of that control. It will still be freezing, though, unless you move it to a separate thread. Right now, that's happening on the same thread that the UI message pump is running on. This is the Dispatcher for that UI.

Is there an event that triggers if the number of ListViewItems in a ListView changes? (Windows Forms)

I'd like to enable/disable some other controls based on how many items are in my ListView control. I can't find any event that would do this, either on the ListView itself or on the ListViewItemCollection. Maybe there's a way to generically watch any collection in C# for changes?
I'd be happy with other events too, even ones that sometimes fire when the items don't change, but for example the ControlAdded and Layout events didn't work :(.
#Domenic
Not too sure, Never quite got that far in the thought process.
Another solution might be to extend ListView, and when adding and removing stuff, instead of calling .items.add, and items.remove, you call your other functions. It would still be possible to add and remove without events being raised, but with a little code review to make sure .items.add and .items.remove weren't called directly, it could work out quite well. Here's a little example. I only showed 1 Add function, but there are 6 you would have to implement, if you wanted to have use of all the available add functions. There's also .AddRange, and .Clear that you might want to take a look at.
Public Class MonitoredListView
Inherits ListView
Public Event ItemAdded()
Public Event ItemRemoved()
Public Sub New()
MyBase.New()
End Sub
Public Function AddItem(ByVal Text As String) As ListViewItem
RaiseEvent ItemAdded()
MyBase.Items.Add(Text)
End Function
Public Sub RemoveItem(ByVal Item As ListViewItem)
RaiseEvent ItemRemoved()
MyBase.Items.Remove(Item)
End Sub
End Class
I can't find any events that you could use. Perhaps you could subclass ListViewItemCollection, and raise your own event when something is added, with code similar to this.
Public Class MyListViewItemCollection
Inherits ListView.ListViewItemCollection
Public Event ItemAdded(ByVal Item As ListViewItem)
Sub New(ByVal owner As ListView)
MyBase.New(owner)
End Sub
Public Overrides Function Add(ByVal value As System.Windows.Forms.ListViewItem) As System.Windows.Forms.ListViewItem
Dim Item As ListViewItem
Item = MyBase.Add(value)
RaiseEvent ItemAdded(Item)
Return Item
End Function
End Class
I think the best thing that you can do here is to subclass ListView and provide the events that you want.

Resources