【问题标题】:Bindings of code-behind-generated controls are not serialized by XamlWriterXamlWriter 不序列化代码隐藏生成的控件的绑定
【发布时间】:2016-03-30 07:38:34
【问题描述】:

我们正在尝试使用 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 面板,它是所有通过代码隐藏创建的控件的父控件。 我们正在创建绑定到 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>

有人知道为什么吗?

提前致谢!

帕特里克

【问题讨论】:

    标签: c# wpf


    【解决方案1】:

    要序列化 ​​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>();
    

    就是这样!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-03-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-10-21
      • 1970-01-01
      相关资源
      最近更新 更多