Have a question. I think it must be quite easy to implement but for some reason I can't do it. Right now the charts are updated if I click on them. I wish that they were updated every time I change a value (just like in Excel), for must be easier to implement a timer.
For some reason I can't implement it and I have already tried a few things that I have found online.
I have two charts and I want the charts to update the values every second (or the interval suitable for the program).
I am working on a Windows form and have the following VB code:
Private Sub Chart1_Click(sender As Object, e As EventArgs) Handles Chart1.MouseClick
AutoSize = False
Chart1.ChartAreas(0).AxisX.Interval = 0.05
Chart1.ChartAreas(0).AxisY.Interval = 200
With Chart1
.Series(0).Points.Clear()
.Series(1).Points.Clear()
.Series(2).Points.Clear()
End With
With Chart1
.Series(0).Points.AddXY(TOCGLong, TOSumMassValues)
.Series(1).Points.AddXY(LandCGLong, LandSumMassValues)
.Series(2).Points.AddXY(ZFWCGLong, ZFWSumMassValues)
End With
With Chart1.ChartAreas
With .Max
.AxisX.Maximum = 3.55
.AxisY.Maximum = 2300
End With
With .Min
.AxisX.Minimum = 3.15
.AxisY.Minimum = 1200
End With
End With
Chart1.Series(3).Points.Clear()
For i As Integer = 0 To 5
Chart1.Series(3).Points.AddXY(CGLimitX(i), CGLimitY(i))
Next
End Sub
Private Sub Chart3_Click(sender As Object, e As EventArgs) Handles Chart3.MouseClick
Dim FuelX() As Integer = {80, 90, 100, 110, 120, 130, 140, 150, 160, 170, 180}
Dim ISA() As Double = {27171.16, 24053.39, 21011, 18043.99, 15152.36, 12336.11, 9595.24, 6929.75, 4339.64, 1824.91, -614.44}
Dim ISA20() As Double = {25421.84, 21987.56, 18644, 15409.16, 12277.04, 9247.64, 6320.96, 3497, 775.76, -1842.76, -4358.56}
Dim ISA35() As Double = {23726.04, 19793.81, 16041, 12467.61, 9073.64, 5859.09, 2823.96, -31.75, -2708.04, -5204.91, -7522.36}
AutoSize = False
Chart3.ChartAreas(0).AxisX.Interval = 10
Chart3.ChartAreas(0).AxisY.Interval = 5000
With Chart3
.Series(0).Points.Clear()
'.Series(1).Points.Clear()
'.Series(2).Points.Clear()
End With
With Chart3
.Series(0).Points.AddXY(FuelConsuption, SDaltitude.Value)
For i As Integer = 0 To 10
.Series(1).Points.AddXY(FuelX(i), ISA(i))
.Series(2).Points.AddXY(FuelX(i), ISA20(i))
.Series(3).Points.AddXY(FuelX(i), ISA35(i))
Next
End With
With Chart3.ChartAreas
With .Max
.AxisX.Maximum = 180
.AxisY.Maximum = 23000
End With
With .Min
.AxisX.Minimum = 80
.AxisY.Minimum = 0
End With
End With
End Sub
Take the code out of the Chart1.MouseClick handler and put it in a Sub of its own with a descriptive name. Do the same for the Chart3.MouseClick handler.
In the Chart1.MouseClick handler, call that first Sub.
In the Chart3.MouseClick handler, call that second method.
For the question in the question:
I wish that they were updated everytime I change a value
In the appropriate textbox leave handlers, call the appropriate method(s), something like this:
Private Sub TextBox1_Leave(sender As Object, e As EventArgs) Handles TextBox1.Leave
UpdateChart1()
UpdateChart2()
End Sub
I suggest using the Leave event so that it doesn't keep changing while the user is in the middle of typing.
For the question in the title:
You should put the chart updating code in its own methods, as written above. Then you can easily call it from a timer's tick event. The essential parts of the code are like this:
Public Class Form1
Dim tim As Timer
Sub UpdateMpgChart()
' ... code here to update the chart
End Sub
Sub UpdateOtherChart()
' ... code here to update the other chart
End Sub
Sub TimerTickHandler(sender As Object, e As EventArgs)
UpdateMpgChart()
UpdateOtherChart()
End Sub
Sub InitialiseTimer()
tim = New Timer With {.Interval = 1000}
AddHandler tim.Tick, AddressOf TimerTickHandler
tim.Start()
End Sub
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
InitialiseTimer()
End Sub
Private Sub Form1_FormClosing(sender As Object, e As FormClosingEventArgs) Handles MyBase.FormClosing
If tim IsNot Nothing Then
tim.Dispose()
End If
End Sub
End Class
I wish that they were updated every time I change a value (just like in Excel), for must be easier to implement a timer.
You can use the ReactiveProperty nuget.
You declare a ReactiveProperty for each value you want to "react":
public ReactiveProperty<int> Prop1 = new ReactiveProperty(0);
public ReactiveProperty<int> Prop2 = new ReactiveProperty(0);
In the constructor (or load event) just tell the property to update the chart:
Prop1.Subscribe(value => Chart1_Click(this, null));
Here I'm telling to Prop1 to execute the Chart1_Click method, but you can create an specific method for updating the charts and put this in the subscribe method instead the Chart1_Click.
And for change the Prop1 or Prop2 values just:
Prop1.Value = 5; // this line will run the specified method
Prop2.Value = 10;
Related
I have an infinite loop in this sub because I want the program to keep testing this process to see if the variable has changed. When I run the program in the debugger, nothing shows up, including the form however when I removed the infinite loop from the program, the form showed up again. Does anyone know why this is happening? I should also mention I've tried a DO LOOP as well. Can anyone help?
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim greenCount As Integer
Dim looptest As Boolean = True
While looptest = True
For Each control In Me.Controls.OfType(Of Button)
If control.BackColor = Color.Lime Then
greenCount += 1
End If
Next
txtFielder.Text = greenCount.ToString
End While
End Sub
You need to get rid of all that code regardless. Depending on how you're changing the BackColor of those Buttons in the first place, updating the lime count might be best done there. Otherwise, you should be handling the appropriate event, e.g.
Private limeButtonCount As Integer = 0
Private Sub Buttons_BackColorChanged(sender As Object, e As EventArgs) Handles Button3.BackColorChanged,
Button2.BackColorChanged,
Button1.BackColorChanged
If DirectCast(sender, Button).BackColor = Color.Lime Then
limeButtonCount += 1
Else
limeButtonCount -= 1
End If
TextBox1.Text = limeButtonCount.ToString()
End Sub
Note that this code assumes that there are only two possible BackColor values and that all Buttons are not lime by default. If your scenario is a bit more complex than that then you may need to change a code a little, e.g.
Private limeButtonCount As Integer = 0
Private Sub Buttons_BackColorChanged(sender As Object, e As EventArgs) Handles Button3.BackColorChanged,
Button2.BackColorChanged,
Button1.BackColorChanged
limeButtonCount = Controls.OfType(Of Button)().Count(Function(b) b.BackColor = Color.Lime)
TextBox1.Text = limeButtonCount.ToString()
End Sub
Form.Load occurs before a form is displayed for the first time.
This means that you'll never see your form as long as you loop in this event. You probably want to use the Shown event instead.
I have the sub routine for when the picture box is clicked.
Public Sub PictureBox5_Click(sender As Object, e As EventArgs) Handles PictureBox5.Click
If player = "p1" Then
PictureBox5.BackgroundImage = My.Resources.cross
pic5 = 1
End If
If player = "p2" Then
PictureBox5.BackgroundImage = My.Resources.nought
pic5 = 2
End If
blnFlag = True
PictureBox5.Update()
End Sub
I want to create a subroutine that clicks the picture box to change the image to a nought.
Does anyone have any idea on how i would go about doing this.
There are a couple ways you can accomplish this:
Call the click event PictureBox5_Click() inside a sub:
Private Sub AnotherClick_Click(sender as Object, e as EventArgs) handles AnotherClick.Click
~Do Stuff
PictureBox5_Click(sender, e)
End Sub
Use PerformClick():
Me.PictureBox5.PerformClick()
Edit: I updated option 1 to include the full click event sub. Should add that this is not really an "acceptable" way of writing code, you shouldn't call these event handlers directly, but rather put your logic in another sub/function and call it from there whenever you need. However, if there is some reason you need to specifically call this event handler, option 1 is the way to go.
Private Sub SetPicture(PicBox as PictureBox)\
If player = "p1" Then
PicBox.BackgroundImage = My.Resources.cross
pic5 = 1
End If
If player = "p2" Then
PickBox.BackgroundImage = My.Resources.nought
pic5 = 2
End If
blnFlag = True
PicBox.Update()
End Sub
Public Sub PictureBox5_Click(sender As Object, e As EventArgs) Handles PictureBox5.Click
SetPicture(PictureBox5)
End Sub
That's how it should be done... now you can also call SetPicture from anywhere else, passing in any other picture box you want...
I created a class that inherits a timer class because I want to customize the Tick function, and I want to use this specific function in many classes without the need to change the function in all the timers every time.
Public Class FadeInTimer
Inherits Timer
Public Sub New()
MyBase.New()
Me.Enabled = False
Me.Interval = 75
End Sub
Private Sub FadeInTimer_Tick(sender As Object, e As EventArgs) Handles Me.Tick
Dim workingAreaWidth As Integer = Screen.PrimaryScreen.WorkingArea.Width - Me.Width
Me.Opacity += 0.1
If Not Me.Location.X <= workingAreaWidth Then
Me.Location = New Point(Me.Location.X - 30, Me.Location.Y)
End If
Me.Refresh()
If Me.Opacity = 1 Then
Me.Stop()
End If
End Sub
End Class
The purpose of this function is to make a simple fade in when the form is created. The problem is I can't use "Me." because I am in the Timer class, so, how can I make changes to the form from this class.
The first thing to do is to pass an instance of the form to be faded in inside the constructor of the custom timer, save that instance in a global class variable and add the tick handler with AddHandler like so
Public Class FadeInTimer
Inherits System.Windows.Forms.Timer
Dim parent As Form
Public Sub New(p As Form)
MyBase.New()
parent = p
AddHandler MyBase.Tick, AddressOf FadeInTimer_Tick
End Sub
Now, when you need to refer to the 'parent' form you use the parent variable and not the Me statement. Also, every time you need to refer to the timer, you should use MyBase statement
Private Sub FadeInTimer_Tick(sender As Object, e As EventArgs)
Dim workingAreaWidth As Integer = Screen.PrimaryScreen.WorkingArea.Width - Parent.Width
parent.Opacity += 0.1
If Not parent.Location.X <= workingAreaWidth Then
parent.Location = New Point(parent.Location.X - 30, parent.Location.Y)
End If
parent.Refresh()
If parent.Opacity = 1 Then
MyBase.Stop()
End If
End Sub
This could be tested in LinqPad using this code
Sub Main
Dim f As Form = New Form()
Dim t As FadeInTimer = New FadeInTimer(f)
f.Opacity = 0
t.Interval = 150
t.Start()
f.ShowDialog()
End Sub
I have grid and those grid is populate on Form's load event. At the end line of that event i am hooking method handler for my SelectionChanged event of this grid. I want to get current selected row's zero cell's 1 value. Unfortunately when i run program my SelectionChanged event method handler is called infinite times... And i have no idea why is that.
So its basically like this:
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
some code which populating data to grid...
'here hooking up method after data is there already to not fire it up during grid population
AddHandler gridArtikels.SelectionChanged, AddressOf gridArtikels_SelectionChanged
End Sub
and this is event handler method itself:
Private Sub gridArtikels_SelectionChanged(sender As Object, e As GridEventArgs)
RemoveHandler gridArtikels.SelectionChanged, AddressOf gridArtikels_SelectionChanged
If gridArtikels.PrimaryGrid.Rows.Count > 0 Then
gridArtikels.PrimaryGrid.SetSelectedRows(0, 1, True)
ItemPanelImgs.Items.Clear()
'Dim images As New List(Of Article_Image)
Dim selectedNummer As String = String.Empty
selectedNummer = gridArtikels.PrimaryGrid.SelectedRows(0).Cells(1).Value.ToString()
'images = ArtikelsAndTheirVariationsFinal.GetImagesForArticle(selectedNummer)
'ItemPanelImgs.DataSource = images
End If
AddHandler gridArtikels.SelectionChanged, AddressOf gridArtikels_SelectionChanged
End Sub
P.S I am using to be concrete supergrid control from DotnetBar devcomponenets but it shouldn't be diffrent from ordinary controls behaviour.
What could be wrong here?
For those whom would like to debug here is sample app
EDIT:
I also tried this way but its still going to infinitive loop...
Public IgnoreSelectionChanged As Boolean = False
Private Sub gridArtikels_SelectionChanged(sender As Object, e As GridEventArgs) Handles gridArtikels.SelectionChanged
If IgnoreSelectionChanged Then Exit Sub
IgnoreSelectionChanged = True
If gridArtikels.PrimaryGrid.Rows.Count > 0 Then
gridArtikels.PrimaryGrid.SetSelectedRows(0, 1, True)
ItemPanelImgs.Items.Clear()
'Dim images As New List(Of Article_Image)
Dim selectedNummer As String = String.Empty
selectedNummer = gridArtikels.PrimaryGrid.SelectedRows(0).Cells(1).Value.ToString()
'images = ArtikelsAndTheirVariationsFinal.GetImagesForArticle(selectedNummer)
'ItemPanelImgs.DataSource = images
End If
IgnoreSelectionChanged = False
End Sub
I want to specify in a text field how many timers I want to add to my form and specify the code that should be into the timer.
For instance: My textbox says "2" and then I click a button and it creates two timers and adds a specific source code for both timers.
I have tried different codes and while they worked, I wasn't able to specify the number of controls on a form to create.
How can I achieve this efficiently?
Thanks
Just to create one timer
Public Class Form1
private _timer as Windows.Forms.Timer
...
Public Sub New()
...
_timer = New Timer(Me)
_timer.Interval = 1000 'Timer will trigger one second after start
AddHandler _timer.tick, AddressOf Timer_tick 'Timer will call this sub when done
End Sub
Sub Button_click(sender as Object, e as EventArgs)
_timer.Start() 'Start the timer
...
End Sub
Private Sub Timer_tick(sender as Object, e as EventArgs)
MessageBox.Show("Timerrr!!")
End Sub
...
End Class
Now if you want to create more than one timer, you can use an array of Timer.
In this case, I used a form conatining a NumericUpDown controll element, a button and a label, plus two labels which only contain text.See this picture
To create the timers, I use the function add_timers(timercount), which looks like this:
Function add_timers(timercount As Integer)
'Using a loop to creat <timercount> timers
For g As Integer = 1 To timercount
'Creating new timer 't'
Dim t As New Timer()
'setting interval of t
t.Interval = 1000
'Enabling timer
t.Enabled = True
'Code which runs when t ticks
AddHandler t.Tick, AddressOf TimerTick
Next
End Function
This function gets called when Button1, the start button gets pressed. It uses NumericUpDown1.Value as the parameter for the function. The function uses a loop to create new timers t, sets their intervals and the code to run when they tick.
Unfourtunately, I didn't find a way to dynamically create code, so every timer performs the same action. Using arrays and loops in a clever way might enable you to use different value for each timer. To create code for the timer use a Sub:
Sub TimerTick(ByVal sender As Object, e As EventArgs)
'Add your code here
Label1.Text += 1
End Sub
The complete code I use is:
Public Class Form1
Function add_timers(timercount As Integer)
'Using a loop to creat <timercount> timers
For g As Integer = 1 To timercount
'Creating new timer 't'
Dim t As New Timer()
'setting interval of t
t.Interval = 1000
'Enabling timer
t.Enabled = True
'Code which runs when t ticks
AddHandler t.Tick, AddressOf TimerTick
Next
End Function
Sub TimerTick(ByVal sender As Object, e As EventArgs)
'Add your code here
Label1.Text += 1
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
add_timers(NumericUpDown1.Value)
End Sub
End Class
Packing the timers into an array is possible, that way you can easily access each timer with its index. Serach for it on the internet, and if you then have no idea of how to do it, tell me in the comments.