December 2009 - Posts
Things are made to be thrown away. It is cheaper to make a new one, we are told. But why should this be so? Is it necessarily the case? Could it be otherwise? What would have to happen to bring about this change?
The alternator in my jeep failed, last week, as did the seal in the master cylinder. We were going to replace the seals, but we gave it to a workshop to have it sleeved and have the seals done at the same time. This is the second time I’ve had this done; the soft cast alloy of the original part eroded after 18 months, but a good quality repair with a stainless steel sleeve lasted almost ten years.
The alternator, on the other hand, couldn’t be repaired. The brushes were mounted in a single piece of plastic moulded directly onto the alternator housing. This is terribly wasteful. The energy cost of making a metal housing, bearings and spindle is stupendous compared to a couple of brush contacts, not to mention the cost of a kilogram of copper for the windings. Then there’s the toxic filth that is an unavoidable consequence of extracting these things from ore.
I know why manufacturers do this. It’s simple economics. Moulding the brushes directly onto the housing saved the maker a couple of cents in production. It cost me two hundred dollars, but from the maker’s point of view this is perfect, because they got to sell another alternator. From a seller’s point of view, repairable is bad: it reduces the opportunity to sell.
The problem here is that the current economic model rewards makers for maximising the cost of replacement/repair. If makers sold capability rather than product, similar to hiring a car on a long-term lease with a comprehensive and built-in maintenance contract, then it would be in the manufacturer’s interest to minimise the cost of maintenance.
I see a number of advantages. Advantages to the consumer would be
- Total cost of ownership (TCO) would be very clear-cut.
- TCO would be reduced because in order to compete on price manufacturers would
- Consolidate their vehicle range
- Maximise parts commonality across their vehicle range
- Maximise parts compatibility over time to reduce parts inventory
- Parts would be designed for longevity to reduce maintenance costs
- Parts would be designed for ease of maintenance to reduce maintenance labour costs
- Manufacturers would design for and perform assembly exchange-repair, radically reducing down-time from the consumer’s point of view and minimising interaction costs.
From the manufacturer’s point of view,
- Significant vendor lock-in – in addition to lease terms brand loyalty is likely to be significant since TCO will be similar across comparable products, so preferences will be governed by familiarity and taste in appointment and handling feel.
- Third-party parts and service providers would be completely eliminated.
- Service schedules could be enforced without inconveniencing customers.
DataContext, DataSource, DomainDataSource, DomainDataContext… what are they, how do they relate and how do you use them together to perform binding in Silverlight?
A DataContext can by anything. More specifically, it can be any type of object. Any IEnumerable is an object, and if you set some IEnumerable as the DataContext of a Grid and pepper the grid with widgets then you can bind the widgets to the properties of the objects in the collection, so if it’s a collection of Foo and Foo has properties A and B, then you can bind textBoxA and textBoxB to A and B with “{Binding Path=A}” and “{Binding Path=B}” respectively.
Each and every bindable widget has a DataContext property. Generally you don’t set this, because it’s what Microsoft has taken to calling a DependencyProperty, which is the same as an ambient property, which is to say the value is “inherited” from the widget’s container widget unless you set it explicitly. So if you have a grid that contains textBoxA and textBoxB, then you only need to set the DataContext for the grid, but you need to set the Path part of the binding explicitly for each widget.
Actually if you think about it what I just wrote cannot be true; the DataContext for the grid’s widgets would have to be the CurrentItem of the grid’s DataContext, and that’s exactly right. It is.
So what’s this CurrentItem business? Well, when you “set” the DataContext property on a widget, it’s not a pure assignment. A wrapper object is created (a derivative of the class DataContext, in fact) and for collections this is a sort of cursor.
I haven’t checked this particular thought yet but it just came to me that things which aren’t collections could be treated as collections of one item, by the simple expedient of constructing such a collection. This would make the CurrentItem the only item, obviating the need for different binding logic. It’s certainly what I would do, having thought of it, as I just did. I bet they did, it’s so elegant.
So there you have it. Now that I know this stuff data binding in Silverlight has suddenly become really straightforward, although we’ll see whether I’m still singing that song in a couple of days.
For a long time I wondered whether Microsoft was ever going to fix basic defects. With every release, there was a slew of sparkly but basically useless multimedia toys. There are remarkably few occasions on which I need spinning cubes with a different movie playing back on each side, whereas the need to bind a DataForm combo-box to a lookup table crops up every damn day.
But along comes design tooling in VS2010 and Lo! the basic engineering gets fixed. It’s not finished, but they did say it was a beta. All in all it’s pretty damn good. RIA is what changes Silverlight from a wannabe Flash competitor to The Next Big Thing in commercial development.
That’s a pretty big change. Flash is basically only good for animated diagrams and putting sparkle on electronic brochures, and Silverlight 1 was basically Microsoft Flash.
Silverlight 2 eliminated the need for interpreted script and the glacially slow event marshalling associated therewith. That was definitely worth a major version number change. Silverlight 3 on the other hand contained multimedia toys and a few bugfixes, and I don’t think it deserved a major version change. I would have called it 2.1, not 3.
Silverlight 4 is a similar story. It adds RIA, which is important, but it doesn’t fundamentally change the rest of the platform, it just adds missing libraries, fixes bugs and refactors the assemblies to match the rest of .NET – which is a very good idea that will eliminate a whole class of headaches, but not worth a major version. Maybe combined with RIA it’s enough, barely.
But that’s all history. The point is it’s nearly ready. The tooling’s here, the libraries are getting shaken down as people start trying to do real work with them and the VS tooling is getting better, although if you ask me it needs rethinking in several respects.
In particular, using grids is a colossal pain in the bum. It’s obvious what needs doing: instead of just XAML and designer views, there ought to be XAML, designer and preview just like Windows Live Writer. The designer ought to use designer styling so that rows and columns set to auto sizing don’t collapse to nothing when you remove their content. When rearranging controls in a grid, they should keep the same margins etc they have when the drag starts instead of getting weird margins and spanning rows and columns. If we want oddness like that we’ll use the property sheet guys! Have you been taking lessons in how to build a crappy designer from the morons over at Crystal Reports? Also a drop-target magnifier would be a good idea if you’re going to do that sort of precision placement with a mouse.
XAML mark-up describes a DOM in which the nodes are instances of classes, some of which are your own code, some of which are .NET framework code. Like your own code, classes must be referenced before they can be instantiation. A reference is written as an attribute of a page or user control and looks like this.
xmlns:riaControls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Ria"
The name riaControls in the above sample can then be used in XAML as an alias for the CLR namespace System.Windows.Controls mentioned in its definition. Once you’ve declared the reference, you can use the alias in XAML to create an instance of a class in that namespace, like this.
<riaControls:DomainDataSource AutoLoad="True" Height="0" LoadedData="employeeGroupDomainDataSource_LoadedData" Name="employeeGroupDomainDataSource" QueryName="GetEmployeeGroupsQuery" Width="0">
<riaControls:DomainDataSource.DomainContext>
<my1:AtomDomainContext />
</riaControls:DomainDataSource.DomainContext>
</riaControls:DomainDataSource>
That example is roughly equivalent to
System.Windows.Controls.DomainDataSource employeeGroupDomainDataSource =
new System.Windows.Controls.DomainDataSource();
employeeGroupDomainDataSource.QueryName = "GetEmployeeGroupsQuery";
employeeGroupDomainDataSource.Width = 0;
employeeGroupDomainDataSource.LoadedData += employeeGroupDomainDataSource_LoadedData;
The biggest hassle with XAML is scope. As far as I’ve been able to determine, declarations that are subordinated in XAML are likewise subordinated in scope, so the DomainContext created in the above sample is effectively declared as a member of the DomainDataSource and an instance of the DomainContext is created and assigned in the constructor of the DomainDataSource.
A quirk of binding is correspondence of scope. If you bind a DataGrid to a DomainDataSource then the columns of that DDS are directly in scope for the immediate children of the DataGrid. I’m still trying to figure out exactly what’s going on here.
Assume your model looks like this, and you compiled and generated a DomainDataSource from it. Compile again and drag both entities onto the form. This will make the designer put all the necessary references and whatnot in place. Delete the data grid that was generated for EmployeeGroup; you don’t need it, you just wanted the designer to write your declarations and associated initialisation code. At this point, both employeeDomainDataSource and employeeGroupDomainDataSource will be in scope in your XAML.
Targeting Silverlight 3
Create a class EmployeeGroupValueConverter in the Helpers folder of your Silverlight project. You will have to edit the namespace to remove “.Helpers”. The code should look like this:
1 using System;
2 using System.Linq;
3 using System.Windows.Controls;
4 using System.Windows.Data;
5 using BusinessApplication1.Web.Models;
6
7 namespace BusinessApplication1
8 {
9 public class EmployeeGroupValueConverter : IValueConverter
10 {
11 public static DomainDataSource ItemsSource { get; set; }
12
13 #region IValueConverter Members
14
15 public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
16 {
17 if (ItemsSource == null)
18 return Guid.Empty;
19 else
20 return ItemsSource.Data.Cast<EmployeeGroup>().First(item => item.Id == (Guid)value);
21 }
22
23 public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
24 {
25 if (value == null)
26 return null;
27 else
28 return (value as EmployeeGroup).Id;
29 }
30
31 #endregion
32 }
33 }
Earlier you dropped the EmployeeGroupDomainDataSource on the design surface causing the following to be generated. Note the fact that a handler has already been generated for the DDS LoadedData event.
9 <riaControls:DomainDataSource
10 AutoLoad="True" Height="0"
>> 11 LoadedData="employeeGroupDomainDataSource_LoadedData"
12 Name="employeeGroupDomainDataSource"
13 QueryName="GetEmployeeGroupsQuery" Width="0">
14 <riaControls:DomainDataSource.DomainContext>
15 <my:DomainService1 />
16 </riaControls:DomainDataSource.DomainContext>
17 </riaControls:DomainDataSource>
This provides a good hook for populating the static property ItemSource of the value converter class.
32 private void employeeGroupDomainDataSource_LoadedData(object sender, LoadedDataEventArgs e)
33 {
>> 34 EmployeeGroupValueConverter.ItemsSource = sender as DomainDataSource;
35 if (e.HasError)
36 {
37 MessageBox.Show(e.Error.ToString(), "Load Error", MessageBoxButton.OK);
38 e.MarkErrorAsHandled();
39 }
40 }
The value conversion is required because the SelectedItem property of the combo box returns a collection item, which in this case is an EmployeeGroup object. The value converter simply converts between the FK value and the corresponding EmployeeGroup object, making the following binding legal. Line 86 is where the rubber meets the road.
83 <ComboBox Grid.Column="1" Grid.Row="6" Height="23" HorizontalAlignment="Left"
84 DisplayMemberPath="GroupName"
85 ItemsSource="{Binding ElementName=employeeGroupDomainDataSource, Path=Data}"
>> 86 SelectedItem="{Binding EmployeeGroupId, Mode=TwoWay, Converter={StaticResource EmployeeGroupValueConverter}}"
87 Margin="3" Name="employeeGroupIdComboBox" VerticalAlignment="Center" Width="120">
88 <ComboBox.ItemsPanel>
89 <ItemsPanelTemplate>
90 <VirtualizingStackPanel />
91 </ItemsPanelTemplate>
92 </ComboBox.ItemsPanel>
93 </ComboBox>
It’s not completely declarative, but it’s close. This works in both data grids and data forms.
Targeting Silverlight 4
Oh happy day! No code required! SelectedValue and SelectedValuePath are finally supported! Just declare it like this:
95 <ComboBox Grid.Column="1" Grid.Row="6" Height="23" HorizontalAlignment="Left"
96 DisplayMemberPath="GroupName"
97 ItemsSource="{Binding ElementName=employeeGroupDomainDataSource, Path=Data}"
>> 98 SelectedValue="{Binding EmployeeGroupId, Mode=TwoWay}"
>> 99 SelectedValuePath="Id"
100 Margin="3" Name="employeeGroupIdComboBox" VerticalAlignment="Center" Width="120">
101 <ComboBox.ItemsPanel>
102 <ItemsPanelTemplate>
103 <VirtualizingStackPanel />
104 </ItemsPanelTemplate>
105 </ComboBox.ItemsPanel>
106 </ComboBox>