AdvancedHMI Software
General Category => Open Discussion => Topic started by: Phrog30 on April 10, 2017, 09:12:30 AM
-
I have a user control which is just a button that I reuse several times throughout the program. The image changes depending on if there are active alarms and I show how many alarms are present (btn.Text). The only way I can get this to work is by a timer. While this does work it's not very elegant and I would assume not best practice. So, I tried to create an event based on alarm count. When the value changes it raises the event. This works, but it will not work on the button image or text. Any ideas why? When I'm using the event I use the same code, but instead on inside of timer tick I just create a sub and call it after the event. The event works, but the image or text for the button doesn't.
Public Class Nav_Active_Alarm
Dim picAnimate As Boolean = False
Private mActive_Alarm_Count As Integer
Public Event Active_Alarm_Count_Changed(ByVal mvalue As Integer)
Public Property Count() As Integer
Get
Count = mActive_Alarm_Count
End Get
Set(ByVal value As Integer)
mActive_Alarm_Count = value
If Not Me.DesignMode Then
'RaiseEvent Active_Alarm_Count_Changed(mActive_Alarm_Count)
End If
End Set
End Property
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
If Not Me.DesignMode Then
If Main.dt_Active_Alarms IsNot Nothing Then
If Main.dt_Active_Alarms.Rows.Count = 0 Then
btn_Goto_Active_Alarm.Image = My.Resources.Alarm_Clock_Static
picAnimate = False
Else
If Not picAnimate Then
btn_Goto_Active_Alarm.Image = My.Resources.Alarm_Clock_Animation
picAnimate = True
End If
End If
If Main.dt_Active_Alarms.Rows.Count > 0 Then
btn_Goto_Active_Alarm.Text = Main.dt_Active_Alarms.Rows.Count
Else
btn_Goto_Active_Alarm.Text = Nothing
End If
End If
End If
End Sub
End Class
-
Is this along the lines of what you want to do:
Public Class AlarmButton
Inherits Button
Public Event AlarmCountChanged As EventHandler(Of AlarmCountChangedEventArgs)
#Region "Properties"
Private m_AlarmCount As Integer
Public Property AlarmCount As Integer
Get
Return m_AlarmCount
End Get
Set(value As Integer)
If m_AlarmCount <> value Then
m_AlarmCount = value
OnAlarmCountChanged(New AlarmCountChangedEventArgs(m_AlarmCount))
If m_AlarmCount > 0 Then
BackgroundImage = m_AlarmImage
Else
BackgroundImage = m_NoAlarmImage
End If
Text = m_AlarmCount
End If
End Set
End Property
Private m_NoAlarmImage As Image
Public Property NoAlarmImage As Image
Get
Return m_NoAlarmImage
End Get
Set(value As Image)
m_NoAlarmImage = value
End Set
End Property
Private m_AlarmImage As Image
Public Property AlarmImage As Image
Get
Return m_AlarmImage
End Get
Set(value As Image)
m_AlarmImage = value
End Set
End Property
#End Region
#Region "Events"
Protected Overridable Sub OnAlarmCountChanged(ByVal e As AlarmCountChangedEventArgs)
RaiseEvent AlarmCountChanged(Me, e)
End Sub
#End Region
End Class
'*************************************************************************************************
'* Event args class to pass arguments to event handler
'*************************************************************************************************
Public Class AlarmCountChangedEventArgs
Inherits EventArgs
#Region "Constructor"
Public Sub New()
MyBase.New
End Sub
Public Sub New(ByVal AlarmCount As Integer)
Me.New
m_AlarmCount = AlarmCount
End Sub
#End Region
Private m_AlarmCount As Integer
Public Property AlarmCount As Integer
Get
Return m_AlarmCount
End Get
Set(value As Integer)
m_AlarmCount = value
End Set
End Property
End Class
- Add a new class to your project named AlarmButton and add the above code.
- Build the project
- From the Toolbox, add an AlarmButton
- Set AlarmImage and NoAlarmImage
- As you change the AlarmCount your image and text should changed
-
That doesn't work either. I can see the event. But for whatever reason the images don't change.
-
I posted a demo project (AlarmButtonDemo) showing the images change with the count:
https://sourceforge.net/projects/advancedhmi/files/advancedhmi/3.5/SampleProjects/
-
Thanks Archie, however, that's not quite what I'm after. Unless I'm missing something, I would still have to add code for each button. If I'm correct on that, then what's the point? What I'm after is a reusable object that you just drag and go. What I want, I was able to get working but only using a timer, which I guess will be good enough.
James
-
What do you want to control the count? Is it linked to a value in the PLC?
-
The PLC does know the count, but I'm using a variable in .NET code. I was actually being lazy and just used the row count in a grid: Main.dt_Active_Alarms.Rows.Count
-
I updated the project with the button alarm count linked to a DataGridView row count
You can also control it through a PLC value by inheriting it into a control based off one of the existing control classes that will give it the ComComponent and the PLCAddress* properties.
-
Thank you Archie, I will look at that when I get a chance later today or tomorrow. Your support and help is stellar!!
James
-
I updated the project again to add an AlarmButtonToDGV that links to the DataGridView through a DatGridViewLink property, therefore encapsulating the code in the control.
-
Archie, your code worked well. Thank you. However, can you explain to me why this code doesn't work?
Public Class Nav_Active_Alarm
Private mActive_Alarm_Count As Integer
Public Event Active_Alarm_Count_Changed(ByVal mvalue As Integer)
Public Property Count() As Integer
Get
Count = mActive_Alarm_Count
End Get
Set(ByVal value As Integer)
mActive_Alarm_Count = value
If Not Me.DesignMode Then
RaiseEvent Active_Alarm_Count_Changed(mActive_Alarm_Count)
test()
End If
End Set
End Property
Private Sub test()
If Not Me.DesignMode Then
If mActive_Alarm_Count = 0 Then
Me.btn_Goto_Active_Alarm.Image = My.Resources.Alarm_Clock_Static
'console or msgbox will work but updating the image will not
Else
Me.btn_Goto_Active_Alarm.Image = My.Resources.Alarm_Clock_Animation
'console or msgbox will work but updating the image will not
End If
End If
End Sub
End Class
I can see the event working by either a console write or message box, but images, text, etc. will not update. However, if I call this code from a timer, it will. I'm at a loss for why this wouldn't work. So, for future reference can you explain how I could make this work? A colleague mentioned I needed to refresh or update for this to work, but I tried and it made no difference.
Thanks in advance...
-
However, can you explain to me why this code doesn't work?
Get
Count = mActive_Alarm_Count
End Get
This piece of code does not return any value, so when something tries to get the Count property, it will return nothing. I would have thought the compiler would have flagged it as an error.
Private Sub test()
If Not Me.DesignMode Then
If mActive_Alarm_Count = 0 Then
Me.btn_Goto_Active_Alarm.Image = My.Resources.Alarm_Clock_Static
Else
Me.btn_Goto_Active_Alarm.Image = My.Resources.Alarm_Clock_Animation
End If
End If
In your test subroutine there is a reference to an undefined object. Based on the Me qualifier, that would tell me it is referring to an object within the class, but there is no btn-Goto_Active_Alarm object in the Nav_Active_Alarm class.
-
Archie-
I actually am getting a valid value on "Count". I can add this to the console and see it return the correct value. Like I said, I can add a timer and on tick it will run the same identical code that you see and it works. That's the part I'm having trouble to understand. It's no big deal, just something that is bugging me why I can't figure it out.
I don't see any error or warnings. By the way, this is a user control and there is a button named "btn_Goto_Active_Alarm".
James
-
By the way, this is a user control and there is a button named "btn_Goto_Active_Alarm".
After I posted that it occurred to me that you said it was a UserControl which means Visual Studio generated a partial class that has the definition of any items you add using the designer.
As for the Getter, try it like this:
Get
Return mActive_Alarm_Count
End Get
-
I tried but that didn't make any difference. I'm still seeing the event, but I can't get text or images, etc, to change. I added a text changed event:
Private Sub txtChanged(sender As Object, e As EventArgs) Handles MyBase.TextChanged
Console.WriteLine("im changed")
End Sub
and I'm seeing this event, but the actual text isn't changing.
-
I posted another project that uses a UserControl and the same code that you posted (only changed to match .NET patterns and practices):
https://sourceforge.net/projects/advancedhmi/files/advancedhmi/3.5/SampleProjects
-
Thanks for posting Archie. The issue with your example is each instance needs code for the count, which is what I'm trying to eliminate. The goal is to mimic the PV+ and global objects. So, I just started with a simple button that I could drop on a form and it would work. The only way I could get it to work was to use a timer to "poll" the value, instead of the value getting "pushed" to each instance. I just didn't like using a timer but I guess I will have to live with it.
Thanks again,
James
-
Here is a very crude example of what I am trying to achieve. The 3 buttons on the form have no code. I just dropped them and they work. The issue, the user control uses a timer to get the values, which I think is clunky. But, it works. I know experienced guys would laugh at this, but with my experience that's the best I could come up with.
-
Now I understand what you are trying to do. This is referred to as Binding. Unfortunately WinForms lacks a graceful way to bind properties between 2 object without writing code(WPF does this nicely). There are a couple ways to go about this. One would be to use an Application Setting to hold the Count value. After you add an object to the form, in the Properties Window at the top is a section named "(Application Settings)". You can drill into this to create a new application setting and link it to the property of choice. So let's say you create a setting named AlarmCount. In your code, you would manipulate the value as such:
My.Settings.AlarmCount = 0
If you binded this to a property, when the value changes it will automatically push the new value into the property also. This is a similar technique where I demonstrated how to mimic tags as done in other HMI software.
Another method for binding that is a bit more complicated is to add properties to your object to specify the control to bind to and the property. Attached is a modified version of your project showing this technique. In design view, if you select the NavActiveAlarm and look at the Properties, you will see where I added DataBindingSource and DataBindingProperty. In the example, I set those to TextBox1 and Text. I also added ImageNoAlarm and ImageAlarm properties. These added properties create a re-usable control in which different instances can be bound to different values.
-
Thanks, I will look at what you did later this evening. I'm sorry I didn't explain myself well. I tend to do a crap job explaining things.
James