仅在设计时使用 MultiBindingConverter (Visual Studio 2015) 获取 XamlObjectWriterException [英] Getting an XamlObjectWriterException only at design time with MultiBindingConverter (Visual Studio 2015)

查看:31
本文介绍了仅在设计时使用 MultiBindingConverter (Visual Studio 2015) 获取 XamlObjectWriterException的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已将原因缩小为 MultiBindingConverter,我用它来计算转换中使用的角度.

我已将其精简到尽可能少的程度.这是我的列表框的代码:

<x:Array Type="{x:Type sys:Int32}" x:Key="卡片"><sys:Int32>2</sys:Int32><sys:Int32>4</sys:Int32><sys:Int32>3</sys:Int32><sys:Int32>5</sys:Int32><sys:Int32>1</sys:Int32></x:数组><local:FanPositionCalculator x:Key="FanPositionCalculator"/></Window.Resources><ListView ItemsSource="{StaticResource Cards}" Horizo​​ntalAlignment="Center" VerticalAlignment="Center" Margin="20" BorderBrush="Black" Padding="5,25,55,15"><ListView.ItemTemplate><数据模板><Grid RenderTransformOrigin="0.5,0.5"><Grid.RenderTransform><旋转变换><旋转变换.角度><MultiBinding Converter="{StaticResource FanPositionCalculator}"><Binding Source="{StaticResource Cards}"/><绑定路径=""/></多重绑定></RotateTransform.Angle></RotateTransform></Grid.RenderTransform><TextBlock Text="测试"/></网格></数据模板></ListView.ItemTemplate></ListView>

多重绑定根据对其索引的计算来扇出内容.为此,我使用了多绑定转换器:

 public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture){int count = (values[0] as IList).Count;int itemIndex = (values[0] as IList).IndexOf(values[1]);double indexFromCenter = itemIndex - count/2;//乘以我们想要的每张卡片的度数返回 indexFromCenter * 3;}

传递的第一个值是对象列表.第二个是有问题的对象.它计算旋转它的角度.

当我运行它时它工作正常:

然而,它在设计时扼杀了整个设计师:

错误是 XamlObjectWriterException:

集合属性 'System.Windows.Data.Binding'.'Source' 为空.在 System.Xaml.XamlObjectWriter.WriteGetObject()在 System.Xaml.XamlWriter.WriteNode(XamlReader 阅读器)在 System.Windows.FrameworkTemplate.LoadTemplateXaml(XamlReader templateReader, XamlObjectWriter currentWriter)

我不知该如何解决.我的转换器中没有错误,但它的存在导致此错误发生在列表所在的窗口中(不是列表本身!).关于如何解决它的任何想法?我想知道是否可以在设计时跳过使用转换器.我很高兴显示未旋转的卡片...不过我对整个设计师的死感到不满意,所以我想解决它.我使用的是 Visual Studio 2015.

解决方案

我不知道如何单步执行 XamlLoader 代码来查看它,但是,根据我在

I've narrowed the cause as a MultiBindingConverter I'm using to calculate the an angle used in a transform.

I've stripped it down to as minimal as I can get. Here's the code for my listbox:

<Window.Resources>
    <x:Array Type="{x:Type sys:Int32}" x:Key="Cards">
        <sys:Int32>2</sys:Int32>
        <sys:Int32>4</sys:Int32>
        <sys:Int32>3</sys:Int32>
        <sys:Int32>5</sys:Int32>
        <sys:Int32>1</sys:Int32>
    </x:Array>
    <local:FanPositionCalculator x:Key="FanPositionCalculator"/>
</Window.Resources>
<ListView ItemsSource="{StaticResource Cards}" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="20" BorderBrush="Black" Padding="5,25,55,15">
    <ListView.ItemTemplate>
        <DataTemplate>
            <Grid RenderTransformOrigin="0.5,0.5">
                <Grid.RenderTransform>
                    <RotateTransform>
                        <RotateTransform.Angle>
                            <MultiBinding Converter="{StaticResource FanPositionCalculator}">
                                <Binding Source="{StaticResource Cards}"/>
                                <Binding Path=""/>
                            </MultiBinding>
                        </RotateTransform.Angle>
                    </RotateTransform>
                </Grid.RenderTransform>
                <TextBlock Text="Test"/>
            </Grid>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

The multibinding fans out the contents based on a calculation of it's index. To do this I used a multi-binding converter:

    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        int count = (values[0] as IList).Count;
        int itemIndex = (values[0] as IList).IndexOf(values[1]);

        double indexFromCenter = itemIndex - count / 2;

        //multiply by the degrees we want for each card
        return indexFromCenter * 3;
    }

