Archive

Archive for the ‘Silverlight 4’ Category

StoredProcedures with multiple result sets and RIA Services

August 20, 2010 3 comments

Here is one way we could work with StoredProcedures that return multiple results when using WCF RIA Services. I grabbed the code for EFExtensions and  was working with a model from MusicStoreSample

I created a simple procedure, which returns the artist details for the given Id and all the Albums for the Artist

create procedure [dbo].[GetArtistDetails](@artistId int) as

select * from Artist where ArtistId=@artistId                    
select * from album where ArtistId=@artistId                    
I extended the generated Entities class in the web project to add a method (followed the Sample in the  EFExtensions sample project).

public partial class MusicStoreEntities : ObjectContext
    {
        private static readonly Materializer<Artist> s_artistMaterializer = new Materializer<Artist>();
        private static readonly Materializer<Album> s_albumMaterializer = new Materializer<Album>();
        public Artist GetArtistAndRelatedAlbums(int artistId)
        {
            DbCommand command = this.CreateStoreCommand(“GetArtistDetails”, CommandType.StoredProcedure, new SqlParameter(“artistId”, artistId));
            Artist artist;

            using (command.Connection.CreateConnectionScope())
            using (DbDataReader reader = command.ExecuteReader())
            {
                // first result set includes the category
                artist = s_artistMaterializer
                    .Materialize(reader)
                    .Bind(this.Artists)
                    .SingleOrDefault();

                // second result set includes the related products
                if (null != artist && reader.NextResult())
                {
                    artist.Albums.Attach(s_albumMaterializer
                        .Materialize(reader)
                        .Bind(this.Albums));
                }
            }

            return artist;
        }
    }

In the DomainService class I created based on the MusicStoreEntities, I added the following method which calls the method we created above

  public Artist GetArtistAndRelatedAlbums(int artistId)
        {
            return this.ObjectContext.GetArtistAndRelatedAlbums(artistId);
        }

In the Generated MetaData class, Added [Include] attribute on Albums property

 public partial class Artist
    {
      
        internal sealed class ArtistMetadata
        {

            // Metadata classes are not meant to be instantiated.
            private ArtistMetadata()
            {
            }

            [Include]
            public EntityCollection<Album> Albums { get; set; }

            public int ArtistId { get; set; }

            public string Name { get; set; }
        }
    }

In the client project, we could write small bit of code to test and see, we get Artist Details along with all the Albums for the artist

Artist artist = null;
MusicStoreDomainContext _ctx = new MusicStoreDomainContext();           
            _ctx.Load<Artist>(_ctx.GetArtistAndRelatedAlbumsQuery(22), (lo) => {
                artist = lo.Entities.FirstOrDefault();
                }, null);                                    
            }, false);

Getting started with PivotViewer control

June 30, 2010 12 comments

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

Categories: Silverlight 4 Tags:

RIA Services and Storedprocedures

May 24, 2010 18 comments

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

May 3, 2010 18 comments

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

April 25, 2010 10 comments

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

Categories: Silverlight 4 Tags:

SL4(RC) RichTextBox and Access Violation

March 31, 2010 2 comments

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

Categories: Silverlight 4 Tags:

Summary rows in Datagrid with multiple groups

March 25, 2010 26 comments

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

Categories: Silverlight 4 Tags:
Follow

Get every new post delivered to your Inbox.