My.Settings "Destination array was not long enough. Check destIndex and length, and the array's lower bounds." - vb.net

I have two forms with combo boxes. The combo box values are stored in My.Settings.testDevices. (System.Collections.Specialized.String.Collection) with a scope of User.
The second form adds the ability to add items to testDevices, and then upon exit it updates My.Settings.testDevices.
Now, only if I make a change to the settings (adding items only), when I exit back to the main form (which remains loaded throughout the process), my application crashes with the following message:
"Additional information: Destination array was not long enough. Check destIndex and length, and the array's lower bounds."
As I understand it, this might be a concurrency issue, however I'm not sure.
My code:
In my main form Load event: (to load from My.Settings)
testDevicesComboBoxMain.Items.Clear()
My.Settings.testDevices.CopyTo(mainFormTestDevices, 0)
testDevicesComboBoxMain.Items.AddRange(mainFormTestDevices)
Where "testDevicesComboBoxMain" is the combo box on the main form.
On the secondary form Close Event: (to save to My.Settings)
Dim items(testDevicesComboBox.Items.Count - 1) As String
testDevicesComboBox.Items.CopyTo(items, 0)
My.Settings.testDevices.Clear()
My.Settings.testDevices.AddRange(items)
My.Settings.Save()
I have found similar questions on here, but none with answers that I understand :P
As I am a beginner with vb.net, could any answers be provided in an easy to understand form please!
Thanks.
I forgot to add:
Public items(My.Settings.testDevices.Count - 1) As String
Public mainFormTestDevices(My.Settings.testDevices.Count - 1) As String
I tried setting separate declarations just in case there was some kind of conflict. These obviously do the same thing, just with different names.

I fixed it by adding a For loop to read from My.Settings.
For Each i As String In My.Settings.testDevices
testDevicesComboBoxMain.Items.Add(i)
Next
This seems to have cured the problem, and may perhaps be a more "modern" way of doing it?

Related

Fill a boolean array from checkbox control array

