Create an `Items` collection containing references to already existing `Item`s - vba

I mean to create an Items collection, and add to it several already existing Items.
For instance, if I have two references to MailItems, I want to set an Items collection containing those two Items.
It would be something like
' ...
' Code that assigns references to olMail1 and olMail2, of type Outlook.MailItem
' ...
Dim MyItems As Outlook.Items
' Assign with Set / create the object
MyItems.Add olMail1
MyItems.Add olMail2
' Code that can use MyItems(1) to get a reference to olMail1
How can that be done?
Things to clarify are:
How to setup the new collection.
How to add items.
Documentation on Items.Add seems to indicate that it is used for adding newly created objects, not references to existing Items.
I would later iterate through that collection, for instance. I would also apply Find or Restrict; this allows for applying the methods on a much smaller collection than a whole Folder.
PS: I cannot get an Items collection even from
Application.ActiveExplorer.Selection (i.e., without need for creating the collection and add Items one by one). This would be good for a starter.
Background
I mean to find what Items have a sender matching a given string. The aspects that perhaps make my case somewhat more complex than a "base case" are:
I mean to apply the filter only on a selected group of items. E.g., only on the Items that are selected in the Inbox index.
I want to do partial matching. At this point I do not need regular expressions, or even full use of wildcards *?. But at least partial matching as in InStr.
I mean to have a specific Function for the minimal unit: testing one Item, for a single condition. Then loop through all target Items, and all conditions.
I conceived 3 approaches:
Use Rules.
Use Filter or Restrict. These do not accept wildcards (in principle?).
"Manually" check conditions, with InStr, e.g.
Each of the aspects above may bear some complexity for one or more of the approaches.
At this point, I was exploring approach 2. I have a reference to a single Item, and I found how to apply a Filter with a matching condition (see
http://www.outlookcode.com/news.aspx?id=30 ,
http://blogs.msdn.com/b/andrewdelin/archive/2005/05/11/416312.aspx , and the non-accepted answer of VBA Search in Outlook). But to apply the Filter, I need an Items collection, containing my single item.
I have something working with approach 3 (as suggested in the accepted answer of VBA Search in Outlook).
Related links
Identify MailItems satisfying a Rule

You can just use a regular collection:
Dim myItems As Collection
Set myItems = New Collection
myItems.Add olMail1
myItems.Add olMail2
Now if you're looking to restrict the type of objects than can be contained by myItems, then it becomes a bit more complicated, but here's a way to do it:
Restrict type in a Collection inside a class module

I'd suggest starting from the Getting Started with VBA in Outlook 2010 article in MSDN.
An instance of the Items class can't be created in the code. It is asociated and belongs to any folder. You can create a folder to get a new Items instance.
You can use the Copy method of Outlook items to create another instance of the object. Then the Move method can be used to move the item to another Items collection (folder).
1.I mean to apply the filter only on a selected group of items. E.g., only on the Items that are selected in the Inbox index.
You need to iterate over all selected items instead. The Find/FindNext and Restrict methods belong to the Items class only. So, you can apply them to the Folder items only.
2.I want to do partial matching. At this point I do not need regular expressions, or even full use of wildcards *?. But at least partial matching as in InStr.
See Filtering Items Using a String Comparison. You can use the ci_startswith or ci_phrasematch operators.
3.I mean to have a specific Function for the minimal unit: testing one Item, for a single condition. Then loop through all target Items, and all conditions.
Take a look at the Filtering Items section in MSDN which describes the general rules for specifying properties in filters that are supported by various objects in Outlook.
The Filter method of the View class is applied to the Outlook view only. The Items property will return the full list of items.
It would be better if you specify the final goal, not possible way to solve the problem which is not clear for us.

Related

How to display in Outlook, using VBA, a list of emails based on their EntryID (based on a list of EntryIDs stored in MS Access)

I have an Access DB that interacts with Outlook, including capturing the EntryID of selected emails as needed (which are stored in a table in Access)
I have code that allows users to view any email whose EntryID is stored, using Outlook's GetItemFromID method. This works as needed - it opens up a single email based on its EntryID.
However, what I am now looking to do is to filter the main Outlook window, to show emails based on a list of EntryIDs I have saved. So, for clarification, if I have a list of eg 3 emails (with their respective EntryIDs), the main Outlook window would be filtered to show those 3 emails. So basically like a search, but based on EntryIDs.
I can't seem to find anyway to do this? Perhaps there is a way to add a search filter via VBA that will search based on EntryIDs, but I can't find anything on this.
Any ideas much appreciated.
Binary properties like EntryID can't be used in any search or filtering operation in Outlook. You need to use any other properties (custom or user-defined ones) for filtering items in Outlook.
The View.Filter property value is a string, in DAV Searching and Locating (DASL) syntax, that represents the current filter for the view. For more information about using DASL syntax to filter items in a view, see Filtering Items.
Private Sub FilterViewToLastWeek()
Dim objView As View
' Obtain a View object reference to the current view.
Set objView = Application.ActiveExplorer.CurrentView
' Set a DASL filter string, using a DASL macro, to show
' only those items that were received last week.
objView.Filter = "%lastweek(""urn:schemas:httpmail:datereceived"")%"
' Save and apply the view.
objView.Save
objView.Apply
End Sub
Be aware, the EntryID value can be changed when items are moved between stores or folders. Moreover, the value is unique only per store.
If you need to show some items with specific EntryIDs strings you can get these item instances by using the GetItemFromID method and then marking them with a specific user property to be able to apply a filter for it. Or just add another string field to the Db with a custom value which can be added to items in Outlook, so you could easily apply a filter in the UI.

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)". (:

