VB.Net ArrayList - arrays

I'm trying to wrap my head around array lists in vb.net. I am self teaching via the internet but can't seem to figure it all out. Some points i'm having trouble connecting the dots to:
How do i make the array list universal so it's not stuck in a subroutine and I can allow any sub to access the list.
Allowing the list to be added to or removed from from another control on the form.
Saving this array list so the program will populate the list box with it on startup.
Here is an image of the basic concept for the visual:
https://imgur.com/lBbopD8

Up to question 3, using a List(Of T) was the way to go. It may still be but certainly not completely and maybe not at all. Before the advent of the List(Of T), Microsoft recognised that storing Strings in a collection was the most common requirement so, to provide type-safety in that case, they provided the StringCollection class. You say that you want to persist your list of values between sessions so that probably means using My.Settings and it is actually possible to create a setting of type StringCollection.
I would suggest that you open the Settings page of the project properties and add a setting of type StringCollection. Once added, that list will be automatically loaded at startup and saved at shutdown, with no code required from you. You can access it anywhere in the app via My.Settings and you can call Add and Remove or index it or loop over it in exactly the same way as you would an ArrayList or List(Of String).
There is one small gotcha with a StringCollection in settings though. It will actually be Nothing by default. The trick to avoiding that is to edit its Value on the Settings page to add an item, commit that, then edit it again to remove the item. You'll see that, instead of the Value field being empty, it will then contain a snippet of XML. It's that that creates the StringCollection object in the settings file.
As I said, if you want to persist this list between sessions then I strongly recommend using settings this way. Just note that, in order to edit settings, they have to be User-scoped rather than Application-scoped. What that means is that each separate Windows user will have their own copy of the setting and thus their own value. If you only log into Windows with one account then it's of no consequence. If multiple Windows users use the app then it may be considered beneficial in most cases but may be a problem if you want universal settings that can be edited. If it's a problem, you will need to handle persistence yourself but be aware that a standard Windows user (as opposed to an admin) won't have access to write data everywhere, which is exactly why User-scoped settings work the way they do.
Also, while you must use a StringCollection for persistence in settings, you may or may not want to use the same collection in the rest of your code. You might access the collection directly all the time or you may choose to copy the collection to a List(Of String) at startup and then copy the data back at shutdown. Unless you want to avoid committing items until shutdown, I wouldn't bother with the extra collection.

So an important thing to know is that you can directly populate and edit the listbox without having an additional ArrayList. You would use the example code below as follows:
'addTb is the text box you had in the image; this will run on button press event
ListBox1.Items.Add(addTB.Text)
If you are looking to dump ArrayList data into the list box use something like this:
'creates new arraylist and adds items to it
Dim listStuff As ArrayList = New ArrayList
listStuff.Add("Hi")
listStuff.Add(2)
'makes listStuff the datasource for your list box
ListBox1.DataSource = listStuff
Finally, if you are wanting to loop through ArrayList items use something like this:
'remember to do count - 1 or you will receive error since index will be out of range
For i = 0 To listStuff.Count - 1
If listStuff.Item(i) = "" Then
'do stuff here
End If
Next
Hopefully that helps. Let me know if I need to be more clear since this is my first stack overflow answer :)

You are using a ListBox control to visualize a collection of presumably String values from a TextBox control. The ListBox exposes the visualized collection via the Items property.
How do i make the array list universal so it's not stuck in a subroutine and I can allow any sub to access the list.
Because the ListBox control resides on the Form, you can access the Items property through any access level in your Form's code.
Allowing the list to be added to or removed from from another control on the form.
From the Items property, you can use the Add method to add a single value, the AddRange method to add multiple values via an array or another ListBox collection, the Insert method to insert a value at a given index, the Remove method a specific item, and the RemoveAt method to remove an item at a given index.
So in your case, since you're presumably adding the value from the Text property of the TextBox to the ListBox, it is as simple as:
ListBox1.Items.Add(TextBox1.Text)
Saving this array list so the program will populate the list box with
it on startup.
You have a few options, but generally the idea is to is write each value in the Items property to its respective line at a given file when the application closes and then load each value back by reading each line from the same file. Another option is to use My.Settings, though I think with your level of expertise, it would probably be better to stick with the read/write to a file option so you don't have to worry about some pitfalls associated with this option. Here would be a quick example of reading/writing the items to a file:
'Write the items to the file
Dim items(ListBox1.Items.Count - 1) As String
ListBox1.Items.CopyTo(items, 0)
IO.File.WriteAllLines("file.txt", items)
'Read the items to the file
ListBox1.Items.AddRange(IO.File.ReadAllLines("file.txt"))

Related

How to quickly populate a combobox with database data (VCL)

