Feed Subscribe
Exception has been thrown by the target of an invocation.


Connectors of Visio n-ary association cannot be right-angled

by ondrejsv 16. September 2009 12:59

This is my very first complaint on my blog but I cannot resist posting. I know that Microsoft Visio is not so popular tool when it comes to UML modeling (though for some diagramming is very useful). Nevertheless I think that its UML stencil could be little bit more friendly.

Today I wanted to draw an n-ary association on a complex diagram. To my surprise I found out that its connectors cannot be made right-angled as connectors of classical binary associations. You must even rotate the diamond shape to position them correctly. However you cannot freely move the role labels. The result is really ugly (I left only relevant shapes on the picture):

image

I wanted to edit the built-in shape but it’s so complicated (not even mentioning to make it work with Visio UML add-on) that I gave up.

You can instead place an ordinary diamond symbol and connect it by connectors with your classes but the association it represents won’t be stored in your model, of course.

This is how it should look (drawn in the Sparx Enterprise Architect):

image

Tags:

Data binding and formatting multiple values in Silverlight

by ondrejsv 16. September 2009 09:15

This article introduces a way to produce formatted strings with standard .NET formatting patterns (as used by the String.Format method) in data-binding on the Silverlight platform.

Oftentimes we require some kind of formatting when binding a value from a data source on the form. In ASP.NET many of the data-bound controls have the DataFormatString property in addition to the DataField property. But if you want to bind against more than one data field and concatenate their values into one formatted string, you usually need to create a template field. An example could be a Contact object from the AdventureWorks model with its FirstName and LastName properties which we want to concatenate and show in a single GridView column.

Windows Presentation Foundation features a more advanced framework for data-binding. However, there is no built-in support for formatting data-bound values. Fortunately, with extensibility in mind, WPF supports so-called converters which help to convert raw values coming from data sources into the final object consumable by the target property. They realize the IValueConverter interface:

public interface IValueConverter { object Convert(object value, Type targetType, object parameter, CultureInfo culture); object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture); }

or IMultiValueConverter interface for converters taking more than one input:

public interface IMultiValueConverter { object Convert(object[] values, Type targetType, object parameter, CultureInfo culture); object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture); }

To achieve our goal we can use one of the already built multi-value converters from the WPF Convertes project, FormatConverter:


...
<my:DataGridTextColumn Header="Name">
<my:DataGridTextColumn.Binding>
<MultiBinding Converter="{cx:FormatConverter {}{0} {1}}">
<Binding Path="Individual.Contact.FirstName" />
<Binding Path="Individual.Contact.LastName" />
</MultiBinding >
</my:DataGridTextColumn.Binding>
</my:DataGridTextColumn>

However, multi-value converters are not supported on the Silverlight platform, so it is not the WPF Convertes package.

To perform a similar string formatting we must write our own value converter. We make use of the parameter parameter of the IValueConverter.Convert method which is filled with any string supplied by the user in markup (through the ConverterParameter attribute of the Binding markup extension). We will support any number of input data fields and also property paths (to enable binding against object graphs). Our converter will be able to process format specified as in this example:


<my:DataGridTextColumn Header="Number" Binding="{Binding Converter={StaticResource MultiStringConverter}, ConverterParameter='[Individual.Contact.FirstName] [Individual.Contact.LastName]'}" />

Notice that data fields values of which we want to substitute, are enclosed in brackets (braces would reflect more nicely the syntax used by the String.Format method but we would need to escape them with a backslash – not so practical).

The code works by picking up all the property paths in the format string and assigning them unique index values. These indices are inserted to the rest of the format string. Then property paths are evaluated against the binding target (we use the code similar to the one in my article Binding object graphs to the ASP.NET GridView control) and passed to the String.Format method together with new format string. More detailed code description follows.

