EffectusBuilding UI based on conventions
I waited for a while after my article was posted, to see if anyone caught on to some of the things that I did in the sample code that weren’t related to NHibernate usage. It seems that no one caught on to that, so I’ll try pointing them out explicitly.
The basic format of a feature in Effectus is this:
Each feature have a Presenter (business logic for the feature), a View Model, which is directly bound to the View and is the main way that Presenter has to communicate with the View. The main responsibility of the Model is to be bound to the UI, and allow the presenter to update it, but it is not quite enough.
The UI also need to raise events and handle user input. A common example is handling button clicks. You can’t really map them into Model behavior, at least not easily and in a way that make sense to the developers. Instead, I defined a set of conventions. As you can see in the picture, based on the name of the element, we match is with the appropriate execute method and the way to decide if the command can execute. Under the cover, we generate the appropriate ICommand instance, bound to the Presenter behavior.
If you look at the code, you can see that the conventions extend even to handling the selected item changed in a list box, including passing the newly selected instance back to the presenter. Those conventions are project specific, based on what the project need, and they result in a code base that is very easy to read and follow. Moreover, they result in a codebase that is relatively free from infrastructure concerns.
I am going to have a few more posts about Effectus, can you figure out what else I put in that code base?
More posts in "Effectus" series:
- (21 Dec 2009) Isolated features
- (20 Dec 2009) Fatten your infrastructure
- (19 Dec 2009) Building UI based on conventions
Comments
A little thing called "love" ?
seriously I have no idea, but I'm sure the following comments will unveil it :-)
Omer,
Read the code, and look at the future posts :-)
What is a "Fact"? What design pattern does that follow?
Read the code, and wait for the post to show up
Also the sql script throw some error in SQL Management studio.
INSERT INTO [ToDo].[dbo].[ToDoActions]
VALUES (1,
@firefly
ayende.com/.../conditions-and-facts.aspx
I don't know why you thought nobody noticed what you put in the code. The command publishing I was looking for was found in your code ;)
Mihai,
I didn't get any response about that
Thanks alwin, fact is a pretty generic word... so it's kinda hard to search for it :)
Reinventing CSLA?
A possibly offtopic question regarding facts. I post it here since unfortunately the comments for the post linked by alwin are closed.
public Fact(INotifyPropertyChanged observable, Func <bool predicate) ...
Why do you require an observable if you could decompile the predicate, hunt for INPCs mentioned there and listen to those? That would work as intended for a good 90-95% of all cases.
In the cases when a simple heuristics works for us (i.e. it detects all INPCs we need and doesn't detect INPCs that are irrelevant) we can get rid of Facts at all, e.g. change the original sample:
public Fact CanMovePrev { get { return new Fact(CurrentPage, () => CurrentPage > 0); } }
to a much more readable analogue:
public bool CanMovePrev { get { return CurrentPage > 0 } }
Ayende - When I read the article the thought did cross my mind that the non-nhibernate aspects of Effectus were extremely interesting. Most "sample applications" have a throwaway / bland architecture and infrastructure. The Effectus architecture and infrastructure really grabbed my attention. I'm very happy you are going to elaborate on these aspects via your blog.
Also wanted to add that the word "decompile" I used doesn't meant fully-fledged decompilation like the ones Reflector does.
Imo we can be fine with just checking all ldflds and calls to property getters - whether they return INPCs. To do that one needs to find a way to convert byte[] method bodies returned by reflection to a stream of ops. For that purpose one can use Cecil, CCI or something hand-made. E.g. for a similar purpose I've developed my own IL reader: xenogears.googlecode.com/.../Parse/
Ayende - The thing that I found weird was that you decoupled the features so much that you use the name of the namespace (or part of it) as a string to fire up the presenter. It is not something I would do (since I don't like strings in code..), I am looking forward to the post about this...
The way you use for wire the events is very nice :)
Pretty interesting, actually. I used to work with MVP for my web applications and I don't reallt like MVVM (It's not bad, but I don't like it).
Features that I would like to see how it's implemented:
Validation: I would like to have the validation in the model side.
Transaction management: maybe an aspect in the On.... methods?
Tell the view to hide/show controls (change the visual state): The properties would be bound to presenter properties?
I didn't downloaded the code, just navigated it a little, so maybe there's something already defined that I missed.
Eugene,
Because that would be:
a) a lot of hard work
b) trigger the too much magic part
c) would invalidate scenarios such as composite observables
Ayende,
a) and c) need to be done only once, and then can be used across all projects that use facts - tho surely do as it feels good for you to do.
What really got me interested is b). How do you detect the "too much magic"? For me things that don't leak their internals are always ok, regardless of their complexity, given that they do simplify routine tasks. What's your opinion on this?
I really like Conventional based stuff a lot. Something that I did in my CQRS example application is using Win Forms and MVP where I conventionally hook-up the view events with presenter event handlers. It is using reflection which doesn't get cached but honestly on a window start this isn't really a big concern. And if there is an event that doesn't get handled I throw an exception.
The base presenter can be found here: github.com/.../IPresenter.cs
-Mark
My first post here to send you a big thank you !
I didn't have much opportunities to use WPF.
Your sample is a gold mine.
Even if i'm not really fond of the autowiring event based on name convention.
And also, why did you do an Observable class instead of using the databindingFactory to generate INotifyPropertyChanged objects ?
Anyway, thanks again for the sample.
Great stuff, always enjoy reading your posts.
Love to see DataBindingFactory extend to support 1-1, 1-M & M-M oh would that require programmer to deal with it themself? Would be cool is I could have the same experience as [ARDataBind] in MR gave :)
Eugene,
a & c may be only needed once, but that is still complex, and I don't see any huge benefit that it gives in return.
As for too much magic, that is simple formula, how much time are you going to spend debugging a particular problem?
Alexandre,
data binding factory is for entities, not for the type of stuff that I used Obserable for. It is simpler to write the INPC for that
Sokun,
You have the code, write the extension
Ayende,
Thank you.
This is how Caliburn does it, I assume it was your contribution?
After some digging I must say that I really like the code base. I love convention over configuration. With some minor refactoring I was able to incorporate most of it into my code base. Very neat :)
This come just in time as I am working on my first WPF application and I started to get a little sick of the wiring.
Nice architecture =) I wonder if I need MS Prism Commands now.
Ayende, how do you think - will it be easy to add validation to model properties?
So is this stuff going to end up in Caliburn v2? :) Somehow I get the feeling that it will be.
It's actually cleaner than the way Caliburn is doing it currently. It's cleaner for two reason. First Caliburn put the INotifyPropertyChanged on the Model, this put it directly on the property.
Putting it on directly the model require that the user have to raise seperate notification. For example
public string LastName
{
get { return _lastName; }
set
{
_lastName = value;
NotifyOfPropertyChange("LastName");
NotifyOfPropertyChange("CanSave");
}
}
I think that is a little ugly. If there is more commands that tied to LastName the situation get hairy real fast...
Slightly off the topic of this post, but the naming convention in the per-scenario is something I struggle with :)
Doesnt this kill the "Go to type" (Ctrl + N for resharper bindings) feature in resharper? For a large code base, this is a must IMO.
I completely agree with a folder (namespace) per scenario, but I typically add the scenario name to Presenter/ViewModel/View just to support resharper navigation. Its redundant, but I would buy a resharper license just for the said feature alone.
Oh and...
[Quote]Each feature have a Presenter (business logic for the feature), a View Model, which is directly bound to the View and is the main way that Presenter has to communicate with the View. The main responsibility of the Model is to be bound to the UI, and allow the presenter to update it, but it is not quite enough.[/Quote]
I cannot agree more. MVVM forces people to shove a bunch of logic into something that should be strictly for databinding. Presenter-ViewModel-View FTW!
Resharper should work fine, each in it own namespace.
Josh,
Nope, that was parallel evolution
FallenGameR,
It should be very easy, sure. Either directly in the property set or as an extra layer that will handle this in the DataBindingFactory.
It may take a bit of infrastructure on the UI side to handle validation errors correctly, but that wouldn't be too hard.
P,
Yes, it would. The naming isn't mandatory, and I am not too attached to it.
@firefly you can use this approach in Caliburn v1 RTW. It still needs to mature a bit, but it's the approach we took on our most recent project. Though we occasionally ran into areas where we had to use the old Caliburn style.
@Christopher, thanks for clarifying that. I studied your example and I wasn't real clear of what convention to follow. Though isn't the new convention doing it right in the xaml?
@firefly
Resharper will work fine calling everything presenter, but when you want to goto the "Inactive Customer Email Presenter" (for example), you can no longer jump to "ICEP" because you only called it "Presenter". You are basically back to manually navigating via solution explorer.
@p You can type Presenter, it will list all of the presenter... along with their namespace. Not that it's optimal, just that it still work.
@p, Oh I also want to say thanks, I didn't know about that feature until you mentioned it the other day. There is always something about Resharper to discover :). I've always used Ctrl Click to navigate
Ayende, thanks so much for this code, it's just what I needed. I converted it to work with .NET 2.0 and Winforms for a project I'm working on.
Happy to hear that.
Hmmm.... I'm not a pro,, so I wonder,,, translations to other languages... it is possible or will break the binding?
//lg
Ig,
No, it wouldn't. It just depend how you are handling the i18n
Comment preview