Archive for the ‘.Net components’ Category

How to turn Silverlight DataGrid to TreeGrid in 15 minutes.

Friday, March 12th, 2010

I am one of those guys who constantly try to convince people around them that Silverlight is a wonderful thing and how it is easy to design user-friendly user interface with it. And as expected my enthusiasm rarely understood by colleagues especially by experienced developers whose favorite argument is “All this can be perfectly done in Windows Forms (MFC, VCL, OWL, HTML, Ajax, you name it), so we don’t need this disgusting Silverlight”. And it is hard to argue against it as nothing absolutely new can be created – as musicians blamed for plagiarism say “There are only seven notes”. Power of Silverlight UI Framework is not in the ability to design incredible pile of effects and animation, but in the simplicity and lightning speed of implementation of sophisticated scenarios.

And today I have a vivid sample. In practice, there often appears a necessity to use a hybrid control joining functionality of the TreeView and DataGrid components, i.e. of the tree representing information by each item in several columns with all abilities characteristic to table view: changing columns by a single mouse movement, sorting upon a click on the column title, editing cell content etc. For example, such control is an essential part of the interface of any mailer: Outlook Express, Windows Mail, Mozilla Thunderbird.

And I needed such a control when decided to create an on-line message board to represent message chains in the thread.

If you are a Windows Forms developer and face the requirement to use this control – this is a bad luck. Most likely you will have to buy special third party library – standard toolbox doesn’t include such control, and independent development will take several hundreds of men hours.

But if you use Silverlight your life will be much easier. Combination of mechanisms of templates and data binding makes it possible to turn an ordinary DataGrid to such control in minutes.

To do it, we need to create a data source making hierarchical structure be flat list.

Such data source creates a wrapper around each element and its Level and IsExpanded properties serve to display correct shift and state of each element in the original hierarchy.

public sealed class GridTreeDataSource

{

public GridTreeDataSource(IList originalSource, Func<object, IList> getChildren)

{

OriginalSource = originalSource;

GetChildren = getChildren;

Source = new ObservableCollection<GridTreeDataItem>();

FillWithGridTreeItems(Source, OriginalSource, 0);

}

public IList OriginalSource { get; private set; }

private Func<object, IList> GetChildren { get; set; }

public ObservableCollection<GridTreeDataItem> Source { get; private set; }

private void FillWithGridTreeItems(IList<GridTreeDataItem> destinationCollection, IList sourceCollection, int level)

{

foreach (object item in sourceCollection)

{

GridTreeDataItem treeItem = new GridTreeDataItem(item, this, level);

treeItem.IsVisible = (level == 0);

destinationCollection.Add(treeItem);

IList list = GetChildren(item);

if (list != null)

FillWithGridTreeItems(treeItem.Children, list, level + 1);

}

}

internal void NotifyIsExpandedChanged(GridTreeDataItem item)

{

if (item.IsVisible)

{

if (!item.IsExpanded)

{

RemoveChildren(item);

}

else

{

int index = Source.IndexOf(item);

AddChildren(item, ref index);

}

}

}

private void AddChildren(GridTreeDataItem element, ref int index)

{

foreach (var item in element.Children)

{

Source.Insert(++index, item);

item.IsVisible = true;

if (item.IsExpanded)

{

AddChildren(item, ref index);

}

}

}

private void RemoveChildren(GridTreeDataItem element)

{

foreach (var item in element.Children)

{

Source.Remove(item);

item.IsVisible = false;

RemoveChildren(item);

}

}

}

public class GridTreeDataItem : INotifyPropertyChanged

{

internal GridTreeDataItem(object item, GridTreeDataSource owner, int level)

{

Item = item;

Owner = owner;

Level = level;

}

public GridTreeDataSource Owner { get; private set; }

public object Item { get; private set; }

public int Level { get; private set; }

public bool IsVisible { get; internal set; }

private bool isExpanded = false;

public bool IsExpanded

{

get

{

return isExpanded;

}

set

{

if (value != isExpanded)

{

isExpanded = value;

RaisePropertyChanged(“IsExpanded”);

Owner.NotifyIsExpandedChanged(this);

}

}

}

private List<GridTreeDataItem> children = new List<GridTreeDataItem>();

public List<GridTreeDataItem> Children

{

get

{

return children;

}

}

public bool HasChildren

{

get

{

return Children.Count > 0;

}

}

#region INotifyPropertyChanged Members

protected void RaisePropertyChanged(string name)

{

OnPropertyChanged(new PropertyChangedEventArgs(name));

}

protected virtual void OnPropertyChanged(PropertyChangedEventArgs args)

{

PropertyChangedEventHandler temp = PropertyChanged;

if (temp != null)

{

temp(this, args);

}

}

public event PropertyChangedEventHandler PropertyChanged;

#endregion

}

