WinForms MenuStrip Isolation to improve Code Maintainability - winforms

In Windows Forms, C#, .NET 3.5, VS2008...
What's a good way to isolate the code for a MenuStrip (or any complex control group), and it's child menu items, from the rest of my form?
For example, when I have a MenuStrip with a number of menus and each menu has a number of menu items, which all have click events, a ton of code is spewed into both the Form.Desinger.cs file and also the Form.cs file. This is not causing any problems technically, but just feels wrong to have so much stuff all dumped in one place (along with everything else in the form).
Running the Code Metrics on my entire project, the form is flagged as having the worst Maintainability index of any project file. Normally, I'd not be too dogmatic about heeding the direction of the Code Metrics tool, but in this case I totally agree.
According to Code Metrics, the form violates these best practices:
Too much class coupling
Too many lines of code
Overall low maintainability
Possible solutions to isolate the MenuStrip from the rest of the form:
Stuff it into a UserControl
Other ideas?

I think you should take care about isolate business logic from presentation logic, for instance does not place too much code in click handlers or implement commands for menu items.
Get sure your code metrics does not touch generated code or don't pay attention to bad metrics in autogenerated code.

Can you disable or filter your CodeMetrics from grabbing *.Designer.cs, for example?
If not, I would use a Factory class so that you can create these structures in one line. The down side to this, is that it reduces the functionality of the Designer. In the factory, you could name each component based on a template string + "_FileMenu", for example, so that you could set the base "name" in the Factory constructor.
To reduce code spew in your Form.cs file, consider more of a MVC approach, so that when the designer generates, say a private void button1_Click method, you abstract some business logic to other methods of other classes. So instead of moving all your files, button1_Click will call InitiateMoveFileMethod( string source, string destination ), for example.

Related

Replacing 3rd party Windows Forms UI controls in a large codebase

Me and my colleagues are investigating how to replace 3rd party WinForms controlls by our new UI controls in our large legacy codebase. Practically we would like to replace the 3rd party controlls in the inheritance chain. The 3rd party controlls are used dozens of places by subclassing the 3rd party UI controlls. We d like to perform this change as safety as possible, with minimal code change all over the solution. Do you have any experience how to start? Obviously the inheritance means strong coupling here, so i d like to find the less painful solution here.
Is the "branch by abstraction" concept applicable here?
This is a pretty subjective decision based on your team's understanding of the code base as well as workflow. The bright side is that you've already subclassed all the controls, so you've already done the tedious work of being able to provide whatever properties the code is looking for to compile.
Given that this is WinForms, this should be much more straightforward, since the control sizes and locations are set on the Control level. You need to worry about mapping properties/methods that exist in the old vendor's controls to your new controls and not as much about form layout. This might be straightforward for some controls and more complex for others (i.e. grids).
The biggest roadblock IMO is going to be handling the current design-time serialized logic in InitializeComponent. If you've created a property to map from old -> new, you're going to have to be careful that when the designer re-serializes everything after you open the form and modify something, you might not want to serialize both the old property and the new. As an example:
Old Vendor
this.myOldComponent.Data = this.dataSource;
New Vendor
this.myNewComponent.DataSource = this.dataSource;
In this case, you may consider creating a new property called Data on your subclassed new component so that the old code works without changing anything. Let's say you open the form in the designed and move the grid a few pixels, causing the designer to serialize the data. You might now have:
this.myNewComponent.Data = this.dataSource;
this.myNewComponent.DataSource = this.dataSource;
You can prevent serialization with attributes (good discussion on it in this SO question, but this is just an example of something you might hit.
I don't think there's really a pattern here to follow, unless you mean on the source control level, in which case I would say to absolutely create a new branch apart from your regular development one; who knows what kinds of roadblocks you may hit and you'd want to shelve your work for a bit.
Ultimately, you may decide that it's just best to suck it up and rip out the old components and put in the new, but as mentioned this is very subjective. It's situations like this that make me really love WPF and the MVVM model, since you could entirely rip out the UI and keep the business logic intact.

Update form from class or interface

I have a winform onto which I have a number of controls across a few tabs. I am writing logic which will enable / disable some of these controls based on the combo box selections made by the user. I am guessing that writing the logic in frmMain.vb isn't best practice so I'm wondering whether I should gain access to my form's controls through:
an interface
through friend-declared properties in frmMain.vb that are accessed by another class or
Other
Any help would be welcome!
In general it is a good idea to tie your front-end code to a buisiness logic layer so that it is the logic that controls the enabling/disabling of the controls. If possible, group the controls based on what will get disabled together, then make a routine that disables them all at once, and name it well, like disableAddContactInfoArea() or SetAddContactInfoArea(boolean isEnabled). In my mind, the routine will sit in frmMain.vb.
One thing to avoid is just exposing each individual control to another class (unless, in ine case, that is all you need for a specific business process - but even then you should put it in a routine and name it well to make future edits easy and less complex). Your primary objective is to manage complexity (check out Steve McConnell's book Code complete, maybe chapter 7 for this).
An ideal would be to have a few public Subs on the frmMain.vb, only there to do exactly what needs done, then have the business logic layer call those routines on the instace of frmMain that you are using.

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.

VB WPF MVC (Model + View + ?)

I have an old VB6 application. I want to recreate it in VB.Net using WPF. But I am a bit confused about the "Model View Controller"-pattern. I have two books about design patterns (GoF and J.Bishop) afair this pattern is indeed not mentioned inside one of the two books. I have also searched the internet I found some java-examples. But I have still no clue how I should use MVC-Pattern (should I?) in my new WPF-application.
Let's say for example my model (in fact it is more complex) is only a wheel rim (circle) with the properties Manufacturer, Diameter and Depth. The user should be able to modify the properties using textboxes and ComboBoxes.
Could somebody create a small example that explaines the MVC-Pattern with WPF?
Of course I like reusable classes to have a feasible concept throughout the whole application.
thanks in advance
Oops
Here's a "brief" description of what the MVC pattern is and how I would apply it to a WPF application.
(I might have a few details slightly off since I've mainy hacked in Silverlight but the concept is similar enough.)
Basically, the idea is to separate concerns and define interfaces between the different parts of an application, with the goal of keeping the code structured and maintainable.
The Model in your example would be pretty much exactly as you described the wheel rim - a WheelRim class with the various properties defined in suitable data types. I would put the model i an separate assembly to keep it apart from the other code, but you can settle for just keeping the model classes in a "Models" folder. The model would also have a "twin" in a database, the model classes being pretty much one-to-one-mapped to tables.
(You might wanna have a look at Linq2SQL or Entity Framework, if the database is defined you can pretty much get the model for free along with suitable database access code.)
The View would be the actual WPF xaml files - Defining the Grid or Canvas or what have you. On the WheelRimView there would be labels and textboxes for displaying or accessing the different properties, perhaps along with product images and the like. The code behind for the view would have all the relevant event handlers (start, button_click and so on) for getting the data from the fields and passing them to the controllers.
The Controller would be any "handler code" that you would use to manipulate the data. We're talking the basic CRUD operations here, along with validation and the like. Also, the controller layer would be responsible for compiling the data in a format that can go seamlessly into the View. The WheelRimController hence would have methods like "GetWheelRimList", "GetWheelRim", "AddWheelRim", "ModifyWheelRim" and "DeleteWheelRim". The methods take the values as in parameters and manipulate the model objects accordingliy. the
I would recommend keeping the code-behind of the xaml files free from any "controller"-ish code like validation, aggregation and the like - the code behind should basically only take the values from the textboxes, listboxes and such and send them on "as is" to the controller methods for processing. Also, you should keep any data formatting code to a minimum when getting data for presentation (i.e., no filtering or translating in the view).
A typical use case of "User opens a wheel rim and edits the diameter" would play out thus in code:
User clicks "Edit" on a list page. The WheelRimView page loads.
The WheelRimView.Load() method (or corresponding) calls WheelRimController.GetWheelRim(wheelRimId).
WheelRimController.GetWheelRim(wheelRimId) gets the corresonding data from a database table and populates the properties of a WheelRim object, which is returned to the WheelRimView.
The WheelRimView inserts the property values into the labels and textboxes.
The user changes the diameter value and clicks the "Save button.
The WheelRimView.Save() method calls the WheelRimController.ModifyWheelRimDiameter(wheelRimId, diameter) method.
The WheelRimController.ModifyWheelRimDiameter(wheelRimId, diameter) method parses the diameter (if it is a string) and loads the model object. It applies the modified value to the model object and saves it into the database.
The WheelRimController.ModifyWheelRimDiameter(wheelRimId, diameter) returns a status code to the WheelRimView (for instance a predefined numeric stating any validation errors) to report the success of the save.
The WheelRimView displays a result message (hopfully "saved") to the user.
I hope that clears a few bits up.
Bevcause of the rich binding support available, WPF (and Silverlight) are well suited to MVVM (Model-View-ViewModel). MVVM is an extension of MVC that uses a view model to bind the current state of a view, instead of manipulating the view directly.
There are a bunch of MVVM frameworks available, as well as Microsoft's own Prism framework (which is arguably more useful if you have a larger, modular application).
WPF is probably more well suited to MVVM (Model-View-ViewModel). I'd recommend reading this MSDN article on MVVM and, perhaps, following their advice. There's also a nice collection of links I found on the Bryant Likes blog.

Best place to bring up new window in Model View ViewModel

I have an MVVM application. In one of the ViewModels is the 'FindFilesCommand' which populates an ObservableCollection. I then implement a 'RemoveFilesCommand' in the same ViewModel. This command then brings up a window to get some more user input.
Where/what is the best way to do this whilst keeping with the MVVM paradigm? Somehow
doing:
new WhateverWindow( ).Show( )
in the ViewModel seems wrong.
Cheers,
Steve
I personally look at this scenario as one where the main window view model wants to surface a task for the end user to complete.
It should be responsible for creating the task, and initializing it. The view should be responsible for creating and showing the child window, and using the task as the newly instantiated window's view model.
The task can be canceled or committed. It raises a notification when it is completed.
The window uses the notification to close itself. The parent view model uses the notification to do additional work once the task has committed if there is followup work.
I believe this is as close to the natural/intuitive thing people do with their code-behind approach, but refactored to split the UI-independent concerns into a view model, without introducing additional conceptual overhead such as services etc.
I have an implementation of this for Silverlight. See http://www.nikhilk.net/ViewModel-Dialogs-Task-Pattern.aspx for more details... I'd love to hear comments/further suggestions on this.
In the Southridge realty example of Jaime Rodriguez and Karl Shifflet, they are creating the window in the viewmodel, more specifically in the execute part of a bound command:
protected void OnShowDetails ( object param )
{
// DetailsWindow window = new DetailsWindow();
ListingDetailsWindow window = new ListingDetailsWindow();
window.DataContext = new ListingDetailsViewModel ( param as Listing, this.CurrentProfile ) ;
ViewManager.Current.ShowWindow(window, true);
}
Here is the link:
http://blogs.msdn.com/jaimer/archive/2009/02/10/m-v-vm-training-day-sample-application-and-decks.aspx
I guess thats not of a big problem. After all, the Viewmodel acts as the 'glue' between the view and the business layer/data layer, so imho it's normal to be coupled to the View (UI)...
Onyx (http://www.codeplex.com/wpfonyx) will provide a fairly nice solution for this. As an example, look at the ICommonDialogProvider service, which can be used from a ViewModel like this:
ICommonFileDialogProvider provider = this.View.GetService<ICommonDialogProvider>();
IOpenFileDialog openDialog = provider.CreateOpenFileDialog();
// configure the IOpenFileDialog here... removed for brevity
openDialog.ShowDialog();
This is very similar to using the concrete OpenFileDialog, but is fully testable. The amount of decoupling you really need would be an implementation detail for you. For instance, in your case you may want a service that entirely hides the fact that you are using a dialog. Something along the lines of:
public interface IRemoveFiles
{
string[] GetFilesToRemove();
}
IRemoveFiles removeFiles = this.View.GetService<IRemoveFiles>();
string[] files = removeFiles.GetFilesToRemove();
You then have to ensure the View has an implementation for the IRemoveFiles service, for which there's several options available to you.
Onyx isn't ready for release yet, but the code is fully working and usable at the very least as a reference point. I hope to release stabilize the V1 interface very shortly, and will release as soon as we have decent documentation and samples.
I have run into this issue with MVVM as well. My first thought is to try to find a way to not use the dialog. Using WPF it is a lot easier to come up with a slicker way to do things than with a dialog.
When that is not possible, the best option seems to be to have the ViewModel call a Shared class to get the info from the user. The ViewModel should be completely unaware that a dialog is being shown.
So, as a simple example, if you needed the user to confirm a deletion, the ViewModel could call DialogHelper.ConfirmDeletion(), which would return a boolean of whether the user said yes or no. The actual showing of the dialog would be done in the Helper class.
For more advanced dialogs, returning lots of data, the helper method should return an object with all the info from the dialog in it.
I agree it is not the smoothest fit with the rest of MVVM, but I haven't found any better examples yet.
I'd have to say, Services are the way to go here.
The service interface provides a way of returning the data. Then the actual implementation of that service can show a dialog or whatever to get the information needed in the interface.
That way to test this you can mock the service interface in your tests, and the ViewModel is none the wiser. As far as the ViewModel is concerned, it asked a service for some information and it received what it needed.
What we are doing is somethng like that, what is described here:
http://www.codeproject.com/KB/WPF/DialogBehavior.aspx?msg=3439968#xx3439968xx
The ViewModel has a property that is called ConfirmDeletionViewModel. As soon as I set the Property the Behavior opens the dialog (modal or not) and uses the ConfirmDeletionViewModel. In addition I am passing a delegate that is executed when the user wants to close the dialog. This is basically a delegate that sets the ConfirmDeletionViewModel property to null.
For Dialogs of this sort. I define it as a nested class of the FindFilesCommand. If the basic dialog used among many commands I define it in a module accessible to those commands and have the command configure the dialog accordingly.
The command objects are enough to show how the dialog is interacting with the rest of the software. In my own software the Command objects reside in their own libraries so dialog are hidden from the rest of the system.
To do anything fancier is overkill in my opinion. In addition trying to keep it at the highest level often involving creating a lot of extra interfaces and registration methods. It is a lot of coding for little gain.
Like with any framework slavish devotion will lead you down some strange alleyways. You need to use judgment to see if there are other techniques to use when you get a bad code smell. Again in my opinion dialogs should be tightly bound and defined next to the command that use them. That way five years later I can come back to that section of the code and see everything that command is dealing with.
Again in the few instances that a dialog is useful to multiple commands I define it in a module common to all of them. However in my software maybe 1 out of 20 dialogs is like this. The main exception being the file open/save dialog. If a dialog is used by dozens of commands then I would go the full route of defining a interface, creating a form to implement that interface and registering that form.
If Localization for international use is important to your application you will need to make sure you account for that with this scheme as all the forms are not in one module.

Resources