Textblock text bind in controltemplate question - silverlight

I'm creating my own control, I'm wondering the best way to bind the textblock text property below, I have the contentpresenter set to bind to content, how can I set the textblock text value?
<Style TargetType="ctrl:Selection">
<Setter Property="Width" Value="200" />
<Setter Property="Height" Value="100" />
<Setter Property="Background" Value="Lavender" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ctrl:Selection">
<Grid x:Name="RootElement">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Rectangle Grid.Row="0" Width="{TemplateBinding Width}" Height="{TemplateBinding Height}" Fill="{TemplateBinding Background}" Stroke="black" RadiusX="16" RadiusY="16" />
<TextBlock Grid.Row="0" Text="How do i bind this?" x:Name="HeaderText" HorizontalAlignment="Center" VerticalAlignment="Center" />
<ContentPresenter Grid.Row="1" x:Name="BodyContent" Content="{TemplateBinding Content}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
public class Selection : ContentControl {
public Selection() {
this.DefaultStyleKey = typeof(Selection);
}
}

You can register a property in your control class, something like this...
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(Selection), new PropertyMetadata(""));
public string Text {
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}

Related

Extended ToolTip for UI Elements

i have a problem with the ToolTips and need some helping hand.
I want to create a custom design for all tooltips, adding a header and a footer.
Because it should be the default tooltip style/template i created attached properties, to set the values for header and footer on all UI elements.
public class ToolTipExtensions
{
public static readonly DependencyProperty HeaderProperty = DependencyProperty.RegisterAttached("Header",
typeof(string), typeof(ToolTipExtensions), new PropertyMetadata(null));
public static readonly DependencyProperty FooterProperty = DependencyProperty.RegisterAttached("Footer",
typeof(string), typeof(ToolTipExtensions), new PropertyMetadata(null));
public static void SetHeader(UIElement element, string value)
{
element.SetValue(HeaderProperty, value);
}
public static string GetHeader(UIElement element)
{
return (string)element.GetValue(HeaderProperty);
}
public static void SetFooter(UIElement element, string value)
{
element.SetValue(FooterProperty, value);
}
public static string GetFooter(UIElement element)
{
return (string)element.GetValue(FooterProperty);
}
}
Im using the following style.
<Style TargetType="{x:Type ToolTip}">
<Setter Property="MinHeight" Value="150" />
<Setter Property="Width" Value="350" />
<Setter Property="Foreground" Value="Black" />
<Setter Property="Background" Value="#ECEFF4" />
<Setter Property="BorderBrush" Value="#394F6D" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ToolTip}">
<Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="1,1,1,1">
<Grid Background="{TemplateBinding Background}">
<Grid.RowDefinitions>
<RowDefinition Height="40" />
<RowDefinition Height="*" />
<RowDefinition Height="30" />
</Grid.RowDefinitions>
<Label
Grid.Row="0"
MinHeight="40"
Content="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(local:ToolTipExtensions.Header)}"
FontFamily="Arial"
FontSize="13"
FontWeight="Bold"
Foreground="{TemplateBinding Foreground}" />
<TextBlock
Grid.Row="1"
MinHeight="40"
Foreground="{TemplateBinding Foreground}"
Text="{Binding ToolTip, RelativeSource={RelativeSource Mode=TemplatedParent}}" />
<Label
Grid.Row="2"
MinHeight="30"
Content="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(local:ToolTipExtensions.Footer)}"
FontFamily="Arial"
FontSize="12"
FontStyle="Italic"
FontWeight="Regular" />
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Im doing something wrong, because the tooltip dosent show anything. No ToolTip, no Header, no Footer.
Whats the problem with it? Wrong DataContext for the ToolTip?
Heres the usage:
<Button
local:ToolTipExtensions.Header="Button"
local:ToolTipExtensions.Footer="To show additional Info"
Content="Test"
ToolTip="Some fancy Text"
ToolTipService.InitialShowDelay="500"
ToolTipService.ShowDuration="999999999" />
Regards,
SyLuS
ToolTip for every control will be updated
Modified Style
<VM:DependecnyObjectTypeConverter x:Key="ConverterPa"/>
<Style TargetType="{x:Type ToolTip}">
<Setter Property="MinHeight" Value="150" />
<Setter Property="Width" Value="350" />
<Setter Property="Foreground" Value="Black" />
<Setter Property="Background" Value="#ECEFF4" />
<Setter Property="BorderBrush" Value="#394F6D" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="1,1,1,1">
<Grid Background="{TemplateBinding Background}">
<Grid.RowDefinitions>
<RowDefinition Height="40" />
<RowDefinition Height="*" />
<RowDefinition Height="30" />
</Grid.RowDefinitions>
<Label
Grid.Row="0"
MinHeight="40"
Content="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},Converter={StaticResource ConverterPa},ConverterParameter=Header}"
FontFamily="Arial"
FontSize="13"
FontWeight="Bold"
Foreground="{TemplateBinding Foreground}" />
<TextBlock
Grid.Row="1"
MinHeight="40"
Foreground="{TemplateBinding Foreground}"
Text="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},Converter={StaticResource ConverterPa},ConverterParameter=ToolTip}"/>
<Label
Grid.Row="2"
MinHeight="30"
Content="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},Converter={StaticResource ConverterPa},ConverterParameter=Footer}"
FontFamily="Arial"
FontSize="12"
FontStyle="Italic"
FontWeight="Regular" />
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
IValueConverter:
public class DependecnyObjectTypeConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var plTarget = value as System.Windows.Controls.ToolTip;
if (parameter.ToString() == "Header")
{
return plTarget.PlacementTarget.GetValue(ToolTipExtensions.HeaderProperty);
}
if (parameter.ToString() == "ToolTip")
{
return plTarget.PlacementTarget.GetValue(Control.ToolTipProperty);
}
if (parameter.ToString() == "Footer")
{
return plTarget.PlacementTarget.GetValue(ToolTipExtensions.HeaderProperty);
}
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}

