alkampfer on November 19th, 2008

Some time ago I read this post that speaks about dynamic query library. I downloaded the code sample, it is interesting, but is bound to Linq to Sql, now I need an implementation of a Dynamic Linq Query generator. Since I already worked in the past with expression parser and I have some generic Infix to postfix converter in a three hours of work I was able to put everything in a project and now I can write

Func<Customer, Boolean> f = DynamicLinq.ParseToFunction<Customer, Boolean>("Name == 'Gian Maria'"); Assert.That(f(aCustomer), Is.True);

This example shows that I can build a simple function that accepts an entity (Customer) and a string representing some conditions, then I’m able to dynamically parse the string, create an expression tree and call Compile on it to obtain a function that I can use in code. As soon as possible I’ll show a complete example if you are interested in.

alk.

Tags:



kick it on DotNetKicks.com

alkampfer on November 18th, 2008

If you work with Windows Forms you cannot live without binding :D, but something is missing from the base structure. I want to create a simple form with a BindingSource, then bind a list of object to a grid and some textboxes to create a master detail form, moreover I set up a simple navigator to make simple for the user navigating the structure, the navigator has the addnew and remove button and the user likes them.

The user can Add and Remove element from the navigator, or directly from a DataGridView, and I simply need to call the appropriate method of Repository to save and delete objects. This structure is really clean, but you encounter a problem: nor the BindingSource nor the BindingList<T> raises a RemovingItem event. I do not know why these classes are designed in this way, you have an AddingNew Event in the Binding Source and BindingList<T> but no corresponding RemovingItem. You have a ListChanged event that tells you when an element is removed, but the element is already gone from the list, so you cannot use to detect object removal.

If you look at the events of BindingList<T> you can notice that there is no way to be informed of the exact element that gets removed. A simple solution is to inherit from the BindingList<T>

public class RemoveItemEventArgs : EventArgs { public Object RemovedItem { get { return removedItem;} } private Object removedItem; public RemoveItemEventArgs(object removedItem) { this.removedItem = removedItem; } } public class MyBindingList<T> : BindingList<T> { public event EventHandler<RemoveItemEventArgs> RemovingItem; protected virtual void OnRemovingItem(RemoveItemEventArgs args) { EventHandler<RemoveItemEventArgs> temp = RemovingItem; if (temp != null) { temp(this, args); } } protected override void RemoveItem(int index) { OnRemovingItem(new RemoveItemEventArgs(this[index])); base.RemoveItem(index); } public MyBindingList(IList<T> list) : base(list) { } public MyBindingList() { } }

As you can see implementing such a functionality is a breeze, you should simply override the RemoveItem method and raise a custom RemovingItem event. Now I can use this new BindingList in this way.

MyBindingList<UIDomainProxy> source = new MyBindingList<UIDomainProxy>( Repository<Domain>.GetAll().Select(d => new UIDomainProxy(d)).ToList()); source.AddingNew +=source_AddingNew; source.RemovingItem +=source_RemovingItem; domainBindingSource.DataSource = source;

I gets all the element from the repository, then I wrap then in a UIProxy (I’ll explain this in a future post), then I intercept the addingNew and RemovingItem events simply calling Save and Delete methods from the repository. Finally I set the BindingList as the source of the BindingSource created by the designer.

I wonder why this event is missing from the basic BindingList<T> implementation.

alk.

Tags:



kick it on DotNetKicks.com

alkampfer on November 17th, 2008

Suppose you have a very simple page where user can add comments to an issue, user can enter plain text and also they can use th HTML tag <b> to render in bold some text. In the example code you can see a very simple implementation (default.aspx). It use a xml file for back end storage (so you can run the example without a database) and in Default.aspx all the text that was entered by the user was stored in a CData section of the XML STorage file. When the comments are rendered on the page we simply output all the content. The result is good but have some problems. First of all you can use every html tag, such as <i> moreover, if some hacker enter the text “you were <script>alert(’hacked’);</script>” into the textbox, all the user that read the page will execute that script, this is a  simple sample of cross site scripting attack.

image

Here is the content of the storage file

<?xml version="1.0" encoding="utf-8"?> <Comments> <Comment> <Author>Hacker</Author> <CommentText><![CDATA[you where <script>alert('hacked');</script>]]></CommentText> </Comment> </Comments>

In Default2.Aspx there is a simple solution to mitigate this problem, trying to remove all node that are not <b>, but with this solution if a user enter “this is a <b>good comment</b> with <i>italic text</i>“, we have a big problem, the part in the <i></i> tag gets completely removed. Moreover if some hacker gets a way to change your storage file, or can insert some data in database you still have problem. A better solution was showed in Default3.aspx. Since I consider data in the file as untrusted input because it is under direct control of the user I need to sanitize the comment before I can render text to the user. The goal is having a SanitizeComment function that gets a html fragment as input and return a new fragment with all tags removed, except those that are permitted.

