Showing posts with label Silverlight. Show all posts
Showing posts with label Silverlight. Show all posts

Friday, 16 December 2011

WP7 MVVM Panorama SelectedIndex Binding

The Panorama control doesn't support SelectedItem or SelectedIndex binding which is important for maintaining state on re-activation after tombstoning and may be required for navigation. To solve this problem, I've created a Panorama Behavior called TrackablePonaramaBehavior which can be attached to a panorama and offers a dependency property for binding to the selected index. The System.Windows.Interactivity library needs referencing to use it:

using System;
using Microsoft.Phone.Controls;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Interactivity;

namespace Demo.Behaviors
{
    public class TrackablePanoramaBehavior : Behavior<Panorama>
    {
        private Panorama _panarama = null;
        private bool _updatedFromUI = false;

        // DP for binding index
        public static readonly DependencyProperty SelectedIndexProperty =
            DependencyProperty.Register("SelectedIndex", typeof(int), typeof(TrackablePanoramaBehavior),
            new PropertyMetadata(0, new PropertyChangedCallback(SelectedIndexPropertyChanged)));

        // Index changed by view model
        private static void SelectedIndexPropertyChanged(DependencyObject dpObj, DependencyPropertyChangedEventArgs change)
        {
            if(change.NewValue.GetType() != typeof(int) || dpObj.GetType() != typeof(TrackablePanoramaBehavior))
                return;

            TrackablePanoramaBehavior track = (TrackablePanoramaBehavior)dpObj;

            // If this flag is not checked, the panorama smooth transition is overridden
            if (!track._updatedFromUI)
            {
                Panorama pan = track._panarama;

                int index = (int)change.NewValue;

                if (pan.Items.Count > index)
                {
                    pan.DefaultItem = pan.Items[(int)change.NewValue];
                }
            }

            track._updatedFromUI = false;
        }

        public int SelectedIndex
        {
            get { return (int)GetValue(SelectedIndexProperty); }
            set { SetValue(SelectedIndexProperty, value); }
        }

        protected override void OnAttached()
        {
            base.OnAttached();

            this._panarama = base.AssociatedObject as Panorama;
            this._panarama.SelectionChanged += _panarama_SelectionChanged;
        }       

        protected override void OnDetaching()
        {
            base.OnDetaching();

            if(this._panarama != null)
                this._panarama.SelectionChanged += _panarama_SelectionChanged;
        }

        // Index changed by UI
        private void _panarama_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            _updatedFromUI = true;
            SelectedIndex = _panarama.SelectedIndex;
        }
    }
}


To implement this in the View, the following XAML is used (I chopped most xmlns out to make it more concise):

<phone:PhoneApplicationPage

    xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"

    DataContext="{Binding Main, Source={StaticResource Locator}}">
  
    <!--LayoutRoot contains the root grid where all other page content is placed-->
    <Grid x:Name="LayoutRoot">
        <controls:Panorama>
            <i:Interaction.Behaviors>
                <track:TrackablePanoramaBehavior SelectedIndex="{Binding Path=SelectedIndex, Mode=TwoWay}" />
            </i:Interaction.Behaviors>


And simply bind to the view model:

public int SelectedIndex
{
    get { return this._selectedIndex; }
    set
    {
        if (this._selectedIndex != value)
        {
            this._selectedIndex = value;

            base.RaisePropertyChanged("SelectedIndex");
        }
    }
}

Monday, 12 December 2011

Silverlight (WP7) ListBox & ItemsControl Item Commanding

ListBoxes are a really powerful and flexible way of displaying data in Silverlight, the main problem I find is when you want to use an MVVM pattern and detect when an item is selected and re-selected. It is possible to bind to the SelectedItem property and look for changes in the VM to perform some kind of action, however if you want to select the same item again you need to select something else, then re-select it because SelectedItem becomes latched.

Instead of using a ListBox, it is more flexible to us an ItemsControl (which a ListBox derives from). This has the functionality we require but without the SelectedItem dependency property which we aren't using. The following example has an Items control with a button containing an image in the ItemTemplate:

