我们正在尝试使用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>
有人知道为什么吗?
提前致谢!
帕特里克
答案 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>();
就是这样!