【问题标题】:Windows Store Apps : Loading styles from a customcontrol class library via reflection, OnApplyTemplate is not called?Windows 应用商店应用程序:通过反射从自定义控件类库加载样式,不调用 OnApplyTemplate?
【发布时间】:2014-02-05 17:06:36
【问题描述】:

我正在尝试通过 Windows 8 Metro C# App 中的反射加载自定义控件库,该库已加载但未加载 generic.xaml 中指定的样式,最终我尝试通过将其设置为来加载 generic.xaml一个嵌入式资源,然后将 Generic.xaml 提取到一个位置并将其位置指定为 ResourceDictionary 对象的 uri,但它会引发错误

"Failed to create a 'System.Type' from the text local:CustomControl1"

我无法创建 nuget 包或扩展 SDK,因为这不是我的要求, 下面是我编写的示例代码,用于复制 generic.xaml 并将其加载到资源字典中

public sealed class CustomControl1 : Control
{
    public CustomControl1()
    {
        this.DefaultStyleKey = typeof(CustomControl1);
        Assembly CurrentAssembly = typeof(CustomControl1).GetTypeInfo().Assembly;
        var names = CurrentAssembly.GetManifestResourceNames();
        var stream = CurrentAssembly.GetManifestResourceStream(names.First());
        //generic.xaml is an embedded resource in the current assembly
        if (stream != null)
        {
            //created new generic.xaml here
            var file = ApplicationData.Current.LocalFolder.CreateFileAsync("Generic.xaml", CreationCollisionOption.ReplaceExisting).Completed = (o, a) =>
            {
                var storageFile = o.GetResults();

                var s = storageFile.OpenStreamForWriteAsync().Result;

                var length = (int)stream.Length;
                byte[] bytes = new byte[length];
                int output = stream.Read(bytes, 0, length);

                s.Write(bytes, 0, length);
                s.Flush();
                s.Dispose();

                var asyncResult = this.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
                {
                    var resourceDict = new ResourceDictionary();
                    var uri = new Uri("ms-appdata:///local/" + storageFile.Name);
                    resourceDict.Source = uri;
                });
            };
        }
    }


    // OnApplyTemplate is not called without loading the style from generic.xaml
    protected override void OnApplyTemplate()
    {
        base.OnApplyTemplate();
    }
}

下面我在自定义控件库的构造函数中写的代码,这样就可以不用generic.xaml来设置控件模板了

由于属性 TargetType="local:CustomControl1" 不存在,因此控件被正确加载,由于我在构造函数中加载了样式,因此调用 OnApplyTemplate

StringBuilder sb = new StringBuilder();
sb.Append(@"<ControlTemplate  
xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation""   
xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml""  
xmlns:d=""http://schemas.microsoft.com/expression/blend/2008""
xmlns:mc=""http://schemas.openxmlformats.org/markup-compatibility/2006"">");
sb.Append(@"<Border Background=""{TemplateBinding 
Background}""                                                             
BorderBrush=""{TemplateBinding BorderBrush}""
BorderThickness=""{TemplateBinding BorderThickness}"">
   <Grid>
      <Button x:Name=""Tbx1"" Content=""Hello World"" Foreground=""HotPink""   
       HorizontalAlignment=""Stretch"" VerticalAlignment=""Stretch""/>
  </Grid>
</Border>");
sb.Append(@"</ControlTemplate>");
this.Template = (ControlTemplate)XamlReader.Load(sb.ToString());

但问题是使用 XamlReader 加载所有样式并不是一个好主意,除非我们没有选项。因为可能需要加载各种依赖样式。

【问题讨论】:

  • 我注意到您正在创建一个ResourceDictionary,但似乎并没有在任何地方使用它。在我看来,你在哪里加载样式(并得到错误),你没有正确定义 local 命名空间。我仍然不确定为什么你需要像这样加载控件。这样做是您要求的一部分吗?或者您可以更改为与我发布的答案更相似的内容吗?
  • 嗨内特,感谢您的回复,
  • 我的资源字典的一部分如下所示
  • 如您所见,我的命名空间已正确定义 schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:MyControlsNameSpace">
  • 在 Generic.xaml 中,按照您的建议放置 xaml 以合并字典

标签: c# windows-store-apps winrt-xaml xamlparseexception


【解决方案1】:

WinRTXamlToolkit 中查看他们是如何做到的。他们为项目创建Generic.xaml,并将所有控件模板包含在单独样式中的不同ResourceDictionarys 中,封装在各个控件旁边。

更简单地说(对于模板控件,就像您正在使用的那样):

  • 为每个控件制作两个文件,MyControl.csMyControl.xaml

  • MyControl.csMyControl 构造函数中,将StyleKey 设置为typeof(MyControl)(就像您当前所做的那样)。

  • 确保您的控件有一个样式,其中TargetType 设置为您的控件类型。对ControlTemplate 执行与Style 中设置的Template 属性相同的操作。

  • MyControl.xaml 中,创建一个ResourceDictionary 来存储所有必要的样式、模板和资源。

  • 在你的Generic.xaml中,在根目录下创建MergedDictionaries标签并为每个控件创建ResourceDictionary,将Source设置为MyControl.xaml的完整路径

将每个 .xaml 文件设置为 Page 的构建类型,并将 CustomTool 设置为 MSBuild:Compile

希望这对您有所帮助,祝您编码愉快!

【讨论】:

  • Assembly assem = Assembly.Load(new AssemblyName("MyControlAssembly"));类型 CType = assem.ExportedTypes.FirstOrDefault(p => p.Name == "MyControl");对象 ob = Activator.CreateInstance(CType); //这里t grid是当前项目的MainPage.xaml中的一个grid对象 tgrid.Children.Add(ob as FrameworkElement);
  • 它仍然给出相同的异常“无法从文本 local:MyControl 创建 'System.Type'”
  • winrttoolkit 和我的代码的唯一区别是,我在我的项目中通过反射加载控件程序集,如上面的代码所示。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-03-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-11-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多