I have read all sorts of documents across the web about what should be a farly common and painless implementation. Since I have found no consistent and slick reply (even the Embarcadero website describes some properties wrong!) I am going to post a "short" howto.
Typical, frequent use case: the developer just wants to painlessy show a couple of database-extracted information in a combo box (i.e. a language selection), get the user's choice out of it and that's it.
Requirements:
Delphi, more or less any version. VCL is covered.
A database table. Let's assume a simple table with an id and value fields.
A DataSet (including queries and ClientDataSets).
A DataSource linked to the DataSet.
A TDBLookupComboBox linked to the DataSource that shall show a list of values and "return" the currently selected id.
Firstly, we decide whether the sort order is the one we want or not and if all the items in that table must be shown. Most often, a simple ORDER BY clause or a DataSet index will be enough. In case it's not, we may add a sort_order field and fill it with integers representing our custom sort order. In case we want to show just some items, we add a visible or enabled field and use it in our SQL. Example:
SELECT id, value
FROM my_database_table
WHERE visible = 1
ORDER BY sort_order
I have defined visible as INTEGER and checking it against 1 and not TRUE because many databases (including the most used SQLite) have little support for booleans.
Then an optional but surprisingly often nice idea: temporarily add a TDBGrid on the form, link it to the same TDataSource of the TLookupComboBox and check that you actually see the wanted data populate it. In fact it's easy to typo something in a query (assuming you are using a SQL DataSet) and get no data and then you are left wondering why the TDBLookupComboBox won't fill in.
Once seen the data correctly show up, remove the grid.
Another sensible idea is to use ClientDataSets for these kinds of implementations: due to how they work, they will "cache" the few rows contained in your look ups at program startup and then no further database access (and slowdown and traffic) will be required.
Now open the TDBLookupComboBox properties and fill in only the following ones:
ListSource (and not DataSource): set it to the TDataSource connected to the DataSet you want to show values of.
ListField: set it to the field name that you want the user to see. In our demo's case it'd be the value field.
KeyField: set it to the field name whose value you want the program to return you. In our demo it'd be the id field.
Don't forget to check the TabOrder property, there are still people who love navigating through the controls by pressing the TAB key and nothing is more annoying than seeing the selection hopping around randomly as your ComboBox was placed last on the form despite graphically showing second!
If all you need is to show a form and read the TDBLookupComboBox selected value when the user presses a button, you are pretty much sorted.
All you'll have to do in the button's OnClick event handler will be to read the combo box value with this code:
SelectedId := MyCombo.KeyValue;
Where SelectedId is any variable where to store the returned value and MyCombo of course is the name of your TDBLookupComboBox. Notice how KeyValue will not contain the text the user sees onscreen but the value of the id field we specified in KeyField. So, if our user selected database row was:
id= 5
value= 'MyText'
MyCombo.KeyValue shall contain 5.
But what if you need to dynamically update stuff on the form, depending un the combo box user selection? There's no OnChange event available for our TDBLookupComboBox! Therefore, if you need to dynamically update stuff around basing on the combo box choices, you apparently cannot. You may try the various "OnExit" etc. events but all of them have serious drawbacks or side effects.
One possible solution is to create a new component inheriting from TDBLookupComboBox whose only task is to make public the hidden OnChange event. But that's overkill, isn't it?
There's another way: go to the DataSet your TDBLookupComboBox is tied to (through a DataSource). Open its events and double click on its OnAfterScroll event.
In there you may simulate an OnChange event pretty well.
For the sake of demonstration, add one integer variable and a TEdit box to the form and call them: SelectedId and EditValue.
procedure TMyForm.MyDataSetAfterScroll(DataSet: TDataSet);
var
SelectedId : integer;
begin
SelectedId := MyDataSet.FieldByName('id').AsInteger;
EditValue.Text := MyDataSet.FieldByName('value').AsString;
end;
That's it: you may replace those two demo lines with your own procedure calls and whatever else you might need to dynamically update your form basing on the user's choices in your combo box.
A little warning: using the DataSet OnAfterScroll has one drawback as well: the event is called more often than you'd think. In example, it may be called when the dataset is opened but also be called more than once during records navigation. Therefore your code must deal with being called more frequently than needed.
At this point you might rub your hands and think your job is done!
Not at all! Just create a short demo application implementing all the above and you'll notice it lacks of an important finishing touch: when it starts, the combo box has an annoying "blank" default choice. That is, even if your database holds say 4 choices, the form with show an empty combo box selected value at first.
How to make one of your choices automatically appear "pre-selected" in the combo box as you and your users expect to?
Simple!
Just assign a value to the KeyValue property we already described above.
That is, on the OnFormCreate or other suitable event, just hard-code a choice like in example:
MyCombo.KeyValue := DefaultId;
For example, by using the sample database row posted above, you'd write:
MyCombo.KeyValue := 5;
and the combo box will display: "MyText" as pre-selected option when the user opens its form.
Of course you may prefer more elegant and involved ways to set a default key than hard-coding its default value. In example you might want to show the first alphabetically ordered textual description contained in your database table or whatever other criterium. But the basic mechanic is the one shown above: obtain the key / id value in any manner you want and then assign it to the KeyValue property.
Thank your for reading this HowTo till the end!

ExtJS 3.4 - Check for recently added items in a store

So I was messing around trying to figure out the best way to find recently added items to a store. To put it into context, I have a grid which data is bound to such store. This store is filled with data from the server side. It also has an "add" button that adds these new records. Whenever a user clicks a line in the grid, I want a different behaviour for saved records and newly created ones. I know I can check if an item has been added recently with
myStore.data.itemAt(0).newRecord //Uses index
or
myStore.data.item(1).newRecord //Uses key; "1" is just for demonstration purposes
However, if the item was added previously to the store (in other words, the data came from the server side), newRecord will return undefined (as a matter of fact, there is no such property in a MixedCollection item that has already been "commited"). Of course this can be easily side-stepped, but will end up looking more like an ugly workaround than anything else.
Is there any better way to do it? I think I'll mess around with the grid methods themselves in order to see if I can figure something out, but any pointers are welcome.

Manipulating ObservableCollection vs Replacing a List

I have a backend Dictionary that is used for synchronization (ie. to both a filestore and a webservice).
Off the top of this I need to generate lists/enumerables for the WPF frontend to consume. What is the difference between either hooking an enumerable up to the dictionary, and calling PropertyChanged when it is updated to using an ObservableCollection and having it automatically called its CollectionChanged.
Synchronizing occurs in the background automatically, and some elements may be removed, others may be updated. I want to propagate this information to the WPF frontend and user smoothly. (ie. if one item is removed, the whole display shouldn't have to be reinitialized). I also want to add animation when items are added and removed (ie. fade in and out) - is this possible if I replace the whole list or will it cause every single item to fade in again?
So should I:
1) use an observable collection and write some fancy synchronization logic between the dictionary and the collection?
2) use linq extension methods to convert the dictionary to an enumerable and simply call propertychanged on the enumerable whenever it changes?
3) synchronize between a dictionary and a list, by replacing the list whenever it is updated?
Also, how would any of these work with sorting and filtering operations that are performed just for the UI? (ie. if I need to filter some elements out of the dictionary based upon user selection, should I use a similar method as the one you have recommended?)
If you "replace" any IEnumerable<T> when you get a change, the entire list will be refreshed in the UI.
In order to avoid that, you'll need to implement INotifyCollectionChanged, and provide a collection which implements this. Instead of replacing the collection, you handle the elements, which in turn fires the appropriate events.
ObservableCollection<T> handles this for you. Personally, if you need to keep this in a dictionary, but want to synchronize it to a list, you may want to consider making a custom collection, possibly based around SortedDictionary. A standard Dictionary has no sense of ordering, which means that there'd be no way to implement INotifyCollectionChanged appropriately.

Should path recursion occur in a class or presentation layer?

I have a WinForms app with an input textbox, button, and a multiline output textbox.
A root path is entered in the textbox. Button click calls a function to recursively check all subdirectories for some proper directory naming validation check.
The results are output into the multiline textbox.
If the recursive work is done in a separate class, I have two options:
Keep track of improper directories in a class property(e.g. ArrayList),return the ArrayList when done, and update the output textbox with all results.
Pass in ByRef the output textbox and update/refresh it for each improper directory.
Even though 1 & 2 are single-threaded, with 2, I would at least get my results updated per directory.
If the recursive work is done in the presentation layer and the validation is done in a separate class, I can multithread.
Which is a cleaner way?
You don't need to pass the TextBox ByRef. It's already a reference object. Passing it ByRef would only have an effect if you planned to assign a different or new TextBox to the reference.
If you're going to do the work in a separate class, it seems as simple as passing in the contents of the TextBox as a string, and getting the results back as a string or a set of strings (array or List<string> or the like). This is better than passing in the TextBox in case someday you decide to use a different kind of control to store this information.
I would suggest something close to option 1. I would have a method that takes the root directory as an input and returns a List of directories that are "bad". Also I would call that method on a background thread so as not to hang the UI while the operation is being performed. Add a progress bar or some sort of wait indicator so the user knows that the operation is ongoing.
Passing the textbox into the method wouldn't allow you to reuse that method for anything else. In the interest of code re-use (if that's important to you) I think it's cleaner to simply have the method return a list and let the callback method figure out how to display the data.
[not sure if this is the place for a follow up to the original question]
so, is it safe to say that a recursive business layer function will not be able to update a presentation level control at each recursive iteration?

How do you make a copy of an Object

I have a collection of an object called Bookmarks which is made up of a collection of Bookmarks. This collection of bookmarks are bound to a treeview control.
I can get the bookmarks back out that I need, but I need a copy of the bookmarks so I can work with it and not change the original.
Any thoughts.
Thanks.
Create a new constructor for your bookmark class that takes an existing bookmark as the parameter.
Within this new constructor, copy all the property values from the existing bookmark onto the new one.
This technique is known as a "Copy Constructor".
There's an article on MSDN that goes into more detail - see How to Write a Copy Constructor.
Most collection classes in .Net provide a constructor overload that allow you to pass in another collection like
dim copyOfBookMars as New List(of BookMark)(myOriginalBookMarkList)
Been awhile with VB, but c# offers a clone() method.
You don't generally make a copy of an Object, an Object makes a copy of itself (clone). Since an object contains state inforrmation, a bitwise copy can't be counted on as being appropriate; so the defining class needs to take care of it.
You may want to implement multiple simultaneous pointers (bookmarks) in your case.

Resources