WPF Dependency Property Bound To User Control

I have a button subclass called MenuButton.
public class MenuButton : Button
{
public string Caption
{
get { return (string)GetValue(CaptionProperty); }
set { SetValue(CaptionProperty, value); }
}
public static readonly DependencyProperty CaptionProperty =
DependencyProperty.Register("Caption", typeof(string), typeof(MenuButton), new UIPropertyMetadata(null));
public UserControl Icon
{
get { return (UserControl)GetValue(IconProperty); }
set { SetValue(IconProperty, value); }
}
public static readonly DependencyProperty IconProperty =
DependencyProperty.Register("Icon", typeof(UserControl), typeof(MenuButton),
new PropertyMetadata(null));
}
In the style I want to show an icon that was created using paths from SVG files. I have created a User Control containing the XAML for the icon:
<UserControl x:Class="WpfApplication1.Views.ScopeIcon"
.
.
.
>
<Viewbox Height="55"
Width="55">
<Grid>
<Path Fill="LightBlue" Data="M98.219,48.111C97..."/>
<Path Fill="LightBlue" Data="M98.219,46.948C97...."/>
</Grid>
</Viewbox>
</UserControl>
And here's the style:
<Style TargetType="Button"
x:Key="TestButtonStyle">
<Setter Property="Height" Value="140"/>
<Setter Property="Width" Value="195"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type controls:MenuButton}">
<Border x:Name="TheBorder">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="50*"/>
<RowDefinition Height="50*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<ContentPresenter Grid.Row="0"/> <===== THE USER CONTROL WILL GO HERE
<TextBlock Grid.Row="1"
Grid.Column="0"
Text="{TemplateBinding Caption}"
HorizontalAlignment="Center"
VerticalAlignment="Top"
Margin="5"
Foreground="White"
FontSize="14"
TextAlignment="Center"
TextWrapping="WrapWithOverflow"/>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I will set it here:
<ListBox Grid.Row="0"
ItemsSource="{Binding MainTools}" >
<ListBox.ItemTemplate>
<DataTemplate>
<controls:MenuButton Caption="{Binding Caption}"
Margin="2"
Width="100"
Style="{StaticResource TestButtonStyle}"
VerticalAlignment="Top"
Command="{Binding Path=ButtonClick}"
CommandParameter="{x:Static enums:Tabs.Oscilloscope}"
Icon=""/> <============= HOW DO I PUT THE SCOPEICON USER CONTROL HERE?
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I'm essentially trying to nest user controls, but I want to tell the button, in XAML, what UserControl to use for its icon.
Thank you
First, you need to update your style to set Content property on your ContentPresenter the same way you did for Text property on TextBlock.
<ContentPresenter Grid.Row="0" Content="{TemplateBinding Icon}"/>
And, then to set Icon on the MenuButton in your DataTemplate update your MenuButton element in DataTemplate to something like:
<controls:MenuButton Caption="Caption"
Margin="2"
Width="100"
Style="{StaticResource TestButtonStyle}"
VerticalAlignment="Top"
Command="{Binding Path=ButtonClick}"
CommandParameter="{x:Static enums:Tabs.Oscilloscope}">
<controls:MenuButton.Icon>
<views:ScopeIcon />
</controls:MenuButton.Icon>
</controls:MenuButton>

