通过互操作加载 WPF window 时标记扩展不起作用

Markup Extension not working when WPF window is loaded through interop

我正在从 VB6 应用程序打开 WPF windows 作为向 .NET 迁移过程的一部分。 windows 在 C# class 库中。我得到一个 XamlParseException(下面的堆栈跟踪),它仅在 XAML window 通过 COM 公开 class 加载时发生。 .NET 文件如下。

没有使用下面的 classes 从 .NET 控制台应用程序加载 window 时遇到任何问题。但是从控制台应用程序加载它的代码几乎相同,只是在控制台应用程序中调用了 dispatcher.run()。所以我不确定为什么互操作会改变这里的行为。

XamlParseException 表示 "The method or operation is not implemented"。堆栈跟踪位于此 post 的底部。它发生在 WindowA.xaml。我已经在它出现的那一行上面发表了评论。它发生在 <Rectangle Width="20" Height="20" Fill="{telerik:Windows8Resource ResourceKey=MainBrush}"/>

项目文件

WindowWrapper.cs - 通过 COM

将 window 公开给 VB6 应用程序
[ComVisible(true),
 ClassInterface(ClassInterfaceType.AutoDual)]
public class WindowWrapper
{
    //Note: This function is exposed to COM so that VB6 apps can call it.
    public void OpenWindow()
    {
        Dispatcher.CurrentDispatcher.Invoke(() =>
        {
            var window = new WindowA();
            window.ShowDialog();
        });

        //WPF will use the running Win32 dispatcher from VB6, so no need to call .Run() here
    }
}

WindowA.xaml.cs

public partial class WindowA : Window
{
    public WindowA()
    {
        // This is sort of a hacky way of getting a xaml dependency to load and
        // getting Visual Studio to track the dependencies across projects
        // So we don't have to manually copy or include them in some way.
        // This reference to Localization manager will cause the proper assembly to load.
        var twc = typeof(LocalizationManager); // Telerik.Windows.Controls.dll

        InitializeComponent();
    }
}

WindowA.xaml - Xaml 使用 COM Interop 时无法加载的文件

<Window x:Class="TelerikProject.WindowA"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation"
            mc:Ignorable="d"
            d:DesignHeight="300" d:DesignWidth="300">
    <Window.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <!-- Telerik Windows8 Theme files -->
                <ResourceDictionary Source="pack://application:,,,/TelerikProject;component/Themes/System.Windows.xaml"/>
                <ResourceDictionary Source="pack://application:,,,/TelerikProject;component/Themes/Telerik.Windows.Controls.xaml"/>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Window.Resources>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <telerik:RadButton Content="Test" Grid.Row="2" Width="75" Height="Auto" VerticalAlignment="Top" Margin="5"/>
        <Border BorderBrush="Black" BorderThickness="1" HorizontalAlignment="Center" VerticalAlignment="Bottom" Grid.Row="0" Margin="5">
            <StackPanel Orientation="Horizontal" HorizontalAlignment="Center">

                <!-- ERROR HAPPENS HERE. Probably isn't loading the MarkupExtension properly -->

                <Rectangle Width="20" Height="20" Fill="{telerik:Windows8Resource ResourceKey=MainBrush}"/>
                <Rectangle Width="20" Height="20" Fill="{telerik:Windows8Resource ResourceKey=BasicBrush}"/>
                <Rectangle Width="20" Height="20" Fill="{telerik:Windows8Resource ResourceKey=AccentBrush}"/>
                <Rectangle Width="20" Height="20" Fill="{telerik:Windows8Resource ResourceKey=StrongBrush}"/>
                <Rectangle Width="20" Height="20" Fill="{telerik:Windows8Resource ResourceKey=ValidationBrush}"/>
                <Rectangle Width="20" Height="20" Fill="{telerik:Windows8Resource ResourceKey=MarkerBrush}"/>
            </StackPanel>
        </Border>
    </Grid>
</Window>

XamlParseException

