XamlWriter失去绑定 - 好吧!但如何保持价值? (ItemsControl的)

时间:2010-07-12 14:29:01

标签: c# binding dependency-properties itemscontrol xamlreader

我知道标准XamlWriter不会持久绑定。但真正令人气馁的是绑定所持有的当前值也没有被序列化。

我当前的 - 非常愚蠢 - 解决方法是创建一个DependencyProperty fooProperty和属性foo。还有一个属性foo2。 foo的Change-eventhandler然后将其值写入foo2。

你知道:愚蠢。

有没有人有更好的解决方案?

欢呼声

- 编辑以响应托马斯 -

基本上你是对的。但是当使用ItemsControl时,它看起来有点不同:

<Window x:Class="TestApp.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:TestApp"
    Title="MainWindow" Height="350" Width="525"
    x:Name="window"
    >
<StackPanel>
    <StackPanel.Resources>
        <x:Array x:Key="arr1" Type="{x:Type local:TestData}">
            <local:TestData Message="itemcontrol binding 1"/>
            <local:TestData Message="itemcontrol binding 2"/>
        </x:Array>
   </StackPanel.Resources>

    <local:Test Foo="hard coded"/>
    <local:Test Foo="{Binding Message, ElementName=window}"/>
    <ItemsControl ItemsSource="{StaticResource arr1}">
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <local:Test Foo="{Binding Path=Message }"/>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
</StackPanel>

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        Message = "direct binding";
    }

    public static DependencyProperty MessageProperty = DependencyProperty.Register("Message", typeof(string), typeof(MainWindow));
    public string Message
    {
        get { return (string)GetValue(MessageProperty); }
        set { SetValue(MessageProperty, value); }
    }
}

public class Test : TextBox
{
    public static DependencyProperty FooProperty = DependencyProperty.Register("Foo", typeof(string), typeof(Test), new PropertyMetadata(OnFooChanged));
    private static void OnFooChanged(DependencyObject d, DependencyPropertyChangedEventArgs a)
    {
        (d as Test).Text = a.NewValue as String;
    }
    public string Foo
    {
        get { return (string)GetValue(FooProperty); }
        set { SetValue(FooProperty, value); }
    }

    protected override void OnMouseEnter(MouseEventArgs e)
    {
        Debug.Print("foo is really: " + Foo);
        Debug.Print(XamlWriter.Save(this));
    }
}

public class TestData : DependencyObject
{
    public static DependencyProperty MessageProperty = DependencyProperty.Register("Message", typeof(string), typeof(TestData));
    public string Message
    {
        get { return (string)GetValue(MessageProperty); }
        set { SetValue(MessageProperty, value); }
    }
}

如果您运行此测试应用程序并将光标移动到不同的TextBox上,您将看到序列化硬编码和直接绑定值没有问题。 相反,数据模板绑定不会被序列化。

顺便说一句,使用代码生成的ItemsSource而不是StaticReference没有区别,只是测试了..

2 个答案:

答案 0 :(得分:0)

值{em> 由XamlWriter保存。见本页:

Serialization Limitations of XamlWriter.Save

  

序列化过程将取消引用各种标记扩展格式(如StaticResource或Binding)对对象的公共引用。在应用程序运行时创建内存中对象时,这些已被解除引用,并且Save逻辑不会重新访问原始XAML以恢复对序列化输出的此类引用。 这可能会将任何数据绑定或资源获取的值冻结为运行时表示最后使用的值,只有有限或间接的能力将此值与本地任何其他值集区分开。图像也被序列化为项目中存在的图像的对象引用,而不是原始的源引用,丢失了最初引用的文件名或URI。甚至在同一页面中声明的资源也被序列化为引用它们的点,而不是保存为资源集合的键。

我刚做了一个简单的测试,对我来说效果很好:

public class Test : DependencyObject
{
    public static DependencyProperty FooProperty = DependencyProperty.Register("Foo", typeof(string), typeof(Test));

    public string Foo
    {
        get { return (string)GetValue(FooProperty); }
        set { SetValue(FooProperty, value); }
    }

    public static DependencyProperty BarProperty = DependencyProperty.Register("Bar", typeof(int), typeof(Test));

    public int Bar
    {
        get { return (int)GetValue(BarProperty); }
        set { SetValue(BarProperty, value); }
    }
}

public partial class App : Application
{
    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);

        Message = "Hello";
        Answer = 42;

        var t = new Test();

        Binding fooBinding = new Binding("Message") { Source = App.Current };
        BindingOperations.SetBinding(t, Test.FooProperty, fooBinding);

        Binding barBinding = new Binding("Answer") { Source = App.Current };
        BindingOperations.SetBinding(t, Test.BarProperty, barBinding);

        var s = XamlWriter.Save(t);
        Debug.Print(s);

    }

    public string Message { get; set; }
    public int Answer { get; set; }
}

该程序在调试输出中打印以下XAML:

<Test Foo="Hello" Bar="42" xmlns="clr-namespace:WpfApplication1;assembly=WpfApplication1" />

所以必须有另一个原因导致它不能用于您的情况......您确定没有任何绑定错误吗?在序列化之前,对象是否具有预期值?

答案 1 :(得分:0)

我见过同样的事情,在ItemTemplate中绑定的相同情况:

<ListBox.ItemTemplate>
    <DataTemplate>
        <TextBlock Text="{Binding}"/>
    </DataTemplate>
</ListBox.ItemTemplate>

我能做的最好的是以下解决方法:

private static UIElement ClonedElement(UIElement original)
{
    var clone = (UIElement)XamlReader.Parse(XamlWriter.Save(original));

    if (original is TextBlock)
        //Handles situation where databinding doesn't clone correctly.
        ((TextBlock)clone).Text = ((TextBlock)original).Text;
    return clone;
}

它很漂亮,但它确实有效。 (并且可以很容易地修改你失去的任何财产。)