<Grid x:Name="LayoutRoot">
  <ItemsControl ItemsSource="{Binding Path=Items, Mode=OneWay}">
    <ItemsControl.ItemsPanel>
      <ItemsPanelTemplate>
        <StackPanel Orientation="Horizontal" />
      </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
    <ItemsControl.ItemTemplate>
      <DataTemplate>
        <Button>
          <Image Source="{Binding Path=Url, Mode=OneWay}" Stretch="Uniform" />
        </Button>
      </DataTemplate>
    </ItemsControl.ItemTemplate>
  </ItemsControl>
</Grid>

This is fairly straight forward, but the problem is, if you add a command to the button like this:

<Button Command="{Binding ImageCommand}" CommandParameter="{Binding}" >
  <Image Source="{Binding Path=Url, Mode=OneWay}" Stretch="Uniform" />
</Button>

The button will try and bind to a command in the data item which doesn't exist; instead of the parent view model.

To get round this, the command needs to get outside of the bound item's context, into the main view model by binding to the grid's data context (it could be any named, non-templated element on a page/user control) using the ElementName and Path binding properties:

<Button Command="{Binding ElementName=LayoutRoot, Path=DataContext.ImageCommand}"
  CommandParameter="{Binding}" >
  <Image Source="{Binding Path=Url, Mode=OneWay}" Stretch="Uniform" />
</Button>

This can now easily be bound to a view model containing a command like this:

private ICommand _imageCommand = null;

public ICommand ImageCommand

{
    get { return this._imageCommand; }
}

private bool IsCommandExecutable

{
    get { return true; }
}

// Call this in VM constructor

private void InitialiseCommands()
{
    // Join
    this._imageCommand = new DelegateCommand((param) =>
    {
        this.DoSomething((ImageDetails)param);
        },
            (p) =>
            {
                return this.IsCommandExecutable;
            });
    }

private void DoSomething(ImageDetails obj)

{

}


This technique obviously works with ListBox controls as well, so buttons can be placed alongside other content whilst maintaining the selection mechanism. This example has a delete button alongside some title text:

<Grid x:Name="LayoutRoot">
  <ListBox ItemsSource="{Binding Items, Mode=OneWay}"
    SelectedItem="{Binding SelectedItem, Mode=TwoWay>
    <ListBox.ItemsPanel>
      <ItemsPanelTemplate>
        <StackPanel Orientation="Horizontal" />
      </ItemsPanelTemplate>
    </ListBox.ItemsPanel>
    <ListBox.ItemTemplate>
      <DataTemplate>
<Grid>
 <Grid.ColumnDefinitions>
   <ColumnDefinition Width="Auto"/>
   <ColumnDefinition Width="*"/>
 <Grid.ColumnDefinitions>
          <TextBlock Text="{Binding Title} />
          <Button Grid.Column="1" Command="{Binding DeleteCommand}" />
        <Grid>
      </DataTemplate>
    </ListBox.ItemTemplate>
  </ListBox>
</Grid>

Wednesday, 7 December 2011

Windows Phone Toolkit Expander Template Fix

The expander has a little line which appears alongside the drop-down panel. I noticed that this line is visible behind the header, so I tweaked the template to animate the line opacity so it is hidden when collapsed:

<Style x:Key="ExpanderViewStyle" TargetType="toolkit:ExpanderView">
            <Setter Property="HorizontalAlignment" Value="Stretch"/>
            <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
            <Setter Property="ItemsPanel">
                <Setter.Value>
                    <ItemsPanelTemplate>
                        <StackPanel/>
                    </ItemsPanelTemplate>
                </Setter.Value>
            </Setter>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="toolkit:ExpanderView">
                        <Grid>
                            <Grid.Resources>
                                <QuadraticEase x:Key="QuadraticEaseOut" EasingMode="EaseOut"/>
                                <QuadraticEase x:Key="QuadraticEaseInOut" EasingMode="EaseInOut"/>
                            </Grid.Resources>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="41"/>
                                <ColumnDefinition Width="*"/>
                            </Grid.ColumnDefinitions>
                            <Grid.RowDefinitions>
                                <RowDefinition Height="Auto"/>
                                <RowDefinition Height="Auto"/>
                                <RowDefinition Height="Auto"/>
                            </Grid.RowDefinitions>
                            <VisualStateManager.VisualStateGroups>
                                <VisualStateGroup x:Name="ExpansionStates">
                                    <VisualStateGroup.Transitions>
                                        <VisualTransition From="Collapsed" GeneratedDuration="0:0:0.15" To="Expanded">
                                            <Storyboard>
                                                <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(FrameworkElement.Height)" Storyboard.TargetName="ItemsCanvas">
                                                    <EasingDoubleKeyFrame EasingFunction="{StaticResource QuadraticEaseOut}" KeyTime="0:0:0.00" Value="0"/>
                                                    <EasingDoubleKeyFrame x:Name="CollapsedToExpandedKeyFrame" EasingFunction="{StaticResource QuadraticEaseOut}" KeyTime="0:0:0.15" Value="1"/>
                                                </DoubleAnimationUsingKeyFrames>
                                                <DoubleAnimation Duration="0" To="1.0" Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="ItemsCanvas"/>
                                                <DoubleAnimation Duration="0" To="1.0" Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="Line"/>
                                            </Storyboard>
                                        </VisualTransition>
                                        <VisualTransition From="Expanded" GeneratedDuration="0:0:0.15" To="Collapsed">
                                            <Storyboard>
                                                <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(FrameworkElement.Height)" Storyboard.TargetName="ItemsCanvas">
                                                    <EasingDoubleKeyFrame x:Name="ExpandedToCollapsedKeyFrame" EasingFunction="{StaticResource QuadraticEaseInOut}" KeyTime="0:0:0.00" Value="1"/>
                                                    <EasingDoubleKeyFrame EasingFunction="{StaticResource QuadraticEaseInOut}" KeyTime="0:0:0.15" Value="0"/>
                                                </DoubleAnimationUsingKeyFrames>
                                                <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="ItemsCanvas">
                                                    <EasingDoubleKeyFrame EasingFunction="{StaticResource QuadraticEaseInOut}" KeyTime="0:0:0.00" Value="1.0"/>
                                                    <EasingDoubleKeyFrame EasingFunction="{StaticResource QuadraticEaseInOut}" KeyTime="0:0:0.15" Value="0.0"/>
                                                </DoubleAnimationUsingKeyFrames>
                                                <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="Line">
                                                    <EasingDoubleKeyFrame EasingFunction="{StaticResource QuadraticEaseInOut}" KeyTime="0:0:0.00" Value="1.0"/>
                                                    <EasingDoubleKeyFrame EasingFunction="{StaticResource QuadraticEaseInOut}" KeyTime="0:0:0.15" Value="0.0"/>
                                                </DoubleAnimationUsingKeyFrames>
                                                <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.TranslateY)" Storyboard.TargetName="ItemsCanvas">
                                                    <EasingDoubleKeyFrame EasingFunction="{StaticResource QuadraticEaseInOut}" KeyTime="0:0:0.00" Value="0.0"/>
                                                    <EasingDoubleKeyFrame EasingFunction="{StaticResource QuadraticEaseInOut}" KeyTime="0:0:0.15" Value="-35"/>
                                                </DoubleAnimationUsingKeyFrames>
                                            </Storyboard>
                                        </VisualTransition>
                                    </VisualStateGroup.Transitions>
                                    <VisualState x:Name="Collapsed">
                                        <Storyboard>
                                            <DoubleAnimation Duration="0" To="0" Storyboard.TargetProperty="(FrameworkElement.Height)" Storyboard.TargetName="ItemsCanvas"/>
                                            <DoubleAnimation Duration="0" To="0.0" Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="ItemsCanvas"/>
                                            <DoubleAnimation Duration="0" To="0.0" Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="Line"/>
                                        </Storyboard>
                                    </VisualState>
                                    <VisualState x:Name="Expanded">
                                        <Storyboard>
                                            <DoubleAnimation Duration="0" Storyboard.TargetProperty="(FrameworkElement.Height)" Storyboard.TargetName="ItemsCanvas"/>
                                            <DoubleAnimation Duration="0" To="1.0" Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="ItemsCanvas"/>
                                            <DoubleAnimation Duration="0" To="1.0" Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="Line"/>
                                        </Storyboard>
                                    </VisualState>
                                </VisualStateGroup>
                                <VisualStateGroup x:Name="ExpandabilityStates">
                                    <VisualState x:Name="Expandable"/>
                                    <VisualState x:Name="NonExpandable">
                                        <Storyboard>
                                            <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="ExpandableContent">
                                                <DiscreteObjectKeyFrame KeyTime="0:0:0.0" Value="Collapsed"/>
                                            </ObjectAnimationUsingKeyFrames>
                                            <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="Line">
                                                <DiscreteObjectKeyFrame KeyTime="0:0:0.0" Value="Collapsed"/>
                                            </ObjectAnimationUsingKeyFrames>
                                            <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="NonExpandableContent">
                                                <DiscreteObjectKeyFrame KeyTime="0:0:0.0" Value="Visible"/>
                                            </ObjectAnimationUsingKeyFrames>
                                        </Storyboard>
                                    </VisualState>
                                </VisualStateGroup>
                            </VisualStateManager.VisualStateGroups>
                            <ListBoxItem x:Name="ExpandableContent" Grid.ColumnSpan="2" Grid.Column="0" toolkit:TiltEffect.IsTiltEnabled="True" Grid.Row="0" Grid.RowSpan="2">
                                <Grid>
                                    <Grid.ColumnDefinitions>
                                        <ColumnDefinition Width="41"/>
                                        <ColumnDefinition Width="*"/>
                                    </Grid.ColumnDefinitions>
                                    <Grid.RowDefinitions>
                                        <RowDefinition Height="Auto"/>
                                        <RowDefinition Height="Auto"/>
                                        <RowDefinition Height="Auto"/>
                                    </Grid.RowDefinitions>
                                    <ContentControl x:Name="Header" Grid.ColumnSpan="2" ContentTemplate="{TemplateBinding HeaderTemplate}" Content="{TemplateBinding Header}" Grid.Column="0" HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch" Grid.Row="0"/>
                                    <ContentControl x:Name="Expander" ContentTemplate="{TemplateBinding ExpanderTemplate}" Content="{TemplateBinding Expander}" Grid.Column="1" HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch" Margin="11,0,0,0" Grid.Row="1"/>
                                    <Grid x:Name="ExpanderPanel" Background="Transparent" Grid.ColumnSpan="2" Grid.Column="0" Grid.Row="0" Grid.RowSpan="2"/>
                                </Grid>
                            </ListBoxItem>
                            <Line x:Name="Line" Grid.Column="1" HorizontalAlignment="Left" Grid.Row="2" Opacity="0" Grid.RowSpan="2" Stretch="Fill" Stroke="{StaticResource PhoneSubtleBrush}" StrokeThickness="3" X1="0" X2="0" Y1="0" Y2="1"/>
                            <ContentControl x:Name="NonExpandableContent" Grid.ColumnSpan="2" ContentTemplate="{TemplateBinding NonExpandableHeaderTemplate}" Content="{TemplateBinding NonExpandableHeader}" Grid.Column="0" HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch" Grid.Row="0" Grid.RowSpan="2" Visibility="Collapsed"/>
                            <Canvas x:Name="ItemsCanvas" Grid.Column="1" Margin="11,0,0,0" Opacity="0.0" Grid.Row="2">
                                <Canvas.RenderTransform>
                                    <CompositeTransform TranslateY="0.0"/>
                                </Canvas.RenderTransform>
                                <ItemsPresenter x:Name="Presenter"/>
                            </Canvas>
                        </Grid>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>

