Should path recursion occur in a class or presentation layer? - winforms

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?

Related

VB.Net ArrayList

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"))

WinForms and child forms - How to consolidate redundant "utility" code?

I searched and Googled first, thinking surely someone must have asked this before, but I sure can't find a good description of this problem.
I have six or eight similar C# .NET 2.0 WinForms applications built with the fairly common model of a main application window with several GUI data fields plus several modal dialogs for further data collection. Many of the data fields (especially TextBoxes) have identical data validation routines. I'm writing the same xxx_Validating() routines over and over, which in the simplest case only capitalize the first character of the entered text (if any) and redisplay the result. I have another one for ZIP Code fields that takes the first 3 digits of a 5-digit US postal ZIP Code and returns the corresponding State, using a 1000-member string array. Simple stuff. There are a few others; here's an example:
public void CapFirstCharTextBox_Validating(object sender, CancelEventArgs e)
{
string strValue = ((TextBox)sender).Text.Trim();
if (strValue.Length >= 1) {
if (char.IsLower(strValue[0])) {
strValue = strValue.Substring(0, 1).ToUpper() + strValue.Substring(1);
((TextBox)sender).Text = strValue; // fires (whatever sender)_TextChanged()
}
}
}
Again this is part of a half-dozen or so such "utility" routines. I've only got one set of these per dialog box class, and all the various TextBoxes in that dialog which need this have their Validating event pointing to the same method. So it's not like I've got 20 of these in a given source file (one for each TextBox) or anything; there's only one for the whole dialog class.
Problem is, the whole set of these exists in every source file where I need them. That's one set for the main window and more for each pop-up dialog box -- and that's too many. I understand modal dialog box classes can't communicate with each other, and making all this stuff global is elusive at best and a big "no-no" at worst.
I have successfully tried passing a reference to "FormMain" (where one copy of these routines exist) to the various dialog constructors, and then calling these validation routines with that from their own validation handlers. It works but feels awfully clunky and certainly not like the best approach.
So, how would I (or would I want to) rearrange the project and organize the code better to have only a single instance of these kinds of things? How would I wire up a global "utility" class of such methods such that I can get to it from the main form's code and from that of a bunch of pop-up modal dialog boxes as well?
I'd like to maintain just one executable with no additional .DLLs if possible (these are all one-project-per-solution, by the way), and if practical I'd like to further be able to share that common code across multiple solutions.
I think the answer will include writing new assemblies, using different namespaces (currently all my code in a given project is contained in the same namespace), and maybe separating this stuff out into its own project in the same solution file.
Is it possible?
You can share code across solutions by keeping the code in one place and adding a link to the file in each solution.
To add a link: right click the project (or folder) you want to add the code to, then select "Add existing item", browse for the file, when found click the down arrow on the button and pick Link to.
This way the projects that link to the file will share the same code.
BTW: take care when using a source control system that doesn't know how to handle these links.

WP7 - Databinding + Italics + Wordwrap issue

Here is what I am trying to do. I can't seem to be able to figure out a solution yet:
I have 2 string data sources from a webservice and on the front end I want to combine them into a single sentence separated by a comma.
The first part I want in normal font, the second part I want in italics
I also want the sentence to word wrap
I am using MVVM so I want to some how bind these string data sources to a textblock some how...
The cases I want to be able to handle:
Normal:
ex. This is the sentence part 1, this is the sentence part 2
No second part so no comma
ex. This is the sentence part 1 I still want this to be able
to word wrap
The second part word wraps
ex. This is the sentence part 1, this is the sentence
part 2 wrapped to second line
Also
Second Part only with word wrapping
First part wordwraps followed by the second part
There seems to be no easy solution.
The one that I can think is have a PropertyChangedEventHandler which notifies me when those strings were returned from the webserver, then format the string in codebehind...
In order to achieve the effect that you are trying to achieve continuous word wrapping with the italic formatting can only sensibly be achieved by using the <Run> element within a TextBlock. However, you cannot bind the contents of a Run element, so you will need to create this in code.
Assuming that you get two separate responses from the web service (they don't come back from a single call), then you cannot rely on which one will come in first, so I would use event aggregation to notify the view from the view model when all the necessary data has been received. The PRISM library for WP7 includes an implementation that you can use to achieve the effect (note that PRISM for WP7 is much lighter than it's WPF or Silverlight counterparts).
In response to the event, your view can pull the properties from the view model, create the TextBlock and it's component Run elements, and then update accordingly. In fact, when you subscribe to an event using PRISM, you can specify that the handler for that event is run on the UI thread, which will no doubt help.

When Should I Retrieve Values from Textbox?

Suppose I have a Window with TextBoxes I want to use the values. Right now I'm thinking of either:
1) Updating each associated value once the cursor is out of focus, and once the user presses Ok I start the program
2) Once the user presses Ok, I retrieve all the values at once then start the program
I'm not sure which one is better though. First alternative seems more modular, but there's more semantic coupling since I each new box is supposed to be updating its respective value.
I realize this isn't all that important, but I'm trying to understand when to centralize and when not to. Other better approachers are appreciated too.
Use data binding to bind the text boxes' contents to objects in your code behind. WPF will take care of updating your attributes. Usually updating the data-bound value is done when the focus is lost on text boxes. However, you can also specify that it will happen whenever the value changes.

contextsensitive RoutedUICommand.CanExecute, Execute

I have a single RoutedUICommand that can be accessed through various places in the UI. Such as global Keyboardshortcut, Menu, ContextMenu or Button. The code that is to be executed in the RoutedUICommand.CanExecute and RoutedUICommand.Execute methods depends on what UI element was used. How can I achieve this differentiation. I was thinking that I could use the (Can)ExecutedRoutedEventArgs.Source or OrigianlSource but the source is always the same. It is the main Root window. How is this usually achieved? What could I possibly be doing wrong?
If you need different code to run depending on the UI that invoked the command you are probably doing something wrong.
If you have something like a just doing something from a keystroke or opening a dialog asking for more information from a menu you should break this apart into two commands (like MS Office "Print" and "Quick Print" commands).
If you truly have to do different things from each UI element you are not getting any advantage from using commands and should think about using old fashioned event handlers, at least then the element specific code is tied to the element and not stored in a central all encompassing "Execute" code .
And if you choose to ignore my advice above, take a look at the CommandParameter property, you can set a different value fro the parameter for each UI element, at least with it you can keep an illusion of the UI/Logic separation commands are designed to provide.
Normally you could have different CommandBinding implementations for different 'Targets' - having different behavior for each 'Source' is unusual.
Could you give an example of what you are trying to do?

Resources