Below is some code that I'm using to create objects with Visual Basic:
For indexCounter As Integer = 1 To TotalParticipants Step 1
participantClock = New Label
participantClock.Size = New Size(100, 20)
participantClock.Name = "participantClock" & indexCounter
participantClock.Location = New Point(139, (5 + ((indexCounter - 1) * 26)))
participantClock.BorderStyle = BorderStyle.Fixed3D
participantClock.TextAlign = ContentAlignment.MiddleRight
CenterPanel.Controls.Add(participantClock)
participantStop = New Button
participantStop.Size = New Size(58, 20)
participantStop.Location = New Point(245, (5 + ((indexCounter - 1) * 26)))
participantStop.BackColor = Color.Red
participantStop.ForeColor = Color.White
participantStop.Font = New Font(participantStop.Font, FontStyle.Bold)
participantStop.Text = "Stop"
CenterPanel.Controls.Add(participantStop)
participantTimer = New Timer
participantTimer.Start()
participantTimer.Enabled = True
participantTimer.Interval = 1
participantStopwatch = New Stopwatch
participantStopwatch.Start()
Next
I'm creating a label, a button, Timer, and Stopwatch. (Though I have sinking feeling I don't need BOTH a timer and stopwatch since I'm counting time.)
What I would like to do, is create the label and set that label's text to be the value from the stopwatch. The button that will be created will stop THAT stopwatch.
The problem that I'm having is that I cannot call the stopwatch by name since it wasn't created yet and VB throws a hissy fit at me for it. (After all it wasn't really declared.)
So the question becomes, how do you call the most recently dynamically created control and assign events using that control. If it's not possible to do, I do not mind dumping the form and starting over creating 30 stopwatches instead (but I'd like to avoid that, if possible).
Thanks for any help.
I assume that you want the timer to update the label based on the value of the stopwatch. Is that right?
One thing that you might try that is a little hacky is this:
Define a storage class like so:
Public Class StopwatchStorage
Public Property Stopwatch as Stopwatch
Public Property Label as Label
Public Property Timer as Timer
End Class
at the top of your form define a private list:
Private _storage as new List(Of StopwatchStorage)
at the end of your for loop do this
Dim storage As New StopwatchStorage()
storage.Label = participantClock
storage.Timer = participantTimer
storage.Stopwatch = participantStopwatch
_storage.Add(storage)
AddHandler participantTimer.Tick, AddressOf Timer_Tick
The above code would give you access to the three objects that you need in your tick function. You will have to loop through the _storage list to find the right "set" of objects but it should work:
Private Sub Timer_Tick(sender As Object, args As EventArgs)
For Each storage As StopwatchStorage In _storage
If storage.Timer Is sender Then
storage.Label.Text = storage.Stopwatch.Elapsed
Exit Sub
End If
Next
End Sub
I didn't try to compile that code so I'm sure there are a few typos but I think that should give you an idea of how to refer to the object without needing to use the object's name.
Related
I'm dynamically creating option buttons on a form in Microsoft Word. I programatically assign the new options buttons a sub by way of the code below, but I can't work out how to assign one class to many buttons. Is this possible?
The code below works, but it only throws the message box up on the last button. I think what my code is doing is reassigning this class to the newest button each time, rather than adding a button to that class if that makes sense? How would I go about doing it the other way around so many buttons call the Focus_btn_Click event?
My Class (called "clsSectorbtn"):
Public WithEvents Focus_btn As MSForms.OptionButton
Sub Focus_btn_Click()
Msgbox "Test"
End Sub
Code within my form:
Dim SectorBtn As New clsSectorBtn
Sub Create_Radios(Radio_Array)
Dim RadioArray (0 to 2) As String
RadioArray(0) = "optionbutton1"
RadioArray(1) = "optionbutton2"
RadioArray(2) = "optionbutton3"
Set Fm = UserForm1("Frame1")
For x = 0 To UBound(Radio_Array)
Set opt = Fm.Object.Controls.Add("Forms.OptionButton.1")
opt.Caption = Radio_Array(x)
Set SectorBtn.Focus_btn = opt
Next x
End Sub
You need a global collection to hold the instances of clsSectorbtn, and it's easier to use a "factory" function to build each instance.
I'd do something like this:
Dim colBtn As Collection
Sub Create_Radios(Radio_Array)
Dim RadioArray (0 to 2) As String
RadioArray(0) = "optionbutton1"
RadioArray(1) = "optionbutton2"
RadioArray(2) = "optionbutton3"
Set Fm = UserForm1("Frame1")
Set colBtn = New collection
For x = 0 To UBound(Radio_Array)
Set opt = Fm.Object.Controls.Add("Forms.OptionButton.1")
opt.Caption = Radio_Array(x)
colBtn.Add OptObject(opt)
Next x
End Sub
'create an instance of clsSectorbtn and assign an option button to it
Function OptObject(opt) As clsSectorbtn
Dim rv as New clsSectorbtn
Set rv.Focus_btn = opt
Set OptObject = rv
End Function
I'm writing a program that has two forms. One form gets the user to enter multiple values, and then does some calculations. Then it passes that information to another form However I can't figure out how to do it. Here is a relevant part of my code. To head some confusion, I am trying to pass 11 values, also initially, form 2 is not shown, and then when the values are passed from form 1 to form 2, then form 1 goes away and form 2 is the only one that shown
NOTE: This is not all my code, I don't believe all my code is required (I have 1000 lines right now) However this is the code with the information I want to be passed to the other form.
A lot of people are apparently saying that this is a duplicate of another question, however that question, he seems to already know how to pass the variables, but is just having issues with it (and even with looking at his, i cant figure it out)
Private Sub btnSubmit_Click(sender As Object, e As EventArgs) Handles btnSubmit.Click
'declarations
Dim intNormal As Integer
Dim intChildren As Integer
Dim intBonanza As Integer
Dim intDiamond As Integer
Dim intPictureFrame As Integer
Dim intKite As Integer
Dim intCrazyT As Integer
Dim intLetterX As Integer
Dim int2PostageStamp As Integer
Dim intPick7 As Integer
Dim intJackpot As Integer
Validate()
If txtNormal1.Enabled = False Then
intNormal = intNormInput
Else
intNormal = CalcNormalBooks()
End If
If txtChildren1.Enabled = False Then
intChildren = intChildInput
Else
intChildren = calcChildrensBooks()
End If
If txtBonanza1.Enabled = False Then
intBonanza = intBonInput
Else
intBonanza = calcBonanza()
End If
If txtSpecial1.Enabled = False Then
intSpecial = intSpeInput
Else
intSpecial = calcSpecialBooks(intSpecial)
End If
If txtDiamond1.Enabled = False Then
intDiamond = intDiaInput
Else
intDiamond = calcDiamond(intSpecial)
End If
If txtPictureFrame1.Enabled = False Then
intPictureFrame = intPicInput
Else
intPictureFrame = calcPictureFrame(intSpecial)
End If
If txtKite1.Enabled = False Then
intKite = intKiteInput
Else
intKite = calcKite(intSpecial)
End If
If txtCrazyT1.Enabled = False Then
intCrazyT = intCrazyInput
Else
intCrazyT = calcCrazyT(intSpecial)
End If
If txtLetterX1.Enabled = False Then
intLetterX = intLettInput
Else
intLetterX = calcLetterX(intSpecial)
End If
If txt2PostageStamp1.Enabled = False Then
int2PostageStamp = intPostInput
Else
int2PostageStamp = CalcPostageStamp(intSpecial)
End If
If txtPick71.Enabled = False Then
intPick7 = intPickInput
Else
intPick7 = calcPick7(intSpecial)
End If
If txtJackpot1.Enabled = False Then
intJackpot = intJackInput
Else
intJackpot = calcJackpot()
End If
End Sub
Since I had almost the same requiremnt lately here is my solution:
Custom Event which fires when your 2nd Form is closing
Public Event HotKeyFormClosed As EventHandler(Of HotKeyFormClosedEventArgs)
Custom EventArgs class where you store your values you want to pass to Main Form
Public Class HotKeyFormClosedEventArgs
Inherits EventArgs
'Your properties here
Public Sub New(...) 'your params here
MyBase.New()
'set your properties here
End Sub
End Class
On 2nd Form handle FormClosed event and pass your values to EventArgs
Private Sub HotKey_FormClosed(sender As Object, e As System.Windows.Forms.FormClosedEventArgs)
RaiseEvent HotKeyFormClosed(Me, New HotKeyFormClosedEventArgs(...)) 'your params here
End Sub
On Main Form handle your custom event (here HotKeyFormClosed) and extract its values
AddHandler frmHotKey.HotKeyFormClosed, AddressOf HotKey_FormClosed;
...
Private Sub HotKey_FormClosed(sender As Object, e As HotKeyFormClosedEventArgs)
'Do stuff with values from e
End If
I have chosen the Event approach since it decouples the two forms from another.
One could easily duplicate the information on both forms, make them public and access it directly thru an object instance.
But I like the observable approach from the events more due to it gives mor flexibility (additonal forms using the same events etc.)
P.S.: I wrote my code in c# and blind entered the VB code here so be gracious.
The values/variables that a method expects to receive (specified in the method's signature) are called Parameters.
The values sent to a method when the method is called are called Arguments.
As long as the arguments used when calling a method match the parameters for that method, those values can be passed.
For example (and I'll try to apply this to your context), if you want to create an instance of a form that takes certain values, you can specify those parameters in the form's New event, like so:
Public Sub New(someInt As Integer)
'do something with someInt here
End Sub
Then when you call this method you'd pass it the arguments, like so:
Dim myInt As Integer = 10
Dim newForm As myForm = New myForm(myInt)
When I say the arguments need to match the parameters, that means the number of values, the order of those values, and the value types must be the same (or in the case of numbers the parameter's type must be the same or larger than the argument's type).
As long as that is true, then it shouldn't really matter how you pass these - you could pass 11 individual arguments, you just have to make sure you are matching the argument to the parameter.
Hope that helps!
I have an UltraGrid in which I have many columns of which 2 columns are DateTime style. Now when I use the filter of that columns it shows all the DateTime values as a text in a dropdown. But I need that as a calendar in order to make the filter easy. It is enough to show just a calendar when clicking the filter.
I have tried some code but it doesn't work.
//Code:
Private Sub grdResult_BeforeRowFilterDropDown(ByVal sender As Object, ByVal e As Infragistics.Win.UltraWinGrid.BeforeRowFilterDropDownEventArgs) Handles grdResult.BeforeRowFilterDropDown
e.Cancel = True
UltraCalendarCombo1.Visible = True
UltraCalendarCombo1.Location = New Point(grdResult.Rows.FilterRow.Cells(e.Column).GetUIElement().Rect.Location.X, grdResult.Rows.FilterRow.Cells(e.Column).GetUIElement().Rect.Location.Y - 2)
UltraCalendarCombo1.Size = New System.Drawing.Size(grdResult.Rows.FilterRow.Cells(e.Column).GetUIElement().Rect.Size.Width, grdResult.Rows.FilterRow.Cells(e.Column).GetUIElement().Rect.Size.Height)
' UltraCalendarCombo1.DroppedDown = True
End Sub
The above event will fire when the filter dropdown is clicked.
private sub applyCustomeViewSettings(byval gridFormat as GridFormat)
....
...
For Each ColumnFormat In gridFormat.ColumnFormats
For Each column In Me.grdResult.DisplayLayout.Bands(0).Columns
If column.Key.ToUpper = ColumnFormat.ColumnKey.ToUpper Then
If column.Key.ToUpper = "PCSSTDT" Then
column.Header.Caption = IIf(ColumnFormat.Caption = "", ColumnFormat.ColumnKey, ColumnFormat.Caption)
column.Hidden = ColumnFormat.Hidden
'column.AllowRowFiltering = IIf(ColumnFormat.AllowRowFiltering = False, ColumnFormat.AllowRowFiltering, DefaultableBoolean.True) 'CType(ColumnFormat.AllowRowFiltering, DefaultableBoolean)
column.Width = ColumnFormat.Width
column.Header.VisiblePosition = ColumnFormat.VisiblePosition
column.Format = ColumnFormat.Format
column.SortIndicator = ColumnFormat.SortIndicator
' column.Style = ColumnStyle.Date
'column.EditorComponent = UltraCalendarCombo1
column.FilterOperandStyle = FilterOperandStyle.Default
Else
column.Header.Caption = IIf(ColumnFormat.Caption = "", ColumnFormat.ColumnKey, ColumnFormat.Caption)
column.Hidden = ColumnFormat.Hidden
column.AllowRowFiltering = IIf(ColumnFormat.AllowRowFiltering = False, ColumnFormat.AllowRowFiltering, DefaultableBoolean.True) 'CType(ColumnFormat.AllowRowFiltering, DefaultableBoolean)
column.Width = ColumnFormat.Width
column.Header.VisiblePosition = ColumnFormat.VisiblePosition
column.Format = ColumnFormat.Format
column.SortIndicator = ColumnFormat.SortIndicator
column.Style = ColumnFormat.Style
End If
End If
Next
....
...
End Sub
The above method makes the grid changes(apply settings) to show the filter as calendar.
But this doesn't work and showing me the same normal grid.
How can I achieve this?
I have managed to show a MonthCalendar when you press the icon to filter a DateTime column in an UltraWinGrid.
When the MonthCalendar is dispayed you could select a specific date using the familiar interface provided by this standard WinForm control. After selecting the date you could use the value to apply programmatically a filter condition to the UltraWinGrid column.
To reach this result you first need to add a reference to the Infragistics4.Win.SupportsDialog.v11.2 assembly where you can find the UltraGridFilterUIProvider class
Now, in your form where you need the filtering to appear, add this code: (it is just an example because I haven't your datasource and thus I have a prebuilt one with just one datetime column)
Imports Infragistics.Win.UltraWinGrid
Imports Infragistics.Win.SupportDialogs.FilterUIProvider
Public Class Form1
' This is the key object that let us customize '
' the Filter for the UltraWinGrid'
Dim _filterUIProvider as UltraGridFilterUIProvider
' In the InitializeLayout event we substitute the normal
' filter handler with the custom filter'
Private Sub UltraGrid1_InitializeLayout(sender As Object, e As Infragistics.Win.UltraWinGrid.InitializeLayoutEventArgs) Handles UltraGrid1.InitializeLayout
e.Layout.Override.AllowRowFiltering = Infragistics.Win.DefaultableBoolean.True
_filterUIProvider = New UltraGridFilterUIProvider()
' Comment out the following line to test the default
' **Excel Filter Style Interface** '
AddHandler _filterUIProvider.BeforeMenuPopulate, AddressOf _filterUIProvider_BeforeMenuPopulate
e.Layout.Override.FilterUIProvider = _filterUIProvider
End Sub
' Before the UltraGridFilterUIProvider shows its standard form interface
' we start a custom form used to apply our filtering logic '
' and block the display of the UltraGridFilterUIProvider interface '
Private Sub _filterUIProvider_BeforeMenuPopulate(sender As Object, e As Infragistics.Win.SupportDialogs.FilterUIProvider.BeforeMenuPopulateEventArgs)
' A custom form with the MonthCalendar and 3 buttons '
' to handle the filter logic '
Using fDate = new FormDate()
' Open our custom form with the monthcalendar
if (DialogResult.OK = fDate.ShowDialog())
' We need a nullable date to allow the removing of the filter'
Dim dtFilter As DateTime? = fDate.SelectedDate
if (dtFilter.HasValue)
' Apply programmatically a filtercondition to the column
' In this case I have only one column. so I use the index 0
' in your case this should change to reflect your column index
Dim fc = new FilterCondition(FilterComparisionOperator.Equals, dtFilter.Value)
ultraGrid1.DisplayLayout.Bands(0).ColumnFilters(0).FilterConditions.Add(fc)
Else
ultraGrid1.DisplayLayout.Bands(0).ColumnFilters.ClearAllFilters()
End If
End If
End Using
e.Handled = true ' Stop the standard interface'
End Sub
End Class
Now we need only a simple form called FormDate that contains a MonthCalendar and three buttons (Set, Clear, Cancel) that return (Set) a Date to set the filter, (Clear) a null value to remove previous filter and a cancel button to abort the processing
Here the code for the form, the design is trivial
Public Class FormDate
Public Property SelectedDate As DateTime?
Private Sub FormDate_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Me.SelectedDate = Nothing
End Sub
Private Sub cmdSet_Click(sender As Object, e As EventArgs) Handles cmdSet.Click
'This button has DialogResult=OK'
Me.SelectedDate = monthCalendar1.SelectionStart
End Sub
Private Sub cmdClear_Click(sender As Object, e As EventArgs) Handles cmdClear.Click
'This button has DialogResult=OK'
Me.SelectedDate = Nothing
End Sub
End Class
This could resolve your problem, however, I have discoverd what seems to be a bug in UltraGridFilterUIProvider. When I call the e.Handled=True my expected result is the filter to not show anything but, instead a small window still appears and I have to press Escape to hide it. I have not found any way to automatically hide it.
It seems to be a problem to signal to the Infragistics team.
I suggest you also to test the Excel Style Filter Interface provided automatically by the UltraGridFilterUIProvider. This interface has a lot of options and is much more preferable to the standard UI filter. To test this interface you should only comment out the AddHandler line above
EDIT Following the comment from #Alhalama I have tried to use the BeforeRowFilterDropDown event and the result are better (well it is perfect now). So I have commented out the line with the AddHandler, removed the code for the BeforeMenuPopulate and added the code for the BeforeRowFilterDropDown
Private Sub UltraGrid1_BeforeRowFilterDropDown(sender As Object, e As BeforeRowFilterDropDownEventArgs) Handles UltraGrid1.BeforeRowFilterDropDown
If e.Column.Key = "DateRequest" Then
Using fDate = New FormDate()
If DialogResult.OK = fDate.ShowDialog() Then
Dim dtFilter As DateTime? = fDate.SelectedDate
If (dtFilter.HasValue) Then
Dim fc As FilterCondition = New FilterCondition(FilterComparisionOperator.Equals, dtFilter.Value)
UltraGrid1.DisplayLayout.Bands(0).ColumnFilters(0).FilterConditions.Add(fc)
Else
UltraGrid1.DisplayLayout.Bands(0).ColumnFilters.ClearAllFilters()
End If
End If
End Using
e.Cancel = True
End If
End Sub
Now, when I try to open the filter for the column named DateRequest I open immediately the FormDate and at the end I set the Cancel property of the BeforeRowFilterDropDownEventArgs to true to avoid further processing of the filter dialog. This seems to be perfect...... Great credit for this to Mr. Alhalama. If you wish I think you should post your own answer because your suggestion really makes the difference.
I am trying to get the datagridview to update when I update the datasource and I'm having no luck whatsoever.
Here is my binding:
Private _dgbNews As SortableBindingList(Of SalesMessageRecord)
Private Sub SalesMessageScreen_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
'_dgbNews.RaiseListChangedEvents = True
_dgbNews = AllNews()
BindingSource1.DataSource = AllNews()
DataGridView1.DataSource = BindingSource1
MassageDemRows()
End Sub
this is AllNews():
Public Function AllNews() As SortableBindingList(Of SalesMessageRecord)
Dim sm = New SortableBindingList(Of SalesMessageRecord)
Dim allnewsitems = News.GetAllNewsItems(Configuration.CompanyID).ToList()
For Each allnewz As News In allnewsitems
Dim smr = New SalesMessageRecord
smr.Body = allnewz.NewsBody
smr.CorporationId = CType(allnewz.CorporationId, Guid)
smr.Expiration = allnewz.Expiration
smr.IsActive = allnewz.IsActive
smr.NewsId = allnewz.NewsId
smr.Title = allnewz.NewsTitle
smr.SortOrder = allnewz.OrderNumber
smr.TokenId = allnewz.TokenId
smr.IsNew = False
sm.Add(smr)
Next
Return sm
End Function
And this is where I'm trying to update it:
Private Sub button_Save_Click(sender As System.Object, e As System.EventArgs) Handles button_Save.Click
If _currentRow < 0 Then
Return
End If
_dgbNews(_currentRow).Expiration = datetimepicker_ExpirationDate.Value
_dgbNews(_currentRow).SortOrder = CInt(numericupdown_SortNumber.Value)
_dgbNews(_currentRow).IsActive = checkbox_Active.Checked
_dgbNews(_currentRow).Body = richtextbox_Body.Text
_dgbNews(_currentRow).Title = textbox_Title.Text
DataGridView1.Refresh()
News.UpdateNewsRecord(_dgbNews(_currentRow).NewsId,
_dgbNews(_currentRow).Expiration,
_dgbNews(_currentRow).SortOrder,
_dgbNews(_currentRow).IsActive,
_dgbNews(_currentRow).Body,
_dgbNews(_currentRow).Title)
End Sub
The database is updating without issue but the datagridview won't update.
Right, I'll take a crack at this. Bindingsources are really useful when used together with a DGV. However, they tend to be aweful in the way that they give no information what so ever why they aren't working.
First of I would say that your binding source has the wrong Datasource.
_dgbNews = AllNews()
BindingSource1.DataSource = AllNews()
DataGridView1.DataSource = BindingSource1
As you can se you have AllNews() as Datasource. But you add stuff to _dgbNews and expect Allnews() to change. Protip, it doesn't. If you were to set the DataSource to this:
BindingSource1.DataSource = _dgbNews
Then atleast you should expect some change when the list is updated. Now this is what you actually do miss. In button save click you add an item to the list, this is now fine if you done the above. But wait, the datasource changed and nothing happened. This is because you don't tell the datagridview to change. Make the Bindingsource tell everything it's connected to to update. With this:
BindingSource1.ResetBindings(True)
This is better than DGV.Refresh (which might work now) since your bindingsource could be attached to other things (I know it isn't, but for future reference).
Try this and well see if it will go better.
Had similiar issue with DataGridView not displaying any data from my datasource, figured out that my binding class properties were regular int or string instead of being declared with get set.
changed from
int RedeemID;
to
public int RedeemID { get; set; }
TRY THIS:
(AFTER EXECUTING THE UPDATE COMMAND FROM THE DATA ADAPTER)
GRIDVIEW_NAME.DATABIND()
Me.TableAdapter1.Fill(Me.DataSet1.Table1)
I have several label boxes on my design form that all share the naming convention lbl_#.text where # ranges from 1 to 60. I want to make a loop that iterates through each lbl_#.text adding some incremental value, let's say multiples of 2 for this question's theoretical purpose.
Something such that the end result would amount to the following:
lbl_1.text = "2"
lbl_2.text = "4"
lbl_3.text = "6"
...
lbl_60.text = "120"
I'm not sure how to access each of these labels through the coding side, I only know how to explicitly mention each label and assign a value :/
There are a few options here.
In this situation the labels will often have a common container, such as panel or groupbox control. In that case:
Dim formLabels = myContainerControl.Controls.OfType(Of Label)()
For Each formLabel As Label In formLabels
'...
Next formLabel
Of course, this mixes logical groups with visual groupings. Those two things don't always align well, so you can also...
Add them all to a Label array (or List(Of Label) or any other enumerable):
Dim formLabels(60) As Label = {lbl_1, lbl_2, lbl_3 .... }
For Each formLabel As Label in formLabels
'...
Next formLabel
But sometimes that's more trouble than it's worth, even if you use a loop to create the collection, and so you can also
Use the .Name property (in conjunction with a naming convention to identify your desired controls):
Dim formLabels = Controls.Where(Function(c) c.Name.StartsWith("lbl_"))
For Each formLabel As Label In formLabels
'...
Next formLabel
Some combination of the above (for example, code in the form load event to create a list based on the name property).
Notice the actual For Each loop is exactly the same in all of those options. No matter what you do, get to the point where you can write a single expression to identify the label control, and then run a simple loop over the expression result.
This points to a final strategy: think in terms of binding to a data source. With a data source, your labels are created as part of a DataGridView, FlowLayoutPanel, or similar control. Then you can iterate the rows in the grid or panel.
Use the Controls collection:
Public Class Form1
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Dim i As Integer
For i = 1 To 3
Dim myLabel As Label = CType(Me.Controls("lbl_" & i), Label)
myLabel.Text = ...whatever value you want to put here
Next
End Sub
End Class
If you don't know how many labels there are, one option is to use a Do Loop.
Dim lblTarget As Label = Nothing
Dim intCursor As Integer = 1
Dim bolFirstIteration As Boolean = True
Do Until lblTarget Is Nothing AndAlso Not bolFirstIteration
If bolFirstIteration Then
bolFirstIteration = False
End If
lblTarget = CType(Me.Controls("lbl_" & intCursor.ToString()), Label)
If Not lblTarget Is Nothing Then
lblTarget.Text = (intCursor * 2).ToString()
End If
intCursor += 1
Loop