XamlWriter.Save 丢失 ListBox 的 ItemsSource 绑定

3
我有一个自定义的 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; }
}

以下样式应用了模板:
<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>

如果我使用XamlWriter保存这个样式,然后再次读取它,ItemsSource绑定就会丢失,但Label上的Content绑定不会丢失。
Style style = Application.Current.TryFindResource("DefaultStyle") as Style;

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

有没有办法确保ItemsSource绑定被正确序列化或轻松地添加回来?

当尝试从另一个项目的ResourceDictionary获取样式时,也会出现此问题,例如:

ResourceDictionary styles = new ResourceDictionary();
styles.Source = new Uri(String.Format("pack://application:,,,/StyleCopyTest;component/Styles/{0}Styles.xaml", type));
return styles;

你尝试过在https://dev59.com/jHVD5IYBdhLWcg3wRpeX中提供的解决方案吗? - Raj Ranjhan
我已经尝试过,但并没有解决问题。.Net 4.0解决方案无法保留ItemsSource绑定,更糟糕的是,在ListBox.ItemTemplate中的Label上失去了Content绑定。创建ExpressionConverter可以修复Content绑定,但仍无法保存ItemsSource绑定。 - Andrew Jones
2个回答

2
在WPF源代码中,ItemsSource被定义为:
[Bindable(true), CustomCategory("Content"),     DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public IEnumerable ItemsSource { get; set; }

因此,这不能被XamlWriter序列化。

所以你需要编写自己的序列化程序或使用这里提到的方法。


1

我在Code Project上找到了这个类(此处链接),它可以帮助您序列化ItemsControl属性绑定:

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;
        }
    }
}

在注册BindingConvertor之后,您只需要像这样进行注册:

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

这是我做的一个快速测试,创建了一个ComboBox并对其进行序列化:

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);

这里是生成的 Xaml,包括 ItemsSource 绑定:

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

希望这有所帮助,我还需要一些时间来理解它,但目前看起来似乎是有效的...

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接