Thursday, 20 October 2011

sllauncher.exe 5.0.60818.0 - OutOfBrowserSettings.Icons Issue

We've been testing an SL4 app which is used both in and out of browser agains SL5RC. Which runs fine in browser, but when launched out-of-browser, sllauncher crashed with the following error:

Faulty module name: unknown v0.0.0.0

Exception Code: 0xc0000005

I created a dummy SL5 test app and it ran OOB fine.

After stripping back our main app to almost nothing I still had the same problem, but I noticed we had icons set in the OOB settings. OutOfBrowserSettings.xml Example:

<OutOfBrowserSettings ShortName="SLApp" EnableGPUAcceleration="True" ShowInstallMenuItem="False">
  <OutOfBrowserSettings.Blurb>Install SLApp application out of the browser</OutOfBrowserSettings.Blurb>
  <OutOfBrowserSettings.WindowSettings>
    <WindowSettings Title="SLApp" Height="60" Width="595" />
  </OutOfBrowserSettings.WindowSettings>
  <OutOfBrowserSettings.SecuritySettings>
    <SecuritySettings ElevatedPermissions="Required" />
  </OutOfBrowserSettings.SecuritySettings>
  <OutOfBrowserSettings.Icons>
    <Icon Size="16,16">Icons/SLApp16x16.png</Icon>
    <Icon Size="32,32">Icons/SLApp32x32.png</Icon>
    <Icon Size="48,48">Icons/SLApp48x48.png</Icon>
    <Icon Size="128,128">Icons/SLApp128x128.png</Icon>
  </OutOfBrowserSettings.Icons>
</OutOfBrowserSettings>

(These settings can be set in the SL app OOB properties dialogue as well)

IF THE ICONS BLOCK IS REMOVED:

<OutOfBrowserSettings ShortName="SLApp" EnableGPUAcceleration="True" ShowInstallMenuItem="False">
  <OutOfBrowserSettings.Blurb>Install SLApp application out of the browser</OutOfBrowserSettings.Blurb>
  <OutOfBrowserSettings.WindowSettings>
    <WindowSettings Title="SLApp" Height="60" Width="595" />
  </OutOfBrowserSettings.WindowSettings>
  <OutOfBrowserSettings.SecuritySettings>
    <SecuritySettings ElevatedPermissions="Required" />
  </OutOfBrowserSettings.SecuritySettings>
</OutOfBrowserSettings>

The aplication runs fine OOB!

This looks like a bug in sllauncher.exe 5.0.60818.0.

Wednesday, 7 September 2011

RichTextBlock XAML Bindable Rich Text Block

I recently needed to use a RichTextBox for a project, but was disapointed to find there is no way of binding the content of the control. Luckily it's possible to access the XAML property and inject RichText format XAML into it. I decided to create a control derived from RichTextBox with a dependency property  used to bind the Rich Text XAML.

The first step is to create the control (I re-exposed the mouse click event as the RichTextBox buries it):

using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;

using System.Windows.Ink;
using System.Windows.Input;

namespace RichTextBlock
{
  /// <summary>
  /// Used for databinding to Xaml property via new XamlSource DP
  /// </summary>
  public class RichXamlTextBlock : RichTextBox
  {
    public event MouseButtonEventHandler MouseClicked = null;
    private static string _xamlStart = "<Section xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"><Paragraph>";
    private static string _xamlEnd = "</Paragraph></Section>";

    public RichXamlTextBlock()
      : base()
    {
      base.Cursor = Cursors.Arrow;
    }

    #region XamlSource

    public static readonly DependencyProperty XamlSourceProperty = DependencyProperty.Register("XamlSource", typeof(string), typeof(RichXamlTextBlock),
        new PropertyMetadata(null, new PropertyChangedCallback(OnXamlSourcePropertyChanged)));

    public string XamlSource
    {
      get { return (string)GetValue(XamlSourceProperty); }
      set
      {
        SetValue(XamlSourceProperty, value);
      }
    }

    private static void OnXamlSourcePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
      RichXamlTextBlock rtb = (RichXamlTextBlock)d;

      rtb.Xaml = string.Format("{0}{1}{2}", _xamlStart, e.NewValue, _xamlEnd as string);
    }

    #endregion

    protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
    {
      if (this.MouseClicked != null)
      {
        this.MouseClicked(this, e);
      }
    }
  }
}

The next step is to create a View Model with some XAML rich text. It is important to be careful what you bind as you could end up doing an 'injection attack' on your markup by accident!

using System;

namespace RichTextBlock
{
    public class ViewModel
    {
        private string _xamlSource = "This is a demonstration of the <Run Text=\"RichTextBlock\" FontWeight=\"Bold\" Foreground=\"Green\"/>. The control's XAML can be data bound unlike a normal <Run Text=\"RichTextBox\" FontWeight=\"Bold\" Foreground=\"Red\"/>.";

        public string XamlSource
        {
            get { return this._xamlSource; }
        }
    }
}

Next the new control needs adding to a view:

<UserControl x:Class="RichTextBlock.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:RichTextBlock"
    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400">
   
    <UserControl.DataContext>
        <local:ViewModel />
    </UserControl.DataContext>
   
    <UserControl.Resources>
       
        <Style x:Key="RichXamlTextBlockStyle" TargetType="local:RichXamlTextBlock">
            <Setter Property="IsReadOnly" Value="True" />
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="local:RichXamlTextBlock">
                        <Grid x:Name="ContentElement" Background="{TemplateBinding Background}" />
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
       
    </UserControl.Resources>

    <Grid x:Name="LayoutRoot" Background="White">
        <local:RichXamlTextBlock XamlSource="{Binding Path=XamlSource, Mode=OneWay}" Style="{StaticResource RichXamlTextBlockStyle}" />
    </Grid>
</UserControl>

I also added a stripped down template to remove the TextBox type appearance to get it looking like a TextBlock.

Wednesday, 10 August 2011

Toolkit Chart ControlTemplate

<ControlTemplate TargetType="toolkit:Chart">
                        <Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Padding="{TemplateBinding Padding}">
                            <Grid>
                                <Grid.RowDefinitions>
                                    <RowDefinition Height="Auto"/>
                                    <RowDefinition Height="*"/>
                                </Grid.RowDefinitions>
                                <toolkit:Title Content="{TemplateBinding Title}" Style="{TemplateBinding TitleStyle}"/>
                                <Grid Margin="0,15,0,15" Grid.Row="1">
                                    <Grid.ColumnDefinitions>
                                        <ColumnDefinition Width="*"/>
                                        <ColumnDefinition Width="Auto"/>
                                    </Grid.ColumnDefinitions>
                                    <toolkit:Legend x:Name="Legend" Grid.Column="1" Header="{TemplateBinding LegendTitle}" Style="{TemplateBinding LegendStyle}"/>
                                    <System_Windows_Controls_DataVisualization_Charting_Primitives:EdgePanel x:Name="ChartArea" Style="{TemplateBinding ChartAreaStyle}">
                                        <Grid Style="{TemplateBinding PlotAreaStyle}" Canvas.ZIndex="-1"/>
                                        <Border BorderBrush="#FF919191" BorderThickness="1" Canvas.ZIndex="10"/>
                                    </System_Windows_Controls_DataVisualization_Charting_Primitives:EdgePanel>
                                </Grid>
                            </Grid>
                        </Border>
                    </ControlTemplate>

Saturday, 23 April 2011

JavaScript Bridge Update

Just added a MVVM pattern. Demo can be switched between UserControl and MVVM mode by using the startPage WebPart property. Use 'mvvm' for MVVM demo and 'uc' for UserControl demo.

Thursday, 17 February 2011

SharePoint 2010 Silverlight Web Part JavaScript Bridge

SharePoint 2010 Silverlight Web Part JavaScript Bridge

