Hello,
We have tab control with multiple tabitems, we allow users to customize the theme and have that set at runtime. However when we create our own styles for tabitems we want to them to based on the same theme as what the user has chosen.
On the tab control we set
Theme="{x:Static RiskManager:RiskManagerApplication.ThemeCurrentLiteral}"> is ThemeManager.ThemeCurrentLiteral which i something like ThemeManager.ThemeNameOffice2k7Blue;
on the tab item i have set BasedOn="{x:Static igThemes:PrimitivesOffice2k7Black.TabItemEx}"> but i want the "igThemes:PrimitivesOffice2k7Black" to be set to the current selected them through something like "{x:Static RiskManager:RiskManagerApplication.ThemeResourceSet.TabItemEx"
else if we dont set a basedon value it just default to the windows style and is not consitent with the rest of the UI.
Thanks Andrew.
Bigger code snipped below.....
<igWindows:XamTabControl Name="_tabControl" Grid.Row="1" SelectionChanged="_tabControl_SelectionChanged" TabLayoutStyle="MultiRowAutoSize"Theme="{x:Static RiskManager:RiskManagerApplication.ThemeCurrentLiteral}">
<igWindows:TabItemEx Header="{x:Static RMProperties:TabView.TabExchangeStatus}">
<igWindows:TabItemEx Name="_tabPositionByMarketByClient">
<igWindows:TabItemEx.Style>
<Style TargetType="igWindows:TabItemEx" BasedOn="{x:Static igThemes:PrimitivesOffice2k7Black.TabItemEx}">
<Setter Property="Header" Value="{x:Static RMProperties:TabView.TabPositionByMarketByClient}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Source={x:Static RiskManager:SettingsManager.Instance}, Path=System.IsExchangePostTradeMode, FallbackValue=False}" Value="True" >
<Setter Property="Header" Value="{x:Static RMProperties:TabView.TabPositionByMarketByTraderKey}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</igWindows:TabItemEx.Style>
<some control goes here"/>
</igWindows:TabItemEx>
There is no simple way to do that. The BasedOn property is not a DependencyProperty so you cannot bind it and x:Static is just going to get the value once so it's not going to update as you change the Theme property. Probably the best thing you could do is try to set the Style to a MultiBinding where one of the Bindings is bound to the Theme and one of the value is the Type for the BaseType that you want to get and another is the Style that you want to include. Then you'll need to create a custom IMultiValueConverter that will take those 3 things and do the actual work to build a new Style where the Setters are copied from the Style provided to the binding and the based on is set the Style you get for the theme. To get the Style for the Theme you might be able to have your multivalueconverter call the ThemeManager's static GetResourceSet passing in the Theme and the grouping (you could probably use "*" or in this case its just "Primitives"). If you get back a ResourceSet you would use that to get a Style for the Type provided via your binding. If the only thing you're using the Style for is to control the Header of a specific TabItem then you may be better off just binding the Header of the TabItem instead. Are you actually using the Style for other things?
Here's how one might create the converter:
public class ThemeStyleConverter : IValueConverter { public Style Style { get; set; } public Type ElementType { get; set; } object IValueConverter.Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { var strValue = value as string; if (strValue != null && this.ElementType != null) { var rs = ThemeManager.GetResourceSet(strValue, ThemeManager.AllGroupingsLiteral); if (rs != null) { var style = rs[this.ElementType] as Style; if (null != style) { // since we're binding if a Style wasn't provided we have to return the one // we find or the property will be set to null which is a local value and // will be the implicit style if (this.Style == null) return style; Debug.Assert(this.Style.BasedOn == null); var newStyle = new Style(this.ElementType); newStyle.BasedOn = style; foreach (var setter in this.Style.Setters) newStyle.Setters.Add(setter); foreach (var trigger in this.Style.Triggers) newStyle.Triggers.Add(trigger); // note it might be possible to just set the BasedOn but if the Style // was sealed then this won't be allowed so i'm just creating a new one return newStyle; } } } return this.Style; } object IValueConverter.ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { return DependencyProperty.UnsetValue; } }
And then one might use it like so:
<igWindows:XamTabControl Grid.Row="1" Theme="{Binding ElementName=cboTheme, Path=SelectedItem}"> <igWindows:XamTabControl.ItemContainerStyle> <Binding Path="Theme" RelativeSource="{RelativeSource Self}"> <Binding.Converter> <local:ThemeStyleConverter ElementType="{x:Type igWindows:TabItemEx}"> <local:ThemeStyleConverter.Style> <Style TargetType="igWindows:TabItemEx"> <Style.Triggers> <DataTrigger Binding="{Binding Path=Tag, RelativeSource={RelativeSource Self}}" Value="{x:Null}" > <Setter Property="FontStyle" Value="Italic"/> <Setter Property="FontFamily" Value="Arial" /> </DataTrigger> </Style.Triggers> </Style> </local:ThemeStyleConverter.Style> </local:ThemeStyleConverter> </Binding.Converter> </Binding> </igWindows:XamTabControl.ItemContainerStyle> <igWindows:TabItemEx Header="Tab 1"/> <igWindows:TabItemEx Header="Tab 2" /> <igWindows:TabItemEx Header="Tab 3" /> </igWindows:XamTabControl>
So basically in this case I'm just binding to the Theme of the XamTabControl instance. The converter for the binding takes the Style that needs to be merged with the Theme's Style and the ElementType defines the Type for which the element should be obtained. One might be able to remove the latter assuming one could rely on the TargetType of the Style that is provided to the converter being set but I prefer to explicitly provide it.
Thanks for trying but as soon as we then set a style on the each of the tab items localy to set the header property then it no longer works.
Andrew.
I never said that would allow you to set the Style on the TabItem. That would mean the ItemContainerStyle would not be used. There is only one local Style for an element. The binding with the converter would still work. You would just need to do that on every tab. Personally I think a per element Style to set a Header is overkill when one can just bind the Header even using a multibinding if you need to obtain information from multiple properties.
Thanks for you help.