Sunday, May 29, 2016

On The Fly Data Driven Submenus With Unlimited Menu Items

The two classes offered in this post, C_MenuItem and C_MenuItems, are used to create data driven submenus. There is no limit to the number of submenu items.



The sample code below illustrates how the top menu's opening and closed events manage the data driven submenu that is appended to the parent menu. In the above example, Setup is the top menu and Savings is the parent menu to which the data driven submenu is appended.

C_MenuItem
The object class which instantiates each submenu item.

 Properties:
 IsSeparator (Boolean) ... Used by the collection class during submenu builds.

SubmenuDelegate (List(Of EventHandler)) ... The AddressOf the routine fired when a submenu item is clicked. Only item 0 of the List(Of T) is used.

SubmenuImage (Image) ... Optional submenu image.

SubmenuName (String) ... The displayed submenu name. If not set, "Name not provided" is displayed.

SubmenuTag (String) ... Optional submenu Tag property.

C_MenuItems
The collection class which maintains the collection of objects and does the work of building submenus.

Methods:
 Add (C_MenuItem) ... Adds a new item to the collection. The submenu is build with menu items listed in the order the objects are added to the collection unless the Sort method is invoked.

AddSeparator ... Adds a separator item to the collection.

AnotherMenuPrefix (ByRef Integer, ByRef String) ... Used by the collection class to create each submenu keyable prefix using 1-9 followed by A-Y. Returns True for a maximum of 34 prefixes. When the 34 submenu items limit is reached, False is returned with the prefix set to Z.

AppendSubmenu (ByRef ToolStripMenuItem) ... Builds the submenu which is attached to the passed parent menu item. When there are no submenu items, the parent menu item is disabled. When a submenu exceeds the 34 item limit, the bottom Z submenu item becomes the parent (with name More ...) to which is appended the next up to 34 submenu items. This chaining of submenus has no limit.

Clear ... Clears the collection.

Sort ... Sorts the collection on SubmenuName. Not advisable to invoke when a collection contains separator submenu items.

Properties:
Count ... Returns the number of objects in the collection.

 Sample Code

Delegates
The below routines are fired when a coincident item in the generated submenu is clicked. The footprint of each routine must match that of the delegate defined in the C_MenuItems class. All three routines are referenced below in the Submenu Builder sample code.
 
     Private Sub NewSavingsClick(ByVal sender As System.Object, _
                                ByVal e As System.EventArgs)
        NewSavings()
    End Sub


    Private Sub EditSavingsClick(ByVal sender As System.Object, _
                                 ByVal e As System.EventArgs)

        '** Extract the saving object's key from the submenu menu item Tag property
        Dim mi As ToolStripMenuItem = DirectCast(sender, ToolStripMenuItem)
        If mi.Tag.ToString.Length > 0 Then
            EditSavings(mi.Tag.ToString)
        End If
    End Sub


    Private Sub ReorderSavingsClick(ByVal sender As System.Object, _
                                    ByVal e As System.EventArgs)
        ReorderSavings()
    End Sub



Top Menu Events
The opening event of the top menu, that contains the parent menu, fires the routine that builds the parent's submenu using current data. The top menu's closed event is where the parent menu's custom submenu is cleared.

    Private Sub mnuSetUp_DropDownOpening(sender As Object, _
                                         e As System.EventArgs) _
                                         Handles mnuSetup.DropDownOpening
        SavingsSubmenus()
    End Sub


    Private Sub mnuSetup_DropDownClosed(sender As Object, _
                                        e As System.EventArgs) _
                                        Handles mnuSetup.DropDownClosed
        mnuSetup_Savings.DropDownItems.Clear()
    End Sub


Submenu Builder
The submenu builder routine is invoked in the top menu's DropDownOpening event (above).

    Private Sub SavingsSubmenus()

        '** Instantiate the collection and object classes  
        Dim mis As New C_MenuItems
        Dim mi As New C_MenuItem


        '** Add the static New submenu item which must have a generated
        '** prefix since all of the letters in the alphabet are used for
        '** clickable submenu item prefixes  
        With mi
            .SubmenuName = "New"
            .SubmenuImage = imgList.Images("savings_new")
            .SubmenuDelegate.Add(AddressOf NewSavingsClick)
        End With
        mis.Add(mi)


        '** Add a submenu item for editing each existing saving object
        Dim svs As New C_Savings(gtApp_Savings_Path)
        If svs.Count > 0 Then
            mis.AddSeparator()
            For Each sv As C_Saving In svs.Items
                mi = New C_MenuItem
                With mi
                    .SubmenuName = sv.Name
                    If Not sv.Shown Then
                        .SubmenuName &= " (Hidden)"
                    End If
                    .SubmenuTag = sv.Key
                    .SubmenuImage = imgList.Images("savings_edit")
                    .SubmenuDelegate.Add(AddressOf EditSavingsClick)
                End With
                mis.Add(mi)
            Next
        End If


        '** Add the static Reorder submenu item, after a separator,

        '** when there are more than one saving edit submenu items
        If svs.Count > 1 Then
            mis.AddSeparator()
            mi = New C_MenuItem
            With mi
                .SubmenuName = "Reorder"
                .SubmenuImage = imgList.Images("savings_updown")
                .SubmenuDelegate.Add(AddressOf ReorderSavingsClick)
            End With
            mis.Add(mi)
        End If


        '** Build the submenu
        mis.AppendSubmenu(mnuSetup_Savings)

    End Sub

 

Download
Click here to download a zipped file that contains both classes.


 

Saturday, April 16, 2016

Restoring CRLF In Multi-Line Text Serialized To XML Files

The XML serializer does not preserve carriage returns and line feeds in text from multi-line textboxes. Here's a simple way to restore each CRLF when displaying such text after an XML file has been deserialized.

Assume a textbox named txtNotes which has the Multiline property set to True. The string property of a class, let's call it C_Bill, in which the note is stored is Notes. Before an instance of the C_Bill class is serialized, the Notes property is populated thusly.

    Dim bi As New C_Bill
    bi.Notes = txtNotes.Text

On the flip side, when bill data is deserialized to an instance of the C_Bill class, the notes text will not display correctly with this code. (The bill collection is persisted in a List(Of T) XML collection discussed here.)

    Dim bis As New C_Bills(pathToXMLFile)
    Dim bi As C_Bill = bis.Item(IndexOfTheBill)
    txtNotes.Text = bi.Notes

The notes will be displayed as one continuous line regardless of the number of carriage returns and line feeds were in the original text. To preserve the line breaks, pass the deserialized text through a temporary textbox.
 
    Dim bis As New C_Bills(pathToXMLFile)
    Dim bi As C_Bill = bis.Item(IndexOfTheBill)
    Dim tempBox As New Textbox
    tempBox.Multiline = True
    tempBox.Text = bi.Notes
    txtNotes.Text = String.Empty
    For Each line As String In tempBox.Lines
        txtNotes.Text &= line & ControlChars.CrLf
    Next





Monday, October 5, 2015

Instant List(Of T) XML Collections

View the video to see how two myOwnDoubleText libraries are used in myOwnRepeater to quickly add a bug free List(Of T) object class and a List(Of T) collection class to a VB.NET project. The collection is serialized to an XML file.





Download the demo project and myOwnDoubleText libraries to look more closely under the hood. The demo project business layer is missing so you can practice using the libraries. (Visit the myOwnRepeater page on the 2 Good Software site to download this free application.)


Sunday, August 23, 2015

Listview Methods & Properties

The C_Listview class encapsulates several useful methods and properties for use with a listview control.

Methods

Add (+2 overloads)
  • Add(ListView, ListViewItem) ... Adds the ListViewItem to the referenced ListView control. Sets ErrorMsg property.
  • Add(ListView, String()) ... Builds a ListViewItem from the passed string array which is added to the ListView control. Sets ErrorMsg property.
  • Add(ListView, String(), String) ... Builds a ListViewItem from the passed String array which is added to the Listview control. The item's Tag property is set to the String property. Sets ErrorMsg property.
