开发者

XamlWriter loses binding - ok! but how to keep the value? (ItemsControl)

i know the standard XamlWriter does not persist bindings. but what really stinks is that the current value the binding holds does not get serialized too.

my current - really stupid - workaround is to create a DependencyProperty fooProperty and the property foo. and also a property foo2. the Change-eventhandler of foo then writes 开发者_开发技巧its value into foo2.

you see: stupid.

does anyone have a better solution?

cheers

-- edit in response to thomas --

basically you're right. but when using a ItemsControl it looks a bit different:

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

if you run this test application and move the cursor over the different TextBoxes, you'll see that theres no problem serializing the hard coded and the directly bound values. the data templated binding on the contrary doesn't get serialized.

by the way there is no difference in using a ItemsSource generated by code instead of a StaticReference, just tested that..


The value is kept by XamlWriter. See this page :

Serialization Limitations of XamlWriter.Save

Common references to objects made by various markup extension formats, such as StaticResource or Binding, will be dereferenced by the serialization process. These were already dereferenced at the time that in-memory objects were created by the application runtime, and the Save logic does not revisit the original XAML to restore such references to the serialized output. This potentially freezes any databound or resource obtained value to be the value last used by the run-time representation, with only limited or indirect ability to distinguish such a value from any other value set locally. Images are also serialized as object references to images as they exist in the project, rather than as original source references, losing whatever filename or URI was originally referenced. Even resources declared within the same page are seen serialized into the point where they were referenced, rather than being preserved as a key of a resource collection.

I just did a simple test, and it works fine for me :

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

This program prints the following XAML in the debug output :

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

So there must be another reason why it's not working in your case... are you sure you don't have any binding errors ? Does the object have the expected values before you serialize it ?


I have seen the same thing, in the identical case of a binding inside an ItemTemplate:

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

The best I could do is the following work-around:

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

It ain't pretty, but it works. (And it can be easily modified for whatever property you are losing.)

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