CustomControl with TemplateParts

I created a CustomControl that has two TemplateParts.
using System;
using System.Windows;
using System.Windows.Controls;
namespace WpfCustomControlLibrary1
{
[TemplatePart(Name = "PART_ControlsLayer", Type = typeof (ContentPresenter))]
[TemplatePart(Name = "PART_DisplayLayer", Type = typeof (ContentPresenter))]
public class CustomControl1 : Control
{
public static readonly DependencyProperty ControlsLayerProperty =
DependencyProperty.Register("ControlsLayer", typeof (object), typeof (CustomControl1),
new UIPropertyMetadata(null));
public static readonly DependencyProperty DisplayLayerProperty =
DependencyProperty.Register("DisplayLayer", typeof (object), typeof (CustomControl1),
new UIPropertyMetadata(null));
private ContentPresenter partControlsLayer;
private ContentPresenter partDisplayLayer;
static CustomControl1()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof (CustomControl1),
new FrameworkPropertyMetadata(typeof (CustomControl1)));
}
public object ControlsLayer
{
get { return GetValue(ControlsLayerProperty); }
set { SetValue(ControlsLayerProperty, value); }
}
public object DisplayLayer
{
get { return GetValue(DisplayLayerProperty); }
set { SetValue(DisplayLayerProperty, value); }
}
public override void OnApplyTemplate()
{
ApplyTemplate();
partControlsLayer = GetTemplateChild("PART_ControlsLayer") as ContentPresenter;
partDisplayLayer = GetTemplateChild("PART_DisplayLayer") as ContentPresenter;
if (partControlsLayer == null || partDisplayLayer == null)
{
throw new NullReferenceException("Template parts not available");
}
}
}
}
In the Generic.xaml I defined the ControlTemplate and a Default-Setter for the DisplayLayer (one of the TemplateParts). Finaly I set this as the Template of CustomControl1.
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfCustomControlLibrary1">
<Style TargetType="{x:Type local:CustomControl1}">
<Style.Resources>
<ControlTemplate x:Key="DefaulTemplate" TargetType="{x:Type local:CustomControl1}">
<Grid>
<ContentPresenter x:Name="PART_ControlsLayer"
Content="{TemplateBinding ControlsLayer}" />
<ContentPresenter x:Name="PART_DisplayLayer"
Content="{TemplateBinding DisplayLayer}" />
</Grid>
</ControlTemplate>
</Style.Resources>
<Setter Property="DisplayLayer">
<Setter.Value>
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Label Grid.Row="0" Content="{Binding FunctionName, FallbackValue=Functionname}" />
<TextBlock Grid.Row="1" Text="{Binding DisplayValue, FallbackValue=0.0dB}" Foreground="Lime"
Background="Black" />
</Grid>
</Setter.Value>
</Setter>
<Setter Property="Template" Value="{StaticResource DefaulTemplate}"/>
</Style>
<Style TargetType="{x:Type local:CustomControl2}" BasedOn="{StaticResource {x:Type local:CustomControl1}}" />
</ResourceDictionary>
Now I create a CustomControl2 BasedOn CustomControl1.
using System.Windows;
namespace WpfCustomControlLibrary1
{
public class CustomControl2 : CustomControl1
{
static CustomControl2()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomControl2), new FrameworkPropertyMetadata(typeof(CustomControl2)));
}
}
}
Then I put both Controls on a WpfWindow.
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:wpfCustomControlLibrary1="clr-namespace:WpfCustomControlLibrary1;assembly=WpfCustomControlLibrary1"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<wpfCustomControlLibrary1:CustomControl1 Grid.Row="0"/>
<wpfCustomControlLibrary1:CustomControl2 Grid.Row="1"/>
</Grid>
</Window>
The Problem is I see the Defaulttemplate only on the second Control. I can't find a solution for this, please help.
#gomi42 - if I change Generic.xaml to this:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfCustomControlLibrary1">
<Style x:Key="BaseStyle" TargetType="{x:Type local:CustomControl1}">
<Style.Resources>
<ControlTemplate x:Key="DefaulTemplate" TargetType="{x:Type local:CustomControl1}">
<Grid>
<ContentPresenter x:Name="PART_ControlsLayer"
Content="{TemplateBinding ControlsLayer}" />
<ContentPresenter x:Name="PART_DisplayLayer"
Content="{TemplateBinding DisplayLayer}" />
</Grid>
</ControlTemplate>
</Style.Resources>
<Setter Property="Template" Value="{StaticResource DefaulTemplate}"/>
<Setter Property="DisplayLayer">
<Setter.Value>
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Label Grid.Row="0" Content="{Binding FunctionName, FallbackValue=Functionname}" />
<TextBlock Grid.Row="1" Text="{Binding DisplayValue, FallbackValue=0.0dB}" Foreground="Lime"
Background="Black" />
</Grid>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type local:CustomControl1}" BasedOn="{StaticResource BaseStyle}" />
<Style TargetType="{x:Type local:CustomControl2}" BasedOn="{StaticResource BaseStyle}" />
</ResourceDictionary>
nothing changes! It's still visible only in the second control.
The style you created only applies to CustomControl1 because of this:
<Style TargetType="{x:Type local:CustomControl1}">
...
You need to create a new style for CustomControl2 e.g. by inheriting:
<Style x:Key="MyBase" TargetType="{x:Type local:CustomControl1}">
...
</Style>
<Style TargetType="{x:Type local:CustomControl1}" BasedOn={StaticResource MyBase} />
<Style TargetType="{x:Type local:CustomControl2}" BasedOn={StaticResource MyBase} />
I found it!
It's the DependencyProperty which causes the problem. I implemented the CustomControl like the examples on this site:
http://www.kunal-chowdhury.com/2011/04/how-to-implement-template-binding-in.html
and what should I say this example is wrong!
Here is how it works.
First CustomControl.cs
using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Controls;
using WpfCustomControlLibrary1.Annotations;
namespace WpfCustomControlLibrary1
{
[TemplatePart(Name = "PART_ControlsLayer", Type = typeof (Grid))]
[TemplatePart(Name = "PART_DisplayLayer", Type = typeof(ContentControl))]
public class CustomControl1 : Control
{
private Grid partControlsLayer;
private ContentControl partDisplayLayer;
static CustomControl1()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof (CustomControl1),
new FrameworkPropertyMetadata(typeof (CustomControl1)));
}
public CustomControl1()
{
DataContext = this;
}
public override void OnApplyTemplate()
{
ApplyTemplate();
partControlsLayer = GetTemplateChild("PART_ControlsLayer") as Grid;
partDisplayLayer = GetTemplateChild("PART_DisplayLayer") as ContentControl;
if (partControlsLayer == null || partDisplayLayer == null)
{
//throw new NullReferenceException("Template parts not available");
}
}
}
}
Then Generic.xaml
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfCustomControlLibrary1">
<Style x:Key="DefaultTemplate" TargetType="{x:Type ContentControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ContentControl}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Label Grid.Row="0" Content="{Binding FunctionName, FallbackValue=Functionname}" />
<TextBlock Grid.Row="1" Text="{Binding DisplayValue, FallbackValue=0.0dB}" Foreground="Lime"
Background="Black" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type local:CustomControl1}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<ContentControl x:Name="PART_DisplayLayer" Style="{StaticResource DefaultTemplate}" />
<Grid x:Name="PART_ControlsLayer" Grid.Row="1">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Button Grid.Row="0" Content="{Binding FunctionName, FallbackValue=Functionname}" />
<Button Grid.Row="1" Content="{Binding DisplayValue, FallbackValue=0.5dB}" />
</Grid>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type local:CustomControl2}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<ContentControl x:Name="PART_DisplayLayer" Style="{StaticResource DefaultTemplate}" />
<Grid x:Name="PART_ControlsLayer" Grid.Row="1">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Button Grid.Row="0" Content="Depp" />
<Button Grid.Row="1" Content="{Binding DisplayValue, FallbackValue=1.0dB}" />
</Grid>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
CustomControl2.cs as Derived-Class
using System.Windows;
namespace WpfCustomControlLibrary1
{
public class CustomControl2 : CustomControl1
{
static CustomControl2()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomControl2), new FrameworkPropertyMetadata(typeof(CustomControl2)));
}
public CustomControl2()
{
DataContext = this;
}
}
}
MainWindow still the same.
This is working since nothing of content is static.

