Was working on a blog post talking about a problem I’ve found in Git (1.7.0.2) and I thought I would check to see if it’s been fixed in the most recent version. After downloading and installing I reviewed the release notes where I’m quite shocked to read the following:
“…All hopes to the contrary, Git for Windows is backed by only a handful of developers, in spite of being downloaded almost one hundred thousand times. You can expect developers to be enthusiastic to fix others' issues in such a situation only for so long. In short: Do not expect other people to fix your issues for you.”
Basically, what I get from this is that the msysgit committers are a bunch of whiners. What they fail to see is that the people who would benefit from their VCS may not have the same skillset to work on the code. This is something we’ve realized long ago with Rhino Mocks.
Should more people help? Possibly. As someone active in open source technology I don’t know that I can be of much assistance to the Git team – I am someone who benefits greatly from their software (I am quite thankful for it) but don’t have the requisite knowledge in C/C++ to assist in any meaningful fashion.
Just a bit disappointed to see this type of whining in their release notes – certainly there are far better ways to say what they’re trying to say. Doesn’t represent msysgit or the broader open source community well.
Today I received the following error and thought it summed up my day perfectly.
Now the real question is what was I doing to receive this error…… I was trying to copy a single line (w/ 50 characters of text) to the clipboard…DOH
Till next time,
UPDATE: I have added subscription donors/contributors who made this possible. UPDATE: Our friends from LosTechies also wanted to participate, and they donated a number of licenses (6 and counting). Thank you guys!
In an internal discussion we had, an interesting idea popped up. MVP bloggers we had at CodeBetter/Devlicio.us were generous enough to donate some of their MSDN subscriptions they were given as part of MVP program. Then the cool kids at LosTechies stepped in, and donated a number of licenses as well! Currently we have 13-16 licenses available, and this number may change depending on the demand and outside donations. The subscriptions are for one year.
We decided to give them away to successful/promising OSS projects that are in need of licenses. The rules are simple.
You can send me an email (tehlike (hat) gmail (hot) com) with title starting with [CDL/MSDN] (or [CD/MSDN]), or DM me on twitter (@tehlike) if you want to participate. You have to include
It wouldn’t have been possible with the help of following fellow bloggers. Thanks to Codebetter Crew: Ben Hall, Ward Bell, James Kovacs. Devlicious Crew: Hadi Hariri, Christopher Bennage, Tim Barcz, Rob Reynolds. Lostechies Crew: Eric Hexter, Jimmy Bogard, Keith Dahlby
Good luck!
As the Windows Phone 7 have either a dark (black) or light (white) theme which can be applied it will become very important for you to know which theme the user enabled in order to determine your applications color schema, this is assuming you are using styles which are not built into the skinning on WP7.
Sadly detecting the current theme is a bit hackish as it requires you to take a look at a current loaded resource to determine the theme. I would have hoped that this would be part of the built in SDK as I see this as a common need for many applications.
Here is how you determine which theme is being used.
Color themeColor = (Color)Application.Current.Resources["PhoneForegroundColor"]; if (themeColor.ToString() == "#FFFFFFFF") { this.PageTitle.Text = "Dark "; } else if (themeColor.ToString() == "#DE000000") { this.PageTitle.Text = "Light "; }
As you can see the code to determine the theme is easy, but not clean in my opinion.
Quite some time ago, several OSS leads asked us about whether JetBrains could provide OSS license for YouTRACK. We’ve taken it one step further, and in collaboration with the great folks at CodeBetter and Devlicio.us, we are pleased to announce YouTRACK at CodeBetter. Having TeamCity running most of the OSS projects on there, it only made sense to offer the same facilities for issue tracking at the same location. And seeing that YouTRACK integrates with TeamCity, we can offer all the functionality the combination is capable of.
I’d like to thank Kyle Baley, James Kovacs and Bredan Tompkins for making this possible, as without their support this wouldn’t happen. Not only are they generous in hosting YouTRACK, but also ran the risk of providing me with the credentials to the server, and at one point it looked like it had all gone to hell. Luckily however all seems to be working great now.
If you are an OSS project lead and would like to use YouTRACK for your issue tracking, please email teamcity@codebetter.com with your details and you’ll be up and running in no time. If you want to learn more about YouTRACK, please visit our web site.
Imagine we are awhile into the future. How do you get open source releases down to your project so that you can use them? How do you get the products down to your computer so that you can use them? Is it easier or harder than the way we’ve always done it before?
Before we can go there, let’s look at what we do now (the past is really the same for us here). Let’s say I want to use NHibernate. What do I do? There are basically three paths we all follow in this process.
1. Never had product x before. If I don’t have a current version, I have to go out and look for it. So what do I do?
2. Use the current version without upgrade. If I have a current version, I may just copy it over and use it in my new project. Nevermind that it is two versions ago. I don’t want to take the time to upgrade because it could be a pain.
3. Upgrade. If I find a bug or I decide it’s time to upgrade to a newer version, how do I get it? Repeat the process of #1 (Never had product x before) and getting the latest version.
It's slow. How long did it take me to get all of that? Yeah, now multiply times every library I want to use.
Too many decisions. I have to make way to many decisions to get the right product and right version downloaded and referenced into my project.
Dependencies may be hard to manage. I may be using projects that depend on the same libraries. In the example above I am using Castle in my project. That got upgraded as well when I got the latest version of NHibernate. Now I may have to test the changes to that. What if the latest version of Castle Windsor I am using is not compatible with the latest version of NHibernate? I won’t see that issue necessarily until I try to run my code. I can try binding redirects, but there is no guarantee that that will work. So now I have a problem. I have to figure out what version of Castle Windsor to use so that I can use the latest version of NHibernate. And this is where dependency management is fully placed on me as a developer. I can now decide to move forward or just continue to use the old version.
So now that we have looked at how we currently do it, how will it be in the future?
Open source developers will publish their latest releases to a central repository (possibly in addition to other methods of offering releases). Then everyone can get their latest releases and develop from there.
Same three scenarios.
1. Never had product x before. Here’s the process.
And I’m done. It’s all brought to me and sitting in my references folder of my project.
2. Use the current version without upgrade. This is still an option. Same as in the present described above.
3. Upgrade. Here’s the process.
Speed. The process is amazingly streamlined. We are talking seconds as compared to minutes of time to get something. I can now concentrate on what I want to do instead of spending all the time I was on just getting the packages I needed.
The decision tree is reduced. How many decisions did I have to make to get what I needed in the present scenario? How about the future scenario? Greatly reduced. Less choices to get what I want actually gets me to a decision faster.
I immediately see the dependency changes. All of the dependencies that are required come along nicely with NHibernate. I can immediately see that it downgraded my versions of Castle Core and Dynamic Proxy2. So now I know immediately that I have a problem between Castle Windsor and NHibernate using different versions. It doesn’t solve my problems on this, but it brings it to the surface. So now I can try something like the aforementioned binding redirects.
The process of upgrade became easier than use the current. Another thing you may have noticed. It actually becomes easier to upgrade to the latest version as compared to #2 (Use the current version). That will move people to upgrade, because people will choose the easiest path to get them on their way. And when everyone using the latest version, everyone wins.
Oh yeah, the future? It’s now. http://groups.google.com/group/nu-net
Before you comment about “cluttering” the ruby community, please be sure to read this (we’re with you on this): http://devlicio.us/blogs/rob_reynolds/archive/2010/07/19/gems-for-net-community-response.aspx
Gems - Package Management for .NET
How To – Gems & .NET & How To – Gems & .NET - Dependencies (References)
Walkthrough - Create Gems Even Easier With a Conventional Build (UppercuT)!
While working on an application targeted towards WP7 I wanted to have the application change style resource which was attached to a border element based on a value (aka a status) in my view model. After searching around for a bit I was not able to come up w/ a complete end to end solution, only bits and pieces. In order to help the next poor newbie I thought I would post my solution (btw, there may be a MUCH better solution than this out there).
In this sample I simply created a test application based off of the Item List Template which comes out of the box with a few minor changes.
Setting up the XAML
The important item to pay attention to above is the highlighted line. This is the line which will change the border brush via the converter based on the value in the ‘Color’ property in my view model.
Changes to the View model
The code above is the property I added which will allow us to trigger the border brush to determine what style to use
Creating the Converter
The above is a pretty standard converter which uses the IValueConverter interface. The Part in here it pay attention to is how I am grabbing the brush resources out of the App.xaml file (there may be a better way to do this, but i could not find this). What this code does is basically determines which loaded resource to return based on the value passed into the converter
Registering the Converter
Once you create your converter class (see above) you do need to register it in your app.xaml class. Do do this simply follow the logic above.
Creating the Brush Resources
The code above will create the various resources needed to allow us to change the color of our item in XAML
Populating the ‘live data’
The default template for the Item List project has all the data hard wired in the MainViewModel class. I simply added the above logic to populate the color properties.
After you put all the above together you get….
Notice how the little colors next to each item change, this is our doing :) Now of course this is a simple example, but it does go to prove how you can accomplish this.
A bit later than expected (ah, work) I published beta 2 of Windsor 2.5 today. The release has the following changes as compared to beta 1.
- added support for selecting components based on custom attributes and their properties. See Component.HasAttribute<T>() methods
- added WithService.DefaultInterface() to fluent API. It matches Foo to IFoo, SuperFooExtended to IFoo and IFooExtended etc. If you know how DefaultConvention works in StructureMap, this is pretty similar
- added support for CastleComponentAttribute in fluent API. Also added helper filter method Component.IsCastleComponent
- added ability to specify interceptors selector as a service, not just as instance
- added ability to specify proxy hook in fluent API
- indexers on IKernel are now obsolete.
- added WithAppConfig() method to logging facility to point to logging configuration in AppDomain's config file (web.config or app.config)
- BREAKING CHANGE: Restructured lifecycle concerns - introduced ICommissionConcern and IDecommissionConcern and favors them over old enum driven style.
- Fixed how contextual arguments are handled. Null is no longer considered a valid value (That would cause an exception later on, now it's ignored).
- Changed method DeferredStart on StartableFacility. It now does not take a bool parameter. A DeferredTryStart() method was introduced instead.
This is probably the last pre-release for 2.5 and if no critical issues are found, we’ll release final release in 2, 3 weeks. Go grab the bits, see if it works for you and if it does not report back. I’m also looking for people who want to contribute sample applications for the final release. Ping me if you’d like to contribute to that.
[Finally I’ve managed to get the second part of the post on plug-ins. Sorry for the delay to everyone who was waiting. Appreciate your patience. And now I’ll resume my holidays!]
In the previous part of this series, we saw the basics of how to create a plug-in for ReSharper, install it and run it. We created a context action that would allow us to mark a public method as virtual (where applicable). However, this was done as an explicit action by the user, as such, you didn’t get any kind of hint or suggestion to do this. What we want to do now is make this a hint, so that highlighting appears under methods that could be made virtual. In this part we are going to expand on the same plug-in and convert it into a QuickFix.
Have you seen the little squiggly lines that appear in Visual Studio?
They usually indicate a Suggestion (field can be made read-only), Warning (possible null reference) or Error. ReSharper analyzes and can detect potential issues in the code (similar to what static checker of Code Contracts does). These are known as Highlights and they are related to QuickFixes in that usually a highlight has an QuickFix associated to it, which invokes a context action. This is usually done by placing the cursor on top of the highlighting and press Alt+Enter
In the gutter of the Visual Studio editor (right-side), ReSharper displays a series of warnings, errors and hints, which indicate potential issues on a specific file. These issues are detected by background processes known as Daemons. Since what we are looking for is for ReSharper to warn us of existing methods that could be made virtual, what we need to do is somehow hook into these daemons.
The Daemons in ReSharper use the Visitor pattern to use act on elements, be it code, files, etc. The first step is to implement an IDaemonStage interface, which hold metadata about our daemon stage at at the same time acts as a factory for the actual process we are implementing.
There are two main methods to implement. The CreateProcess is what creates the actual process for us and the NeedsErrorStrip which indicates whether this daemon uses the gutter to display strips. The DaemonProcessKind parameter passed into the first method helps us discriminate on when this process should be executed, i.e. only during checking of visible (current) document, during solution wide analysis, etc.
The next step is to implement the process via the IDaemonStageProcess interface:
The main meat of this class is in the Execute method. We first check to make sure that we’ve not received an interruption (Interrupt Flag raised) due to some external action. Next step is to get access to the current file (remember that we are visiting the entire visible document, not just a specific method). Having the file, we can now create a RecusiveElementProcessor* to perform a tree walk of the AST and perform the specific action on each element. The action to perform is declared as the lambda expression. Since we’re interested in the method declaration, the type is IMethodDeclaration (there are many others). If we look at the expression, we can see that it’s pretty much the same as that of Part 1, the only difference is that we add the results to the highlighting variable.
The HighlightingInfo class has a parameter which can be a Suggestion, Warning or Error, as explained previously. Since in our case we need a suggestion, we pass in the MakeMethodVirtualSuggestion:
This class is pretty simple. The main property to define is the ToolTip, which is the text that will show when we hover of the highlighting. The ErrorStripeToolTip is what’s displayed in the right-hand side gutter. Finally the Attribute StaticSeverityHighlighting is to indicate what type of tip it is (Warning, Error, etc.).
[*Note: In this case, the operation we want to perform is very simple. If we want a more complex scenario where we need to do some processing before and after each element is visited or have a more fine-grained control, we can implement the IRecurisveElementProcessor. I’ll cover this in another post].
To recap, right now we would have everything place to display highlighting when a method that could be made virtual is encountered. The only remaining part is to now be able to apply a QuickFix. This is in many ways similar to the ContextAction we saw in Part 1:
The MakeMethodVirtualQuickFix needs to implement the IBulbItem and IQuickFix interfaces. For ease of implementation we can inherit from BulbItemImpl. The constructor should take as parameter always the actual highlighting that has given way to invoking the QuickFix, in our case the MakeMethodVirtualSuggestion. Similar to the ContextAction we implemented in Part 1, the actual fix itself is pretty trivial. All we need to do is make the method virtual. How do we get access to the method? The easiest way is via the Declaration property of the highlighting passed in (this is a property we added before). The only thing left is to call the SetVirtual method on it. Since we are in the ExecuteTransaction method, ReSharper makes sure that any change made is executed as a whole.
The rest of the properties are trivial. Text returns the text of the QuickFix (what appears in the menu), and IsAvailable indicates when the QuickFix is available, which in our case is whenever the highlighting is valid.
Once we compile the plug-in and place it in the corresponding Plugins folder under ReSharper\Bin, we’re done. Here’s the end result:
and invoking Alt+Enter on the highlighting gives us:
Extending ReSharper to create highlightings and quick fixes is pretty simple once you understand how all the pieces fall into place. Most of the code will usually be the same and what will vary will be the actual element processing to be performed and the corresponding QuickFix. As mentioned previously (in the Note), for complex scenarios, we can have more control over the tree walk and that’s something we’ll examine in a future post.
I’ve placed the code up on my github account so feel free to download it, play with it and ping me if you have any comments or questions. The code is updated to work with ReSharper 5.1
In a previous post I mentioned how I was going to show you how UppercuT (UC) has the ability to make gems stupid simple to create and publish. You ask if gems can get any easier and to that I answer, “Why YES, they can!” How about just filling out the information for the gemspec, running a build and having a nice, shiny new gem ready for publishing?
Basically you want to get the latest release of UppercuT. You can download it or grab the source and compile.
There are already instructions out there for how to get UC in your project, so I’m not going to concentrate on that.
Once you upgrade (or add and get everything else set up), you want to have this gems folder at your top level (just under trunk or branch name).
In that gems folder you are going to find a file named something like the file below. Rename that file to your new gemname.gemspec.
Note: Once you have a gemspec file in a gems folder, your build server NOW needs to also have ruby and gems installed.
Open that file in your favorite text editor and fill in the details. Here’s a good post on how to do that.
Then just for having the gems folder with a gemspec in it, UC will automatically try to build the gem for you (the code in your code_drop/projectname folder is brought over to code_drop/gems/lib folder).
Once we are good with what we are getting back for the gem, we can start cleaning up. So we go into our build.custom (don’t have one? create it right next to the build folder) folder and create a file named gemsBuild.post.step.
Let’s open the file and insert this:
<?xml version="1.0" encoding="utf-8"?> <project name="CUSTOM POST GEMSBUILD" default="go"> <!-- Project UppercuT - http://projectuppercut.org --> <property name="build.config.settings" value="__NONE__" overwrite="false" /> <include buildfile="${build.config.settings}" if="${file::exists(build.config.settings)}" /> <property name="dirs.current" value="${directory::get-parent-directory(project::get-buildfile-path())}" /> <property name="path.to.toplevel" value=".." /> <property name="folder.code_drop" value="code_drop" overwrite="false" /> <property name="dirs.drop" value="${dirs.current}\${path.to.toplevel}\${folder.code_drop}" overwrite="false" /> <property name="folder.gems" value="gems" overwrite="false" /> <target name="go" depends="run_tasks" /> <target name="run_tasks"> <delete> <fileset basedir="${dirs.drop}/${folder.gems}" > <exclude name="*.gem" /> <include name="**/*" /> </fileset> </delete> </target> </project>
Note: Don’t like NAnt? You can also use Ruby or PowerShell instead of NAnt to write your custom extensions.
Now when we run our build again, we have a nice clean folder.
Interested in influencing what goes INTO your gem in the first place? That’s a pretty good thing to be concerned with so that you don’t have all of your referenced assemblies sitting in there. Read about how to set up dependencies. Then you will create a file next to gemsbuild.post.step named gemsPrepare.post.step.
In that file, you will insert something similar to the following (roundhouse file):
<copy todir="${dirs.drop}\${folder.gems}\lib"> <fileset basedir="${dirs.drop}\${folder.gems}\lib\MSBuild"> <include name="**/*.*" /> </fileset> </copy> <copy todir="${dirs.drop}\${folder.gems}\lib"> <fileset basedir="${dirs.drop}\${folder.gems}\lib\NAnt"> <include name="**/*.*" /> </fileset> </copy> <delete> <fileset basedir="${dirs.drop}\${folder.gems}\lib" > <include name="ConsoleApp/**" /> <include name="MSBuild/**" /> <include name="NAnt/**" /> </fileset> </delete>
With this knowledge, you shall build. Interested in more UppercuT? Check out the ChuckNorris framework and join the group.
The Future is Now!
There has been a lot of response in the community about this gems idea we’ve been talking about. I even had the opportunity to sit down with Nick Quaranto, the guy behind Rubygems.org, over coffee Sunday and talk about where we think we are going and what it will take to get there.
One of the biggest things that everyone wants to see carrying this idea forward is that we migrate off of Rubygems.org and have our own gem server. And we all agree this is a great idea. There are just two things that really keep that from happening at the current moment. The devil is in the details and when we are ready to move off to our own server, it’s real money at that point (to do it right).
Right now we are embracing the idea of “one community,” and we are certainly not the first non-ruby community to use RubyGems.org. Long term a different host for the .net gems will be both necessary and beneficial. As we move off, we’ll be able to even expand some of the things we can do as far as checking gems as they are published to make sure they meet certain constraints.
If you are concerned about getting started versus later migrating – the process will be well thought out and seamless for the user experience of getting your gems and pushing them. The user experience is still going to be rock solid, and the same concept of gem install gemname is going to be there no matter what happens.
And for you gem owners – we’ll be working closely with Nick to ensure that the process is still rockstar.
The more community involvement there is from you and others like you, the faster we move to our own servers. And as far as when, well that all depends on you.
"Maybe There is a Better Way"
I recently presented at DeveloperDeveloperDeveloper in Sydney, and although my talk was Stuff About CQRS, I opened with the slide
My Object Today Is to Make You Think ... 'Maybe There is a Better Way'
(slides here)
The real focus of this was around how normal people think, and how unlike normal people we developers really are. My role in development is all about enabling better communication, because fundamentally I believe the real value a developer brings to a project is not technical, but is in the way they interact with the team, and more importantly with the normal people they are actually creating software for.
Obviously some of this has roots in DDD, the Ubiquitous Language is obviously an attempt to traverse this chasm that seems to exist between us.
Then I touched on user interfaces, and how they are so rarely designed the way people think - normal people think about their objectives and goals, not in terms of data like we developers do. Inductive UIs are focused on tasks, unlike the traditional data driven UIs that we tend to throw at users - users and people don't think in grids and columns and rows, they think "I want to change my address", not "open my customer record, edit the three fields under address and save to the database" - only a sadist or a developer would think that way.
And finally I touched on things like NoSQL databases, which neatly solve a communication problem - they stop us thinking about How to store information and let us focus on What we are storing and Why.
And lastly I tried to show the link between CQRS and these business problems - how it made you focus on the language, on tasks and objectives and how it let you detach the How from the What and Why.
But most importantly, what I was trying to do in that presentation was to throw some non-mainstream ideas out into the audience, to spark discussion and debate, and to get people to think - Maybe There is a Better Way
If only a few of those in the audience went away and Googled some of the ideas I was talking about, it will be another step towards moving development away from it's heavy focus on technology and technological solutions - and towards a people and business driven focus, where technology is an artifact, not the deciding factor.
We briefly introduced actions in Pt. 1, but there is so much more to know. To begin our investigation, we’ll take our simple “Hello” example and see what it looks like when we explicitly create the actions rather than use conventions. Here’s the Xaml:
<UserControl x:Class="Caliburn.Micro.Hello.ShellView" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" xmlns:cal="http://www.caliburnproject.org"> <StackPanel> <TextBox x:Name="Name" /> <Button Content="Click Me"> <i:Interaction.Triggers> <i:EventTrigger EventName="Click"> <cal:ActionMessage MethodName="SayHello" /> </i:EventTrigger> </i:Interaction.Triggers> </Button> </StackPanel> </UserControl>
As you can see, the Actions feature leverages System.Windows.Interactivity for it’s trigger mechanism. This means that you can use anything that inherits from System.Windows.Interactivity.TriggerBase to trigger the sending of an ActionMessage.* Perhaps the most common trigger is an EventTrigger, but you can create almost any kind of trigger imaginable or leverage some common triggers already created by the community. ActionMessage is, of coarse, the Caliburn.Micro-specific part of this markup. It indicates that when the trigger occurs, we should send a message of “SayHello.” So, why do I use the language “send a message” instead of “execute a method” when describing this functionality? That’s the interesting and powerful part. ActionMessage bubbles through the Visual Tree searching for a target instance that can handle it. If a target is found, but does not have a “SayHello” method, the framework will continue to bubble until it finds one, throwing an exception if no “handler” is found.** This bubbling nature of ActionMessage comes in handy in a number of interesting scenarios, Master/Details being a key use case. Another important feature to note is Action guards. When a handler is found for the “SayHello” message, it will check to see if that class also has either a property or a method named “CanSayHello.” If you have a guard property and your class implements INotifyPropertyChanged, then the framework will observe changes in that property and re-evaluate the guard accordingly. We’ll discuss method guards in further detail below.
Now you’re probably wondering how to specify the target of an ActionMessage. Looking at the markup above, there’s no visible indication of what that target will be. So, where does that come from? Since we used a Model-First approach, when Caliburn.Micro (hereafter CM) created the view and bound it to the ViewModel using the ViewModelBinder, it set this up for us. Anything that goes through the ViewModelBinder will have its action target set automatically. But, you can set it yourself as well, using the attached property Action.Target. Setting this property positions an ActionMessage “handler” in the Visual Tree attached to the node on with you declare the property. It also sets the DataContext to the same value, since you often want these two things to be the same. However, you can vary the Action.Target from the DataContext if you like. Simply use the Action.TargetWithoutContext attached property instead. One nice thing about Action.Target is that you can set it to a System.String and CM will use that string to resolve an instance from the IoC container using the provided value as its key. This gives you a nice way of doing View-First MVVM if you so desire. If you want Action.Target set and you want Action/Binding Conventions applied as well, you can use the Bind.Model attached property in the same way.
Let’s see how we would apply this to achieve MVVM using a View-First technique (gasp!) Here’s how we would change our bootstrapper:
public class MefBootstrapper : Bootstrapper { //same as before protected override void DisplayRootView() { Application.Current.RootVisual = new ShellView(); } //same as before }
Because we are using View-First, we’ve inherited from the non-generic Bootstrapper. The MEF configuration is the same as seen previously, so I have left that out for brevity’s sake. The only other thing that is changed is how the view gets created. In this scenario, we simply override DisplayRootView, instantiate the view ourselves and set it as the RootVisual (or call Show in the case of WPF). Next, we’ll slightly alter how we are exporting our ShellViewModel, by adding an explicitly named contract:
[Export("Shell", typeof(IShell))] public class ShellViewModel : PropertyChangedBase, IShell { //same as before }
Finally, we will alter our view to pull in the VM and perform all bindings:
<UserControl x:Class="Caliburn.Micro.ViewFirst.ShellView" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:cal="http://www.caliburnproject.org" cal:Bind.Model="Shell"> <StackPanel> <TextBox x:Name="Name" /> <Button x:Name="SayHello" Content="Click Me" /> </StackPanel> </UserControl>
Notice the use of the Bind.Model attached property. This resolves our VM by key from the IoC container, sets the Action.Target and DataContext and applies all conventions. I thought it would be nice to show how View-First development is fully supported with CM, but mainly I want to make clear the various ways that you can set targets for actions and the implications of using each technique. Here’s a summary of the available attached properties:
Now, let’s take a look at another interesting aspect of ActionMessage: Parameters. To see this in action, let’s switch back to our original ViewModel-First bootstrapper, etc. and begin by changing our ShellViewModel to look like this:
using System.ComponentModel.Composition; using System.Windows; [Export(typeof(IShell))] public class ShellViewModel : IShell { public bool CanSayHello(string name) { return !string.IsNullOrWhiteSpace(name); } public void SayHello(string name) { MessageBox.Show(string.Format("Hello {0}!", name)); } }
There are a few things to note here. First, we are now working with a completely POCO class; no INPC goop here. Second, we have added an input parameter to our SayHello method. Finally, we changed our CanSayHello property into a method with the same inputs as the action, but with a bool return type. Now, let’s have a look at the Xaml:
<UserControl x:Class="Caliburn.Micro.HelloParameters.ShellView" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" xmlns:cal="http://www.caliburnproject.org"> <StackPanel> <TextBox x:Name="Name" /> <Button Content="Click Me"> <i:Interaction.Triggers> <i:EventTrigger EventName="Click"> <cal:ActionMessage MethodName="SayHello"> <cal:Parameter Value="{Binding ElementName=Name, Path=Text}" /> </cal:ActionMessage> </i:EventTrigger> </i:Interaction.Triggers> </Button> </StackPanel> </UserControl>
Our markup now has one modification: We declared the parameter as part of the ActionMessage using an ElementName Binding. You can have any number of parameters you desire. Value is a DependencyProperty, so all the standard binding capabilities apply to parameters. Did I mention you can do all this in Blend?
One thing that is nice about this is that every time the value of a parameter changes, we’ll call the guard method associated with the action(CanSayHello in this case) and use its result to update the UI that the ActionMessage is attached to. Go ahead and run the application. You’ll see that it behaves the same as in previous examples.
In addition to literal values and Binding Expressions, there are a number of helpful “special” values that you can use with parameters. These allow you a convenient way to access common contextual information:
You must start the variable with a “$” but the name is treated in a case-insensitive way by CM.
Word to the Wise
Parameters are a convenience feature. They are very powerful and can help you out of some tricky spots, but they can be easily abused. Personally, I only use parameters in the simplest scenarios. One place where they have worked nicely for me is in login forms. Another scenario, as mentioned previously is Master/Detail operations.
Now, do you want to see something truly wicked? Change your Xaml back to this:
<UserControl x:Class="Caliburn.Micro.HelloParameters.ShellView" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> <StackPanel> <TextBox x:Name="Name" /> <Button x:Name="SayHello" Content="Click Me" /> </StackPanel> </UserControl>
Running the application will confirm for you that CM’s conventions even understand ActionMessage parameters. We’ll discuss conventions a lot more in the future, but you should be happy to know that these conventions are case-insensitive and can even detect the before-mentioned “special” values.
Now, lets look at a simple Master/Detail scenario that demonstrates ActionMessage bubbling, but let’s do it with a shorthand syntax that is designed to be more developer friendly. We’ll start by adding a simple new class named Model:
using System; public class Model { public Guid Id { get; set; } }
And then we’ll change our ShellViewModel to this:
using System; using System.ComponentModel.Composition; [Export(typeof(IShell))] public class ShellViewModel : IShell { public BindableCollection<Model> Items { get; private set; } public ShellViewModel() { Items = new BindableCollection<Model>{ new Model { Id = Guid.NewGuid() }, new Model { Id = Guid.NewGuid() }, new Model { Id = Guid.NewGuid() }, new Model { Id = Guid.NewGuid() } }; } public void Add() { Items.Add(new Model { Id = Guid.NewGuid() }); } public void Remove(Model child) { Items.Remove(child); } }
Now our shell has a collection of Model instances along with the ability to add or remove from the collection. Notice that the Remove method takes a single parameter of type Model. Now, let’s update the ShellView:
<UserControl x:Class="Caliburn.Micro.BubblingAction.ShellView" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:cal="http://www.caliburnproject.org"> <StackPanel> <ItemsControl x:Name="Items"> <ItemsControl.ItemTemplate> <DataTemplate> <StackPanel Orientation="Horizontal"> <Button Content="Remove" cal:Message.Attach="Remove($dataContext)" /> <TextBlock Text="{Binding Id}" /> </StackPanel> </DataTemplate> </ItemsControl.ItemTemplate> </ItemsControl> <Button Content="Add" cal:Message.Attach="Add" /> </StackPanel> </UserControl>
The first thing to notice is that we are using a more Xaml-developer-friendly mechanism for declaring our ActionMessages. The Message.Attach property is backed by a simple parser which takes its textual input and transforms it into the full Interaction.Trigger/ActionMessage that you’ve seen previously. If you work primarily in the Xaml editor and not in the designer, you’re going to like Message.Attach. Notice that neither Message.Attach declarations specify which event should send the message. If you leave off the event, the parser will use the ConventionManager to determine the default event to use for the trigger. In the case of Button, it’s Click. You can always be explicit of coarse. Here’s what the full syntax for our Remove message would look like if we were declaring everything:
<Button Content="Remove" cal:Message.Attach="[Event Click] = [Action Remove($dataContext)]" />
Suppose we were to re-write our parameterized SayHello action with the Message.Attach syntax. It would look like this:
<Button Content="Click Me" cal:Message.Attach="[Event Click] = [Action SayHello(Name.Text)]" />
But we could also leverage some smart defaults of the parser and do it like this:
<Button Content="Click Me" cal:Message.Attach="SayHello(Name)" />
You can specify literals as parameters as well and even declare multiple actions by separating them with a semicolon:
<Button Content="Let's Talk" cal:Message.Attach="[Event MouseEnter] = [Action Talk('Hello', Name.Text)]; [Event MouseLeave] = [Action Talk('Goodbye', Name.Text)]" />
WARNING: Those developers who ask me to expand this functionality into a full-blown expression parser will be taken out back and…dealt with. Message.Attach is not about cramming code into Xaml. It’s purpose is to provide a streamlined syntax for declaring when/what messages to send to the ViewModel. Please don’t abuse this.
If you haven’t already, run the application. Any doubts you had will hopefully be put to rest when you see that the message bubbling works as advertised :) Something else I would like to point out is that CM automatically performs type-conversion on parameters. So, for example, you can pump TextBox.Text into a System.Double parameter without any fear of a casting issue.
So, we’ve discussed using Interaction.Triggers with ActionMessage, including the use of Parameters with literals, element bindings*** and special values. We’ve discussed the various ways to set the action target depending on your needs/architectural style: Action.Target, Action.TargetWithoutContext, Bind.Model or View.Model. We also saw an example of the bubbling nature of ActionMessage and demoed it using the streamlined Message.Attach syntax. All along the way we’ve looked at various examples of conventions in action too. Now, there’s one final killer feature of ActionMessage we haven’t discussed yet…Coroutines. But, that will have to wait until next time.
*Currently, the full version of Caliburn is not based on System.Windows.Interactivity. Caliburn’s trigger mechanism was around long before Blend’s. You may notice a shocking similarity in the markup. That said, Caliburn v2.0 will be migrated to use the Blend model in the near future.
**Actually, if no handler is found, before an exception is thrown, the framework will check the current DataContext to see if it has the requested method. This seamed like a reasonable fallback behavior.
***One important detail about ElementName Bindings that I didn’t mention…It doesn’t work with WP7 currently. Due to the fact that WP7 is based on a version of Silverlight 3 which had an incomplete implementation of DependencyObject/DependencyProperty, the infrastructure is not present to make this work in any sort of sane way. However, parameter literals and special values still work as described along with all the rest of the ActionMessage features.
In my last post I didn’t mention dependencies. Dependencies are their own animal. They require a couple more things to be in place. Let’s talk about those things.
In the .NET world, the dependency for compiled bits is usually an exact version of a reference.
Let me explain. So for example, you have a reference to log4net, and you don’t ILMerge it into your assembly. You now have a dependency that the DLL needs to be there and a particular version (outside of redirecting the bindings). So what I’m getting at is that you require an exact version of a particular DLL. And what you really need is an exact name, version, culture, and public key token of a DLL. But let’s keep things simple. It’s really the version and the name when culture is neutral (and the key shouldn’t change in the same version). So just the name and version.
For each reference you have to a library, you find out what version it is (assembly version) and then add that as a dependency. You can do that by cracking open reflector and taking a look at the actual assembly version.
Don’t use the properties. Neither file version or product version are going to be accurate here:
There is nothing out there that says that assembly, file and informational (also known as product) versions have to be the same. .NET relies on the assembly version for referencing. It makes sense that we should as well. Here’s a better example where things are different:
So what would I put in my gemspec? If your reference was to log4net version 1.2.10.0, then you need to assign a dependency to that exact version. Done like so:
spec.add_dependency('log4net','= 1.2.10.0')
I believe you add each referenced dependency to it’s own line.
Now to the sanity check. Before you even add it as a dependency, you want to ensure that the gem exists.
Go to http://rubygems.org and in the top right there is a search box. Search for your reference there.
So let’s search for log4net to be sure it’s there.
Sweet! I can move on to my next reference because the right version of the gem exists.
Keep in mind that the name of the gem may not be the one you are looking for and/or the name may be slightly different. For example. I have a gem for UppercuT. The gem is named uppercutbuild because there was already a gem named uppercut.
Now if it’s not there, you can add it. When the actual authors want to start managing the gem, you can just add them as owners so they can push their own gems.
To check the owners of a gem you type:
gem owner gemname
To add someone, according to the gem docs, you issue this command (all on one line):
gem owner gemname --add users.confirmed.email.address.for.ruby.gems@wherever.com
And that’s it.
You see how I am listed as the owner of the log4net gems? I am not really the developer, when I created the gem, I tied it as closely as I could to the apache project and the committers. When those guys are ready to own the gem, I have the specs for both 1.2.9 and 1.2.10 (both are commonly referred to without the last version octet) and I can just add them as owners.
Gems - Package Management for .NET & How To – Gems & .NET
In my last post I showed gems being used for .NET. Now let’s talk about How. Most of this stuff I’ve learned over the past two days, so if I have a mistake here or you have a better idea, please don’t hesitate to offer a better solution.
The GemSpec
The Gem::Specification reference is your friend.
In order to create a gem, you need to define a gem specification, commonly called a “gemspec”. A gemspec consists of several attributes. Some of these are required; most of them are optional.
In order to create a gem, you need to define a gem specification, commonly called a “gemspec”.
A gemspec consists of several attributes. Some of these are required; most of them are optional.
From here you learn what is required and what will just get you there.
1. Create a folder named gems in your top level source directory.
2. In that folder we are going to put our gemspec and version files. This is where we will store the files in source control (and one of them may become autogenerated).
3. We will bring our gems folder to our compiled source folder after we build. Then we can add in the compiled output.
1. Create a file named project.gemspec. In our example it is roundhouse.gemspec. This is the most important file for this entire process.
2. Open the gemspec in your favorite notepad editor. Copy the below in and change it for you needs.
version = File.read(File.expand_path("../VERSION",__FILE__)).strip Gem::Specification.new do |spec| spec.platform = Gem::Platform::RUBY spec.name = 'roundhouse' spec.version = version spec.files = Dir['lib/**/*'] spec.summary = 'RoundhousE - Professional Database Change and Versioning Management' spec.description = 'RoundhousE is a Professional Database Change and Versioning Management tool' spec.authors = ['Rob "FerventCoder" Reynolds','Pascal Mestdach','Jochen Jonckheere','Dru Sellers'] spec.email = 'chucknorrisframework@googlegroups.com' spec.homepage = 'http://projectroundhouse.org' spec.rubyforge_project = 'roundhouse' end
3. Just about everything with tick marks above you will edit to suit your needs. spec.name and spec.rubyforge_project (and the gemspec file name) should all match and not be an already existing project name on RubyForge.
4. If you are a singular author, instead of spec.authors, replace it with
spec.author = 'somebody'
You can also set up description for multiple lines.
spec.description = <<-EOF Rake is a Make-like program implemented in Ruby. Tasks and dependencies are specified in standard Ruby syntax. EOF
What we call references. You have a dependency on them existing for your library to run. See this post.
This file is stupid simple. It’s a version number. I believe you can put whatever you want in here. Use the Assembly Version number here and stick with the .NET 4 octets of numbers (like 0.0.0.0) for version.
1. Create a file named VERSION.
2. Open it in your favorite editor and put the version you want here.
1. Create a folder called lib.
2. Copy YOUR compiled DLLs into here. Your references (or dependencies) should not go here. See How To: Gems and .NET - Dependencies.
Create a docs folder. In that folder goes your documentation. This could be release notes, a ReadMe, actual documentation. This area is open. Just make sure you add the docs folder to the specification.
spec.files = Dir['lib/**/*'] + Dir['docs/**/*']
If you want to give someone the ability to run an executable from the command line after installing your application with gems, and you use .NET, this is how you do it.
1. Create a folder named bin as a subdirectory next to the gemspec.
2. Put your executable (and dependencies) into the bin directory.
3. Create your shim file (it’s named what you would type at the command line). I’ve called mine rh. The one above allows ruby to be able to actually execute the Windows executable. Let’s open it and see what it looks like. This, we learned from an answer to a post on stack overflow. This shim also goes in source control
result = system(File.dirname(__FILE__) + "/rh.exe " + ARGV.join(' ')) exit 1 unless result
4. Don’t forget that space at the end of your executable name.
5. Open your gemspec. We need to make sure we have spec.executables filled out and our new directory added to our Files.
version = File.read(File.expand_path("../VERSION",__FILE__)).strip Gem::Specification.new do |spec| spec.platform = Gem::Platform::RUBY spec.name = 'roundhouse' spec.version = version spec.files = Dir['lib/**/*'] + Dir['docs/**/*'] + Dir['bin/**/*'] spec.bindir = 'bin' spec.executables << 'rh' spec.summary = 'RoundhousE - Professional Database Change and Versioning Management' spec.description = 'RoundhousE is a Professional Database Change and Versioning Management tool' spec.authors = ['Rob "FerventCoder" Reynolds','Pascal Mestdach','Jochen Jonckheere','Dru Sellers'] spec.email = 'chucknorrisframework@googlegroups.com' spec.homepage = 'http://projectroundhouse.org' spec.rubyforge_project = 'roundhouse' end
You must already have Ruby (1.8.6 or better) and RubyGems installed and/or updated to at least 1.3.7 (gem update –system). You will want to use a RubyInstaller version of Ruby under the Ruby on Windows section.
1. Open a command line and type
gem build project
2. If there are no issues, you should have a gem for upload.
3. Create an account with RubyGems.org.
4. Ensure your project name isn’t already taken by searching for it. If it is you will need to rename your gemspec file, spec.name, and spec.rubyforge_project to a name that is not taken.
5. Type the following command:
gem push project_someversionnumbers.gem
6. Let it finish. Head out to rubygems.org and look at your shiny, new gem!
7. Test it.
gem install project gem uninstall project
gem install project
gem uninstall project
FollowUp
In my next post, I’ll show you how to make it stupid simple. UppercuT will support this feature natively so all you have to do is have a directory called gems (at the top level of your source) with the gemspec. During the build, it will create the VERSION file and copy your output the the lib folder (custom step for bin folder will be necessary). Then it will execute a step that builds the gem. All for the price of “build”. That’s coming up in the next blog post. In the meantime, feel free to ask any questions you have. Stay tuned…
Gems - Package Management for .NET & How To – Gems & .NET - Dependencies (References)
About The CodeBetter.Com Blog NetworkCodeBetter.Com FAQOur Mission Advertisers should contact Brendan
Subscribe Google Reader or Homepagedel.icio.us CodeBetter.com Latest ItemsAdd to My Yahoo!Subscribe with BloglinesSubscribe in NewsGator OnlineSubscribe with myFeedsterAdd to My AOLFurl CodeBetter.com Latest ItemsSubscribe in Rojo
Member ProjectsDimeCasts.Net - Derik Whittaker
Friends of Devlicio.usRed-Gate Tools For SQL and .NETNDependSlickEdit SmartInspect .NET Logging NGEDIT: ViEmu and Codekana LiteAccounting.Com DevExpressFixxNHibernate ProfilerUnfuddle Balsamiq MockupsScrumyJetBrains - ReSharper <-- NEW Friend!
Site Copyright © 2007 CodeBetter.Com Content Copyright Individual Bloggers