How can I base a style on a Silverlight toolkit theme? - silverlight

is it possible to do this?
In my xaml code, I have some ComboBoxes with a style, defined like this:
<Style x:Key="comboProjectsStyle"
TargetType="ComboBox">
<Setter Property="ItemTemplate">
<Setter.Value>
<DataTemplate>
<Grid>
<TextBlock Text="{Binding Path=Name}"
FontSize="14" />
</Grid>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="comboDataSourcesStyle"
TargetType="ComboBox">
<Setter Property="ItemTemplate">
<Setter.Value>
<DataTemplate>
<Grid>
<TextBlock Text="{Binding Path=DescriptiveName}"
FontSize="14" />
</Grid>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
<ComboBox Width="300"
Style="{StaticResource comboProjectsStyle}" />
<ComboBox Width="300"
Style="{StaticResource comboDataSourcesStyle}" />
The silverlight theme (eg: ExpressionDark) is correctly applied on every controls except on the ones where I have defined a style, like above.
As I understand, in WPF we could use x:Style an base our Style on the silverlight theme using the "BasedOn" property. However, it seems like it is not possible to do so with Silverlight 4.
Any ideas on how to approach this?
Thanks!

Declare your ItemTemplate as a resource rather than in a style then your theme style will apply.
<UserControl xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"
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"
x:Class="Silverlight_Spike.MainPage"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">
<UserControl.Resources>
<DataTemplate x:Key="DataTemplate1">
<Grid>
<TextBlock Text="{Binding Name}" FontSize="14" />
</Grid>
</DataTemplate>
</UserControl.Resources>
<ComboBox ItemTemplate="{StaticResource DataTemplate1}" />
</UserControl>

Remove the key in the style:
<Style TargetType="ComboBox">
<Setter Property="ItemTemplate">
<Setter.Value>
<DataTemplate>
<Grid>
<TextBlock Text="{Binding Path=Name}" FontSize="14" />
</Grid>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="comboProjectsStyle" TargetType="ComboBox">
<Setter Property="ItemTemplate">
<Setter.Value>
<DataTemplate>
<Grid>
<TextBlock Text="{Binding Path=Name}" FontSize="14" />
</Grid>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="comboDataSourcesStyle" TargetType="ComboBox">
<Setter Property="ItemTemplate">
<Setter.Value>
<DataTemplate>
<Grid>
<TextBlock Text="{Binding Path=DescriptiveName}" FontSize="14" />
</Grid>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
<ComboBox Width="300" Style="{StaticResource comboProjectsStyle}" />
<ComboBox Width="300" Style="{StaticResource comboDataSourcesStyle}" />
<ComboBox Width="300" />
<ComboBox Width="300" />
<ComboBox Width="300" />
Basically this means the style you made above applies to a target type of ComboBox, it is not named therefore every Combobox without a style set to it will inherit this as default.
UPDATE:
As you can see all the 3 styles can coexist in the same resource, whenever you use a named style it will be applied to the said control but, for the last three comboboxes, all 3 will be having the style without the key. This is how it is done in themming, like in the JetPack skin from MS.
hope this helps.

Hi this is not exactly using basedOn but by using a converter we can achieve the goal of basing the custom style to the theme. Here's how we do it.
Expose a Theme and place it where you can bind it to the style.
Create a value converter to convert the style that you are using. This converter will return a style that is based on the theme, so here's a snippet.
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (parameter is Style)
{
Style retStyle = parameter as Style;
Theme themeContainer;
if (value is Theme)
themeContainer = value as Theme; //(App.Current as App).AppTheme;
else
themeContainer = (App.Current as App).AppTheme;
if (themeContainer != null)
{
foreach (DictionaryEntry i in themeContainer.ThemeResources)
{
if (i.Value is Style)
{
Style t = i.Value as Style;
if (t.TargetType == retStyle.TargetType)
{
Style newStyle = new Style();
newStyle.TargetType = retStyle.TargetType;
newStyle.BasedOn = t;
foreach (Setter set in retStyle.Setters)
newStyle.Setters.Add(new Setter() { Property = set.Property, Value = set.Value });
return newStyle;
}
}
}
}
return retStyle;
}
return null;
}
Bind the theme to the style and use the converter on every custom style that you use
Style="{Binding Theme, Converter={StaticResource styleConverter}, ConverterParameter={StaticResource ButtonStyle1}}"
Where theme is a property of type Theme(System.Windows.Controls.Theming).
I uplaoded my sample project here
The sample code is not updated but you can start from there.

