Archive
Getting started with PivotViewer control
Here is a basic walkthrough to get up and running with PivotViewer Control
After getting all the stuff as mentioned in the faq, Open VS2010 and create a silverlight application. Add a website hosted in IIS adding a clientAccesspolicy file as needed. Make sure it is set a startup project. Go the Property pages and select silverelightApplications and add a test page. set the testpage as startup page. Make sure there are MimeTypes added in IIS for .cxml, .dzi and .dzc as “text/xml”
once you unzipped the tools, you will find a docs folder in PAuthor folder. There is a sample.xlsx file. we will use that. The images are in the sample_images folder.
For simplicity, I copied everything in that folder to bin folder under PAuthor directory, If you are getting started, I would do the same, so we can get up and running without hiccups.
Go to command prompt to run the following command
Pauthor.exe /source excel sample.xlsx /target deepzoom output\sample.cxml
one it is done in the “output” directory you will have “sample.cxml” and a folder “sample-deepzoom”. copy both of them to the website.
Now Add references to all the Pivot Assemblies from the folder “C:\Program Files (x86)\Microsoft SDKs\Silverlight\v4.0\PivotViewer\Jun10\Bin\”. Not sure exactly which ones will be required as we are doing only minimal stuff.
open MainPage.xaml and add this namespace
xmlns:local=”clr-namespace:System.Windows.Pivot;assembly=System.Windows.Pivot”
Add the following XAML
<Grid x:Name=”LayoutRoot” Background=”White”>
<local:PivotViewer x:Name=”pv1″/>
</Grid>
go to codebehind of the MainPage, after the line with InitializeComponent();
add the following line to load the collection. In my case the website name is “Mysample”
pv1.LoadCollection(“http://localhost/Mysample/sample.cxml“, null);
Now if you run the sample, you should have everything working
RIA Services and Storedprocedures
If we have a storedprocedure that we want to use to retrieve data and it returns an entity that is already in our model, it is pretty easy. Create a function import and indicate the type of object it returns and add a method in the domainservice to get the data
I am using Northwind database. I created a simple procedure to get the customers by city
create procedure GetCustomersByCity @city varchar(100) as
select * from Customers where City=@city
Created a Import(GetCustomersByCity) and added a method in the DomainService
public IQueryable<Customer> GetCustomersByCity(string city)
{
return this.ObjectContext.GetCustomersByCity(city).AsQueryable<Customer>();
}
and called like this
ctx.Load<Customer>(ctx.GetCustomersByCityQuery(“London”), (lo) => { dataGrid1.ItemsSource = lo.Entities; }, false);
it works fine
I tried to do the same using a existing procedure “CustOrderHist”
create PROCEDURE [dbo].[CustOrderHist] @CustomerID nchar(5)
AS
SELECT ProductName, Total=SUM(Quantity)
FROM Products P, [Order Details] OD, Orders O, Customers C
WHERE C.CustomerID = @CustomerID
AND C.CustomerID = O.CustomerID AND O.OrderID = OD.OrderID AND OD.ProductID = P.ProductID
GROUP BY ProductName
this doesnt return data that maps to any entity in our model. when I created a function import, I let it create a complexobject(CustOrderHistoryResult).
Then I added the following method similar to the one above in the DomainService
public IQueryable<CustOrderHistoryResult> GetCustOrderHistory(string customerId)
{
return this.ObjectContext.CustOrderHistory(customerId).AsQueryable<CustOrderHistoryResult>();
}
When I built the App, it gave me the following error
The entity ‘CustOrderHistoryResult’ in DomainService ‘NorthwindDomainService’ does not have a key defined.
Entities exposed by DomainService operations must have at least one public property marked with the KeyAttribute.
one way I got this to work is to add the following class in the Metadata.cs file of the DomainService, marking “ProductName” as the Key
[MetadataTypeAttribute(typeof(CustOrderHistoryResult.CustOrderHistoryResultMetadata))]
public partial class CustOrderHistoryResult
{
internal sealed class CustOrderHistoryResultMetadata
{
private CustOrderHistoryResultMetadata()
{
}
[Key]
public string ProductName { get; set; }
public int Total { get; set; }
}
}
after this I was able to call and get data from the procedure
ctx.Load<CustOrderHistoryResult>(ctx.GetCustOrderHistoryQuery(“ALFKI”), (lo) => { dataGrid2.ItemsSource = lo.Entities; }, false);
Not sure if this is the right way, but it seems to work
you can download the code here
RIA services and combobox lookups
update: Here is another version using Async ctp, that will let us wait untill all the data for the related comboboxes is loaded.
Here is a quick sample on how to setup comboboxes for lookups whether in datagrid or dataform
I am using Chinook database which can be downloaded from here. I am using Employee and Customer tables only.
This sample uses for employee look up to populate the Support Rep ID column in the customer table
In Metadata class for the Customer, we add an attribute [Include] for the employee property
public partial class Customer
{
internal sealed class CustomerMetadata
{
// Metadata classes are not meant to be instantiated.
private CustomerMetadata()
{
}
…
[Include]
public Employee Employee { get; set; }
…
}
}
and in the DomainService, we include the Employee like below to fetech the related employee record for the customer
[EnableClientAccess()]
public class ChinookDomainService : LinqToEntitiesDomainService<ChinookEntities>
{
…
public IQueryable<Customer> GetCustomers()
{
return this.ObjectContext.Customers.Include(“Employee”);
}
…
}
In XAML, we Add a DomainDataSource in the resources
<UserControl.Resources>
<my1:DomainDataSource AutoLoad=”False” x:Name=”salesRepList” QueryName=”GetEmployeesQuery” LoadedData=”salesRepList_LoadedData”/>
</UserControl.Resources>
The DataGrid is defined like this
<my:DataGrid x:Name=”datagrid1″ Grid.Row=”0″ AutoGenerateColumns=”False”>
<my:DataGrid.Columns>
<my:DataGridTextColumn Header=”FirstName” Binding=”{Binding FirstName}”/>
<my:DataGridTextColumn Header=”LastName” Binding=”{Binding LastName}”/>
<my:DataGridTextColumn Header=”City” Binding=”{Binding City}”/>
<my:DataGridTextColumn Header=”Country” Binding=”{Binding Country}”/>
<my:DataGridTextColumn Header=”Employee” Binding=”{Binding Employee.FirstName}”/>
<my:DataGridTemplateColumn Header=”Support Rep”>
<my:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text=”{Binding Employee.FirstName}”/>
</DataTemplate>
</my:DataGridTemplateColumn.CellTemplate>
<my:DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ComboBox ItemsSource=”{Binding Data, Source={StaticResource salesRepList}}” SelectedValuePath=”EmployeeId”
DisplayMemberPath=”FirstName” SelectedValue=”{Binding SupportRepId, Mode=TwoWay}” />
</DataTemplate>
</my:DataGridTemplateColumn.CellEditingTemplate>
</my:DataGridTemplateColumn>
<my:DataGridTemplateColumn Header=”Support Rep”>
<my:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text=”{Binding Employee.FirstName}”/>
</DataTemplate>
</my:DataGridTemplateColumn.CellTemplate>
<my:DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ComboBox ItemsSource=”{Binding Data, Source={StaticResource salesRepList}}” SelectedItem=”{Binding Employee, Mode=TwoWay}”
DisplayMemberPath=”FirstName” />
</DataTemplate>
</my:DataGridTemplateColumn.CellEditingTemplate>
</my:DataGridTemplateColumn>
</my:DataGrid.Columns>
</my:DataGrid>
there are 2 Template columns defined. 1st column shows a way to specify the Id value (Foreign Key) and the 2nd shows how to set the selectedItem
The codebehind looks like this
public partial class MainPage : UserControl
{
ChinookDomainContext ctx = new ChinookDomainContext();
public MainPage()
{
InitializeComponent();
DomainDataSource ds = this.Resources["salesRepList"] as DomainDataSource;
ds.DomainContext = ctx;
ds.Load();
}
private void salesRepList_LoadedData(object sender, LoadedDataEventArgs e)
{
ctx.Load(ctx.GetCustomersQuery(), (lo) => {datagrid1.ItemsSource = lo.Entities;}, false);
}
private void Button_Click(object sender, RoutedEventArgs e)
{
ctx.SubmitChanges();
}
}
you can download the sample from here
Drag and Drop grouping in Datagrid
Most 3rd party components have a datagrid where you can a drag column header to the area above the grid to group the data. Here is one way to do the same with a regular datagrid
I am using SL4 and the latest Silverlight toolkit.
Here is my XAML, a Grid with 2 rows. In row 0 - a listbox where we drop the dragged columnheaders, in row 1 a Datagrid with columns defined
<Grid x:Name=”LayoutRoot” Background=”White” Height=”500″ Width=”400″>
<Grid.RowDefinitions>
<RowDefinition Height=”40″/>
<RowDefinition Height=”*”/>
</Grid.RowDefinitions>
<Grid Grid.Row=”0″>
<toolkit:ListBoxDragDropTarget AllowDrop=”True” Drop=”PanelDragDropTarget_Drop” Background=”#FFA74F4F”
HorizontalContentAlignment=”Stretch” VerticalContentAlignment=”Stretch”>
<ListBox x:Name=”list1″>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation=”Horizontal”/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<Border BorderBrush=”Black” BorderThickness=”1″ >
<StackPanel Orientation=”Horizontal” VerticalAlignment=”Center”>
<TextBlock Text=”{Binding}” Height=”20″/>
<Button Content=”x” Height=”20″ Margin=”5,0,0,0″ Click=”btn_Click” VerticalContentAlignment=”Top” />
</StackPanel>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</toolkit:ListBoxDragDropTarget>
</Grid>
<my:DataGrid x:Name=”datagrid1″ Grid.Row=”1″ AutoGenerateColumns=”False” Style=”{StaticResource DataGridStyle1}”>
<my:DataGrid.Columns>
<my:DataGridTextColumn Header=”Name” Binding=”{Binding Name}”/>
<my:DataGridTextColumn Header=”Title” Binding=”{Binding Title}”/>
<my:DataGridTextColumn Header=”City” Binding=”{Binding City}”/>
<my:DataGridTextColumn Header=”Country” Binding=”{Binding Country}”/>
</my:DataGrid.Columns>
</my:DataGrid>
</Grid>
Next we need to allow the DataGridColumnHeaders to be draggable, so I grabbed the template of the datagrid, wrapped DataGridColumnHeadersPresenter in a PanelDragDropTarget like below. This is the only change in the template
<toolkit:PanelDragDropTarget Grid.Column=”1″ HorizontalContentAlignment=”Stretch”
VerticalContentAlignment=”Stretch” AllowedSourceEffects=”Copy”>
<sdk:DataGridColumnHeadersPresenter x:Name=”ColumnHeadersPresenter” AllowDrop=”True” />
</toolkit:PanelDragDropTarget>
we handle the drop event to add the dragged item to the list of items we grouped with allowing the user to ungroup by clicking the button
private void PanelDragDropTarget_Drop(object sender, Microsoft.Windows.DragEventArgs e)
{
// Retrieve the dropped data in the first available format.
object data = e.Data.GetData(e.Data.GetFormats()[0]);
DataGridColumnHeader dgch = ((data as ItemDragEventArgs).Data as SelectionCollection)[0].Item as DataGridColumnHeader;
list1.Items.Add(dgch.Content.ToString());
UpdateGroups();
}
void btn_Click(object sender, RoutedEventArgs e)
{
list1.Items.Remove((sender as Button).DataContext);
UpdateGroups();
}
private void UpdateGroups()
{
if (pcv.GroupDescriptions != null)
pcv.GroupDescriptions.Clear();
list1.Items.ToList().ForEach(x =>
{
pcv.GroupDescriptions.Add(new PropertyGroupDescription { PropertyName = x.ToString() });
}
);
}
you can download the code from here
SL4(RC) RichTextBox and Access Violation
update: looks like this has been fixed and will work fine in RTW
There is a post in silverlight forums talking about listitem in RichTextBox and editing them.
I tried this
<RichTextBox IsReadOnly=”True” >
<Paragraph>
<Run Text=”hello, this is a listbox”/>
<LineBreak/>
<InlineUIContainer>
<ItemsControl Margin=”10,0,0,0″>
<ListBoxItem >
<TextBox Text=”1. Item 1″/>
</ListBoxItem>
<ListBoxItem >
<TextBox Text=”2. Item 2″/>
</ListBoxItem>
</ItemsControl>
</InlineUIContainer>
</Paragraph>
</RichTextBox>
As soon as I started editing the TextBox, got this error. looks like bug to me
Summary rows in Datagrid with multiple groups
Sometime back I posted one way to show the Summary/Totals in the group header and be able to resize individual columns when the columns are resized. That method has a limitation of not supporting nested groups. we can extend the same method to support multiple groups
Here are the steps.
we will start with what we had like in the previous sample.
1. we will add 1 GroupHeader style for each group we have. In my sample all the GroupHeader styles are same. One important change is there is a stackpanel in the GroupHeader named “ghsp” which hosts all the columns in the GroupHeader. we will add a Tag attribute and give a value of “n-1″ when “n” is the number of groups in the datagrid. if you have 3 groups then there will be 3 group header styles and Tag attribute will be 2, 1, 0. This is how we can differentiate the different level group headers
2. The next change is in the Resize method. ” * 20″ is for resizing the column with groupname in the group header, 20 is roughly the size of indentation for each group (could be different if you change defaults in the template)
void Resize()
{
headers = datagrid1.GetChildrenByType<StackPanel>().Where(x => x.Name == “ghsp”).ToList();
headers.ForEach(x =>
{
(x.Children[0] as DataGridCell).Width = dghc.Children[1].RenderSize.Width + (Int32.Parse(x.Tag.ToString()) * 20) ;
for (int i = 2; i < dghc.Children.Count – 2; i++)
(x.Children[i - 1] as DataGridCell).Width = dghc.Children[i].RenderSize.Width;
});
}
3. In the converter we have to change the way we get the totals from the CollectionViewGroup
private decimal GetProductTotal(CollectionViewGroup cvg)
{
decimal total = 0;
int counter = 0;
while (counter < cvg.Items.Count)
{
if (cvg.Items[counter] is CollectionViewGroup)
total += GetProductTotal(cvg.Items[counter] as CollectionViewGroup);
else
total = (cvg.Items as IEnumerable<object>).Sum(x => (x as Product).Total);
counter++;
}
return total;
}
you can download the code from here
PathListBox in SL4 – firstlook
PathListBox is new in SL4 and is in Microsoft.Expression.Controls.dll. It makes Path based layout easy. We could make nice menu buttons with little bit more styling to remove border of the listbox, the highlight color etc
The top part of the above image is just a plain listbox with items laid out horizontally
<ListBox x:Name=”list1″ Margin=”30″ Grid.Row=”0″>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation=”Horizontal”/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
The second listbox starts with the same, but uses PathListBox in the ItemTemplate.
<ListBox x:Name=”list2″ Margin=”30″ Grid.Row=”1″ FontSize=”8″>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation=”Horizontal”/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Margin=”15,0,15,0″>
<ec:PathListBox Margin=”0,0,0,0″ ItemsSource=”{Binding}” FontSize=”9.333″ ItemTemplate=”{StaticResource DataTemplate1}” FontWeight=”Bold” >
<ec:PathListBox.LayoutPaths>
<ec:LayoutPath SourceElement=”{Binding ElementName=path}” Distribution=”Even” Start=”.8″ Orientation=”OrientToPath” Padding=”0″ Span=”0.5″ />
</ec:PathListBox.LayoutPaths>
</ec:PathListBox>
<Ellipse Width=”40″ Height=”40″ x:Name=”path” HorizontalAlignment=”Right” Margin=”0,25,0,0″>
<Ellipse.Fill>
<LinearGradientBrush EndPoint=”0.5,1″ StartPoint=”0.5,0″>
<GradientStop Color=”Black” Offset=”1″/>
<GradientStop Color=”White”/>
</LinearGradientBrush>
</Ellipse.Fill>
</Ellipse>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
We start with placing a PathListbox, seting the itemssource to whatever string we are bound to and assigning it a LayoutPath which in this case is an ellipse and set few properties on the LayoutPath to customize the appearance.
Here is the codebehind to set the ItemsSource of the listbox
list2.ItemsSource = list1.ItemsSource = Enumerable.Range(0, 5).Select(x => “Option” + x.ToString());
Summary row in DataGrid
SummaryRow in DataGrid is one of the frequently asked request. Here is one way we can customize the GroupHeader in Datagrid to create one
By default when we do grouping in the datagrid we get something like this
more often we need something like this.

Here are the steps
1.Add a style for the groupheader, In the above snapshot we see 4 columns, so we assume we need the Groupheader also to have 4 columns
We start by changing the existing GroupHeader Style. In the style, we see a stackPanel. we dont want this
<StackPanel Grid.Column=”3″ Grid.Row=”1″ Orientation=”Horizontal” VerticalAlignment=”Center” Margin=”0,1,0,1″>
<TextBlock x:Name=”PropertyNameElement” Margin=”4,0,0,0″ Visibility=”{TemplateBinding PropertyNameVisibility}”/>
<TextBlock Margin=”4,0,0,0″ Text=”{Binding Name}” />
<TextBlock x:Name=”ItemCountElement” Margin=”4,0,0,0″ Visibility=”{TemplateBinding ItemCountVisibility}”/>
</StackPanel>
we will replace that stackpanel with this, we have to name it something so we can get to this when we want the cells to resize. We will add 4 controls in this case as we have 4 columns in the datagrid. Any control would work here, I went with datagridcell to keep them consitent with colors.
<StackPanel x:Name=”ghsp” Orientation=”Horizontal” Grid.Column=”3″ Grid.Row=”1″ VerticalAlignment=”Center” Margin=”0,1,0,1″>
<my:DataGridCell Content=”{Binding Name}”/>
<my:DataGridCell Content=”"/>
<my:DataGridCell HorizontalContentAlignment=”Right” Content=”{Binding Converter={StaticResource myConverter}, ConverterParameter=Orders}”/>
<my:DataGridCell HorizontalContentAlignment=”Right” Content=”{Binding Converter={StaticResource myConverter}, ConverterParameter=Total,StringFormat=’{0:C}’}”/>
</StackPanel>
1st column is to display the name of the group, 2nd we dont want any summary in that, 3rd,4th will have the sum of orders and Totals
The rest of the xaml is just styling the headers for right alignment, etc
The code in the converter looks like this
public class MyConverter : IValueConverter
{
// This converts the DateTime object to the string to display.
public object Convert(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
CollectionViewGroup cvg = value as CollectionViewGroup;
string param = parameter as string;
if (param == “Orders”)
return cvg.Items.Sum(x => (x as Product).Orders);
else
return cvg.Items.Sum(x => (x as Product).Total);
}
// No need to implement converting back on a one-way binding
public object ConvertBack(object value, Type targetType,
object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
2.Binding the data
products = new List<Product>();
products.Add(new Product { Category = “Beverages”, Name = “Chai”, Orders = 110, Total = 648.00M });
products.Add(new Product { Category = “Beverages”, Name = “Chai”, Orders = 120, Total = 259.20M });
products.Add(new Product { Category = “Condiments”, Name = “Northwoods Cranberry Sauce”, Orders = 120, Total = 216.00M });
products.Add(new Product { Category = “Condiments”, Name = “Grandma’s Boysenberry Spread”, Orders = 130, Total = 1200.00M });
products.Add(new Product { Category = “Dairy Products”, Name = “Queso Cabrales”, Orders = 150, Total = 1400.00M });
products.Add(new Product { Category = “Dairy Products”, Name = “Queso Manchego La Pastora”, Orders = 160, Total = 1280.00M });
products.Add(new Product { Category = “Confections”, Name = “Pavlova”, Orders = 210, Total = 1500.00M });
products.Add(new Product { Category = “Confections”, Name = “Teatime Chocolate Biscuits”, Orders = 114, Total = 800.00M });
PagedCollectionView collectionViewSource = new PagedCollectionView(products);
SortDescription sortDescription = new SortDescription(“Category”, ListSortDirection.Ascending);
collectionViewSource.SortDescriptions.Add(sortDescription);
GroupDescription groupDescription = new PropertyGroupDescription(“Category”);
collectionViewSource.GroupDescriptions.Add(groupDescription);
datagrid1.ItemsSource = collectionViewSource;
The above code is pretty standard, we need to add handlers so that the groupheader cells are resized when header cells of the datagrid
datagrid1.LayoutUpdated += new EventHandler(datagrid1_LayoutUpdated);
datagrid1.UnloadingRowGroup += (s, e) => { Resize(); };
datagrid1.LoadingRowGroup += (s, e) => { refreshUI = true; };
void datagrid1_LayoutUpdated(object sender, EventArgs e)
{
if (refreshUI && headers == null)
{
dghc = datagrid1.GetChildrenByType<DataGridColumnHeadersPresenter>().FirstOrDefault();
foreach (DataGridColumnHeader dgch in dghc.Children)
{
dgch.SizeChanged += (s, args) => { Resize(); };
}
}
if (refreshUI)
Resize();
refreshUI = false;
}
void Resize()
{
headers = datagrid1.GetChildrenByType<StackPanel>().Where(x => x.Name == “ghsp”).ToList();
headers.ForEach(x =>
{
for (int i = 1; i < dghc.Children.Count – 2; i++)
(x.Children[i - 1] as DataGridCell).Width = dghc.Children[i].RenderSize.Width;
});
}
what we are doing in the event handlers is, when the groups are loading/unloading or the datagrid is scrolled we adjust the width of the cells in the groupheader
3.If you look at the how the columns are defined in the datagrid
<my:DataGrid.Columns>
<my:DataGridTextColumn Header=”Category” Binding=”{Binding Category}” Width=”Auto”/>
<my:DataGridTextColumn Header=”Name” Binding=”{Binding Name}” Width=”*”/>
<my:DataGridTextColumn HeaderStyle=”{StaticResource headerStyle}” CellStyle=”{StaticResource cellStyle}” Header=”Orders” Binding=”{Binding Orders}” Width=”Auto” />
<my:DataGridTextColumn HeaderStyle=”{StaticResource headerStyle}” CellStyle=”{StaticResource cellStyle}” Header=”Total” Binding=”{Binding Total}” Width=”150″/>
<my:DataGridTemplateColumn Header=”" Width=”Auto” Visibility=”Collapsed”>
<my:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock />
</DataTemplate>
</my:DataGridTemplateColumn.CellTemplate>
</my:DataGridTemplateColumn>
</my:DataGrid.Columns>
There is an extra column at the end and Visibility=”Collapsed” is set. This is a hack as I could not get the last column to align right. If you dont have a summary column at the end. you dont need that.
you can download the code here