public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { Dictionary<string , paramvalueentity> paramValue = new Dictionary<string, ParamValueEntity>(); string formatString = (string)parameter; StringBuilder realFormatString = new StringBuilder(); int position = 0; string currentParam = ""; int lastIndex = 0;

We will store all found property paths in the format string together with their assigned index in a ParamValueEntity object (justs a wrapper for this pair). realFormatString variable stores the new format string.

We’ll proceed in a while loop by looking at the characters of the original format string (TagStart constant refers to '['):

while (position < formatString.Length) { if (formatString[position] == TagStart) { // We've just entered a parameter

… escaping is handled by repeating the left bracket…:

// Handle escaping (double TagStart) if (position+1<formatString.Length && formatString[position+1] == TagStart) { realFormatString.Append(TagStart); position += 2; continue; }

… otherwise we found beginning of a property path and we pick it up…:

position++; currentParam = ""; int startPosition = position; while ((char.IsLetterOrDigit(formatString[position]) || formatString[position] == '.') && position < formatString.Length) { position++; } currentParam = formatString.Substring(startPosition, position - startPosition);

… we check if such property path has already been found. If so, we put its index in the new format string (we evaluate a single property path only once – both for performance reasons but more importantly the property getter could have side effects and its repeated evaluations could return different values). If this is the first occurrence of the property path, we assign a new index value… :

// Have we met it already? ParamValueEntity pve; if (!paramValue.TryGetValue(currentParam, out pve)) { pve = new ParamValueEntity(); pve.Index = lastIndex++; paramValue[currentParam] = pve; }

… the we pick anything up to the finalizing right bracket (for example any formatting patterns – think of [Person.BirthDate:D])… :

// Parse up to the finalizing ']' startPosition = position; while (formatString[position] != TagEnd && position < formatString.Length) position++; string rest = ""; if (startPosition < position) rest = formatString.Substring(startPosition, position - startPosition);
... and put the index value to the new format string...:
// Add to the string builder realFormatString.Append("{" + pve.Index.ToString() + rest + "}"); // Pass the } if (position < formatString.Length) position++;

… all other characters are copied verbatim…:

} else { realFormatString.Append(formatString[position++]); } }

… then we evaluate all property paths…:

Type type = value.GetType(); // Let's find values foreach (var pvde in paramValue) { pvde.Value.Value = GetValueFromPropertyPath(value, pvde.Key); }

… and call the String.Format method on the new format string and just evaluated values…:

var q = from pve in paramValue.Values orderby pve.Index select pve.Value; return string.Format(realFormatString.ToString(), q.ToArray());

The GetValueFromPropertyPath method evaluates a property path against an object graph. It’s code is almost identical to the one described in my previous article (Binding object graphs to the ASP.NET GridView control)) but it uses pure Reflection instead of the TypeDescriptor and PropertyDescriptor wrappers as these are unavailable in Silverlight.

You can download the complete code together with a sample featuring a DataGrid showing some data from the AdventureWorks. Happy coding!

kick it on DotNetKicks.com vote it on WebDevVote.com [digg]

Tags: ,

Binding object graphs to the ASP.NET GridView control

by ondrejsv 1. September 2009 15:31

One of the drawbacks of the standard ASP.NET GridView control is that you can’t use the “dot” convention to bind object graphs when using the BoundField columns. This article explains how to do it with a new inherited column.

Suppose you have a typical object graph from the AdventureWorks model consisting of objects of the Order, OrderDetail, Product, ProductCategory, and ProductSubcategory classes as shown on the figure below. It’s not important how you get the graph, be it as a result of a LINQ-to-SQL or LINQ-to-Entities query or you have created it manually from a direct SQL query through ADO.NET. Our task is to create a simple grid of all items on a given order (represented by a collection of OrderDetail objects). The user is interested in the name of the product placed on the order, it’s category and subcategory and quantity ordered and unit price it was sold for. Note that the product name, category and subcategory are attributes of the Product object while quantity and unit price are attributes of the OrderDetail object itself. In the object world the standard way of getting information from “subordinate” objects is by traversing via object references (while in the relational world we would be issuing a JOIN statement).

image

If we were binding a single OrderDetail object to a FormView control, for example, we could use a perfectly valid Eval statements:


<%# Eval("Product.Name") %>
<%# Eval("Product.Subcategory.Name ") %>
<%# Eval("Product.Subcategory.Category.Name ") %>

However, a first try to use a BoundField column within a regular ASP.NET GridView control:


<asp:BoundField HeaderText="Product" DataField="Product.Name" />

results in the HttpException "field or property with the name 'Product.Name' was not found on the selected data source."

The problem is that a BoundField column looks up only for properties of the object it’s bound to and does not analyze the DataField string if it contains a navigation path to the property.

The solution is simple: in the attached sample project you find the NavigationBoundField class which inherits from the standard BoundField class and overrides the key method GetValue.

The code uses TypeDescriptors to retrieve object properties (via GetProperties method) and PropertyDescriptors to get value of a property (GetValue method).

Disclaimer: This article and its enclosed sample just demonstrates a single technical aspect. It by no means shows how to create a properly designed multi-layered application or how to write a properly designed code in a complex application.

kick it on DotNetKicks.com vote it on WebDevVote.com

Tags: