Getting controls from inside a DataTemplate

Today I had to access a control which resided inside a DataTemplate used by a PivotItem (WP7) and there’s no straight forward way to do this as far as I know.

Thing is that pivotControl.Items[idx] returns the databound item and pivotControl.ItemContainerGenerator.ContainerFromItem(item) yields a PivotItem. But I figured that pivotItem.Content would contain the controls defined in the DataTemplate – wrong, it gives back the databound item.

So, my next try was to use the pivotItem.ContentTemplate.LoadContent() which actually did give me my controls – but LoadContent() actually creates a new object and doesn’t give you the reference to the actual live object on your screen.

Of course, the solution is rather simple if you can just think of it right off the batWinking smile

First, get the container:

DependencyObject container = pivotControl.ItemContainerGenerator.ContainerFromItem(item);

Then we use the VisualTreeHelper to take a walk around and find the control we want (by name).

var details = container.GetChild<Detail>("details");

This is all possible with this little extension method:

public static T GetChild<T>(this DependencyObject parent, string name) where T:DependencyObject{
            for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++) {
                DependencyObject child = VisualTreeHelper.GetChild(parent, i);

                if (child.GetType() == typeof(T) && ((FrameworkElement)child).Name == name)
                    return (T)child;

                if (child.GetChild<T>(name) != null)

                    return child.GetChild<T>(name);
            }
            return null;
        }

If there’s a simpler solution to this, please let me knowSmile

SharePhone

Today I released a basic version of SharePhone, my WP7 library for working with SharePoint sites. Binaries and source code can be found here: Codeplex

I’m currently porting this code from a more functional Silverlight project so I apologize if the source is a bit messy.

Anyway, the current release lets you open up any SharePoint 2007/2010 web and its sub webs and work with their lists and list items.

Here’s a short example how:

First we instantiate our context and supply the URL for the root web.

ClientContext ctx = new ClientContext("http://sharepointdev");
//supply credentials if needed
ctx.Settings.Credentials = new CredentialSettings { Domain = "<domain>", UserName = "<user>", Password = "<password>" };

Then we get our root web object (remember, Silverlight requires any web service call to be made asynchronously which is why this is the only method available in this library).

ctx.GetRootWeb((object s, SPWebLoadCompletedEventArgs result) => {
    SPWeb rootWeb = result.Webs[0];               
});

Next, let’s load all the lists available at the root web:

web.Lists.LoadCompleted += (object sender, SPListLoadCompletedEventArgs e) => {
    Deployment.Current.Dispatcher.BeginInvoke(() => {
        list1.ItemsSource = e.Lists;
    });
};
web.Lists.LoadAll();

list1 is a ListBox and it’s defined like this in XAML:

            <ListBox Name="list1">
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <TextBlock Text="{Binding Title}" Style="{StaticResource PhoneTextNormalStyle}"/>
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>

And we have our lists neatly listed out on our phone, ready to continue working with them. sample

Most of the operations on collections, such as sub webs on a web or lists or even list items in a list requires you first to load the collection like in the previous example (web.Lists.LoadAll()). The reason why I don’t load this data on first call is because I want to keep the data traffic at a minimum. Basically you load what you need and nothing more.

Next post I’ll show an example how to read list items, update the data and post them back to SharePoint using strong typed custom classes.

Silverlight & SharePoint 2007

“It’s just an object tag”

One of the things that was in focus during the Las Vegas SharePoint Conference 2009 was Silverlight in Sharepoint 2010. But you don’t need SharePoint 2010 to enjoy this, its easy to do the same in SharePoint 2007. As it states above, Silverlight in SharePoint is just an object tag; there are no requirements and no need to install or configure anything extra.

In its most basic form, all you need is:

<object width="300" height="300"    data="data:application/x-silverlight-2,"     type="application/x-silverlight-2" >    <param name="source" value="SilverlightApplication1.xap"/></object>

Just upload the .XAP file to a SharePoint document library and set the value of the source parameter to the URL.

If you need to pass startup values to your Silverlight application you add a initparams parameter with key/value pairs (comma separated):

   1: <param name="initParams" value="param1=value1"/>

And you retrieve them like so:

var value1 = Application.Current.Host.InitParams["param1"];

Now, to actually read and write data from SharePoint, you’ll need to use SharePoint’s web services. This is where SharePoint 2010 provides a brand new client object model that gives you this ability, but its easy enough to do this yourself.

Using SharePoint 2007 user profile service with Silverlight

When developing a Silverlight application that required the use of the user profile web service provided by SharePoint 2007, I discovered a rather frustrating bug.

Adding the service as a .NET 2.0 web reference, it will function as one would expect. However, when adding it as a service reference in a Silverlight project (as Silverlight has certain requirements of the web services it consumes) the bug kicks in.

It seems that the WSDL provides elements in the wrong order and also the autogenerated classes fails to handle objects of type like GUID (when calling methods they will fail stating that they don’t know how to handle the GUID values).

This only applies when reading data from the web service, when writing back it seems to function normally.

One solution I’ve found to work around this is to implement your own class for calling the web service and do the parsing yourself. I’ll probably do a post later on how I did this.

Silverlight presence indicator

A while back I published a Silverlight presence indicator control on CodePlex and I thought I’d explain this in more detail here.

The control is nothing more than an integration with the standard OCS presence indicator that comes with Microsoft Office (using Name.dll made famous by SharePoint sites). The integration is done using the Silverlight HTML bridge and javascript.

It’s all very simple and straightforward; the control injects javascript into the page where your Silverlight application is hosted and using these scripts it instantiates a new ActiveX object as well as hooking up the OnStatusChanged event.

if(!IMControl){{ IMControl = new ActiveXObject(\"Name.NameCtrl.1\");

IMControl.OnStatusChange=OCSOnStatusChange;

The function called when the ActiveX object fires a status change event will the use the HTML bridge to call a method in the Silverlight control to update the indicator icon.

function OCSOnStatusChange(name, state, id) {

  eval('{0}.Content.' + callbacks[id] + '.UpdateImage(' + state + ',\"'+ name +'\");');
}

This is made possible by registering the Silverlight control as a scriptable object so it can be called from Javascript.

HtmlPage.RegisterScriptableObject(objectName, this);

The UpdateImage method is the tagged as a ScriptableMember, otherwise it won’t be exposed to the HTML bridge.

[ScriptableMember()]
public void UpdateImage(object state, object name) {
    int stateValue = Int32.Parse(state.ToString());
    string imgName = GetStateIndicatorImage(stateValue, true);
    indicator.Source = new BitmapImage(new Uri(string.Format("/Silverlight.OCS;component/Images/{0}", imgName), UriKind.Relative));
    FireStatusChanged(name as string, stateValue);
}

Now, to get the ActiveX object to display the Communicator panel (where you can initiate chat sessions, see details, etc) we will use the HTML bridge to go in the other direction: From Silverlight to Javascript.

Among the Javascript functions we’ve injected into the page is the OCSOpenUI function, which we will call from Silverlight when we receive the MouseOver event on the indicator icon.

function OCSOpenUI(user, x, y) {    if(IMControl){       IMControl.ShowOOUI(user, 0, x, y);    } }

To call this function, all we need to do is invoke it:

HtmlPage.Window.Invoke("OCSOpenUI", User,x, y);

The coordinates are a bit tricky, as the Office 2010 Communicator panel will function just fine with the standard mouse coordinates. However, previous Office versions require that you locate the exact position of the Silverlight control then adjust for mouse coordinates. This involves using Javascript again to find the location of the control within the HTML page.

if (OfficeVersionCompatibility == Version.Office2007AndOlder) {
    ScriptObject result = (ScriptObject)HtmlPage.Window.Invoke("findPos", HtmlPage.Document.GetElementById(HtmlPage.Plugin.Id));
    int left = Int32.Parse(result.GetProperty("Left").ToString());
    int top = Int32.Parse(result.GetProperty("Top").ToString());
    Point posOnIcon = e.GetPosition(indicator);
    x = left + (int)(pos.X - posOnIcon.X);
    y = top + (int)(pos.Y - posOnIcon.Y);
}

 

In closing…

I’ve skipped a lot of steps here as the source code is freely available and should be fairly easy to understand. Should there be any questions, I’ll be more than happy to explain it in more detail 🙂 Also, should anyone have a better way of doing this, I’d appreciate some feedback.

You can find a compiled version of the control and the source code available here: CodePlex