My program creates an array of checkboxes at runtime as shown below:
For Looper = 0 To 36
Dim Ex1ConfigCheck As New CheckBox
frmSetup.Controls.Add(Ex1ConfigCheck) ' Add Control to from
Ex1ConfigCheck.Top = (Looper + 45) + (Looper * 18) ' Set Location
Ex1ConfigCheck.Left = 210
Ex1ConfigCheck.Text = Setup.ExCheckName(Looper) ' Set Text property from strArray
Next
This is where I don't know how to proceed.
I would like to fill a boolean array (ex. MyBoolean(37)) with the value of Ex1configCheck().Checked. The reason I would like to fill another array is because I need to be able to reference the value of the checkboxes in other parts of the code but can't access them until they are created. Also, I plan on saving the array out to a binary file.
Could someone point me in the right direction please?
If there are no other CheckBoxes in the same container as those ones then you can do this:
Dim flags = Me.Controls.OfType(Of CheckBox)().
Select(Function(cb) cb.Checked).
ToArray()
If the controls are in a different container than the form itself, replace Me with that container.
As suggested by #Jimi, you could also create a List(Of CheckBox) and assign that to a field, populating it when you create the controls. You can then use that list instead of creating one on demand:
Dim flags = myCheckBoxList.Select(Function(cb) cb.Checked).
ToArray()
Of course, if you know exactly how many CheckBoxes you are going to be adding, why do you need to wait until run time to create them? Why can't you create them at design time and then modify them at run time? You usually only create controls at run time if you don't know how many there will be until run time, but that seems not to be the case here.
Thanks all for your answers and comments. I always have a fear of being roasted when I ask what some may consider a simple question online.
I have found an alternative way of accomplishing my task. Instead of creating 8 "Arrays" of checkboxes, I have learned of a very simple control available called "CheckedListBox".
I really didn't need to create the checkboxes at runtime but was trying to find an easier way to create 8 groups of 37 checkboxes without having to manually name and set the properties of each one during design. I also wanted to be able to index them in my code to be able to update and read the value using simple loops. I could have done this by creating arrays of CheckBox but again, I would have had to manually initialize the arrays.
Once I found the CheckedListBox, I was able to accomplish what I want very quickly. I only had to set the properties of the 8 "groups" (CheckedListBox's) and fill them using the items property. The ListBox essentially created a List like Jimi suggested automatically and I can index thru each list with a loop as desired. Jimi's suggestion actually lead me to finding the CheckedListBox while I was searching for more information on using "List(of CheckBox)".
Sometimes talking to others helps me find the right questions to ask. Google was able to figure out what I wanted when I searched for "List(of CheckBox)". (:

Why did I have to manually set the .value from the .text on losing focus in an unbound textbox

I have an unbound textbox to accept the delete older than: number of days. It is in the report header. I set it to 30 days but I want the user to be able to change it. I was banging my head trying to figure out why entering 40 was not being accepted and it reverted back to 30 every time. I finally decided on using the lost_focus event to set .value to .text. That worked.
Further research showed that when the textbox get's focus text and value are both the same, 30 in my case. Changing the number in the text box to 40 shows the values of text at 40 and value at 30. Unless I specifically set Value to the value of text Access changes text to the value of value. This is different behavior than other places in Access such as forms.
Can anyone tell me why this might be? I can't find any setting that might do this. Is it because it's in a report header? what is the difference between this and every other text box I've ever used?
From a "best practices" viewpoint, Access Reports are not intended to be used interactively despite the ability to manipulate some unbound controls. Although workarounds can be implemented that function sufficiently well, such solutions are often incomplete and buggy and function differently depending on the active view: Report View vs. Print Preview. Appropriate design patterns include using Access Forms for specifying report options which then open the Report in a static configuration.
This may not satisfy the question "Why?" if seeking a deeper answer as to why Microsoft implemented inconsistent binding behavior in Access, or why they allowed interactive controls in reports at all if they don't behave the same way as in forms. But Access has plenty of other quirky behaviors that have no known/published explanation.
Regarding the priority of the Value property updating the Text property (and not vice versa): Value is the key field because it contains the actual data for the control (bound or unbound). Although it is natural to have a single control for both display and input (uh, that's how almost all controls work), the processes of displaying data and parsing user input are two distinct functions. The visual representation returned by the Text property can be manipulated using the various formatting properties, and technically could display an incomplete representation of the underlying Value data. If there are any conflicts between the stored Value property and the Text property, it is natural that the existing Value property has precedent.
My guess is that the automatic binding behavior was "relaxed" for reports to allow more flexible custom reporting output. First consider an Access Form in Datasheet view: An unbound Form control shows the same value for all records. Even if the control is edited while on a particular row, the updated value is displayed for all rows. The same control object is essentially repainted for each row and there is no concept of individual instances of the control that can hold different values. Bound controls have built-in code that repaint the control with data from the particular row, but there are still not multiple instances each "holding" the individual values. The visual output differs from an intuitive object-oriented paradigm where our minds what to assign each visual row its own in-memory instance of the controls--it just doesn't work like that in Access.
Unlike the Form behavior just described, the Report's Print Preview (and actual printed output) allows unbound controls to display different data per row using the Detail_Format() event. Within the Detail_Format() event, one can set the Value property of a control at which time the Text property is automatically updated according to various formatting properties. This update Text is then output for the current row. Perhaps (just guessing) that this behavior would not function properly if the Text property updated the value property. I suspect it would cause recursive events during report generation. Because reports are not meant to be interactive, relevant text-input parsing code was "disconnected" so that it doesn't behave like on a form.
All that explanation doesn't make Access any less frustrating nor remove its limitations, but at least learn to adapt and design things in the "Access-esque" way rather than fighting it.
your best bet is to design a form with the unbound combo boxes and have your data displayed in a subreport. I like to design my reports so that when values are updated the query for the recordsource of the report is generated doing this requires 2 queries to exist, one with all data possible and a filtered one as subreport recordsource. This will control the data for printing and also allow users to close or navigate away from the report and return to the data later.
Private Sub ComboBox1_AfterUpdate()
Dim Query1 as Object
Dim Temp_Name as Variant
Temp_Name = SubReport.SourceObject
SubReport.SourceObject = Empty
Set Query1 = Me.Form.Application.DBEngine.Workspaces(0).Databases(0).QueryDefs ("SubReport_Query")
Query1.SQL = "Select * Unfiltered_Query WHERE Field1 <= " ComboBox1 & ";"
SubReport.SourceObject = Temp_Name
End Sub

Refreshing form from timer in another form

I have a problem with the snippet of the code I have got to extend and improve. It is not my original code and I cannot change the logic that much of it, just to be clear.
I have one main form called MDIServer, which has a timer set for every second. In Timer.Tick I have some other code which works fine (timer is running okay). Newly, I had to check there, if one form is Active and if so, change some stuff (labels text and tags) in that form and refresh it.
I add there this code:
If IsActiveForm("frmName") Then
frmName.ChangeSomething()
End If
The Sub ChangeSomething is, how you can see, located in the form I want to refresh and do the changes. In that function I simply change the label text and tags of few controls.
My question is: Form is not refreshing => labels are not visible changed, why?
I think I tried already almost anything with Refresh() function in the ChangeSomething() function or in the timer after I called this function. Also I tried to add there new timer (in frmName) and do the changes there, which works perfectly with
Label.Text = "something new"
Label.Refresh()
So I guess problem is somewhere with the refreshing form from Timer in different form. I also tried to do it with my own InvokeReguired() function etc...
P.S. When I am debugging the code, labels and tags are changing and every single function which has to be called, is called, but it is just not visible on the form itself.
EDIT Info
formName is not declared in MDIServer explicitely and in this case and many other cases, forms are used as default instances. Timer is from System.Windows.Forms.Timer. Also MDIServer is not a MDIParent of the formName and I cannot use Me.ActiveMdiChild Is. Lets just say, these two forms are not dependent on each other in any way.. and everything is done through name of the form (default instance, so nothing like Dim frm As Form and frm = frmName).
I would be really glad for any tip or anything :D
Thanks guys,
Vojta
So, I fixed my problem after some research and the problem was (expected) that I am not calling the subroutine ChangeSOmething() for one specific instance of the form frmName. So I had to change my code, that I will call it exactly for the instance which is active and visible.
New code looks like this:
Dim frmCollection = Windows.Forms.Application.OpenForms
Dim listfrmname = frmCollection.OfType(Of frmName).ToList()
If listfrmName.Count > 0 Then
Dim tmpFrm As frmName = listVZT15.Last()
tmpFrm.ChangeSomething()
End If
I also could not use combination of frmCollection.OfType(Of frmName).Any and frmCollection.Item("frmName"), because when I was closing the form and opening again, it created new and new instances (I dont know, why it is not closing the old one, but like I said, it is not my code). So the logic is, to list all open forms of the needed type, and then take the last instance from that list and for that instance call the subroutine. Also Me.Refresh() is placed in the subroutine ChangeSomething() itself.
Thanks everyone for help, it surely helped me to understand, how the instances works here.

VB.Net ComboBox (as Dropdown) not translating text to DisplayMember with Databinding

I inherited a fairly large project at work that is undocumented and written in VB (originally started pre .NET, ended around .NET 2). I'm in the process of updating / refreshing a lot of the code, but have run into an annoying issue that I haven't found the solution for yet. This system utilizes a UI, a Web Service, and a SQL DB.
Problem: I have a Databound Combobox (originally set to DropDownList - I'm changing it to DropDown, which is what started this mess - going back isn't an option) that is tied to a DataSet that comes from a Web Service. When a user types in the item they want manually, the data from the text field doesn't seem to associate itself with the DisplayMember, which forces the WS/SQL query to fail (it is sent a blank value when it's expecting a ValueMember). If the user types in a partial selection and then chooses the value they want from the DisplayMember list using the arrow keys or tab, the query goes off without a problem.
My Question: How do I get the text field to translate to the DisplayMember which will then properly tie itself to the ValueMember which will then allow the query to execute correctly? Sorry for making this sound complicated or convoluted; I'm sure the answer is easy and I'm just glazing over it.
The relevant bit of code is:
With cmbDID
If dtsLU.Tables.Contains(reqTable) = True Then
.DataSource = dtsLU.Tables(reqTable)
.DisplayMember = "zip"
.ValueMember = "gridID"
End If
End With
cmbDID.DataBindings.Clear()
cmbDID.DataBindings.Add("SelectedValue", dtsData, strDT & ".gridID")
I've tried changing "SelectedValue" to "Text", which almost works - but it directly translates to gridID and skips zip which ends up with an incorrect Web Service response since the zip and gridID field values are not synced (zip (DisplayMember) may be 5123 while gridID (ValueMember) may be 6047). I've tried changing "SelectedValue" to "SelectedIndex", and that got me no where.
Any help is greatly appreciated.
EDIT
To add some clarification to the process, the below pseudo code / description is roughly what happens. I could post the whole module, but I feel that would just muddy the whole question even more.
Private Sub A
FormAlpha is created with 1 ComboBox in the form of a DropDown
This DropDown is populated with a DataSet
DataBinding with a blank DataSet is added to the control to keep track of the users input
End Sub
lblSubmit_Click event is triggered on FormAlpha by the user after they have populated the DropDown with their data. lblSubmit_Click calls Private Sub Submit
Private Sub Submit
BindingContext(DropDown DataSet, tableName).EndCurrentEdit() is called
DataSet.HasChanges() is processed
If changes are present, changes are processed
HERE lies the problem
If the user has manually typed in the DropDown field, but not hit an arrow key or tab, then the DataSet registers a change, but returns a null value in all fields - it knows something was entered, but that data apparently didn't pass through the DataSet for the ComboBox (ListItems or SelectedIndex didn't change / fire I'm guessing). If the user selects the item with the arrow keys, the DataSet has the proper input (I'm assuming the Data was validated by the control at this point).
If the processed data is good, a value is entered into the database
If the processed data is bad (empty), an error is returned
End Sub
If the above can't be solved with what I've provided, but someone still knows a better way to handle this type of situation, I'm all ears. Rewriting the module isn't ideal, but fixing this problem is a necessity.
Alright, while this fix may not be ideal, it is a fix none the less.
The bare bones problem was that the text value of the DropDown wasn't causing the data to actually affect the SelectedIndex / SelectedValue of the control unless you interacted with it using the arrow keys or a mouse click. So, while the DropDown would read "1234", in reality the control saw "".
The fix I have in place for this is simply calling comboBox.text = comboBox.text whenever the user hits the submit button.

How can I save listbox items to my.settings

Intro
I have looked up how to save the items in a listbox to my.settings for a while now and there are so many different answers. I've tried them all (a bit excessively to say), but none have really worked. Its probably because I'm doing something wrong due to a bad explanation or my new-beginner stage at programming.
So I have a form where the user can set a bunch of settings. All of them are going to stay the way they were when he closes the application and re-opens it again. Textboxes, checkboxes and so on works fine, but for some reason the Listbox is harder than I'd expect to be saved.
My listbox
The user adds items to the listbox like this (Writes something like c:\test in a textbox tbpath1, presses a button btnAdd1 and the text will become a item in the listbox lbchannel1)
Private Sub btnAdd1_Click(sender As Object, e As EventArgs) Handles btnAdd1.Click
Dim str As String = tbPath1.Text
If str.Contains("\") Then
lbchannel1.Items.AddRange(tbPath1.Text.Split(vbNewLine))
tbext1_1.Text = (tbext1_1.Text)
My attempt (probably one out of ten attempts)
So this is one of my attempts so far. I wish it was this easy.
My.Settings._lbchannel1.Clear()
For Each item In lbchannel1.Items
My.Settings._lbchannel1.Add(item)
Next
My.Settings.Save()
At the attempt above, I get error 'NullReferenceException was unhandled : Object reference not set to an object instance'
I'm guessing it has something to do with items not being a string and so on, but I'm not sure where to go with this. Can someone wrap it up in a simple explained way?
If you do not add at least one item in the IDE, VS doesnt initialize the collection you create in Settings because it doesnt look like you are using it.
If My.Settings._lbchannel1 Is Nothing Then
My.Settings._lbchannel1 = New System.Collections.Specialized.StringCollection()
End If
My.Settings._lbchannel1.Clear()
For Each item In lbchannel1.Items
My.Settings._lbchannel1.Add(item)
Next
My.Settings.Save()
You can also "trick" it into initializing it for you. Add an item via the Settings Tab, save the project, then remove the item.
You can also create a List(of String) to store the data. Serialize it yourself with 1-2 lines of code and use it as the DataSource for the listbox. It is simpler than shuttling items from one collection to another and keeping them in synch. This answer shows a serializing a List(Of Class) but the principle is the same.