XamlWriter不会序列化代码隐藏生成的控件的绑定

时间:2016-03-30 07:38:34

标签: c# wpf

我们正在尝试使用VS 2013和WPF编写WPF窗口设计器应用程序。 在此应用程序中,控件在运行时创建并放置在Canvas上;包括属性和绑定的设置。 在完成这样一个动态WPF窗口之后,我们希望将Canvas及其子控件序列化为XML文件。 为此,我们正在使用XamlWriter:

public string SerializeControlToXaml(FrameworkElement control)
{
    StringBuilder outstr = new StringBuilder();

    XmlWriterSettings settings = new XmlWriterSettings();
    settings.Indent = true;
    settings.OmitXmlDeclaration = true;
    XamlDesignerSerializationManager dsm =
        new XamlDesignerSerializationManager(XmlWriter.Create(outstr, settings));
    dsm.XamlWriterMode = XamlWriterMode.Expression;
    System.Windows.Markup.XamlWriter.Save(control, dsm);

    string xaml = outstr.ToString();
    return xaml;
}

“control”参数在我们的例子中包含Canvas面板,它是所有代码隐藏创建的控件的父控件。 其中我们正在创建TextBoxes,它们绑定到SelectedItem和DataGrid的列。

private void CreateTextboxes()
{
    CreateTextbox("firstname", _datagridname, "SelectedItem.vorname", 220, 10);
    CreateTextbox("familyname", _datagridname, "SelectedItem.nachname", 220, 40);
}

private void CreateTextbox(string name, string sourceName, string path, double leftPos, double topPos)
{
    TextBox tb = new TextBox();
    tb.SetValue(Canvas.LeftProperty, leftPos);
    tb.SetValue(Canvas.TopProperty, topPos);
    tb.Width = 150;
    tb.Name = name;

    // Binding to the selected item of the DataGrid.
    Binding tbbinding = new Binding();
    FrameworkElement sourceElement;
    ControlList.TryGetValue(sourceName, out sourceElement);
    if (sourceElement != null)
    {
        tbbinding.Source = sourceElement;
    }
    tbbinding.Path = new PropertyPath(path);
    tb.SetBinding(TextBox.TextProperty, tbbinding);

    _canvasPanel.Children.Add(tb);

    // The new TextBox is added to the Controllist.
    ControlList.Add(name, tb);
}

在我们的示例中,创建TextBox和设置其属性和绑定的方法被调用两次。 最后,我们在窗口中有两个TextBox,它们绑定到DataGrid列“firstname”和“familyname”。

但是当我们序列化父控件时,绑定不会被序列化。 我们得到的是:

<Canvas Background="#FFF0F8FF" Name="DropInCanvas" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:sd="clr-namespace:System.Data;assembly=System.Data" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  <DataGrid CanUserAddRows="False" AutoGenerateColumns="True" Name="datagrid1" Canvas.Left="20" Canvas.Top="10">
    <DataGrid.ItemBindingGroup>
      <BindingGroup Name="{x:Null}" NotifyOnValidationError="False" ValidatesOnNotifyDataError="True" SharesProposedValues="True" />
    </DataGrid.ItemBindingGroup>
    <sd:DataRowView />
    <sd:DataRowView />
    <sd:DataRowView />
    <sd:DataRowView />
  </DataGrid>
  <TextBox Name="firstname" Width="150" Canvas.Left="220" Canvas.Top="10" xml:space="preserve"></TextBox>
  <TextBox Name="familyname" Width="150" Canvas.Left="220" Canvas.Top="40" xml:space="preserve"></TextBox>
</Canvas>

有人知道为什么吗?

提前致谢!

帕特里克

1 个答案:

答案 0 :(得分:0)

要序列化BindingExpressions,我们需要一个转换器类。

/// <summary>
/// Class for conversion of binding-attributes while XML-serialization.
/// With this converter it is possible to serialize the control-bindings.
/// Works together with the class EditorHelper.
/// </summary>
public class BindingConvertor : ExpressionConverter
{
    /// <summary>
    /// Finds out if the converter can convert an expression-object to the given destinationtype.
    /// </summary>
    /// <param name="context">An ITypeDescriptorContext-interface which provides a context for formatting.</param>
    /// <param name="destinationType">A type-class which represents the target-type of the conversion.</param>
    /// <returns>Returns an object of type bool. True = the destinationtype can be converted.</returns>
    public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
    {
        if (destinationType == typeof(MarkupExtension))
            return true;

        return false;
    }

    /// <summary>
    /// Converts the expression to the given destinationtype.
    /// </summary>
    /// <param name="context">An ITypeDescriptorContext-interface which provides a context for formatting.</param>
    /// <param name="culture">The System.Globalization.CultureInfo which is actually used as culture.</param>
    /// <param name="value">The object to convert.</param>
    /// <param name="destinationType">A type-class which represents the target-type of the conversion.</param>
    /// <returns></returns>
    public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value,
        Type destinationType)
    {
        if (destinationType == typeof(MarkupExtension))
        {
            BindingExpression bindingExpression = value as BindingExpression;
            if (bindingExpression == null)
                throw new Exception();
            return bindingExpression.ParentBinding;
        }

        return base.ConvertTo(context, culture, value, destinationType);
    }
}

接下来,我们需要一个编辑器来注册哪个转换器负责哪种类型的表达式:

/// <summary>
/// Class for registering the class BindingConvertor as the type-converter for the type BindingExpression
/// With this converter it is possible to serialize the control-bindings.
/// </summary>
public class EditorHelper
{
    /// <summary>
    /// Registers which converter is responsible for which type.
    /// </summary>
    /// <typeparam name="T">The type.</typeparam>
    /// <typeparam name="TC">The converter.</typeparam>
    public static void Register<T, TC>()
    {
        Attribute[] attribute = new Attribute[1];
        TypeConverterAttribute typeConverterAttribute = new TypeConverterAttribute(typeof(TC));
        attribute[0] = typeConverterAttribute;
        TypeDescriptor.AddAttributes(typeof(T), attribute);
    }
}

要完成它,我们需要在MainWindow的代码中(或者需要它的地方)的某处实现以下代码行:

EditorHelper.Register<BindingExpression, BindingConvertor>();

就是这样!