This is a migrated thread and some comments may be shown as answers.

Live Theme Swapping

4 Answers 142 Views
General Discussions
This is a migrated thread and some comments may be shown as answers.
Beijerinc
Top achievements
Rank 1
Beijerinc asked on 25 Nov 2011, 04:18 PM
Dear Telerik,

As of yet, I believe swapping Themes at runtime is only possible if all controls have a StyleManager.Theme property set. Setting an application-wide Theme is only possible when the application starts, before the InitializeComponents() method call.

I am currently fiddling around with Themes, trying to find the best way to setup a custom theme project. I believe I have found a way to set a Theme at runtime. Basically, the idea is to add a ResourceDictionary to the App.Resources in the App.xaml file. When the theme needs to be swapped, the Source property of the ResourceDictionary will be pointed to a different Generic.xaml file (which should reference all applicable .xaml files defining a theme). Silverlight automatically picks up on this change and applies the new theme.

There is, however, still one problem. I have created a small demo project to illustrate the problem and the setup I am working on (see below). The project contains an App class that swaps between Theme1 (blue text) and Theme2 (red text) every two seconds. Somehow, when the first swap is introduced, the style is not applied. You may notice the text on the Button changing from blue (Theme1) to black (no theme), while it should become red (Theme2). After this buggy behavior, every two seconds the theme is changed and properly applied. However, from this point on, the applied theme does not match the colors of the text (when Theme2 is applied, the text color is blue).

Since I do not know that much about ResourceDictionaries, styles and themes, I though I could ask you. Would you be able to find a fix for this behavior (even though it is not technically a Telerik issue)? If this is working, perhaps it can be applied to the Telerik theming system to provide developers with runtime application-wide theme swapping with only 1 line of code:
ApplicationResourceDictionary.Instance.Theme = new Theme2();

Coincidentally, any control that has a matching <Style> element in the theme project will be themed, not just RadControls.

E-mail me for the demo project (rbe@optimizers.nl), as I cannot upload it to the forum. Also, I cannot create a "Feature Request" in the issue tracker and I do not believe this post should be in there in the first place.

4 Answers, 1 is accepted

Sort by
0
Accepted
Pana
Telerik team
answered on 30 Nov 2011, 08:07 AM
Hallelujah.

I've been trying to force all our XAML teams to adopt that theming mechanism ever since Silverlight 3 introduced Implicit Styles but yet to no avail. You see it is a brutal breaking change they say although I am pretty sure we could keep both approaches working. The theming was developed during Silverlight 2 where a Style could be applied only once and no implicit Styles were available. I think we outlived its usefulness. Perhaps with the release of Silverlight 5 we will be able to transition to the approach you are pointing here.

In general if you create a project, reference the Controls and Input as well as the Office_Blue and Office_Silver you should be able to use the following xaml:

<UserControl x:Class="SilverlightThemingThroughImplicitStyles.MainPage"
        mc:Ignorable="d"
        d:DesignHeight="300" d:DesignWidth="400">
  
    <Grid x:Name="LayoutRoot" Background="White">
        <StackPanel>
            <Button x:Name="Black" Content="Office Black" Margin="2" Click="Black_Click" />
            <Button x:Name="Blue" Content="Office Blue" Margin="2" Click="Blue_Click" />
            <Button x:Name="Silver" Content="Office Silver" Margin="2" Click="Silver_Click" />
  
            <telerik:RadComboBox SelectedIndex="0">
                <telerik:RadComboBoxItem Content="Item 1" />
                <telerik:RadComboBoxItem Content="Item 2" />
                <telerik:RadComboBoxItem Content="Item 3" />
            </telerik:RadComboBox>
            <CheckBox Content="non telerik control" />
        </StackPanel>
    </Grid>
  
</UserControl>

With the code behind that will swap themes:

using System;
using System.Windows;
using System.Windows.Controls;
  
namespace SilverlightThemingThroughImplicitStyles
{
    public partial class MainPage : UserControl
    {
        public MainPage()
        {
            InitializeComponent();
        }
  
        private void Black_Click(object sender, RoutedEventArgs e)
        {
            Application.Current.Resources.MergedDictionaries.Clear();
            Application.Current.Resources.MergedDictionaries.Add(new ResourceDictionary() { Source = new Uri("/Telerik.Windows.Controls;component/themes/System.Windows.xaml", UriKind.RelativeOrAbsolute) });
        }
  
