A nearshore team from Uruguay, South America (GMT-3) June 2011 - Posts - UruIT Blog

UruIT Blog

A nearshore team working with Microsoft technologies.

Sponsors

News

UruIT at WEBLOGS asp.net

June 2011 - Posts

Grouping items in a Silverlight ListBox

[Click here to download sample code for this article]

Introduction

ListBox grouping is one of the features that Silverlight did not inherit from WPF, even though is a common requirement. Here we present a very simple yet powerful way to implement grouping in the ListBox control that also allows subgrouping and sorting.

This approach is supported on traditional Silverlight applications, and also on Windows Phone 7 applications.

 

Untitled2 Untitled

 

The problem

Although WPF supports grouping for the ListBox control, Silverlight doesn’t. The developer is restricted to load a plain, unstructured collection of items. However, the hierarchical scenario is very common in applications that display different types of data.

The solution

To overcome this issue, we will use a Binding Converter, which will group the items internally, adding to the ListBox a ContentControl element with a specific template, for each group header it creates, and a ContentControl with another template, for each item on each group it creates. Sounds complicated? It’s not, let’s do it!

Suppose you have a list of items that you want to display hierarchically. You proceed by creating the ListBox and binding its ItemsSource dependency property to the collection of items. In XAML it would look like this:

<ListBox x:Name="List" ItemsSource="{Binding}"/>

And the code-behind would look like this:

IEnumerable<ItemType> items = ...;

List.DataContext = items;

At this point, the ListBox shows the list of items without any structure. Now, let’s add a Converter to the binding. A converter is an instance of a class that implements IValueConterter, which has only two methods: Convert and ConvertBack. Before Silverlight binds the collection of items, it will call the Convert function, allowing us to transform the data as we want. The ConvertBack method will never be called for this type of binding, since this is a OneWay binding. For more information about data bindings, see http://msdn.microsoft.com/en-us/library/ms752347.aspx

So, all we need is a new class that implements the IValueConverter interface. In this case, “PetGrouper”:

public class PetGrouper : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        // ...
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

Because ConvertBack will never be called, we can leave it untouched, throwing a NotImplementedException. For the Convert method, we already know the type of data we are assigning to the DataContext. That same instance comes as an object in the value parameter. We can ignore the rest of the parameters in this case.

Next, we have to group and sort the data. For each group we create, we will add a control with a template designed for headers, and for each one of its items, we will add a control with a template designed for items.

public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
    IEnumerable<Pet> pets = value as IEnumerable<Pet>;
    List<ContentControl> groupedPets = new List<ContentControl>();
    foreach (IGrouping<string, Pet> animal in pets.GroupBy(x => x.Animal).OrderBy(x => x.Key))
    {
        groupedPets.Add(new ContentControl() { Content = animal.Key, ContentTemplate = App.Current.Resources["AnimalTemplate"] as DataTemplate });
        foreach (Pet pet in animal.OrderBy(x => x.Breed))
        {
            groupedPets.Add(new ContentControl() { Content = pet, ContentTemplate = App.Current.Resources["BreedTemplate"] as DataTemplate });
        }
    }
    return groupedPets;
}

AnimalTemplate and BreedTemplate are resources in App.xaml. In this example we are grouping the pets by animal type, sorting the groups by animal type name, and sorting each item on the groups by breed name.

The type IGrouping<string, Pet>, describes a list of groups whose headers (“keys”) are strings, and each one of them is populated by pets. The Key type may vary for different scenarios. For example if we were grouping by vertebrates and invertebrates, the Key would be a bool value, and we would have to add a significant text to the header content, so it doesn’t show “True” and “False”.

As you may note, this approach is unrestricted as to how the data is shown. You can group items on several levels, sort the items by any property, and even sort the groups themselves.

Finally, we have to link our converter to the binding. This is done by adding a resource in the page containing the ListBox, and linking the binding to that resource:

<UserControl.Resources>
    <local:PetGrouper x:Key="PetValueConverter"></local:PetGrouper>
</UserControl.Resources>
<Grid x:Name="LayoutRoot" Background="White">
    <ListBox x:Name="List" ItemsSource="{Binding Converter={StaticResource PetValueConverter}}" />