The first value passed is the list of objects. The second is the object in question. It calculates the angle to rotate it.

It works fine when I run it:

However, it kills the whole designer at design time:

The error is an XamlObjectWriterException:

Collection property 'System.Windows.Data.Binding'.'Source' is null.

at System.Xaml.XamlObjectWriter.WriteGetObject()
at System.Xaml.XamlWriter.WriteNode(XamlReader reader)
at System.Windows.FrameworkTemplate.LoadTemplateXaml(XamlReader templateReader, XamlObjectWriter currentWriter)

Which I'm at a loss how to fix. There's no errors in my converter, but it's presence causes this error to occur in the Window the list is in (not the list itself!). Any ideas on how to fix it? I was wondering if I could just skip using the converter during design-time. I'm happy with un-rotated cards being displayed... I'm not happy with the whole designer dying though, so I'd like to solve it. I'm using Visual Studio 2015.

解决方案

I don't know how to step through the XamlLoader code to see it but, based on what I observed in this question, I think there is a bug in it.
As it parses the xaml, the XamlLoader recurses on the elements, maintaining a context stack as it goes. When it recurses into the Resources element, it establishes the value for the Cards array by reading the xaml and setting a value in that context, either immediately or as a deferred value. It then pops the stack and continues parsing. When it recurses on the ListView, it creates a new context but that context does not seem to receive the value for the static resource. Meanwhile, the XamlLoader assumes that it has and writes the unset (null) value to the receiving property (in your case, the Binding Source) and this causes the error.

In other words, WPF seems (to me anyway) to be confused about values set in one parsing context and consumed in another. The workaround is to minimise the different contexts. In your case, this means setting the Resources on the ListView instead of the Window. The ItemsSource needs to be set using Object Element Syntax, which you can then reference using a RelativeSource. I like to instantiate the RelativeSource in the element resources as well because I think it's cleaner.

<ListView HorizontalAlignment="Center" VerticalAlignment="Center" Margin="20" BorderBrush="Black" Padding="5,25,55,15">
    <ListView.Resources >
        <RelativeSource x:Key="List" Mode="FindAncestor" 
                    AncestorType="{x:Type ListView}" />
        <local:FanPositionCalculator x:Key="FanPositionCalculator"/>
    </ListView.Resources>
    <ListView.ItemsSource>
        <x:Array Type="{x:Type system:Int32}" x:Name="Cards">
            <system:Int32>2</system:Int32>
            <system:Int32>4</system:Int32>
            <system:Int32>3</system:Int32>
            <system:Int32>5</system:Int32>
            <system:Int32>1</system:Int32>
        </x:Array>
    </ListView.ItemsSource>
    <ListView.ItemTemplate>
        <DataTemplate>
            <Grid RenderTransformOrigin="0.5,0.5">
                <Grid.RenderTransform>
                    <RotateTransform>
                        <RotateTransform.Angle>
                            <MultiBinding Converter="{StaticResource FanPositionCalculator}">
                                <Binding RelativeSource="{StaticResource List}" Path="ItemsSource"/>
                                <Binding Path=""/>
                            </MultiBinding>
                        </RotateTransform.Angle>
                    </RotateTransform>
                </Grid.RenderTransform>
                <TextBlock Text="Test"/>
            </Grid>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

I'm using VS 2017 RC and this fixes the design view for me and same result for VS 2013 so, should work for you also.

这篇关于仅在设计时使用 MultiBindingConverter (Visual Studio 2015) 获取 XamlObjectWriterException的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