Check (apply when a ListView control's CheckBoxes property is True)
  • CheckAll(ListView) ... Checks all items in the ListView control.
  • UncheckAll(ListView) ... Unchecks all items in the ListView control.
Find
  • Find(ListView, String, Boolean) ... Searches for the String in all ListView control columns. Boolean sets case sensitivity. Returns the index of the item in which the String is found or -1 when the String is not found.
  • FindNext(ListView, String, Integer, Boolean) ... Continues the search started by Find at item index Integer + 1. Boolean sets case sensitivity. Returns the index of the item in which the String is found or -1 when the String is not found.
FindIncludingTag
  • FindIncludingTag(ListView, String, Boolean) ... Searches for the String in all of the SubItems of all ListView control items plus the Tag property. Boolean sets case sensitivity. Returns the index of the item in which the String is found or -1 when the String is not found.
  • FindNextIncludingTag(ListView, String, Integer, Boolean) ... Continues the search started by FindIncludingTag at item index Integer + 1. Boolean sets case sensitivity. Returns the index of the item in which the String is found or -1 when the String is not found.

FindInTag
  • FindInTag(ListView, String, Boolean) ... Searches for the String in the Tag property of all ListView control items. Boolean sets case sensitivity. Returns the index of the item in which the String is found or -1 when the String is not found.
  • FindNextInTag(ListView, String, Integer, Boolean) ... Continues the search started by FindInTag at item index Integer + 1. Boolean sets case sensitivity. Returns the index of the item in which the String is found or -1 when the String is not found.
GetColumnIndex
  •  GetColumnIndex(ListView, MouseEventArgs, Integer) ... Gets the index of the clicked ListView control column based on the MouseEventArgs. Returns Boolean indicating whether the Get was successful. The column index is returned in Integer.
Move
  • MoveItemUp(ListView) ... Moves the selected ListView control item up one row. Returns Boolean indicating whether the move was successful. When successful, the moved item is made visible.
  • MoveItemDown(ListView) ... Moves the selected ListView control item down one row. Returns Boolean indicating whether the move was successful. When successful, the moved item is made visible.
Remove
  • RemoveItem(ListView, ListViewItem) ... Removes the passed ListViewItem from the referenced ListView control. Sets ErrorMsg property.
  • RemoveAt(ListView, Integer) ... Removes the indexed ListViewItem from the referenced ListView control. Sets ErrorMsg property.
  • RemoveAll(ListView) ... Removes all items from the ListView control.
  • ClearAll(ListView) ... Removes all items and columns from the ListView control.
Resize Column
  • ResizeColumn(ListView, Integer) ... Resizes the ListView control column with index of Integer (the 'rubber' column) to the maximum possible width for the listview control's width. Keeps all columns visible while eliminating all blank space to the right of the right-most column. Space is left for the vertical scrollbar. (Invoke this method in the ListView control's SizeChanged event. It may need to also be invoked in the Form Load event to correctly initialize the column width when the form is first shown.)
Select
  • SelectAll(ListView) ... Selects all items in the ListView control.
  • UnselectAll(ListView) ... Unselects all items in the ListView control.
  • SelectItemByTag(ListView, String, Boolean) ... Unselects all items in the ListView control and then selects the first item in the ListView control whose Tag property contains String. Boolean sets case sensitivity.
Update (+2 overloads)
  • Update(ListView, ListViewItem, Integer) ... Replaces the ListView control's item indexed by Integer with the ListViewItem. Sets ErrorMsg property.
  • Update(ListView, String(), Integer) ... Builds a ListViewItem from the String array. Replaces the ListView control's item indexed by Integer with the built ListViewItem. Sets ErrorMsg property.
  • Update(ListView, String(), String, Integer) ... Builds a ListViewItem from the String array. Sets the built ListViewItem's Tag property to String. Replaces the ListView control's item indexed by Integer with the built ListViewItem. Sets ErrorMsg property.
Properties

AnError ... Returns Boolean indicating whether the ErrorMsg property is populated.

ErrorMsg ... Returns the text of the error message.

IndexOf(ListView, Integer, String, Boolean, Boolean) ... Returns the index of the ListView control's item containing the String in any column. The first Boolean sets case sensitivity. The second Boolean sets whether the search is for a whole word.

Item(ListView, Integer) ... Returns the ListView control's ListViewItem with the Integer index. Sets ErrorMsg property.

ItemArray(ListView, Integer) ... Returns all of the subitem's text from the ListView control's ListViewItem, with the Integer index, as a String array.

TextIsUnique(ListView, Integer, String, Boolean, Boolean) ... Returns Boolean indicating whether the String is NOT found in the ListView control's column with index of Integer. True means the String was not found. The first Boolean sets case sensitivity. The second Boolean sets whether the search is for a whole word.

Download

 Click here to download a zipped copy of the C_Listview class.










Saturday, July 11, 2015

A Message Box On Steroids

The MessageBox available in VB.NET is limited in terms of options for tailoring the displayed message to fit the application. The F_MessageBox form offered in this post is an alternative to MessageBox that provides the expected properties plus several more as illustrated by the demo project.




The Properties

Same as MessageBox
FormTitle (String) ... The message box title. Defaults first to the application's ProductName. If the ProductName is not available, the default is "Message Box".
FormMessage (String) ... The message being displayed. Required.
FormButtons (F_MessageBox Enum) ... The expected MessageBox set of button options plus None. Defaults to OKOnly.
FormDefButton (F_MessageBox Enum) ... The expected MessageBox set of default button options. Defaults to Button3.
FormIcon (F_MessageBox Enum) ... The expected MessageBox set of message icons. Defaults to Information.

Added in F_MessageBox
FormFont (Font) ... Any available font and font size. Defaults to Microsoft Sans Serif, 8.25pt.
FormFontBold (Boolean) ... Defaults to False.
FormFontColor (Color) ... Any available color. Defaults to ControlText (black).
FormMessageWidth (Integer) ... Any value in the range 275-900 pixels. Defaults to 400 pixels.
FormShowButtonIcons (Boolean) ... Defaults to True.
FormShowCopyButton (Boolean) ... Defaults to True.

Here's the message box resulting from the property settings in the above demo project illustration.


Of course, having the source code for F_MessageForm, you can modify the form to add features that are limited only by your imagination.

 Instantiating The Form
There are two options for instantiating the F_MessageBox form ... with and without parameters. The below code samples, generated with myOwnRepeater, are samples of each instantiation option.

With Parameters


 Without Parameters


Download

Click here to download the message box demo project which contains the F_MessageBox form.

Saturday, May 30, 2015

Persisting Treeviews

Saving the contents of a treeview control (the tree) to an XML file and then displaying the tree from a previously saved XML file are made exceptionally easy with the library of Double Text files* offered in this post.


1. Download the library.

 Click here to download the zipped Double Text library which contains these library files.
  • C_TreeNode Class
  • C_TreeNodes Class
  • DisplayTreeview Procedure
  • SaveTreeview Procedure
Unzip the library files into a folder of your choice.

2. Add the classes to a project.

a. Start myOwnRepeater*. Open the Persisting Treeviews library folder where you saved the unzipped library files.

b. Repeat the C_Treenode class. You will be prompted for the developer's name. Follow the instructions displayed in an in-line message and a reminder.

c. Repeat the C_Treenodes class. You will be prompted for the developer's name. Follow the instructions displayed in the in-line messages and the reminder.


3. Add the procedures to a form in the project that contains a treeview control.

a. Start myOwnRepeater*. Open the Persisting Treeviews library folder where you saved the unzipped library files.

b. Repeat the DisplayTreeview procedure. You will be prompted for the developer's name and the name of the treeview control. Follow the instructions in the reminder.

c. Repeat the SaveTreeview procedure. You will be prompted for the developer's name and the name of the treeview control. Follow the instructions in the reminder.

------------------------------

* The free app myOwnRepeater is used to repeat the files in Double Text libraries. Click here to find out more and to download this free utility. You might also check out the post in which myOwnRepeater was first introduced.







Tuesday, May 12, 2015

Sorting Dates Without DateDiff