at System.Windows.Baml2006.Baml2006SchemaContext.ResolveBamlType(BamlType bamlType, Int16 typeId)
at System.Windows.Baml2006.Baml2006SchemaContext.GetXamlType(Int16 typeId)
at System.Windows.Baml2006.Baml2006SchemaContext.GetProperty(Int16 propertyId, XamlType parentType)
at System.Windows.Baml2006.Baml2006Reader.Process_PropertyWithConverter()
at System.Windows.Baml2006.Baml2006Reader.Process_OneBamlRecord()
at System.Windows.Baml2006.Baml2006Reader.Process_BamlRecords()
at System.Windows.Baml2006.Baml2006Reader.Read()
at System.Windows.Markup.WpfXamlLoader.TransformNodes(XamlReader xamlReader, XamlObjectWriter xamlWriter, Boolean onlyLoadOneNode, Boolean skipJournaledProperties, Boolean shouldPassLineNumberInfo, IXamlLineInfo xamlLineInfo, IXamlLineInfoConsumer xamlLineInfoConsumer, XamlContextStack\`1 stack, IStyleConnector styleConnector)
at System.Windows.Markup.WpfXamlLoader.Load(XamlReader xamlReader, IXamlObjectWriterFactory writerFactory, Boolean skipJournaledProperties, Object rootObject, XamlObjectWriterSettings settings, Uri baseUri)

at System.Windows.Markup.WpfXamlLoader.Load(XamlReader xamlReader, IXamlObjectWriterFactory writerFactory, Boolean skipJournaledProperties, Object rootObject, XamlObjectWriterSettings settings, Uri baseUri)
at System.Windows.Markup.WpfXamlLoader.LoadBaml(XamlReader xamlReader, Boolean skipJournaledProperties, Object rootObject, XamlAccessLevel accessLevel, Uri baseUri)
at System.Windows.Markup.XamlReader.LoadBaml(Stream stream, ParserContext parserContext, Object parent, Boolean closeStream)
at System.Windows.Application.LoadComponent(Object component, Uri resourceLocator)
at TelerikProject.WindowA.InitializeComponent() in <pathToProject>\TelerikProject\WindowA.xaml:line 1

更新

根据Rob Relyea的建议,我也试过在代码中调用这个。以下内容在两种情况下都可以正常工作。我希望我重新创建它是正确的。

var rect = new Rectangle();
rect.Width = 20;
rect.Height = 20;

var ext = new Windows8ResourceExtension();
ext.ResourceKey = (Windows8ResourceKey)(new Windows8ResourceKeyTypeConverter().ConvertFrom("AccentBrush"));
var resourceReferenceExpression = ext.ProvideValue(new MockServiceProvider(rect));
var type = resourceReferenceExpression.GetType();
var mi = type.GetMethod("GetValue", BindingFlags.NonPublic | BindingFlags.Instance);
rect.Fill = (Brush)mi.Invoke(resourceReferenceExpression, new object[] { rect, Shape.FillProperty });

并且 ProvideValue 需要一个 IServiceProvider 作为参数。我猜这会为它提供元素和它所应用的 属性,所以我嘲笑提供者提供我创建的矩形。 (这样对吗?)

public class MockServiceProvider : IServiceProvider
{
    private Rectangle _rect;

    public MockServiceProvider(Rectangle rect)
    {
        _rect = rect;
    }

    public object GetService(Type serviceType)
    {
        if(serviceType.Name == "IProvideValueTarget")
        {
            return new MockProviderTarget(_rect, Rectangle.FillProperty);
        }
        else
        {
            throw new NotImplementedException();
        }
    }
}

public class MockProviderTarget : IProvideValueTarget
{
    private object _targetObject;
    private object _targetProperty;

    public MockProviderTarget(object targetObject, object targetProperty)
    {
        _targetObject = targetObject;
        _targetProperty = targetProperty;
    }

    public object TargetObject {
        get
        {
            return _targetObject;
        }
    }

    public object TargetProperty {
        get
        {
            return _targetProperty;
        }
    }
}

尝试从中取出 xaml。构建代码以创建该标记扩展实例,然后调用 providevalue()。我的猜测是当您以相同方式启动应用程序时会失败吗?可能 telerik 的代码依赖于随您的两种启动技术而改变的东西。