3

I have a custom ContentControl

public class DataControl : ContentControl
{
    public List<DataItem> Options
    {
        get { return (List<DataItem>)GetValue(OptionsProperty); }
        set { SetValue(OptionsProperty, value); }
    }

    public static readonly DependencyProperty OptionsProperty =
        DependencyProperty.Register("Options", typeof(List<DataItem>), typeof(DataControl));

    public DataControl()
    {
        Options = new List<DataItem>();
    }

    public string Label
    {
        get { return (string)GetValue(LabelProperty); }
        set { SetValue(LabelProperty, value); }
    }

    // Using a DependencyProperty as the backing store for Label.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty LabelProperty =
        DependencyProperty.Register("Label", typeof(string), typeof(DataControl));
}

public class DataItem
{
    public DataItem(string key, string value)
    {
        Key = key;
        Value = value;
    }

    public string Key { get; set; }

    public string Value { get; set; }
}

whose template is applied by the following Style:

<Style TargetType="{x:Type local:DataControl}" x:Key="DefaultStyle">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type local:DataControl}">
                <StackPanel>
                <ListBox ItemsSource="{TemplateBinding Options}" >
                    <ListBox.ItemTemplate>
                        <DataTemplate>
                        <Label Content="{Binding Key}" />
                        </DataTemplate>
                    </ListBox.ItemTemplate>
                </ListBox>
                <Label Content="{TemplateBinding Label}" />
                </StackPanel>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

If I use a XamlWriter to Save this style and then read it back again, the ItemsSource binding is lost, but the Content binding on the Label isn't.

Style style = Application.Current.TryFindResource("DefaultStyle") as Style;

string s = XamlWriter.Save(style);
Style secondStyle = XamlReader.Parse(s) as Style;

Is there a way to ensure the ItemsSource binding is serialized correctly or to add it back in easily?

This also occurs when trying to get the Style from a ResourceDictionary from another project, e.g.

ResourceDictionary styles = new ResourceDictionary();
styles.Source = new Uri(String.Format("pack://application:,,,/StyleCopyTest;component/Styles/{0}Styles.xaml", type));
return styles;
abatishchev
  • 98,240
  • 88
  • 296
  • 433
Andrew Jones
  • 1,001
  • 1
  • 13
  • 25
  • Did you try the solution in [http://stackoverflow.com/questions/32541/how-can-you-clone-a-wpf-object](http://stackoverflow.com/questions/32541/how-can-you-clone-a-wpf-object)? – Raj Ranjhan Jan 25 '12 at 19:41
  • I have and it doesn't resolve the issue. The .Net 4.0 solution doesn't keep the ItemsSource binding and worse loses the Content binding on the Label in the ListBox.ItemTemplate. Creating an ExpressionConverter fixes the Content binding but still does not save the ItemsSource binding. – Andrew Jones Jan 26 '12 at 09:19

2 Answers2

2

In the WPF source code the ItemsSource is defined as

[Bindable(true), CustomCategory("Content"),     DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public IEnumerable ItemsSource { get; set; }

So this cannot be serialized by XamlWriter.

So you will have to write your own serializer or use approach mentioned here

Raj Ranjhan
  • 3,869
  • 2
  • 19
  • 29
1

I found this class here in code project that helps you Serialize the ItemsControl Property binding:

using System;
using System.Linq;
using System.ComponentModel;

namespace GUIKonfigurator
{
    using System.Windows.Controls;

    public class ItemsControlTypeDescriptionProvider:TypeDescriptionProvider
    {
        private static readonly TypeDescriptionProvider defaultTypeProvider = TypeDescriptor.GetProvider(typeof(ItemsControl));

        public ItemsControlTypeDescriptionProvider(): base(defaultTypeProvider)
        {
        }

        public static void Register()
        {
            TypeDescriptor.AddProvider(new ItemsControlTypeDescriptionProvider(), typeof(ItemsControl));
        }

        public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType,object instance)
        {
            ICustomTypeDescriptor defaultDescriptor = base.GetTypeDescriptor(objectType, instance);
            return instance == null ? defaultDescriptor: new ItemsControlCustomTypeDescriptor(defaultDescriptor);
        }
    }

    internal class ItemsControlCustomTypeDescriptor: CustomTypeDescriptor
    {
        public ItemsControlCustomTypeDescriptor(ICustomTypeDescriptor parent): base(parent)
        {
        }

        public override PropertyDescriptorCollection GetProperties()
        {
            PropertyDescriptorCollection pdc = new PropertyDescriptorCollection(base.GetProperties().Cast<PropertyDescriptor>().ToArray());
            return ConvertPropertys(pdc);
        }

        public override PropertyDescriptorCollection GetProperties(Attribute[] attributes)
        {
            PropertyDescriptorCollection pdc = new PropertyDescriptorCollection(base.GetProperties(attributes).Cast<PropertyDescriptor>().ToArray());
            return ConvertPropertys(pdc);
        }

        private PropertyDescriptorCollection ConvertPropertys(PropertyDescriptorCollection pdc)
        {
            PropertyDescriptor pd = pdc.Find("ItemsSource", false);
            if (pd != null)
            {
                PropertyDescriptor pdNew = TypeDescriptor.CreateProperty(typeof(ItemsControl), pd, new Attribute[]
                                                                                                       {
                                                                                                           new DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Visible),
                                                                                                           new DefaultValueAttribute("")
                                                                                                       });
                pdc.Add(pdNew);
                pdc.Remove(pd);
            }
            return pdc;
        }
    }
}

You just need to register it like this after registering the BindingConvertor:

EditorHelper.Register<BindingExpression, BindingConvertor>();
ItemsControlTypeDescriptionProvider.Register();

Here a quick test I did, creating a ComboBox and Serializing it:

ComboBox cb = new ComboBox();
cb.Width = 100;
cb.Height = 20;
Binding b = new Binding("Model.Activity");
b.Source = this.DataContext;
cb.SetBinding(ComboBox.ItemsSourceProperty, b);
string xaml = _Serializer.SerializeControlToXaml(cb);

And here the resulting Xaml Including the ItemsSource binding:

<ComboBox Width="100" Height="20" ItemsSource="{Binding Path=Model.Activity}" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" />

Hope this helps, I still need to take some time to understand it but so far seems to be working...

Adolfo Perez
  • 2,834
  • 4
  • 41
  • 61