</Grid>

Sample application

Here is a sample application that implements grouping and sorting in a ListBox.

The solution contains two projects: a traditional browser application, and a Windows Phone 7 application.

Conclusion

Even the Silverlight ListBox does not have native support for structured data; we can use a binding converter to customize the binding, creating the layout that best fits our case. We have seen that this approach is simple, straightforward, and yet incredible powerful, and it’s supported on classic Silverlight applications and Phone applications.

 

Alfonso Cora

Consuming OData from IPHONE

Hi,

We have seen in the previous post how to consume OData services from Windows Phone 7, this time I want to show you how to do it from an iPhone.

1. To consume OData from an iPhone App I have used a library called “OData Client for Objective-C”. It can be downloaded from here: http://odataobjc.codeplex.com/

The library contains some examples using the Netflix Catalog that can help you get familiar with more advanced stuffs than what are going to be explained in this article.

2. Once you downloaded the library the first thing you have to do on XCode is to add the references to the OData library.

This is done by accessing the Project Properties:

clip_image002

3. On the Build tab of the Properties window locate the section “Search Paths” and add the following reference in “Header Search Paths” and “Library Search Paths”:

clip_image004

Also make sure that you selected the recursive checkbox to both paths.

4. After both paths are added you need to add the library to the Frameworks folder of the solution, looking into the file system for the libMSODataLib.a located at this relative path inside the OData SDK folder: “/framework/bin/odatalib/lib/iPhoneSimulatorLibs/iPhone_Simulator_4.1/release/libMSODataLib.a”

clip_image006

5. After that’s done you will have the library for the project ready to be used.

6. Creating the proxy class.

To connect to OData you need to generate the proxy class. To do that open terminal, navigate to the folder where the odatagen executable is located and run this command:

./odatagen /uri=http://domain.com/YourService.svc /out=/Development/odataproxy

On the folder odataproxy you will find the generated proxy, now you need to import the “.h” and “.m” file into the classes folder of the project.

7. After that’s ready, use the proxy to connect to the server at any of the classes.

In this example, I assume that the proxy generated is called CustomersEntities.

With the next line you will have the proxy connected to the server:

CustomersEntities *proxy = [[CustomersEntities alloc]initWithUri:@”URL TO SERVICE” credential:nil];

And to get data simply do the following:

NSArray *customers;

customers = [proxy Customers];

With this example you will have a list of Customers loaded using OData services.

Of course, you can run much more complex queries using OData but that’s not the main focus of this article.

Here is a screen capture of my iPhone App consuming OData :)

clip_image008

Sebastián Rodríguez

Consuming OData from Windows Phone7

Hi,

In this article I want to show you quickly how easy, funny and powerful is to consume OData services in Windows Phone 7.

I am going to take this article from Microsoft as reference, adding screenshots and comments about the process.

So, in order to write your first Windows Phone 7 application that consumes OData, you may want to follow the following steps:

1. Download Windows Phone 7 OData Client libraries.

http://go.microsoft.com/fwlink/?LinkId=207900

The OData client library for Windows Phone generates HTTP requests to a data service that supports OData and transforms the entries in the response feed into objects on the client. Using this client, you can bind Windows Phone controls, such as ListBox or TextBox, to an instance of a DataServiceCollection class that contains an OData data feed. This class handles the events raised by the controls to keep the DataServiceContext class synchronized with changes that are made to data in the controls. For more information about using the OData protocol with Windows Phone applications, see Open Data Protocol (OData) Overview for Windows Phone.

Pasted from <http://msdn.microsoft.com/en-us/library/gg521145(v=vs.92).aspx>

clip_image001

2. Create a new Windows Phone application. In Solution Explorer, right-click the Solution, point to Add, and then select New Project.

3. In the Add New Project dialog box, select Silverlight for Windows Phone from the Installed Templates pane, and then select the Windows Phone Application template. Write a name for the project (e.g. 'WP7ODataSampleApp')

clip_image002

4. Click OK. This creates the application for Silverlight.

5. Generate the proxy classes by running the command datasvcutil.exe included in the  OData client libraries ZIP.

Go to Start->Run

Write 'cmd' and press Enter

clip_image004

Within the console, move to the directory where you have downloaded the OData client libraries.

For instance, '> cd D:\pablop\Documents\WP7\odata client libraries'

clip_image005

Then, run the datasvcutil.exe in order to generate the local proxy for the exposed OData services.

For the purposes of this article we are going to use the Northwind public OData services, exposed at the following Url: http://services.odata.org/Northwind/Northwind.svc

(you can take this as an example and actually use whatever OData service you have access to)

Take the following command line  as reference to generate the proxy:

datasvcutil /uri:http://services.odata.org/Northwind/Northwind.svc/ /out:.\NorthwindModel.cs /Version:2.0 /DataServiceCollection

clip_image006

You can check the generated proxy class file by running a 'dir *.cs' command, like shown below:

clip_image008

6. Move the generated file to the directory where the WP7 solution is located. You can do that just with Windows Explorer.

7. Add the file by selecting it from Project -> Add Existing item … or just by clicking on 'Show All Files' in Solution Explorer, right clicking then on the new .cs file  and choosing 'Include in Project'

clip_image010

clip_image011

8. Add a reference in the project to the System.Data.Services.Client.dll included in the OData Client library ZIP.

clip_image012

clip_image013

9. In the project, double-click the MainPage.xaml file.

Add the following attributes to your phone:PhoneApplicationPage node:

xmlns:my="clr-namespace:NorthwindModel"

d:DataContext="{d:DesignInstance Type=my:Customer, CreateList=True}"

Loaded="PhoneApplicationPage_Loaded">

Take the following xaml as a reference:

<phone:PhoneApplicationPage

    x:Class="ODataNorthwindPhone.MainPage"

    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"

    xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"

    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

    xmlns:my="clr-namespace:NorthwindModel"

    mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="768"

    d:DataContext="{d:DesignInstance Type=my:Customer, CreateList=True}"

    FontFamily="{StaticResource PhoneFontFamilyNormal}"

    FontSize="{StaticResource PhoneFontSizeNormal}"

    Foreground="{StaticResource PhoneForegroundBrush}"

    SupportedOrientations="Portrait" Orientation="Portrait"

    shell:SystemTray.IsVisible="True" Loaded="PhoneApplicationPage_Loaded">

    <Grid x:Name="LayoutRoot" Background="Transparent">

        <Grid.RowDefinitions>

            <RowDefinition Height="Auto"/>

            <RowDefinition Height="*"/>

        </Grid.RowDefinitions>

        <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">

            <TextBlock x:Name="ApplicationTitle" Text="Northwind Sales"

                       Style="{StaticResource PhoneTextNormalStyle}"/>

            <TextBlock x:Name="PageTitle" Text="Customers" Margin="9,-7,0,0"

                       Style="{StaticResource PhoneTextTitle1Style}"/>

        </StackPanel>

        <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">

            <ListBox x:Name="MainListBox" Margin="0,0,-12,0" ItemsSource="{Binding}">

                <ListBox.ItemTemplate>

                    <DataTemplate>

                        <StackPanel Margin="0,0,0,17" Width="432">

                            <TextBlock Text="{Binding Path=CompanyName}" TextWrapping="NoWrap"

                                       Style="{StaticResource PhoneTextExtraLargeStyle}"/>

                            <TextBlock Text="{Binding Path=ContactName}" TextWrapping="NoWrap"

                                       Margin="12,-6,12,0" Style="{StaticResource PhoneTextSubtleStyle}"/>

                            <TextBlock Text="{Binding Path=Phone}" TextWrapping="NoWrap" Margin="12,-6,12,0"

                                       Style="{StaticResource PhoneTextSubtleStyle}"/>

                        </StackPanel>

                    </DataTemplate>

                </ListBox.ItemTemplate>

            </ListBox>

        </Grid>

    </Grid>

</phone:PhoneApplicationPage>

The 'd:DataContext' line above allows to create data bindings at design time for a data context that is assigned at run time. This way you will be able to see sample data within the Silverlight Visual Designer in  Visual Studio and Expression Blend. Obviously this line is not required but recommended :).

