简体   繁体   中英

Windows Store Apps : Loading styles from a customcontrol class library via reflection, OnApplyTemplate is not called?

I am trying to load a custom control library via reflection in windows 8 Metro C# App, the library is loaded but the styles specified in generic.xaml are not loaded, eventually I tried to load the generic.xaml by making it as an Embedded resource and ,then extracted the Generic.xaml to a location and specified the location of it as uri of a ResourceDictionary object, but it throws an error

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

I cannot create a nuget package or extension SDK as unfortunately that is not my requirement, Below sample code I wrote to copy the generic.xaml and load it in a resource dictionary

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

The below code I wrote in the custom control library's constructor, so that the control template can be set without generic.xaml

Here since the attribute TargeType="local:CustomControl1" is not present the control gets loaded properly, here since I loaded the style in the constructor, the OnApplyTemplate gets called

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

but the problem is loading all the styles using XamlReader is not a good idea, unless we are out of Options. As there may be various dependent styles which too have to be loaded.

Check out how they do it in the WinRTXamlToolkit . They create the Generic.xaml for the project and include all control templates inside of separate styles in different ResourceDictionary s packaged next to the respective controls.

To put it more simply (for Templated controls, like you are using):

  • Make two files for each control, MyControl.cs and MyControl.xaml

  • In MyControl.cs in your MyControl constructor, set the StyleKey to be typeof(MyControl) (like you are doing currently).

  • Make sure there is a style for your control with TargetType set to the type of your control. Do the same thing for the ControlTemplate that you have as the Template property set in the Style .

  • In MyControl.xaml , make a ResourceDictionary that stores all of the necessary styles, templates, and resources.

  • In your Generic.xaml , create a MergedDictionaries tag under the root and create a ResourceDictionary for each control, setting the Source to the full path of MyControl.xaml

Set each of the .xaml files to be build type of Page with CustomTool set to MSBuild:Compile .

Hope this helps and Happy coding!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM