Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to skin the StatusBar and the ToolBar using MahApps Metro #57

Closed
Camuvingian opened this issue Sep 1, 2014 · 36 comments
Closed

How to skin the StatusBar and the ToolBar using MahApps Metro #57

Camuvingian opened this issue Sep 1, 2014 · 36 comments
Labels
Milestone

Comments

@Camuvingian
Copy link

All, I am having difficulty attempting to style the ToolBar and StatusBar using MahApps metro. There is an example of how to do this for the main window, but I can seem to apply the same principles to skin the Tool/Status bars. Can anyone point me in the right direction here please?

@4ux-nbIx
Copy link
Contributor

4ux-nbIx commented Sep 2, 2014

Hi, try to remove the styles from src\Gemini\Modules\ToolBars\Resources\Styles.xaml and see what happens.

@Camuvingian
Copy link
Author

Hi @4ux-nbIx, thanks very much for your reply, it is most appreciated. I have also recently discovered your VS2013 theme for AvalonDock - great stuff! Thank you! I will try what you suggest after work tonight and get back to you...

@4ux-nbIx
Copy link
Contributor

4ux-nbIx commented Sep 2, 2014

You're welcome! Also, please note that AvalonDock VS2013 theme is unfinished. There's still a lot of work to do and I don't know when I'll be able to get back to it.

@Camuvingian
Copy link
Author

Oh, I downloaded the test application and the solution to have a look a minute ago and it looks good. Have you seen the theme used in Wild (https://github.com/chandramouleswaran/Wide/), it is very similar to your own (marked as a VS2012 theme which has light and dark versions) and could possibly be used to embellish the work you have already done?

@4ux-nbIx
Copy link
Contributor

4ux-nbIx commented Sep 2, 2014

Yes, I've seen Wide. I wanted to fork their AvalonDock theme repository at first, but after some source code browsing I've decided that building the theme from scratch would be much easier.

@Camuvingian
Copy link
Author

Oh, right. I would like to adopt your theme, is there anything I should be aware of before doing so? I would like to offer to do some work on it, but I am so busy I just don't think I would have time...

@4ux-nbIx
Copy link
Contributor

4ux-nbIx commented Sep 2, 2014

No, nothing to be aware of. The theme is based on standard AvalonDock's Metro theme, so it should work fine as is, except the fact that some UI elements styles weren't updated yet.

@Camuvingian
Copy link
Author

Brilliant. Thanks for your help.

@4ux-nbIx
Copy link
Contributor

4ux-nbIx commented Sep 2, 2014

No problem!

@Camuvingian
Copy link
Author

I have looked into this and merely removing the style causes more trouble than it is worth as there are binding in that view. Moreover, I want to also do this with the StatusBar which does not have a style to remove. Have you any other ideas or experience in doing this with MahApps? Thanks again...

@4ux-nbIx
Copy link
Contributor

4ux-nbIx commented Sep 3, 2014

Off course this kind of change causes trouble. Those styles were added on purpose, right? I just wanted to make sure they do not interfere with MahApps Metro styles. They shouldn't actually, because they have the BasedOn attribute defined. The problem is that WPF doesn't see MahApps Metro resources for some reason when it constructs Gemini UI elements. I tried to define Metro resources at the application level, at the Metro demo App.xaml, but it didn't work out. I also tried to play with ThemeInfo assembly attribute and it didn't help either. This problem is quite interesting and I wanted to play with Gemini theming myself, so I am going to investigate this.

@Camuvingian
Copy link
Author

Yeah, I have also tried to include the MahApps declarations in the App.xaml. I have also tried to put these as a resource dictionary in the MainWindow for Gemini itself to see if this would do it, but nothing. I had another play with this last night in more depth. On aspect of the solution that seems to shed some light on how to theme those controls is the the VS2010/Theme.xaml et al. which skins the StatusBar/MenuItems. In light of this I attempted to include the MahApps declarations in the Metro/Theme.xaml, but this too did not work. I would be very interested if you get this working or have any further ideas as I have spent ages on this and can't work this out!

@tgjones
Copy link
Owner

tgjones commented Sep 8, 2014

It's been a while since I worked on Gemini, so to help me ease back into it, please could you give me an idea of the kind of styles you want to apply? If you have a not-working-as-desired VS project you can send over, that would be ideal.

I remember I wrote a DynamicStyleExtension, used in StatusBarView.xaml, which handles the current theme being changed, but ended up rather more complicated than I would have liked. That may be affecting things, but then it's not used by the Toolbar module, so... maybe not.

@tgjones
Copy link
Owner

tgjones commented Sep 8, 2014

I've dug into this a bit more, and found a couple of interesting things that might point in the direction of a solution:

  1. In a vanilla MahApps Metro-styled window with a StatusBar, the StatusBar is not styled. I don't think MahApps Metro includes any styles for StatusBar's.
  2. I've used WPF Inspector to look at the different styling applied to (a) a vanilla MahApps Metro-styled window with a toolbar and (b) the toolbar in Gemini. In (a), the ToolBar gets both the default WPF ToolBar style, which is then overridden by MahApp's ToolBar styles. But the Gemini toolbar doesn't get any styles at all (!) so it seems to be somehow falling back to the default WPF style.

I think the root of the problem with ToolBar styling is that I'm using a custom ToolBarEx class, inherited from WPF's ToolBar class. It's really hard (i.e. I don't know how) to say "when rendering this ToolBarEx, use whatever ToolBar style you have available at the time". The only thing I can do is create a Style with the BasedOn property explicitly set to WPF's ToolBar style, but then it won't pick up the MahApps Metro style.