Related

WPF - ToolTip in TextBlock that use the Text Property from the TextBlock

We have many TextBlocks in our application and many of them must have ToolTips.
Often the ToolTip should display the same as the TextBlock.Text property.
Also the ToolTip should not be displayed if the TextBlock.Text = "" or null.
So we have this solution in many many places:
<TextBlock Text="{Binding SomeTextProperty}">
<TextBlock.ToolTip>
<ToolTip Visibility="{Binding SomeTextProperty}, Converter={StaticResource StringToVisibilityConverter}">
<TextBlock Text="{Binding SomeTextProperty}" />
</TextBlock.ToolTip>
</TextBlock>
Notice:
I have to specify the SomeTextProperty three times on each TextBlock that need a TooLTip with this functionality.This seems very redundant!
The ToolTip.Content is a TextBlock itself.This is because I need to have a Style on the TextBlock.I have omitted that style to keep this post as simple as possible.
So I have tried to invent a Style for TextBlocks that use Bindings with RelativeSource to get the TextBlock.Text property for the ToolTip. I came up with this solution:
MainWindow (Just copy-paste more or less)
<Window x:Class="Main.Views.ToolTips"
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"
xmlns:local="clr-namespace:Main.Views"
xmlns:converters="clr-namespace:Main.Converters"
mc:Ignorable="d"
Title="ToolTips" Height="450" Width="800">
<Window.Resources>
<Style TargetType="TextBlock" x:Key="TextBlockWithToolTipStyle">
<Style.Resources>
<converters:StringToVisibilityConverter x:Key="StringToVisibilityConverter" />
</Style.Resources>
<Setter Property="ToolTip">
<Setter.Value>
<ToolTip Visibility="{Binding Text, RelativeSource={RelativeSource AncestorType={x:Type TextBlock}}, Converter={StaticResource StringToVisibilityConverter}}">
<ToolTip.Content>
<TextBlock Text="{Binding RelativeSource={RelativeSource AncestorType={x:Type TextBlock}}}" />
</ToolTip.Content>
</ToolTip>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<StackPanel Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBlock Text="This should display a tooltip" />
<TextBlock Text="asdf"
Background="Gray"
Style="{StaticResource TextBlockWithToolTipStyle}" />
<Separator Height="50" Visibility="Hidden" />
<TextBlock Text="This should not display a tooltip" />
<TextBlock Text=""
Background="LightGray"
Style="{StaticResource TextBlockWithToolTipStyle}" />
</StackPanel>
</Window>
StringToVisibilityConverter
public class StringToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var input = value is null ? string.Empty : value.ToString();
return string.IsNullOrWhiteSpace(input) ? Visibility.Collapsed : Visibility.Visible;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
This, of course, don't work!
I put a breakpoint in the StringToVisibilityConverter and that breakpoint is not hit when I hover both TextBlocks in the Window.
In VS's XAML Binding Failures view I see two binding errors that regards ToolTip.Visibility and TextBlock.Text:
ToolTip.Visibility: Cannot find source: RelativeSource FindAncestor, AncestorType='System.Windows.Controls.TextBlock', AncestorLevel='1'
TextBlock.Text: Cannot find source: RelativeSource FindAncestor, AncestorType='System.Windows.Controls.TextBlock', AncestorLevel='1'
Is this possible to solve?
If so, how?
/BR,
Steffe
The ToolTip is not part of the visual tree. That's why your Binding.RelativeSource does not resolve.
To reference the element that is decorated by the ToolTip you must reference the ToolTip.PlacementTarget property:
<TextBlock Text="{Binding RelativeSource={RelativeSource AncestorType=ToolTip}, Path=PlacementTarget.Text}" />
Furthermore, you should not try to toggle the ToolTip.Visibility. It won't work.
Instead use a Trigger to set the ToolTip.
The correct solution would be as followed:
<Style TargetType="TextBlock"
x:Key="TextBlockWithToolTipStyle">
<Style.Resources>
<ToolTip x:Key="ToolTip">
<TextBlock Text="{Binding RelativeSource={RelativeSource AncestorType=ToolTip}, Path=PlacementTarget.Text}" />
</ToolTip>
</Style.Resources>
<Setter Property="ToolTip"
Value="{StaticResource ToolTip}" />
<Style.Triggers>
<Trigger Property="Text" Value="">
<Setter Property="ToolTip" Value="{x:Null}" />
</Trigger>
</Style.Triggers>
</Style>

Pass listbox selected item information through to usercontrol (WPF)

I have a listbox, and each listbox item is a custom usercontrol I made. I have used styles to remove all of the default highlighting for a listbox item (ie. removing the blue background highlight for a selected item).
What I want is to be able to do something special to my user control to denote that the listbox item is highlighted. Such as make the border on the user control more bold, something like that.
If I could get a boolean into the user control, I think from there I'd be able to figure out how to make the necessary changes to the user control... through a converter or something most likely.
What I'm not sure of, is how do I pass into the usercontrol the information that shows whether the listbox item which the user control is in is highlighted.
The code in question is like this:
<ListBox.ItemTemplate>
<DataTemplate>
<hei:OrangeUserCtrl DataContext="{Binding}" Height="40" Width="40" />
</DataTemplate>
</ListBox.ItemTemplate>
How can I pass in to the user control (preferably as a true/false) if the listbox item it is in is highlighted?
Thanks
You can use Tag property and RelativeSource binding.
In my example when item is highlighted I changed Border properties (BorderBrush=Red and BorderThickness=3).
Source code:
Simple class to hold data:
class Person
{
public string Name { get; set; }
public string Surname { get; set; }
}
ListBox:
<ListBox ItemsSource="{Binding}">
<ListBox.ItemTemplate>
<DataTemplate>
<local:MyCustomPresenter DataContext="{Binding}"
Tag="{Binding Path=IsSelected, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ListBoxItem}, UpdateSourceTrigger=PropertyChanged}"
Height="60" Width="120" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
UserControl to display custom data:
<UserControl x:Class="WpfTextWrapping.MyCustomPresenter"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Border Margin="10">
<Border.Style>
<Style TargetType="Border">
<Setter Property="BorderBrush" Value="Green" />
<Setter Property="BorderThickness" Value="1" />
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Tag, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=UserControl}, UpdateSourceTrigger=PropertyChanged}" Value="True">
<Setter Property="BorderBrush" Value="Red" />
<Setter Property="BorderThickness" Value="3" />
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding Name}" />
<TextBlock Text="{Binding Surname}" />
</StackPanel>
</Border>
</UserControl>
If I understand you well, you need to add a property to your custom UserControl bound to the nested ComboBox something like :
public object MySelectedItem
{
get { return myNestedCombox.SelectedItem; }
set { myNestedCombox.SelectedItem = value; }
}
You need to NotifyPropertyChanged as well.

Set a Usercontrol as a Datatemplate

I have the following ProductList template snipet
<Style x:Key="ProductListStyle" TargetType="{x:Type s:SurfaceListBox }">
<Setter Property="Background" Value="{DynamicResource {x:Static s:SurfaceColors.ListBoxItemBackgroundBrushKey}}" />
<Setter Property="SelectionMode" Value="Single" />
<Setter Property="Height" Value="234" />
<Setter Property="ItemTemplateSelector">
<Setter.Value>
<sc:ProductListTemplateSelector>
<sc:ProductListTemplateSelector.NormalItemTemplate>
<DataTemplate>
<StackPanel RenderTransformOrigin="0.5, 0.5"
Margin="7,0,0,0"
MinWidth="171" MaxWidth="171"
MinHeight="235" MaxHeight="235">
<Image Margin="14,21,21,11" Source="{Binding XPath=#Image}"
Height="149" Width="101" />
<TextBlock Text="{Binding XPath=#Name}"
MaxWidth="116"
FontSize="12"
Margin="21,0,21,21"
FontFamily="Segoe360"
TextAlignment="Center"
TextWrapping="Wrap"
Foreground="{DynamicResource {x:Static s:SurfaceColors.ListBoxItemForegroundBrushKey}}"
HorizontalAlignment="Center" />
</StackPanel>
</DataTemplate>
</sc:ProductListTemplateSelector.NormalItemTemplate>
I need to replace the DataTemplate of this style to be set using my user control like
<local:MyUserControl>
By keeping only between section I did not get my control displayed when my Itemsource is set with a collection of myUserControl
Usually I just add the DataTemplate in the Resources. This can be <Window.Resources> or <App.Resources> if the data template is global, or FrameworkElement.Resources if the template should only be applied in a specified scope. For example, adding the template to ListView.Resources would only apply the template within a specific ListView.
<Window.Resources>
<DataTemplate DataType="{x:Type local:ProductModel}">
<local:MyUserControl />
</DataTemplate>
</Window.Resources>
As a side note, your original question leads me to believe that you are binding a ListView to a collection of MyUserControl objects. I really wouldn't recommend this, but if this is the case you can use a ContentControl in your DataTemplate with it's Content bound to your object, and it should display correctly.
<ContentControl Content="{Binding }" />

UserControl in DataTemplate doesn't applied style for font

I have a user control in a DataTemplate, The Style of a TextBlock doesn't change the FontSize but changes the Background.
Attached are the samples:
Create a WPF window.
Create a User control, UserControl1
Inside the Window paste the below code:
<Window.Resources>
<Style TargetType="{x:Type TextBlock}"
x:Key="TextBlockStyleFontAndBackgound">
<Setter Property="FontSize"
Value="20" />
<Setter Property="Background"
Value="Blue" />
</Style>
<DataTemplate x:Key="contentTemplate">
<StackPanel>
<m:UserControl1 />
</StackPanel>
</DataTemplate>
</Window.Resources>
<Grid>
<ContentControl FontSize="10">
<StackPanel x:Name="stackPanel">
<Button Click="Button_Click" />
<ContentControl ContentTemplate="{StaticResource contentTemplate}" />
<!--<m:UserControl1 />-->
</StackPanel>
</ContentControl>
</Grid>
In the user control paste the following code:
<UserControl.Resources>
<DataTemplate x:Key="contentTemplateInsideUserControl">
<TextBlock Name="textBlockInResourse" Text="textBlockInsideUserControlResource"
Style="{DynamicResource TextBlockStyleFontAndBackgound}"/>
</DataTemplate>
</UserControl.Resources>
<Grid>
<StackPanel>
<ContentControl ContentTemplate="{StaticResource contentTemplateInsideUserControl}" />
<Button Content="St" Click="Button_Click" />
<TextBlock Name="textBlockInControl" Text="textBlockInsideUserControl"
Style="{DynamicResource TextBlockStyleFontAndBackgound}" />
</StackPanel>
</Grid>
We have 2 text blocks with the same background color, blue, but with different font sizes.
textBlockInResourse FontSize = 20, taken from the style TextBlockStyleFontAndBackgound
textBlockInControl FontSize = 10, inherited value, why does it happen?
I have added a handle in the user control:
private void Button_Click(object sender, RoutedEventArgs e)
{
Style style = FindResource("TextBlockStyleFontAndBackgound") as Style;
textBlockInControl.Style = null;
textBlockInControl.Style = style;
}
And now the Font is set to the style TextBlockStyleFontAndBackgound, and it's size is 20
Why now the FontSize is taken from the style TextBlockStyleFontAndBackgound.
Thanks,
barak
That's a very peculiar problem you have found there. I'm not sure why the FontSize is not affected when not in a DataTemplate... looking at the two property descriptions and remarks on MSDN, the only difference between them is that TextBlock.FontSize is also an AttachedProperty, but I can't see how that would affect anything.
I can however offer a solution to the problem if you're still interested. Try declaring your Style in your App.xaml file:
<Application.Resources>
<Style TargetType="{x:Type TextBlock}" x:Key="TextBlockStyleFontAndBackgound">
<Setter Property="FontSize" Value="20" />
<Setter Property="Background" Value="Blue" />
</Style>
</Application.Resources>
Then declare your TextBlock in your UserControl using StaticResource like so:
<TextBlock Text="text" Style="{StaticResource TextBlockStyleFontAndBackgound}" />

How can I use a custom TabItem control when databinding a TabControl in WPF?

I have a custom control that is derived from TabItem, and I want to databind that custom TabItem to a stock TabControl. I would rather avoid creating a new TabControl just for this rare case.
This is what I have and I'm not having any luck getting the correct control to be loaded. In this case I want to use my ClosableTabItem control instead of the stock TabItem control.
<TabControl x:Name="tabCases" IsSynchronizedWithCurrentItem="True"
Controls:ClosableTabItem.TabClose="TabClosed" >
<TabControl.ItemTemplate>
<DataTemplate DataType="{x:Type Controls:ClosableTabItem}" >
<TextBlock Text="{Binding Path=Id}" />
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate DataType="{x:Type Entities:Case}">
<CallLog:CaseReadOnlyDisplay DataContext="{Binding}" />
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
EDIT: This is what I ended up with, rather than trying to bind a custom control.
The "CloseCommand" im getting from a previous question.
<Style TargetType="{x:Type TabItem}" BasedOn="{StaticResource {x:Type TabItem}}" >
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabItem}">
<Border
Name="Border"
Background="LightGray"
BorderBrush="Black"
BorderThickness="1"
CornerRadius="25,0,0,0"
SnapsToDevicePixels="True">
<StackPanel Orientation="Horizontal">
<ContentPresenter x:Name="ContentSite"
VerticalAlignment="Center"
HorizontalAlignment="Center"
ContentSource="Header"
Margin="20,1,5,1"/>
<Button
Command="{Binding Path=CloseCommand}"
Cursor="Hand"
DockPanel.Dock="Right"
Focusable="False"
Margin="1,1,5,1"
Background="Transparent"
BorderThickness="0">
<Image Source="/Russound.Windows;component/Resources/Delete.png" Height="10" />
</Button>
</StackPanel>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="FontWeight" Value="Bold" />
<Setter TargetName="Border" Property="Background" Value="LightBlue" />
<Setter TargetName="Border" Property="BorderThickness" Value="1,1,1,0" />
<Setter TargetName="Border" Property="BorderBrush" Value="DarkBlue" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
found a way,
derive a class from TabControl and override this function, in my case I want the items of the tab control (when bound) to be CloseableTabItems
public class CloseableTabControl : TabControl
{
protected override DependencyObject GetContainerForItemOverride()
{
return new CloseableTabItem();
}
}
HTH Someone
Sam
You don't want to set the DataType of the DataTemplate in this case. The value of the ItemTemplate property is used whenever a new item needs to be added, and in the case of a tab control it will be used to create a new TabItem. You should declare an instance of your class within the DataTemplate itself:
<TabControl x:Name="tabCases" IsSynchronizedWithCurrentItem="True" Controls:ClosableTabItem.TabClose="TabClosed">
<TabControl.ItemTemplate>
<DataTemplate>
<Controls:ClosableTabItem>
<TextBlock Text="{Binding Path=Id}" />
</Controls:ClosableTabItem>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate DataType="{x:Type Entities:Case}">
<CallLog:CaseReadOnlyDisplay DataContext="{Binding}" />
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
This will cause a new ClosableTabItem to be created whenever a new tab is added to the TabControl.
Update; From your comment, it sounds like that the ItemTemplate controls what is created within the TabItem, rather than changing the TabItem itself. To do what you want to do, but for a TreeView, you would set the HeaderTemplate. Unfortunately, I don't see a HeaderTemplate property of TabControl.
I did some searching, and this tutorial modifies the contents of the tab headers by adding controls to TabItem.Header. Maybe you could create a Style for your TabItems that would add the close button that your class is currently adding?

Resources