1 private String SanitizeComment(String commentText) 2 { 3 try 4 { 5 XElement doc = XElement.Parse("<span>" + commentText + "</span>"); 6 doc.Descendants().Where(elem => elem.Name != "b") 7 .ToList().ForEach(elem => 8 { 9 elem.AddAfterSelf(new XText((String)elem)); 10 elem.Remove(); 11 }); 12 13 String retvalue = doc.ToString(); 14 return retvalue; 15 } 16 catch (System.Xml.XmlException) 17 { 18 return AntiXss.HtmlEncode(commentText); 19 } 20 }

This code use Linq2Xml; first of all in line 5 I create a XElement with a concatenation of a <span> tag and the original content of the comment. Then I select in line 6 all the XML nodes that have a name different from “b”,(the only permitted tag in the output). Then for each of the unpermitted nodes I simply add a new XML node of type XText after the node, and then remove the original node.

If some XMLException occurs, it means that the input is not a well formed XML fragment, so I default to use the AntiXss HtmlEncode function that avoid any cross site scripting risk.

Here is a result of the page when the storage file contains this comments.

<?xml version="1.0" encoding="utf-8"?> <Comments> <Comment> <Author>Hacker</Author> <CommentText><![CDATA[you where <script>alert('hacked');</script>]]></CommentText> </Comment> <Comment> <Author></Author> <CommentText><![CDATA[this is a <b>good comment</b>]]></CommentText> </Comment> <Comment> <Author></Author> <CommentText><![CDATA[this is a <b>good comment</b> with <i>italic text</i> and <b>again Bold<i> italicbold</i></b>]]></CommentText> </Comment> </Comments>

You can see that the content of the file contains dangerous html code, but here is what is rendered by Default3.aspx that calls SanitizeComment function.

image

As you can notice the alert(‘hacked’) was shown without the <script> tag, moreover all the text between <i> and </i> gets no removed. SanitizeComment function leaves only the tag <b> and remove all other unwanted tags.

sample code here.

alk.

Tags:



kick it on DotNetKicks.com

alkampfer on November 8th, 2008

Binding in WPF is really more powerful respect its counterpart in windows forms. Suppose you have to show this hierarchy of classes in an interface.

image

This is a simple structure where a LogGroup have a collection of LogMessages and each LogMessage has a collection of StackSteps. I have a collection of LogGroup and I need to create a three level master detail interface to make the user browse through all objects in hierarchy.

Thanks to powerful WPF binding you can create such an interface without procedural code. I used three ListView enclosed in a StackPanel. The first peculiarity of WPF binding is that you can set an ObservableCollection<LogGroup> object into the BindingContext on the StackPanel that contains the three listviews . The first listview have this declaration.

<ListView x:Name="GroupsView" ItemsSource="{Binding}" IsSynchronizedWithCurrentItem="True" HorizontalContentAlignment="Stretch" ScrollViewer.HorizontalScrollBarVisibility="Disabled">

The IsSynchronizedWithCurrentItem is true to enable automatic synchronization, the binding is set with the instruction {Binding}, that means “create a binding object with default properties”. A default binding walks controls stack and bind to the first Datacontext property that is not null, in our situation the ObservableCollection<LogGroup> set in the StackPanel that contains the ListView.

Now the second ListView should bind to the property Messages of the item currently selected in the previous ListView, the solution is really simple

<ListView x:Name="GroupsView" ItemsSource="{Binding Path=Messages}" HorizontalContentAlignment="Stretch" ScrollViewer.HorizontalScrollBarVisibility="Disabled" IsSynchronizedWithCurrentItem="true">

The binding is {Binding Path=Messages} that means, “build a Binding object and set the Path Property to the value Messages”, since the IsSynchronizedWithCurrentItem is true you get this master detail binding for free. Thus when you change selection of the first ListView the second ListView updates accordingly and shows the element of the Messages collection of selected LogGroup. To bind the third and last ListView I have to use a special syntax of WPF Path binding

<ListView x:Name="GroupsView" ItemsSource="{Binding Path=Messages/StackSteps}" HorizontalContentAlignment="Stretch" ScrollViewer.HorizontalScrollBarVisibility="Disabled" IsSynchronizedWithCurrentItem="true">

the binding now is {Binding Path=Messages/StackSteps}. If you wonder why I had not written the path “Messages.StackSteps” take a look back at the object model. Messages is a collection, if you write Messages.StackSteps WPF binding engine throws and errors. Now you need to be aware that every exception that is thrown during binding operation is swallowed by the binding engine, and you should look into the debug windows of visual studio to see binding errors. In fact you can find this error

System.Windows.Data Error: 39 : BindingExpression path error: ‘StackSteps’ property not found on ‘object’ ”List`1′ (HashCode=58377472)’. BindingExpression:Path=Messages.StackSteps; DataItem=’ObservableCollection`1′ (HashCode=7457061); target element is ‘ListView’ (Name=’GroupsView’); target property is ‘ItemsSource’ (type ‘IEnumerable’)