The DateDiff function can be unreliable in certain circumstances returning unexpected values especially when two dates are close together. I have found that using what I call sort dates for comparisons and ordering processes is reliable in all circumstances.

The C_Date_Sort class sorts dates into ascending or descending sequence based on a sort date (yyyyMMdd) or sort date and time (yyyyMMddHHmmss).  A second, optional string value, when present, is sorted in consort with the dates being sorted. In other words, the second, or tag array ends up in date sequence.

Values to be sorted can be passed to the C_Date_Sort class either as arrays or one at a time using the Add method. Class methods are:
  • Add(Date, Optional String, Optional Boolean)
  • Sort(Optional Boolean)
  • Sort(Date(), Optional String(), Optional Boolean, Optional Boolean)
Class properties include:
  • Count as Integer
  • Item(Index) as C_Date_Sort.CollectionItem
  • ItemDate(Index) as Date
  • Items as List(Of CollectionItem)
  • ItemTag(Index) as String

The C_Date_Sort class is stored in a Double Text library file. Adding the class to a project is very easy with the use of the free myOwnRepeater utility.

What follows may be new and strange. Please perform the simple steps in order as presented to remove any confusion.

1. Install myOwnRepeater


Go to the myOwnRepeater webpage to both view a short explanation of what this application is all about, and to download the install package. Again, it's free.

After installing myOwnRepeater, run the program and add your name to the program's My List (File menu). This will make your name available to replace prompt responses that have a default of my name. Unless, of course, you prefer to give me credit in your projects. If that's the case, thank you.

Exit the program.

2. Download The Demo Project


Download the SortDemo project which was written in Visual Studio 2010. Unzip the project into a folder of your choice. DO NOT open the project in Visual Studio yet. The project will not run until the C_Date_Sort class is added. We'll get to that shortly.

3. Download The Library File


Download the zipped C_Date_Sort.DbC library file. Unzip the file into a library folder of your choice, however, naming the folder Sort will match what follows below.

The library file cannot be added to a Visual Studio project directly. It has to be repeated using myOwnRepeater.

Note: Since future posts will be offering Double Text library files, you might consider setting up a master folder in which this, and future library folders are saved. For example, the name of my top folder in which I keep my library folders is named Repeater Libraries.

4. Repeat The Library File


This is how the C_Date_Sort class is added to the SortDemo, as well as any other Visual Studio project, using myOwnRepeater.

a. Open the SortDemo solution in Visual Studio.
b. Start myOwnRepeater. Open the library folder containing the C_Date_Sort.DbC library file. The library folder, which I named Sort, is listed on the left in the Library list. The library file, C_Date_Sort, is listed on the right in the Filename list.


c. Start a repeat by clicking on C_Date_Sort in the Filename list. What happens during a repeat depends on the markers that are embedded in the file.

For this library file, an in-line message is displayed with instructions to add a class to the Visual Studio project with a name of your choice. Add the class to the demo project. Click OK when that's done.



A prompt is displayed in which you enter the class name. If you named your class in the project something other than the default name, enter that class name and click OK. If you used the default name, just click OK or press Enter.




The next prompt is for the developer's name. Here's where My List comes into play. If you added your name to My List, open that menu and click on your name to replace the default name. Click on OK or press Enter.



At the end of the repeat an in-line message is displayed with instructions for pasting the repeated class into your project. Do the select and paste to add the complete C_Date_Sort to the DemoSort project. Click OK or press Enter to end the repeat.




You have finished generating and adding to your project about 240 lines of bug free code that is fully documented with the current date and the developer's name.

The demo project will now compile and run. The demo program generates random dates as well as random string values for the tag array. The number of dates listed, 10-100, is set in the numeric spin control.



Post Script

This is an uncomplicated example of how to use myOwnRepeater to generate code from Double Text library files. In this case, you have the choice of continuing to use myOwnRepeater to generate the C_Date_Sort class for future projects, or copying the class from the demo project and adding it to other projects as an existing item. Either way works. Hopefully, you will continue to use myOwnRepeater for this and other posts some which will be much more complex than this one. Having experience with myOwnRepeater will come in handy.