10. Replace ContentPanel grid with something like this:

      <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
            <ListBox x:Name="MainListBox" Margin="0,0,-12,0" ItemsSource="{Binding}">
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <StackPanel Margin="0,0,0,17" Width="432">
                            <TextBlock Text="{Binding Path=CompanyName}" TextWrapping="NoWrap"
                                       Style="{StaticResource PhoneTextExtraLargeStyle}"/>
                            <TextBlock Text="{Binding Path=ContactName}" TextWrapping="NoWrap"
                                       Margin="12,-6,12,0" Style="{StaticResource PhoneTextSubtleStyle}"/>
                            <TextBlock Text="{Binding Path=Phone}" TextWrapping="NoWrap" Margin="12,-6,12,0"
                                       Style="{StaticResource PhoneTextSubtleStyle}"/>
                        </StackPanel>
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>
        </Grid>

11. Open MainPage.xml and add the following namespaces:

using System.Data.Services.Client;

using NorthwindModel;

12. Add the following declarations to the MainPage class:

        private DataServiceContext northwind;

        private readonly Uri northwindUri =

            new Uri("http://services.odata.org/Northwind/Northwind.svc/");

        private DataServiceCollection<Customer> customers;

        private readonly Uri customersFeed = new Uri("/Customers", UriKind.Relative);

13. Add the following PhoneApplicationPage_Loaded method to the MainPage class:

        private void PhoneApplicationPage_Loaded(object sender, RoutedEventArgs e)

        {

            // Initialize the context and the binding collection

            northwind = new DataServiceContext(northwindUri);

            customers = new DataServiceCollection<Customer>(northwind);

            // Register for the LoadCompleted event.

            customers.LoadCompleted

                += new EventHandler<LoadCompletedEventArgs>(customers_LoadCompleted);

            // Load the customers feed by using the URI.

            customers.LoadAsync(customersFeed);

        }

The above code initializes the binding collection and registers the method 'customers_LoadCompleted' (to be created on next step) to be called when collection has been loaded.

Remember that in Silverlight, all data must be retrieved asynchronously.

14. Now, let's handle the LoadCompleted event by adding the customers_LoadCompleted method. Take the following code as reference:

void customers_LoadCompleted(object sender, LoadCompletedEventArgs e)

{

   if (e.Error == null)

    {

        // Handling for a paged data feed.

        if (customers.Continuation != null)

        {

            // Automatically load the next page.

            customers.LoadNextPartialSetAsync();

        }

        else

        {

            // Set the data context of the listbox control to the sample data.

            this.LayoutRoot.DataContext = customers;

        }

    }

    else

    {

        MessageBox.Show(string.Format("An error has occurred: {0}", e.Error.Message));

    }

}

When the LoadCompleted event is handled, the following operations are performed if the request returns successfully:

o The LoadNextPartialSetAsync method of the DataServiceCollection object is called to load subsequent results pages, as long as the Continuation property of the DataServiceCollection object returns a value.

o The collection of loaded Customer objects is bound to the DataContext property of the element that is the master binding object for all controls in the page.

o Now, we are ready to test our first WP7 OData sample application. Just press F5  and wait for the emulator to be loaded and run our app. (this typically takes several secs)

clip_image014

clip_image015

clip_image016

16. As shown above, the main page is retrieving customers from OData.

Hope you find this article useful in order to start to play and/or build robust solutions on top of OData and Windows Phone 7, two extremely powerful platforms. Better together, for sure! :)

PP.

HOW To hide items from “Settings” menu

In some cases it is necessary to hide some items from the side menu according to the CRM user’s assigned role. To do this we need to modify the SiteMap from the CRM.

The example given in this post is done on CRM 2011 but it also applies to CRM 4.

We are going to see:

· How to hide items from the Settings section.

· How to hide the whole Settings area.

Firstly, we are going to export the CRM SiteMap as follows:

Go to Settings > Solutions >

Create a new solution.

image

Complete data.

image2

In the “Add Existing” menu we select “SiteMap” option and then “Save and Close”.

image3

Once we create the solution we select and export it, by clicking the “Export” button.

Click “Next”

image4

Select “Customizations”

image5

Mark the “Unmaneged” choice. Click “Export” and save the file .zip.

image6

When extracting the zip we find the customizations.xml file which contains the customizations from our CRM, just the ones from the “SiteMap” in this case.

Inside the <SiteMap> node is the complete structure from the side menu.

The privilege level the user has over the entity assigned to the menu item means what is going to determine it to be shown for some users but not for others.

Example:

image7

In this case only those roles having the “Read” permission over the “Solution” entity will be able to see this menu item. This compels us to attach a role to an entity in order to determine if the menu item is visible or not.

So we have two options: to create an entity just to define the visibility of the menu item, or to use an already created entity not working with it in our solution, for example “Solution”.

To determine which permissions has a role over an entity we go to Settings > Administration > Security Roles

image8

In the role properties window we can assign or remove the permissions over the entities. In this case we remove the “Read” permission from the “Solution” entity.

In this example the users of the role “User” won’t have the “Business Management” option visible inside the “Settings” menu.

Keep in mind the privileges are set over the <SubArea> nodes, if it necessary to hide the “Settings” menu we need to hide all the <SubArea> nodes.

How to create a Multiple Selection Picklist in CRM 4.0

In CRM 4.0 by default we do not have a Picklist where to select multiple items. In the post below I am giving the details to get this functionality.

Firstly, we create a Picklist with all values.

ScreenShot005

Then, we create a text field which is going to store the selected values from the multi-value Picklist.

ScreenShot001

Using the following script we extend the Picklist functionality to allow it to select multiple values. We change new_mypicklist and new_mypicklistvalue by the names of previously created attributes.

The code was extracted from this post

var PL = crmForm.all.new_mypicklist;
var PLV = crmForm.all.new_mypicklistvalue;

if( PL != null && PLV != null )
{
  PL.style.display = "none";
  PLV.style.display = "none";

  // Create a DIV container
  var addDiv = document.createElement("<div style='overflow-y:auto; height:80px; border:1px #6699cc solid; background-color:#ffffff;' />");
  PL.parentNode.appendChild(addDiv);

  // Initialize checkbox controls
  for( var i = 1; i < PL.options.length; i++ )
  {
    var pOption = PL.options[i];
    if( !IsChecked( pOption.text ) )
      var addInput = document.createElement("<input type='checkbox' style='border:none; width:25px; align:left;' />" );
    else
      var addInput = document.createElement("<input type='checkbox' checked='checked' style='border:none; width:25px; align:left;' />" );

    var addLabel = document.createElement( "<label />");
    addLabel.innerText = pOption.text;

    var addBr = document.createElement( "<br />");
    PL.nextSibling.appendChild(addInput);
    PL.nextSibling.appendChild(addLabel);
    PL.nextSibling.appendChild(addBr);
  }

  // Check if it is selected
  function IsChecked( pText )
  {
    if(PLV.value != "")
    {
      var PLVT = PLV.value.split("||");
      for( var i = 0; i < PLVT.length; i++ )
      {
        if( PLVT[i] == pText )
          return true;
      }
    }
    return false;
  }

  // Save selected text
  crmForm.attachEvent( "onsave" , OnSave);
  function OnSave()
  {
    PLV.value = "";
    var getInput = PL.nextSibling.getElementsByTagName("input");

    for( var i = 0; i < getInput.length; i++ )
    {
      if( getInput[i].checked)
      {
        PLV.value += getInput[i].nextSibling.innerText + "||";
      }
    }
  }
}

Now, we are going to insert this code in the Form OnLoad() event. In order to do this we select Settings > Customizations > Customize Entities and choose the entity where we want to put the multi-value Picklist.

We go to the entity form and click in Form Properties and Edit the Event OnLoad().

 

ScreenShot004

Verify the checkbox “The Event is Enabled” is checked.

The field that contains the values “new_mypicklistvalue”, must be in the page. We can hide its label but we cannot completely hide it, as it is going to take the script.

As loading the page takes some time, in order to load the jscript making the field containing the values not visible, what we can do is to put that field in another tab, for example in the tab Notes which is always present by default.

 

ScreenShot003

 

So this way it is not seen when the page is loading.

Regards!

Santiago Gonnet

More Posts