        private void Blue_Click(object sender, RoutedEventArgs e)
        {
            Application.Current.Resources.MergedDictionaries.Clear();
            Application.Current.Resources.MergedDictionaries.Add(new ResourceDictionary() { Source = new Uri("/Telerik.Windows.Themes.Office_Blue;component/themes/System.Windows.xaml", UriKind.RelativeOrAbsolute) });
            Application.Current.Resources.MergedDictionaries.Add(new ResourceDictionary() { Source = new Uri("/Telerik.Windows.Themes.Office_Blue;component/themes/Telerik.Windows.Controls.xaml", UriKind.RelativeOrAbsolute) });
            Application.Current.Resources.MergedDictionaries.Add(new ResourceDictionary() { Source = new Uri("/Telerik.Windows.Themes.Office_Blue;component/themes/Telerik.Windows.Controls.Input.xaml", UriKind.RelativeOrAbsolute) });
        }
  
        private void Silver_Click(object sender, RoutedEventArgs e)
        {
            Application.Current.Resources.MergedDictionaries.Clear();
            Application.Current.Resources.MergedDictionaries.Add(new ResourceDictionary() { Source = new Uri("/Telerik.Windows.Themes.Office_Silver;component/themes/System.Windows.xaml", UriKind.RelativeOrAbsolute) });
            Application.Current.Resources.MergedDictionaries.Add(new ResourceDictionary() { Source = new Uri("/Telerik.Windows.Themes.Office_Silver;component/themes/Telerik.Windows.Controls.xaml", UriKind.RelativeOrAbsolute) });
            Application.Current.Resources.MergedDictionaries.Add(new ResourceDictionary() { Source = new Uri("/Telerik.Windows.Themes.Office_Silver;component/themes/Telerik.Windows.Controls.Input.xaml", UriKind.RelativeOrAbsolute) });
        }
    }
}

And it works in Silverlight like a charm swapping themes at runtime. It even changes the theme of the CheckBox. It will not work so flawless in WPF since we have custom DefaultStyleKeys set based on the theme for each control but I think we could nail it down there too. Also if you want to use custom styles for your own UserControls you could pretty much merge your own dictionaries the same way.

Project included.

I have no idea what the problem in your demo is. I'd bet its something small like mismatched colors -placed Red in the Purple theme and vice versa?

Regards,
Pana
the Telerik team
Explore the entire Telerik portfolio by downloading the Ultimate Collection trial package. Get it now >>
0
Beijerinc
Top achievements
Rank 1
answered on 30 Nov 2011, 09:37 AM
Thank you for your enthousiastic response.

The problem I am having is probably the DispatcherTimer I am using to swap themes every 2 seconds (which I can imagine is a little too much). Luckally, this was just a test. I downloaded your project and it is working fine, so the concept works.

I will reference the Theme.Source property instead of every XAML by itself and I will build a shell around the resource dictionary to be able to use the Telerik Theme class better. Great example though.

Thanks for the Application.Current.Resources.MergedDictionaries, I did not know that was possible.

I hope you get through to your XAML teams. Having to write theming code in controls and having the theming only apply to Telerik controls is a real downside. Good luck.

Regards,
Beijerinc
0
Beijerinc
Top achievements
Rank 1
answered on 30 Nov 2011, 10:25 AM
Just a heads up, Pana:

You must clear the theming ResourceDictionary from the Application.Current.Resources.MergedDictionaries. Simply setting the Source property on an existing ResourceDictionary will not work, as you will get the strange behavior I described in my first post (it was not the DispatcherTimer after all).

By the way, it would be helpful to create a Generic.xaml file in all of the Telerik theming libraries that references all theming XAMLs. The Theme objects do reference this Generic.xaml, but the file is missing. Setting the Source property of a ResourceDictionary to the Theme.Soruce property will cause some kind of COM exception (wrapped in an Exception object).

Regards,
Beijerinc
0
Pana
Telerik team
answered on 30 Nov 2011, 01:23 PM
Hi,

I think you could keep collection of theme dictionaries you have added and remove them from the merged dictionaries without clearing all merged dictionaries.

We can not create Generic.xaml so freely since it is supposed to reference all available xaml in the theme. So if you have Controls and Input like in the attached project and that Generic.xaml references the styles for Navigation you will get some XAML errors since the styles from navigation will not work without the Navigation assembly added in your project. I believe there are not much applications which reference all possible Silverlight Telerik assemblies.

Kind regards,
Pana
the Telerik team
Explore the entire Telerik portfolio by downloading the Ultimate Collection trial package. Get it now >>
Tags
General Discussions
Asked by
Beijerinc
Top achievements
Rank 1
Answers by
Pana
Telerik team
Beijerinc
Top achievements
Rank 1
Share this question
or