Tag Archives: Style

Workaround for binding not supported on Style/Setter/Value in Silverlight

This morning I was trying to bind the Orientation property of a PanoramaItem to a panorama item view model bound to the Panorama through its ItemsSource property and kept getting this weird exception when Silverlight was parsing the xaml stating that it “Cannot set read-only property”. I tried working around that and have some sort of attached property that would be bound to Orientation property of the view model and update the PanoramaItem’s Orientation property, but that produced the same result…

Well, it just so turns out – it is one of those things that are supported in WPF, but not in Silverlight

As a workaround – I created some attached dependency properties that are not bound to the view model themselves, but specify just the binding path and when set – create a binding to the PanoramaItem properties – as below…

public static class BindingBuilder
{
    #region PanoramaItemOrientation
    /// <summary>
    /// PanoramaItemOrientation Attached Dependency Property
    /// </summary>
    public static readonly DependencyProperty PanoramaItemOrientationProperty =
        DependencyProperty.RegisterAttached(
            "PanoramaItemOrientation",
            typeof(string),
            typeof(BindingBuilder),
            new PropertyMetadata(null, OnPanoramaItemOrientationChanged));

    /// <summary>
    /// Gets the PanoramaItemOrientation property. This dependency property
    /// indicates the binding path for the PanoramaItem.Orientation binding.
    /// </summary>
    public static string GetPanoramaItemOrientation(DependencyObject d)
    {
        return (string)d.GetValue(PanoramaItemOrientationProperty);
    }

    /// <summary>
    /// Sets the PanoramaItemOrientation property. This dependency property
    /// indicates the binding path for the PanoramaItem.Orientation binding.
    /// </summary>
    public static void SetPanoramaItemOrientation(DependencyObject d, string value)
    {
        d.SetValue(PanoramaItemOrientationProperty, value);
    }

    /// <summary>
    /// Handles changes to the PanoramaItemOrientation property.
    /// </summary>
    private static void OnPanoramaItemOrientationChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        string oldPanoramaItemOrientation = (string)e.OldValue;
        string newPanoramaItemOrientation = (string)d.GetValue(PanoramaItemOrientationProperty);
        var panoramaItem = (PanoramaItem)d;

        panoramaItem.SetBinding(PanoramaItem.OrientationProperty, new Binding(newPanoramaItemOrientation));
    }
    #endregion

    #region PanoramaItemHeader
    /// <summary>
    /// PanoramaItemHeader Attached Dependency Property
    /// </summary>
    public static readonly DependencyProperty PanoramaItemHeaderProperty =
        DependencyProperty.RegisterAttached(
            "PanoramaItemHeader",
            typeof(string),
            typeof(BindingBuilder),
            new PropertyMetadata(null, OnPanoramaItemHeaderChanged));

    /// <summary>
    /// Gets the PanoramaItemHeader property. This dependency property
    /// indicates the binding path for the PanoramaItem.Header binding.
    /// </summary>
    public static string GetPanoramaItemHeader(DependencyObject d)
    {
        return (string)d.GetValue(PanoramaItemHeaderProperty);
    }

    /// <summary>
    /// Sets the PanoramaItemHeader property. This dependency property
    /// indicates the binding path for the PanoramaItem.Header binding.
    /// </summary>
    public static void SetPanoramaItemHeader(DependencyObject d, string value)
    {
        d.SetValue(PanoramaItemHeaderProperty, value);
    }

    /// <summary>
    /// Handles changes to the PanoramaItemHeader property.
    /// </summary>
    private static void OnPanoramaItemHeaderChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        string oldPanoramaItemHeader = (string)e.OldValue;
        string newPanoramaItemHeader = (string)d.GetValue(PanoramaItemHeaderProperty);
        var panoramaItem = (PanoramaItem)d;

        panoramaItem.SetBinding(PanoramaItem.HeaderProperty, new Binding(newPanoramaItemHeader));
    }
    #endregion
}

Now I can have my Panorama built dynamically from its ItemsSource binding while still supporting setting of the PanoramaItems’ orientation and header through binding:

<Controls:Panorama
    DataContext="{Binding Panorama}"
    ItemsSource="{Binding Items}"
    Title="{Binding Title}"
    Background="{Binding Background}">
    <Controls:Panorama.ItemContainerStyle>
        <Style
            TargetType="Controls:PanoramaItem">
            <Setter
                Property="CacheMode"
                Value="BitmapCache" />
            <Setter
                Property="HorizontalContentAlignment"
                Value="Stretch" />
            <Setter
                Property="VerticalContentAlignment"
                Value="Stretch" />
            <Setter
                Property="local:BindingBuilder.PanoramaItemOrientation"
                Value="Orientation" />
            <Setter
                Property="local:BindingBuilder.PanoramaItemHeader"
                Value="HeaderText" />
            <Setter
                Property="Template">
                <Setter.Value>
                    <ControlTemplate
                        TargetType="Controls:PanoramaItem">
                        <Grid
                            Background="{TemplateBinding Background}"
                            Margin="12,0,0,0″>
                            <Grid.RowDefinitions>
                                <RowDefinition
                                    Height="auto" />
                                <RowDefinition
                                    Height="*" />
                            </Grid.RowDefinitions>
                            <ContentControl
                                x:Name="header"
                                ContentTemplate="{TemplateBinding HeaderTemplate}"
                                Content="{TemplateBinding Header}"
                                FontSize="{StaticResource PhoneFontSizeExtraExtraLarge}"
                                FontFamily="{StaticResource PhoneFontFamilySemiLight}"
                                HorizontalAlignment="Left"
                                Margin="10,-2,0,26″>
                                <ContentControl.RenderTransform>
                                    <TranslateTransform
                                        x:Name="headerTransform" />
                                </ContentControl.RenderTransform>
                            </ContentControl>
                            <ContentPresenter
                                Content="{TemplateBinding Content}"
                                HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                                Margin="{TemplateBinding Padding}"
                                Grid.Row="1″
                                VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />
                        </Grid>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </Controls:Panorama.ItemContainerStyle>
</Controls:Panorama>

Then again – you could use the generic solution…

Tagged , , , , , , , ,