Excel Indirect() type function For VB.net?

In excel I could say =INDIRECT("A" & G3) where G3 had a value of 4 and my cell would then refer to A4. What I am looking for is a similar kind of function for VB.net.
Is there a way to refer to a different variable based on a variable. EG. first pass I was to refer to txtJobNum1, txtBatNum1, and lblBat1. on pass two txtJobNum2, txtBatNum2, and lblBat2. If it were only a few, 3-4 maybe, it wouldnt be bothersome, but it's 50. The best I have come up with now to work around is build a class that holds references to those objects and make an array of that class. Below is an example table showing What I want to make with a given input number.
You can see how if I could make use of an "INDIRECT" function It could potentially shrink down to a 5-6 line loop instead of 200 lines of just variable assignments.
my concept of how it would work
BatchGroups(<NUMBER>).Label = lblBatNum<NUMBER+1>
0 BatchGroups(0).Label = lblBatNum1
0 BatchGroups(0).Number = txtBatNum1
0 BatchGroups(0).Quantity = txtQtybat1
0 BatchGroups(0).JobNumber = txtJobNum1
1 BatchGroups(1).Label = lblBatNum2
1 BatchGroups(1).Number = txtBatNum2
1 BatchGroups(1).Quantity = txtQtybat2
1 BatchGroups(1).JobNumber = txtJobNum2
2 BatchGroups(2).Label = lblBatNum3
2 BatchGroups(2).Number = txtBatNum3
All of the controls are stored in the Controls collection of their parent, and the controls in the Controls collection are addressable by name, like this:
Dim theControl As Control = Me.Controls("txtJobNum" & theNumber)
(Where Me is the Form) If the controls are in some other container control, such as a panel, you can get to them through that container control's Controls property, for instance:
Dim theControl As Control = MyPanel.Controls("txtJobNum" & theNumber)
However, having 50 some-odd controls like that sounds like it may be a bad design anyway. It may be better to consider having a grid, or an editable list of some sort. If you must have all of the separate controls like that, it would probably be better to dynamically load them in a loop, and then store references to them in a list of BatchGroup objects as you were thinking. It would be much easier to to that in a loop because you'd only write the code once rather than separately for each batch group.
More generally, the term in .NET for what you are asking is called "Reflection". However, using reflection can cause your code to be more brittle and it is also not very efficient, so, when there are other alternatives, is is the case here, it is usually best to take one of them, rather than resorting to reflection.
Create a dictionary of your objects by Name. Then use TryGetValue to retrieve. In this case it would expect a String value as a Key, so you can have a custom naming scheme, which maps 1-to-1 onto your controls list.
Read more about a Dictionary class on MSDN:
Dictionary(Of TKey, TValue) Class
You could use .Controls of the parent container, but then your controls could be nested in each other, so you'd have to use recursion or linearize into flat list, either adds complexity and maintainbility effort, and reduces performance. Dictionary is the fastest, especially if your Key is a string.