Silverlight Tooltip Style ContentPresenter TextWrapping

I want to create a Silverlight Tooltip Style but I don't want to use a textblock because the tooltip content might be an image or something else. So I'm using a ContentPresenter. My problem is how to set MaxWidth and force TextWrapping when content is text. This is what I have so far:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:System="clr-namespace:System;assembly=mscorlib"
xmlns:vsm="clr-namespace:System.Windows;assembly=System.Windows"
xmlns:toolkit="http://schemas.microsoft.com/winfx/2006/xaml/presentation/toolkit">
<Style TargetType="ToolTip">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToolTip">
<Border BorderBrush="DimGray" BorderThickness="1" CornerRadius="5" Background="WhiteSmoke" Opacity="0.8"
HorizontalAlignment="Center" VerticalAlignment="Bottom" Margin="0,10,0,-5" Width="Auto">
<Border.Effect>
<DropShadowEffect BlurRadius="16" ShadowDepth="8" Direction="-45" Color="Black" Opacity="0.6"/>
</Border.Effect>
<Grid>
<ContentPresenter Content="{TemplateBinding Content}" Margin="3"/>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
I found one way to do this:
Create 2 Tooltip styles - one for text and another for everything else, like this:
<Style x:Key="TooltipStyleForText" TargetType="ToolTip">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToolTip">
<Border BorderBrush="DimGray" BorderThickness="1" CornerRadius="5" Background="WhiteSmoke" Opacity="0.8"
HorizontalAlignment="Center" VerticalAlignment="Bottom" Margin="0,10,0,-5" Width="Auto">
<Border.Effect>
<DropShadowEffect BlurRadius="16" ShadowDepth="8" Direction="-45" Color="Black" Opacity="0.6"/>
</Border.Effect>
<Grid>
<TextBlock Text="{TemplateBinding Content}" MaxWidth="400" TextWrapping="Wrap" Margin="3"/>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="TooltipStyleForOtherStuffThanText" TargetType="ToolTip">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToolTip">
<Border BorderBrush="DimGray" BorderThickness="1" CornerRadius="5" Background="WhiteSmoke" Opacity="0.8"
HorizontalAlignment="Center" VerticalAlignment="Bottom" Margin="0,10,0,-5" Width="Auto">
<Border.Effect>
<DropShadowEffect BlurRadius="16" ShadowDepth="8" Direction="-45" Color="Black" Opacity="0.6"/>
</Border.Effect>
<Grid>
<ContentPresenter Content="{TemplateBinding Content}" Margin="3"/>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
and than when we use a TextBlock, call it's ToolTip style like this:
<TextBlock Text="something">
<ToolTipService.ToolTip>
<ToolTip Style="{StaticResource TooltipStyleForText}">
<TextBlock Text="Some text"/>
</ToolTip>
</ToolTipService.ToolTip>
</TextBlock>
This might not be the best solution because we have two styles so we need to specify write several lines for a simple TextBlock.
I hope someone has a better solution.
We needed to do the same thing and tried a few approaches before setting on the following solution. The problem is that when the ToolTip.Content is a string you need to show the tooltip using a TextBlock. When ToolTip.Content is not a string you need to use a ContentPresenter. So we ended up defining a ControlTemplate that has both, and we show/hide the appropriate one based on the type of ToolTip.Content. This approach is applied to all ToolTips in the application:
The following converter class helps out with this:
public class TooltipContentVisibilityConverter : IValueConverter
{
public Visibility VisibilityWhenToolTipContentIsAString { get; set; }
public Visibility VisibilityWhenToolTipContentIsNotAString { get; set; }
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var toolTip = value as ToolTip;
if (toolTip != null && toolTip.Content is string)
{
return VisibilityWhenToolTipContentIsAString;
}
return VisibilityWhenToolTipContentIsNotAString;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Then in your App.xaml you define the following.
<Converter:TooltipContentVisibilityConverter VisibilityWhenToolTipContentIsAString="Visible" VisibilityWhenToolTipContentIsNotAString="Collapsed" x:Key="tooltipStringContentVisibilityConverter" />
<Converter:TooltipContentVisibilityConverter VisibilityWhenToolTipContentIsAString="Collapsed" VisibilityWhenToolTipContentIsNotAString="Visible" x:Key="tooltipNonStringContentVisibilityConverter" />
<Style TargetType="ToolTip">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToolTip">
<Border CornerRadius="0" Background="{TemplateBinding Background}" BorderBrush="Black" BorderThickness="1" Padding="4">
<StackPanel Orientation="Vertical">
<TextBlock Text="{TemplateBinding Content}" TextWrapping="Wrap" MaxWidth="300"
Visibility="{Binding RelativeSource={RelativeSource AncestorType=ToolTip},Converter={StaticResource tooltipStringContentVisibilityConverter}}"/>
<ContentPresenter Content="{TemplateBinding Content}"
Visibility="{Binding RelativeSource={RelativeSource AncestorType=ToolTip},Converter={StaticResource tooltipNonStringContentVisibilityConverter}}"/>
</StackPanel>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
It seems like a bit of an awkward way to have to solve this problem, but it does work and we haven't found a better solution yet.

WPF - Hosting content inside a UserControl

I'm trying to create a user control that has a Grid with two rows.
the first row for a title and the second one for a content that will be defined outside the user control such as a Button in our example.
Somehow I didn't get it to work.
UserControl1 xaml:
<Grid Background="LightBlue">
<Grid.RowDefinitions>
<RowDefinition Height="50" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBlock Text="Title" FontSize="30" Margin="10,0,0,0"/>
</Grid>
MainWindow xaml:
<Grid>
<local:UserControl1>
<Button>Click me</Button>
</local:UserControl1>
</Grid>
The picture below should explain what's my problem:
The following code
<local:UserControl1>
<Button>Click me</Button>
</local:UserControl1>
Means that you set UserControl1's Content property to be that button. This button simply replaces that UserControls1's markup. So all the things that you have in UserControl1.xaml are not there any more.
EDIT
If you want your UserControl to host some markup that will be set somewhere outside of it, you can add a DependencyProperty to it, for example:
/// <summary>
/// Gets or sets additional content for the UserControl
/// </summary>
public object AdditionalContent
{
get { return (object)GetValue(AdditionalContentProperty); }
set { SetValue(AdditionalContentProperty, value); }
}
public static readonly DependencyProperty AdditionalContentProperty =
DependencyProperty.Register("AdditionalContent", typeof(object), typeof(UserControl1),
new PropertyMetadata(null));
And add some element to it's markup to host that additional content. Here's an example extending the markup you provided:
<UserControl ... Name="userControl">
<Grid Background="LightBlue">
<Grid.RowDefinitions>
<RowDefinition Height="50" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBlock Text="Title" FontSize="30" Margin="10,0,0,0"/>
<ContentPresenter Content="{Binding AdditionalContent, ElementName=userControl}" />
</Grid>
</UserControl>
Now you can use it as following:
<local:UserControl1>
<local:UserControl1.AdditionalContent>
<Button>Click me</Button>
</local:UserControl1.AdditionalContent>
</local:UserControl1>
You have to set the ControlTemplate:
<UserControl>
<UserControl.Resources>
<Style TargetType="{x:Type local:UserControl1}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:UserControl1}">
<Grid Background="LightBlue">
<Grid.RowDefinitions>
<RowDefinition Height="50" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="Title" FontSize="30" Margin="10,0,0,0"/>
<ContentPresenter Grid.Row="1" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
</UserControl>
You can template the user control to add additional visuals like the TextBlock.
<UserControl>
<UserControl.Style>
<Style TargetType="{x:Type UserControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid Background="LightBlue">
<Grid.RowDefinitions>
<RowDefinition Height="50" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBlock Text="Title" FontSize="30" Margin="10,0,0,0"/>
<ContentPresenter Grid.Row="1" Content="{TemplateBinding Content}" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Style>
<Button>
Click me!
</Button>
</UserControl>
Use template with
< ContentControl />
Instead of using Content Presenter
So place this:
<UserControl.Style>
<Style TargetType="{x:Type UserControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type UserControl}" >
<Grid Background="LightBlue">
<Grid.RowDefinitions>
<RowDefinition Height="50" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBlock Text="Title" FontSize="30" Margin="10,0,0,0"/>
<ContentControl Grid.Row="1" Content="{TemplateBinding Content}" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Style>
to your userControl
This is the simple general template for a user control (without using styles or properties to set the content):
<UserControl ...>
<UserControl.Template>
<ControlTemplate TargetType="{x:Type UserControl}">
<!-- control contents here -->
<ContentPresenter/><!-- outside contents go here -->
<!-- control contents here -->
</ControlTemplate>
</UserControl.Template>
</UserControl>
The <ControlTemplate> represents the user control's XAML duplicated for each control.
The <ContentPresenter> is where the Content gets put when consuming the control.

Resources