If I change ToolBarsViewModel.cs:46 to create a ToolBar instead of a ToolBarEx, then it picks up the Metro styling properly (and also breaks rendering of my custom view models, but that's expected).

I'll look into getting rid of my custom ToolBarEx class, since that seems to be causing the problem. And I'm not smart enough to know how to implicitly style it using the "current" ToolBar style.

@tgjones
Copy link
Owner

tgjones commented Sep 8, 2014

Actually, I think I've fixed it, although the solution seems a bit... unusual. When creating the ToolBarEx, I just grab the style with the key typeof(ToolBar), which, using WPF's style inheritance mechanism, gives me MahApp's style.

You just need to change ToolBarsViewModel.cs OnViewLoaded to this:

protected override void OnViewLoaded(object view)
{
    // TODO: Ideally, the ToolBarTray control would expose ToolBars
    // as a dependency property. We could use an attached property
    // to workaround this. But for now, toolbars need to be
    // created prior to the following code being run.
    var toolBarStyle = (Style) ((IToolBarsView) view).ToolBarTray.FindResource(typeof(ToolBar));
    foreach (var toolBar in Items)
        ((IToolBarsView) view).ToolBarTray.ToolBars.Add(new ToolBarEx
        {
            Style = toolBarStyle,
            ItemsSource = toolBar
        });

    base.OnViewLoaded(view);
}

@Camuvingian do you mind trying this, and letting me know if it works for you?

@Camuvingian
Copy link
Author

Hi @tgjones, thank you very much for your reply. I have tried this on Gemini and the code for the tool bar I am using in my own application and the ToolBar does not style what-so-ever. The Metro overrides are not getting applied; why this is working for you and not me is beyond me. Again, thanks very much for your time...

@4ux-nbIx
Copy link
Contributor

4ux-nbIx commented Sep 9, 2014

Hi @tgjones, @Camuvingian, I've bee investigating this today and here's what I've found so far:

  • Gemini sets it's theme resources a little too late. This happens when the ShellView has been loaded, so most of the UI have already been initialized and UI elements styles have been resolved to defaults.
  • Default styles for custom controls can be defined by overriding Style dependency property metadata in static constructor, like this:
static MenuEx()
{
    StyleProperty.OverrideMetadata(
        typeof(MenuEx), 
        new FrameworkPropertyMetadata(Application.Current.TryFindResource(typeof(Menu))));
}
  • Overriding MenuEx's DefaultStyleKeyProperty metadata to typeof(Menu) didn't help, although the style from MahApps.Metro resources is accessible. Looks like the part of WPF that resolves default styles doesn't see all application resources. Or it behaves as StaticResource markup extension which doesn't see all app resources.
  • Custom control styles (MenuItemEx styles for example) ignore theme styles, because StaticResource markup extension doesn't see themes resources. This can be solved by creating a markup extension that'll fetch resources from current app resource dictionary.
  • Anything other than that works just fine.

I believe all this (theme initialization time, default styles and custom markup extension) can be easily done with minimal consequences, so I'd vote for going forward with it.

@Camuvingian
Copy link
Author

Hi @4ux-nbIx, great stuff! I have checked this solution and it seems to work very well for the Menu and the ToolBar. However, having said that, it is not applicable to the StatusBar due to the use of Tim's DynamicStyleExtension class. The styles for this component seem to get set from the underlying target type (i.e. System.Windows.Components.Primative.StatusBar) but something else is going on which is preventing the underlying style being used. I will look into this tonight after work...

@tgjones
Copy link
Owner

tgjones commented Sep 9, 2014

Does MahApps have any StatusBar styles? If so, DynamicStyleExtension should, in theory, be picking them up. If not, that's a bug.

@tgjones
Copy link
Owner

tgjones commented Sep 9, 2014

@4ux-nbIx Where did you see the "markup extension that'll fetch resources from current app resource dictionary" being used? I've got something cobbled together in MenuItemEx, which uses DynamicStyleExtension.CloneStyle to clone the CheckableMenuItem / MenuItem styles, but with BasedOn set to frameworkElement.FindResource(typeof(System.Windows.Controls.MenuItem)). It works, but I'd still prefer to have something that responds correctly to theme changes, which this solution doesn't.

@Camuvingian
Copy link
Author

It is not at all clear how the MahApps StatusBar is getting styled. There is no reference to "StatusBar" anywhere in the MahApps source code. In light of this, I will need to find out a bit more about how this is done and get back to you... Your point about @4ux-nbIx's solution is a fair one.

@tgjones
Copy link
Owner

tgjones commented Sep 9, 2014

This seems promising as a way of having a dynamic-ish BasedOn:
http://stackoverflow.com/questions/9490264/dynamicresource-for-style-basedon

I'll try it now.

@tgjones
Copy link
Owner

tgjones commented Sep 9, 2014

It works! I've borrowed DynamicStyle from that SO answer, and then changed MenuItemEx.GetContainer to this:

internal static DependencyObject GetContainer(FrameworkElement frameworkElement, object item)
{
    if (item is MenuItemSeparator)
        return new Separator { Style = (Style)frameworkElement.FindResource(SeparatorStyleKey) };

    string styleKey = (item is CheckableMenuItem) ? "CheckableMenuItem" : "MenuItem";

    var result = new MenuItemEx();
    result.SetResourceReference(DynamicStyle.BaseStyleProperty, typeof(System.Windows.Controls.MenuItem));
    result.SetResourceReference(DynamicStyle.DerivedStyleProperty, styleKey);
    return result;
}

That should, at least in theory, allow the theme to be changed at runtime, and the new styles will be used by MenuItemEx.

@Camuvingian
Copy link
Author

When you say DynamicStyle do you mean the class named DynamicContainerStyle in the SO answer you referenced?

@tgjones
Copy link
Owner

tgjones commented Sep 9, 2014

I've checked in a fix for menu styles (7250316). Let me know if it works for you!

I'll look at toolbars now.

(@Camuvingian yes, sorry - I renamed it in my version because it's setting the style, not the container style.)

@Camuvingian
Copy link
Author

That's quality stuff. Cheers, I will implement and feedback accordingly... I just wish i could be of more help with this stuff, but I am fairly new to this and it is somewhat ahead of me.

@tgjones
Copy link
Owner

tgjones commented Sep 9, 2014

No problem, it's pretty confusing for me too :-)

WPF doesn't make it easy to have a style BasedOn a dynamic resource, and that makes sense, because Style objects are frozen for performance reasons. Still, it seems like a common enough scenario to have some kind of official guidance. In any case, hopefully these attached properties will work.

@Camuvingian
Copy link
Author

Works for me - brilliant!

@tgjones
Copy link
Owner

tgjones commented Sep 9, 2014

I've checked in a fix for ToolBar styles too.

Please could you attach a screenshot of what the StatusBar should look like? Because I've got a test app with MahApps Metro and a StatusBar, and it looks exactly like the default WPF styling to me?

@tgjones tgjones added the Bug label Sep 9, 2014
@tgjones tgjones added this to the 0.6.0 milestone Sep 9, 2014
@Camuvingian
Copy link
Author

Attached is a screenshot of another application I recently wrote using MahApps metro with a StatusBar.
metrothemewithstatusbar

@4ux-nbIx
Copy link
Contributor

4ux-nbIx commented Sep 9, 2014

@tgjones, when talking about the "markup extension that'll fetch resources from current app resource dictionary" I was referring to something similar to DynamicStyleExtension. Didn't know you already have it. Anyway, the SO solution is even better!

By the way, since we're talking about theming here, I wanted to share another idea. Looking at Gemini.Metro module I think that it should be a theme actually, i.e. it should be able to provide a resource dictionary with all it's nice stuff, like current Gemini themes, and it should also be able to hook into Caliburn.Micro WindowManager to provide MetroWindow instances instead of normal windows. So I suggest to make themes a little smarter, i.e. it should be some MEF exported interface with a name and may be some other useful properties and an ability to integrate into the app as deep as they need to. This way it'll be at least possible to gather all available themes and display a combo box in options dialog. What do you think.

@tgjones
Copy link
Owner

tgjones commented Sep 10, 2014

@Camuvingian Sorry for the silly question, but are you sure you don't have any custom styles on that StatusBar? Because like I said, I've got a newly-created WPF application, with MahApps Metro styles added, and a StatusBar, and it looks like this:

statusbar

@tgjones
Copy link
Owner

tgjones commented Sep 10, 2014

@4ux-nbIx I like that idea! I've created issue #60 for it.

@Camuvingian
Copy link
Author

@tgjones Hi Tim, thanks for your reply. You are right, I have styled it. Apologies... Issue now completely sorted. Thanks again for your time and help here, it is most appreciated.

@tgjones
Copy link
Owner

tgjones commented Sep 10, 2014

Ah, that explains it! You're welcome.

@tgjones tgjones closed this as completed Sep 10, 2014
@4ux-nbIx
Copy link
Contributor

@tgjones, awesome! Would be interesting to implement it, so I might get my hands on it some day.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants