I have a (problem?) where my application's main form code is becoming quite large. Because it is the main section of the app, there are a ton of event handlers.
I really don't want to use partial classes to separate sections of the code because I might as well just use regions then. Also partial form classes don't show up in the designer and are in general just confusing.
Not really sure what to do to be honest. The file is around 1,000 lines of code while all my other classes are 10-150 lines each. I can't really encapsulate the behavior in to other objects because most of the event handlers manipulate controls on the form or member variables of the class and doing so would require a lot of parameter passing and confusion.
Trying to think of a possible course of action. I really would like to encapsulate a lot of the behavior in to other classes but the problem is the event handlers modify private controls and member variables. These classes would need to somehow have access to this data but I'd rather not just give an instance of the form to the class and make all the controls and member data public >.>
Any ideas?
Break it down into user controls.
Make sure there's nothing in the form that is not about presentation (display to the user, collection of user input). Move all data access out into separate classes, and the same for business logic.
Typically, this is done via encapsulating portions of the Form into UserControls, which manage their own internal events/controls. The UserControl can be passed/instantiated with the appropriate data from the Main form.
Related
So I am still new to wpf/mvvm. I'm not using any framework, just a VS wpf project.
I have a main window view model, it has 5 properties, 3 commands, several private functions that get the data to populate the view with, 2 dialogs and their functions, and one delegate for an event handler.
I tried creating a helper class to handle the private functions, but those functions update properties that raise property changed events, so i had to add the same properties to the helper class. It felt strange to duplicate the properties in helper class from the view model.
Also, when I tried this, the property changed events were null because the data context of the view is set to the view model, not the helper. So my attempt failed.
The properties in the view model are for one component each-a combo box.
I don't really know how to break up the class anymore than I already have.
Should commands go in a separate file? If I move my private function into a helper class, how can I propagate a OnPropertyChanged event from the helper to the view model to the xaml?
You could also work with partial Classes to separate different concerns.
You can use foody to inject the property-Changed Code at compile-Time.
With foody you can use [ImplementPropertyChanged]-Attributes, so there is no need for all the OnProperty-Changed stuff in your viewmodel.
The first thing that comes to mind is that you should not have all that code in your MainWindowViewModel, it should only act as a container for your embedded views and handle global events and such. So try to modularize it as much as possible into multiple views (with correct naming which tells you the purpose of the view).
I am pretty certain that the dialogs can be extracted into own classes.
If you can't reduce the amount of methods by refactoring them into helper classes, maybe you still can reduce the size of the methods by refactoring as many commonly used code snippets as possible?
One view can consist of many views. So, if one of your viewmodel is too big, you could consider creating new viewmodels and views which could be nested in the main viewmodel. That could be one way to do this.
i originally had a main form with 2 panels, one had a graph in that i clicked on the other had the results displayed related to what you clicked.
All this code was dumped in the main form.
I tried to be a smart alec and use inheritance, so now i have a main form which has a graph objet and i have a superclass graph -> subclass specific graph heirarchy
however now when the graph is displayed in the main form, how do i get the data back to display in the other panel? The reason i ask this is now that the graph stuff is in its own heirarchy, it has no knowledge of the panel on the main form so i have no hwere to set the data when it is set
i hope someone can help
thanks
edit: i think i might just pass the control collection for the panel in to my graph class so i can add the relevant items in there
Depending on the relation between the two forms, you can use events to message data between them.
A click event handeled in the graph can raise an event with the data you need to display, which would end up on the owning form. Depending on the relationship, you either call a function on the 2nd form to display the data, or raise an event to which the 2nd form subscribed to send over the data.
[Edit]
I read Forms instead of Panels.
The main idea remains the same though. From your style of writing, it looks like you have an Objective C history. I know on the Mac, these things would be done in Interface Builder, setting bindings to make this stuff happen, but in the .NET Winforms you do these things in code (handeling events, calling methods, etc). If you like to stick more to the way it's done with Interface Builder, you might want to take a look at WPF applications; bindings work in a very similar way to how it's done on the Mac.
I would design this as an object that aggregates another object. Have a look at the way master-detail view works.
I want to refactor some code.
Basically the code I want to refactor is a Form (using System.Windows.Forms;)
The way it is setup now, depending on which radio button you selected it shows a different layout for the window: different labels, buttons, etc. Not always a big difference, but different. This is a lot of conditional statement junk all over the place. I wanted instead to refactor it with the State pattern. There are three main states.
I'm not sure the best way to do this. Right now the IState interface has a DoAction() method which does some action particular to the unique State, and a DrawForm() method which re-draws the form based on the current State... However, to do the DrawForm(), the State classes need to be able to access the Form's member variables. That's what threw me for a loop. I didn't really want to expose them.
Is there a better way to do this?
You could make your state classes nested in your form. They will then be able to acces fields of form without having to expose them.
If your primary concern is keeping form stuff encapsulated, and you want to keep the state machine outside, I had a similar question a while back, you could check out the answer here - basically it entails creating a private inner 'action' class which DOES have access to form methods, then passing this to the state machine so that it can use the action class to invoke form methods indirectly.
I don't know if this helps, but why not have something like a strategy pattern for drawing the form?
So something along the lines of
interface IDrawStrategy
{
void Draw(FormType form);
}
And then when you pick which state the form is in, you can assign the form's draw strategy. Yes, you will still need to give the draw method access to the Forms variables to allow it to position them, but it at least allows you to make it easier to add different looks based on the states.
In wanting to extract the "state" of the Form, you are describing a pattern better known as a "Presentation Model". You will find some good information and tips on how you generally want the Form and State to interact by reading this article by Martin Fowler. In your case, your Form is the "View" and your State is the Presentation Model. As always, Fowler does a great job answering this question and more.
I have a a user control which contains several other user controls. I am using MVVM. Each user control has a corresponding VM. How do these user controls send information to each other? I want to avoid writing any code in the xaml code behind. Particularly I am interested in how the controls (inside the main user control) will talk to each other and how will they talk to the container user control.
EDIT:
I know that using events-delegates will help me solve this issue. But, I want to avoid writing any code in xaml code-behind.
Typically, it's best to try to reduce the amount of communication between parts, as each time two user controls "talk" to each other, you're introducing a dependency between them.
That being said, there are a couple of things to consider:
UserControls can always "talk" to their containing control via exposing properties and using DataBinding. This is very nice, since it preserves the MVVM style in all aspects.
The containing control can use properties to "link" two properties on two user controls together, again, preserving clean boundaries
If you do need to have more explicit communication, there are two main approachs.
Implement a service common to both elements, and use Dependency Injection to provide the implementation at runtime. This lets the controls talk to the service, which can in turn, keep the controls synchronized, but also keeps the dependency to a minimum.
Use some form of messaging to pass messages between controls. Many MVVM frameworks take this approach, as it decouples sending the message from receiving the message, again, keeping the dependencies to a minimum.
Your conceptual problem is here:
Each user control has a corresponding VM.
Having a separate ViewModel for every view pretty much defeats the concept of a ViewModel. ViewModels should not be one-to-one with views, otherwise they are nothing but glorified code-behind.
A ViewModel captures the concept of "current user interface state" -- such as what page you are on and whether or not you are editing -- as opposed to "current data values'.
To really reap the benefits of M-V-VM, determine the number of ViewModel classes used based on distinct items that need state. For example, if you have a list of items each of which can be displayed in 3 states, you need one VM per item. Contrarily, if you have three views all of which display data in 3 different ways depending on a common setting, the common setting should be captured in a single VM.
Once you have strucutred your ViewModels to reflect the requirements of the task at hand you generally find there is no need nor desire to communicate state between views. If there is such a need, the best thing to do is to re-evaluate your ViewModel design to see if a shared ViewModel could benefit from a small amount of additional state information.
There will be times when the complexity of the application dictates the use of several ViewModels for the same model object. In this case the ViewModels can keep references to a common state object.
There are many differenct mechanisms for this, but you should first find out in what layer of your architecture this communication belongs.
One of the purposes of the MVVM framework is that different views can be made over the same viewmodel. Would those usercontrols talk to each other only in the view you are currently implementing, or would they have to talk to each other in other possible views? In the latter case, you want to implement it below the view level, either in the viewmodel or the model itself.
An example of the first case may be if your application is running on a very small display surface. Maybe your user controls have to compete for visual space. If the user clicks one usercontrol to maximize, the others must minimize. This would have nothing to do with the viewmodel, it's just an adaption to the technology.
Or maybe you have different viewmodels with different usercontrols, where things can happen without changing the model. An example of this could be navigation. You have a list of something, and a details pane with fields and command buttons that are connected to the selected item in the list. You may want to unit test the logic of which buttons are enabled for which items. The model isn't concerned with which item you're looking at, only when button commands are pressed, or fields are changed.
The need for this communication may even be in the model itself. Maybe you have denormalized data that are updated because other data are changed. Then the various viewmodels that are in action must change because of ripples of changes in the model.
So, to sum up: "It depends...."
I think the best solution would be using Publisher/Subscriber pattern. Each control registers some events and attaches delegetes to events exposed by other controls.
In order to expose events and attach to them you would need to use some kind of Mediator/EventBroker service. I found a good example here
The best way to do this in my opinion is via Commanding (Routed Commands / RelayCommand, etc).
I want to avoid writing any code in the xaml code behind.
While this is a laudable goal, you have to apply a bit of practicality to this, it shouldn't be applied 100% as a "thou shalt not" type of rule.
You can communicate between elements on the UI by using element binding, so assuming a user control you created exposes a property, the other user controls could bind to it. You can configure the binding, use dependency properties instead of basic properties / implement INotifyPropertyChanged but it is in theory possible, but does require some forethought to enable to communication this way.
You will probably find it far easier using a combination of events, code and properties than try a pure declarative way, but in theory possible.
You can share some View Model objects between controls as well as Commands...
For example, you have some main control, which contains two other controls. And you have some filtering functionality in the main control, but you want to allow user to set some part of the filter in the first sub-control (like "Full filter") and some part of the filter in another (like "Quick filter"). Also you want to be able to start filtering from any of sub-controls. Then you could use code like this:
public class MainControlViewModel : ObservableObject
{
public FirstControlViewModel firstControlViewModel;
public SecondControlViewModel firstControlViewModel;
public ICommand FilterCommand;
public FilterSettings FilterSettings;
public MainControlViewModel()
{
//...
this.firstControlViewModel = new FirstControlViewModel(this.FilterSettings, this.FilterCommand);
this.secondControlViewModel = new SecondControlViewModel(this.FilterSettings, this.FilterCommand);
}
}
public class FirstControlViewModel : ObservableObject
{
//...
}
public class SecondControlViewModel : ObservableObject
{
//...
}
In the main control XAML you will bind sub-controls DataContext to the appropriate View Models. Whenever a sub-control changes filter setting or executes a command other sub-control will be notified.
As others have said you have a couple of options.
Exposing DepedencyProperties on your user controls and binding to those properties provides a pure XAML solution in most cases but can introduce some UI dependencies in order for the bindings to see each other
The other option is a decoupled messaging pattern to send messages between ViewModels. I would have your user controls bind to properties on thier own VM's and then on the property change inside that VM it can "publish" a message that notifies other "subscribers" that something has happened and they can react to that message however they want to.
I have a blog post on this very topic if it helps: http://www.bradcunningham.net/2009/11/decoupled-viewmodel-messaging-part-1.html
If you're using strict MVVM, then the user-control is a View and should only "talk", or rather, bind, to its ViewModel. Since your ViewModels most likely already implement INotifyPropertyChanged, as long as they have a reference to each other, they can use the PropertyChanged events to be notified when properties change, or they can call methods (better if it's through an interface) to communicate with each other.
I could use some advice/help on a piece of software I've developed.
The application is a wizard style app where users fill out fields on each form before choosing to go to the next form or back to the previous. Fairly simple.
Right now the menu calls frmWiz1(InitialData) and when frmWiz1 returns with DialogResult.OK the menu will call frmWiz2(frmWiz1.Data) (not exactly, it stores all of the Data from each form, and passes those references in to the next form). Each data object inherts from an IPrintable interface that defines methods for printing itself, so at the last page in the wizard (print preview/sign), it adds each Data object to a custom PrintDocument object that just iterates through the data objects, calling their print functions and manages pagination etc.
Initially I thought this was a good idea but now I'm thinking that:
- The menu form is handling too much flow logic.
- The Data objects (which handle all of the business logic that applies to their particular set of data) should be decoupled from print logic (cause as they are now, they're in the printing namespace - maybe just a relocation will set my mind at ease).
I don't know. I'm decent with the language, but I'm still a rookie at design.
Screw "frm" prefixes!
With respect to the overall flow of the application, I would recommend using Application Controller or something of the kind in order to centralize the logic.
As far as the UI goes, each Wizard stage should be a separate User Control (with no "Cancel", "Finish", "Next" or whatever buttons) wich is placed on the root form with the aforementioned buttons.
No object should be responsible for printing itself - use IPrinterService for doing that.
Just a few general thoughs:
This is a great Wizard control. We use it here at work, and I must say this guy did a real good job with it. Not sure if it can be useful to you, but check it out
Figure out exactly what you need to know about an object in order to print it. Try to come up with methods and/or events that you would need an object to have in order to be "printable". Put those into an interface, and have your business objects implement that interface. Then, have your printing helper class deal strictly with interfaces.