Get Values from Listbox for if functions

Hey guys very new here.
Have a listbox that gets account names from a specific game server using this command line
Dim apikeyinfo As APIKeyInfo = api.getApiKeyInfo()
lstbxCharacters.DataSource = apikeyinfo.Characters
this code gets all the characters in a single account by displaying it in a listbox.
Now i would like to reference a character from the lisbox but not sure how
Any method such as Listbox.Get to get the value and compare it with something else?
Thanks
you can try something like
lstbxCharacters.SelectedItem
Update
To read the data from the listbox I think there are multiple ways (Assuming that it is readable).
-> Usually listbox display strings, so it should work to read to a string variable
Dim a_string as Strin = lstbxCharacters.SelectedItem
also you may like to add a small check before, assuring that an Item is currently selected:
If lstbxCharacters.SelectedIndex < 0 then return
This jumps out of current sub if no item is selected
And finally, to read the first entry, you can also do it this way:
a_string = lstbxCharacters.Items(0)
if it returns objects, then instead of accessing the object directly, it may work to do
a_string = lstbxCharacters.Items(0).ToString
(most objects allow a .ToString() Function )
Here two ideas for different solutions:
As a user commented, you could access the DataSource directly + the information which listIndex was selected. But if you do so, then maybe it is more easy (if you need to access it anyways, to go with solution 2)
Create a variable of type list(Of some_object) and fill it with the data from the datasource. It will take some time to do this, but if you define for the class some_object a function ToString, then you can fill all objects directly to the lstbxCharacters, and access them without any worries, by doing CType(lstbxCharacters.SelectedItem, some_object)
Update 2
If with reference you mean to access further information from the datasource, then you need to build some kind of query, or set the content of the listbox in relation to another control that shows the database content (in that way the listbox lstbxCharacters would act like a filter)

VB.Net Copy a list to store original values to be used later

I have a WPF form that takes a list of objects that have locations and sizes and plots them on the canvas. I'm currently trying to implement an undo button that will throw out all the changes that have been made to the positions of the objects and revert back to the original collection that was retrieved when the form loaded.
As it stands now I go out to the database on the load of the form and get all the objects that will need to be displayed then assign the list that is returned to two seperate collections. The problem that comes up is that the two collections are actually pointers to the original collection and whenever one is changed the changes are reflected in the second collection.
Is it possible to copy a list of objects so that changes made to one collection won't affect the secondary collection?
So far I've tried simply using the assignment operator, passing the source collection into a function byval and scrolling through each element of the list manually adding it to the second collection and using linq to get all the objects from the original list and pushing the results to a separate temporary list and assigning the second collection to the temporary list.
I feel like I'm overcomplicating the issue but almost all the places I've come across while googling say that this behavior is by design, which I understand but it seems like this would be a fairly common idea.
Here's a function I have used before to make "Deep" copies of objects:
Public Function DeepCopy(ByVal ObjectToCopy As Object) As Object
Using mem as New MemoryStream
Dim bf As New BinaryFormatter
bf.Serialize(mem, ObjectToCopy)
mem.Seek(0, SeekOrigin.Begin)
Return bf.Deserialize(mem)
End Using
End Function
This is kind of a low level approach compared to some of the other answers, but allows you to deep copy any object. I've used it successfully in a situation similar to yours where I needed a deep copy of an array.
Simply assign all items of the listA to listB using this code
For Each elm In ListA
ListB.Add(elm)
Next
There was another answer that was since deleted that said to use var copy = list.ToList(); to get a copy of the list. This will work with the following caveat: Both lists will still reference the same objects, so any changes to those objects will reflect in both lists. As long as you're only changing the order of the objects in the list, this solution is perfectly viable.
You would have to create a new list and add copies of the items in list1 to it. You could do this using object initialization, e.g.
Dim list2 = (From item in list1
Select New ItemType With {.Property1 = item.Property1, .Property2 = item.Property2}.ToList()
An alternative would be to add a Copy constructor to ItemType
Public Sub New(item as ItemType)
Me.Property1 = item.Property1
Me.Property2 = item.Property2
End Sub
And your list copy could be simplified to
Dim list2 = (From item in list1
Select New ItemType(item)}.ToList()
Just beware that if any of the properties of your ItemType are references, you would need to make copies of these objects also. (This is known as a Deep Copy)