the binding engine told you that he could not find a StackSteps Property in the object List<Messages>. In such a situation the WPF binding engine permits you to use slash “/” character to indicate “The current selected item in the view” (read here for more details).  Thus writing the path “Messages/StackSteps” told the engine to take selected LogGroup from the first level, then take the Messages property, then the slash indicates to take the current selected element of Messages collection and finally bind to the StackSteps collection of that element.

image

Whoa, you get three level master detail with not a single line of procedural code :D

alk.

Tags:

DotNetKicks Image


kick it on DotNetKicks.com

alkampfer on November 8th, 2008

TextBlock have the possibility to wrap, but sometimes you can get surprised by its behaviour. When I first began to work in WPF I started to use ListView to show complex object, because of the rich possibility to format the output with great flexibility. One day I created this little listview.

<ListView x:Name="GroupsView" ItemsSource="{Binding}" > <ListView.ItemTemplate> <DataTemplate> <StackPanel Orientation="Horizontal"> <TextBlock TextWrapping="Wrap" Margin="2,0,2,0" Text="{Binding Path=LogIdentifier}" VerticalAlignment="Center" FontSize="14" /> <TextBlock Margin="2,0,2,0" Text="Count:" FontWeight="Bold" VerticalAlignment="Center" FontSize="14" /> <TextBlock Margin="2,0,2,0" Text="{Binding Path=Messages.Count}" VerticalAlignment="Center" FontSize="14" /> </StackPanel> </DataTemplate> </ListView.ItemTemplate> </ListView>

It is a simple list and each element is composed by three textblock, the first should be the long one and I want it to wrap when the form is resized, the other two are small and should stay to the right. When I launched the form the result is not the desidered one, the first text does not wrap , nor the Count: is aligned to the right. The first error is that you should use DockPanel when you want elements to stay somewhere (right in this situation) and you want one of the element to fill remaining space, then I moved to this solution.

<ListView x:Name="GroupsView" ItemsSource="{Binding}" > <ListView.ItemTemplate> <DataTemplate> <DockPanel > <TextBlock TextWrapping="Wrap" Margin="2,0,2,0" Text="{Binding Path=LogIdentifier}" VerticalAlignment="Center" FontSize="14" /> <TextBlock DockPanel.Dock="Right" Margin="2,0,2,0" Text="Count:" FontWeight="Bold" VerticalAlignment="Center" FontSize="14" /> <TextBlock DockPanel.Dock="Right" Margin="2,0,2,0" Text="{Binding Path=Messages.Count}" VerticalAlignment="Center" FontSize="14" /> </DockPanel> </DataTemplate> </ListView.ItemTemplate> </ListView>

You can be still surprised that the result is not the desidered one, the first textblock does not gets wrapped, and “count: X” text is still not aligned to the right. To understand why the layout is arranged in such a way you should consider how the various container constrain dimension for their content. In this post you can find a good description. We have to face two problems, the first is that we want the content of our ListView to have X size equal to that of the listview itself, this can be accomplished with HorizontalContentAlignment="Stretch" attribute, that instructs the ListView to stretch the X size of its content on the horizontal size of the list itself. Then you should instruct the ListView not to show horizontal scrollbar. When scrollbar are enabled the listview grabs the “desidered size” of the controls, then it renders scrollbars accordingly, thus avoiding the word wrapping. To disable scrollbar you can simply use the attribute ScrollViewer.HorizontalScrollBarVisibility="Disabled". Finally you should notice that the order of the elements in the DockPanel is wrong. The dockPanel starts aligning from the first control to the last, and if the last has no DockPanel.Dock specified it is considered to fill remaining space. I need to work from right to left, first element is the number of count, then the Count: string, both with right alignment, finally the text that I wanted to wrap with no Dock specified.

<ListView x:Name="GroupsView" ItemsSource="{Binding}" HorizontalContentAlignment="Stretch" ScrollViewer.HorizontalScrollBarVisibility="Disabled"> <ListView.ItemTemplate> <DataTemplate> <DockPanel > <TextBlock DockPanel.Dock="Right" Margin="2,0,2,0" Text="{Binding Path=Messages.Count}" VerticalAlignment="Center" HorizontalAlignment="Right" FontSize="14" /> <TextBlock DockPanel.Dock="Right" Margin="2,0,2,0" Text="Count:" FontWeight="Bold" VerticalAlignment="Center" HorizontalAlignment="Right" FontSize="14" /> <TextBlock TextWrapping="WrapWithOverflow" Margin="2,0,2,0" Text="{Binding Path=LogIdentifier}" VerticalAlignment="Center" FontSize="14" /> </DockPanel> </DataTemplate> </ListView.ItemTemplate> </ListView>

Finally I got what I wanted.

image

I’m doing this post because this morning I woke up early, and wrote again the same wrong XAML (errare umanum est …), when I launch the application I though “mmm I solved this long time ago…” and begin crawling in my projects, then I preferred to do a post, so the next time I can search in my blog. :D

Alk.

Tags:

DotNetKicks Image


kick it on DotNetKicks.com