Overview
This template contains base classes and scripts enabling Silverlight web parts (and ASP.Net web parts) to be created enabling client-side interaction with other Silverlight Web Parts. A JavaScript file is injected into the page automatically and allows Web Parts to register themselves for interaction with the bridge. If only Silverlight to Silverlight communication is required, local messaging could be used as an alternative mechanism, however this technique allows interaction from both Silverlight and ASP.Net web parts which is more flexible for SharePoint applications.  A Web Part using the base classes has the following properties required for JS Bridge connection:

  • JSClientID - Used to pass the JavaScript Bridge ClientID from the SharePoint UI to the Web Part. JSClientID is a simple identifier for components connecting to the JS Bridge.
  • JSClientSourceID - Used to pass the JavaScript Bridge ClientSourceID from the SharePoint UI to the Web Part. JSClientSourceID indicates to the JS Bridge where a components connection is sourced.
  • JSUniqueClientID - JSClientSourceID indicates to the JS Bridge the real ID of the component on the page. It is derived from the WebParts’s ClientID with an ‘SL’ suffix.
 
Registering Clients
When a JS Bridge Web Part loads, it registers itself with the JS Bridge using the registerClient method, this allows the bridge to locate the WebPart on the page and lets it know where to route data.

Installing
The package can be deployed straight to a local SharePoint 2010 server from Visual Studio 2010. The feature should appear in the default site collection features list:


Testing
To test the functionality, simply place 2 instances of the demo web part on a page. The demo web part is designed so one instance can talk to another instance of itself but normally different web parts would talk to each other.
The first web part can be configured as follows:

    The second web part can be configured as follows:


    Using the Silverlight UserControlBase Class
    Obviously the contents of the base class can be copied to a user control, but to implement it as a base for a user control the following steps must be taken:

    Application Manifest
    The ‘ExternalCallersFromCrossDomain’ attribute must be set to ‘ScriptableOnly’ to allow applications hosted on a different site to the SharePoint site (cross-domain) to access the DOM; otherwise the JSBridge will not work. This can be a security risk particularly for public facing sites, so it's best to host the XAP in SharePoint.


    <Deployment xmlns="http://schemas.microsoft.com/client/2007/deployment"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            ExternalCallersFromCrossDomain="ScriptableOnly" >
        <Deployment.Parts>
        </Deployment.Parts>
    </Deployment>

    AssemblyInfo
    The following attribute must be included in the AssemblyInfo file so that the UserControlBase can be implemented. The ‘System.Windows.Markup’ namespace needs referencing for this.


    [assembly: XmlnsDefinition("http://schemas.microsoft.com/winfx/2006/xaml/presentation", "WebberCross.JavaScriptBridge.SilverlightApplication11")]



    Calling  JSBridgeSend
    The example shows how an object can easily be JSON serialized then sent to the JS Bridge where it will be multi-cast to all registered web parts with a matching JavaScriptClintSourceID

                            
    // Send a message to the bridge
    DemoMessage msg = new DemoMessage("Hello from Silverlight Application", base._jsClientID.ToString());
    string jsonMsg = JSONHelper.Serialize(msg);
    base.JSBridgeSend(jsonMsg);


    Overriding JSBridgeReceive
    The example shows how to override the 'JSBridgeReceive' method to deserialize the sent object:

                            
    // Override base JSBridgeReceive method
    public override void JSBridgeReceive(string arg)
    {
        try
        {
            // Deserialize message
            DemoMessage msg = JSONHelper.Deserialize<DemoMessage>(arg);

            // Write message to label
            this.lbl3.Text = string.Format("{0}, from JSBridge Client {1}", msg.Message, msg.JSClientID);
        }
        catch (Exception ex)
        {
             MessageBox.Show(ex.ToString());
        }
    }



    Conclusion
    The JavaScript Bridge is a great way of enabling Silverlight web parts to interact with each other in the browser, it also allows Silverlight web parts to be communicated with from a web page. The base classes provide a quick way of getting standard behavior in sets of web parts.