As you see, here are only 150 lines of trivial code. (And there could be even less if I wasn’t a follower of the formatting style when each curly bracket takes a separate line :)).

Then substitute template for the first table column using the designed structure as a data source:

<data:DataGrid x:Name=”dataGrid” AutoGenerateColumns=”False” IsReadOnly=”True” CanUserSortColumns=”False”>

<data:DataGrid.Columns>

<data:DataGridTemplateColumn Header=”Ex”>

<data:DataGridTemplateColumn.CellTemplate>

<DataTemplate>

<StackPanel Orientation=”Horizontal” Margin=”{Binding Level, Converter={StaticResource mc}}”>

<ToggleButton x:Name=”ExpanderButton” IsChecked=”{Binding IsExpanded, Mode=TwoWay}”

Visibility=”{Binding HasChildren, Converter={StaticResource vc}}”

VerticalAlignment=”Center” IsTabStop=”False” TabNavigation=”Once” Margin=”2″ Template=”{StaticResource btnTemplate}”/>

<TextBlock Text=”{Binding Item.Name}” VerticalAlignment=”Center”/>

</StackPanel>

</DataTemplate>

</data:DataGridTemplateColumn.CellTemplate>

</data:DataGridTemplateColumn>

<data:DataGridTextColumn Binding=”{Binding Item.Num}” Header=”Num”/>

</data:DataGrid.Columns>

</data:DataGrid>

And this is it! TreeGrid can be used! The result looks like that.

To make it understandable and more obvious, I simplified this implementation a little; the sample is not ideal as it comes to performance and it doesn’t include functions for manipulations over the tree, such as ExpandAll etc. But the main point is that this code is taken from the real application. Prototype of this project is being tested at the moment and you can make sure that it really works.

You can download source code of this sample and use it for examination and in your applications.

If you have any questions or comments, please feel free to contact me directly at pr@perpetuumsoft.com

New PDF Export Filter – New Features at Your Service.

Friday, March 5th, 2010

Perpetuum Software Team congratulates everyone on the long-awaited spring!

Spring is time of changes and updates. Asking ourselves what we can do to facilitate and increase the effectiveness of the components work we concluded that it would be great if our clients had advanced PDF export abilities.

Now, we are glad to announce that we are about to finish the work over a new PDF export filter. In this post I won’t go deeply in describing advantages of exporting reports to PDF since there are a lot of articles on this theme in the Internet. I just want to summarize all our clients’ words about the necessity to use PDF export filter: “We need convenient and easy-to-use means to quickly deliver reports to our customers. And our customers, in their turn, should be able to work with these reports.”

So, all mentioned above lead us to the idea to re-write PDF export filter. And we did it…

A new PDF export filter includes advanced abilities of exporting documents to PDF.
• First of all, text view after export is improved. It doesn’t matter that the text size doesn’t fit the TextBox it will be correctly output on the page if you use the TextBox.StringTrimming property.
• Secondly, a new PDF export filter allows the optimization of the size of exported documents. PDF documents often have big size because of images. Now, you can set image quality to decrease the document size.
• Thirdly, we amplified PDF export filter functionality. We improved support for extended character set symbols and custom characters. This will considerably increase your abilities and options when exporting to PDF.
• Fourthly, we added support for encrypting. This will give you the opportunity to protect your documents from modifying and your work from redistributing.
• And finally, we made it possible to export bookmarks, text, and background fill. This will keep report quality and provide better visualization of the information.

A new PDF export filter will be included in a new release of Report Sharp-Shooter. It is expected in April, 2010. But we are still open for any your ideas and suggestions on how to improve PDF export features at our forum.