Rescale listboxes or other items? but not text (WPF) - wpf

The question is quite clear. I have listviews or listboxes in my view.
If i make my screen smaller, i want that my text (labels etc) stay the same size. But the listviews and listboxes have to become smaller, eventually with a scrollbar?
how do i do this?
Thanks

the ListBox has a ScrollViewer built in.
<ListBox ScrollViewer.VerticalScrollBarVisibility="Auto" />

The question is quite clear.
Well, not exactly. But I think this is what you're looking for. First, create a value converter that takes a Double and returns its reciprocal:
public class ReciprocalValueConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
Double? d = value as Double?;
return (d == null || d == 0)
? null
: 1/d;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Now you can scale any content control, using a ScaleTransform, and use the ReciprocalValueConverter to keep any individual element contained within it to the original scale. So if the content control's scale is doubled, the content that you want to remain unchanged has its scale halved.
This example shows both scaling content controls and "unscaling" the items in a list box by assigning the LayoutTransform to each item:
<Window x:Class="ScaleTransformDemo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:ScaleTransformDemo="clr-namespace:ScaleTransformDemo" Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<ScaleTransformDemo:ReciprocalValueConverter x:Key="Reciprocal" />
</Window.Resources>
<DockPanel>
<Slider x:Name="ScaleSlider"
Orientation="Vertical"
Minimum=".2"
Maximum="4"
Value="1" />
<DockPanel>
<DockPanel.LayoutTransform>
<ScaleTransform ScaleX="{Binding ElementName=ScaleSlider, Path=Value}"
ScaleY="{Binding ElementName=ScaleSlider, Path=Value}" />
</DockPanel.LayoutTransform>
<Label DockPanel.Dock="Top">
The content of this label scales with the slider.
</Label>
<Label DockPanel.Dock="Top">
<Label.LayoutTransform>
<ScaleTransform ScaleX="{Binding ElementName=ScaleSlider, Path=Value, Converter={StaticResource Reciprocal}}"
ScaleY="{Binding ElementName=ScaleSlider, Path=Value, Converter={StaticResource Reciprocal}}" />
</Label.LayoutTransform>
<Label.Content>
This label's content stays the same size.
</Label.Content>
</Label>
<Label DockPanel.Dock="Top">
The ListBox below scales with the slider, too, but the ListBoxItems don't:
</Label>
<ListBox Height="50"
DockPanel.Dock="Top">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="LayoutTransform">
<Setter.Value>
<ScaleTransform ScaleX="{Binding ElementName=ScaleSlider, Path=Value, Converter={StaticResource Reciprocal}}"
ScaleY="{Binding ElementName=ScaleSlider, Path=Value, Converter={StaticResource Reciprocal}}" />
</Setter.Value>
</Setter>
</Style>
</ListBox.ItemContainerStyle>
<ListBoxItem>Item 1</ListBoxItem>
<ListBoxItem>Item 2</ListBoxItem>
<ListBoxItem>Item 3</ListBoxItem>
<ListBoxItem>Item 4</ListBoxItem>
<ListBoxItem>Item 5</ListBoxItem>
<ListBoxItem>Item 6</ListBoxItem>
</ListBox>
<TextBlock DockPanel.Dock="Top" />
</DockPanel>
</DockPanel>
</Window>

Related

Unable to center a canva inside a canva

I've a view in WPF where I need to center a canvas within a canvas.
I know it's not the most suitable container for this, but we have other components that will come in this canvas where it will simplify a lot our job.
So basically, I've currently this code:
<Canvas Name="RootContainer" Background="#373B3F" ClipToBounds="True" MouseLeftButtonDown="OnMainCanvasMouseLeftButtonDown">
<Canvas.DataContext>
<local:SomeViewModel/>
</Canvas.DataContext>
<Canvas Name="SomeContainer" Background="#373B3F" MouseMove="OnCanvasMouseMove" MouseWheel="OnCanvasMouseWheel">
<Canvas.Left>
<MultiBinding Converter="{StaticResource CenterValueConverter}">
<Binding ElementName="RootContainer" Path="ActualWidth" />
<Binding ElementName="SomeContainer" Path="ActualWidth" />
</MultiBinding>
</Canvas.Left>
<Canvas.Top>
<MultiBinding Converter="{StaticResource CenterValueConverter}">
<Binding ElementName="RootContainer" Path="ActualHeight" />
<Binding ElementName="SomeContainer" Path="ActualHeight" />
</MultiBinding>
</Canvas.Top>
<ItemsControl ItemsSource="{Binding SomeChilds}" Name="ItemsControl">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<Setter Property="Canvas.Left" Value="{Binding Left}" />
<Setter Property="Canvas.Top" Value="{Binding Top}" />
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemTemplate>
<DataTemplate>
<ContentControl Content="{Binding}" ContentTemplateSelector="{StaticResource ConventionBasedDataTemplateSelector}"
MouseLeftButtonDown="OnMouseLeftButtonDown" PreviewMouseLeftButtonUp="OnPreviewMouseLeftButtonUp" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<!-- Some other controls over here -->
</Canvas>
</Canvas>
And this converter:
public class CenterValueConverter : IMultiValueConverter
{
public object Convert(object[] values,
Type targetType,
object parameter,
CultureInfo culture)
{
if (values == null || values.Length != 2)
{
return null;
}
double totalWidth = (double)values[0];
double size = (double)values[1];
return totalWidth / 2 - (size / 2);
}
public object[] ConvertBack(object value,
Type[] targetTypes,
object parameter,
CultureInfo culture)
{
throw new NotSupportedException();
}
}
The issue is that the second value(SomeContainer.ActualWidth/ActualHeight) always comes with a value of 0(even when I've some real elements it).
Any idea why and how to fix this? Or another XAML way of centering SomeContainer inside of the RootContainer ?
EDIT
Maybe some additional informations on why I planned to use so much Canvas.
The RootContainer one is because SomeContainer will have transformation(scale for zooming) and translation for panning
The SomeContainer could be something different I guess
The Canvas inside the ItemControls is because each elements will be positioned as a very specific place.
I guess your ActualHeight/ActualWidth are = 0 because you didn't load the window yet.
To make these calculous taking in account dimensions of Canvas you may use 'OnContentRendered' event, that will be launched after the window is displayed, so you will have their dimensions appearing. If your Canvas may change dimensions, you may also use SizeChanged event.
Even I think it is a bit complicated, and you could just use that XAML code taken from Clemen's answer :
<Canvas Background="Transparent"
SizeChanged="ViewportSizeChanged"
MouseLeftButtonDown="ViewportMouseLeftButtonDown"
MouseLeftButtonUp="ViewportMouseLeftButtonUp"
MouseMove="ViewportMouseMove"
MouseWheel="ViewportMouseWheel">
<Canvas x:Name="canvas" Width="1000" Height="600">
<Canvas.RenderTransform>
<MatrixTransform x:Name="transform"/>
</Canvas.RenderTransform>
<Ellipse Fill="Red" Width="100" Height="100" Canvas.Left="100" Canvas.Top="100"/>
<Ellipse Fill="Green" Width="100" Height="100" Canvas.Right="100" Canvas.Bottom="100"/>
</Canvas>
</Canvas>

Combine design time menu elements into databound menu item binding

Hopefully I got the vernacular right on my question so I don't throw people completely off.
I have a menu that is data bound and using a HierarchicalDataTemplate that handles the various nested types in my binding object. So far everything is working fantastically; but now I would like to add a couple additional menu items to menu items of a certain type, but of course that breaks the binding as I cannot bind to a collection that already contains elements. CompositeCollection seems to be what I am looking for but I keep running into syntax errors when trying to apply that to my HierarchicalDataTemplate.
<Menu.Resources>
<HierarchicalDataTemplate DataType="{x:Type ODIF:PluginContainer}" ItemsSource="{Binding Instance.Devices}">
<HierarchicalDataTemplate.ItemsSource>
<CompositeCollection>
<CollectionContainer Collection="{Binding Instance.Devices}"/>
<MenuItem>One more item!</MenuItem>
<MenuItem>Two more items!</MenuItem>
</CompositeCollection>
</HierarchicalDataTemplate.ItemsSource>
<StackPanel HorizontalAlignment="Left" Orientation="Horizontal">
<Image Source="{Binding PluginIcon}" Width="16" Height="16">
<Image.Style>
<Style TargetType="{x:Type Image}">
<Style.Triggers>
<DataTrigger Binding="{Binding PluginIcon}" Value="{x:Null}">
<Setter Property="Visibility" Value="Collapsed" />
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
<TextBlock Text="{Binding PluginName}"/>
</StackPanel>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type ODIF:Device}" ItemsSource="{Binding InputChannels}">
<StackPanel HorizontalAlignment="Left" Orientation="Horizontal">
<Image Source="{Binding StatusIcon}" Width="16" Height="16">
<Image.Style>
<Style TargetType="{x:Type Image}">
<Style.Triggers>
<DataTrigger Binding="{Binding StatusIcon}" Value="{x:Null}">
<Setter Property="Visibility" Value="Collapsed" />
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
<TextBlock Text="{Binding DeviceName}"/>
</StackPanel>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type ODIF:DeviceChannel}">
<local:ChannelBox Channel="{Binding}" Width="200" Click="ChannelClicked"/>
</HierarchicalDataTemplate>
</Menu.Resources>
This throws:
The specified value cannot be assigned. The following type was
expected: "BindingBase".
and
Property 'ItemsSource' does not support values of type 'CompositeCollection'.
I guess you have to use a converter for solving your issue.
Let's suppose that MenuModel is a class which represents a menu item. It is really simple:
public class MenuModel
{
private List<MenuModel> children = new List<MenuModel>();
public string Description { get; set; }
public IList Children
{
get
{
return children;
}
}
}
Now we have our XAML:
<Window.Resources>
<collections:ArrayList x:Key="someOtherMenus">
<local:MenuModel Description="Menu A">
<local:MenuModel.Children>
<local:MenuModel Description="SubMenu i" />
<local:MenuModel Description="SubMenu ii" />
</local:MenuModel.Children>
</local:MenuModel>
<local:MenuModel Description="Menu B" />
</collections:ArrayList>
</Window.Resources>
<DockPanel>
<Menu DockPanel.Dock="Top" Margin="3" ItemsSource="{Binding MenuModels}">
<Menu.ItemTemplate>
<HierarchicalDataTemplate>
<HierarchicalDataTemplate.ItemsSource>
<Binding ConverterParameter="someOtherMenus">
<Binding.Converter>
<local:CompositeCollectionConverter />
</Binding.Converter>
</Binding>
</HierarchicalDataTemplate.ItemsSource>
<TextBlock Text="{Binding Description}" Margin="3" />
</HierarchicalDataTemplate>
</Menu.ItemTemplate>
</Menu>
<TextBlock Text="text" Margin="10" />
</DockPanel>
So now we can consider the converter implementation:
public class CompositeCollectionConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
MenuModel menuModel = value as MenuModel;
if (parameter != null)
{
CollectionContainer collectionContainer = new CollectionContainer();
collectionContainer.Collection = menuModel.Children;
CompositeCollection compositeCollection = new CompositeCollection();
compositeCollection.Add(collectionContainer);
collectionContainer = new CollectionContainer();
collectionContainer.Collection = (IEnumerable)App.Current.MainWindow.FindResource(parameter);
compositeCollection.Add(collectionContainer);
return compositeCollection;
}
else
{
return menuModel.Children;
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
As you can see it uses its parameter to get a specific resource (in our case I am using the resource called "someOtherMenus", which is an IEnumerable of MenuModels).
Of course the HierarchicalDataTemplate is recursive so the "someOtherMenus" MenuModels will be added to each level (but the first one) of your Menu.
I hope my sample can help you.

WPF Radcalender customization day template format

I want to cretae wpf radcalender in my wpf mvvm application. I can't able to customize the calender day template. I want to this format.
I am using this code. But the text has been overwrite the date.
xmal:
<telerik:RadCalendar Margin="30" SelectableDateStart="2015-01-01" SelectableDateEnd="2015-01-31" >
<telerik:RadCalendar.DayTemplate>
<DataTemplate>
<StackPanel>
<TextBox Text="Test"/>
</StackPanel>
</DataTemplate>
</telerik:RadCalendar.DayTemplate>
</telerik:RadCalendar>
I've worked with the RadCalendar within a Winrt app and here how i fixed that (with some help from the official Docs)
First add a new converter that will return the Date Label like that:
public class CellModelConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
var cellModel = value as CalendarCellModel;
return cellModel.Label;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
then in you Xaml add a textBlock to display the Cell Date using the previous converter :
<telerik:RadCalendar Margin="30" SelectableDateStart="2015-01-01" SelectableDateEnd="2015-01-31" >
<telerik:RadCalendar.DayTemplate>
<DataTemplate>
<StackPanel>
<TextBox Text="Test"/>
<TextBlock Text="{Binding Converter={StaticResource CellModelConverter}}" FontSize="13.333" VerticalAlignment="Bottom" Margin="6,0,0,4" />
</StackPanel>
</DataTemplate>
</telerik:RadCalendar.DayTemplate>
</telerik:RadCalendar>
and don't forget to add the Converter to the Static Resources:
<converters:CellModelConverter x:Key="CellModelConverter" />
--EDIT 1
the code above work fine in a Winrt application, for a WPF project you don't need a converter at all :
<telerik:RadCalendar Margin="30" SelectableDateStart="2015-01-01" SelectableDateEnd="2015-01-31" >
<telerik:RadCalendar.DayTemplate>
<DataTemplate>
<StackPanel>
<TextBox Text="Test"/>
<TextBlock Text="{Binding}" FontSize="13.333" VerticalAlignment="Bottom" Margin="6,0,0,4" />
</StackPanel>
</DataTemplate>
</telerik:RadCalendar.DayTemplate>
</telerik:RadCalendar>
--EDIT 2
the above code shows the "test" text in all the cells including WeekName, DayName ..., to fix that you can use either a Converter or a DataTrigger, here how to do it using a DataTrigger :
<telerik:RadCalendar Margin="30" SelectableDateStart="2015-01-01" SelectableDateEnd="2015-01-31" >
<telerik:RadCalendar.DayTemplate>
<DataTemplate>
<StackPanel>
<TextBox Text="Test">
<TextBox.Style>
<Style TargetType="TextBox">
<Setter Property="Visibility" Value="Collapsed"></Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding DataContext.ButtonType,ElementName=tb}"
Value="Date">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
<TextBlock x:Name="tb" Text="{Binding Converter={StaticResource CellModelConverter}}" FontSize="13.333" VerticalAlignment="Bottom" Margin="6,0,0,4" />
</StackPanel>
</DataTemplate>
</telerik:RadCalendar.DayTemplate>
</telerik:RadCalendar>
Output:

Windows Explorer Tooltip in WPF

its not that hard what i want, but i'm pulling my hairs for days!
i just want the same tooltip behaviour like the WIndows Explorer:
overlay a partially hidden tree/list element with the tooltip that displays the full element
i use the following datatemplate in my treeview
<HierarchicalDataTemplate DataType="{x:Type TreeVM:SurveyorTreeViewItemViewModel}" ItemsSource="{Binding Children, Converter={StaticResource surveyorSortableCollectionViewConverter}}">
<StackPanel x:Name="SurveyorStackPanel" Margin="0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Orientation="Horizontal" Height="20" Width="auto">
... (Textblocks, properties, usercontrol, border,... )
<StackPanel.ToolTip>
<ToolTip Placement="RelativePoint" Padding="0" HasDropShadow="False"
DataContext="{Binding ElementName=SurveyorStackPanel}">
<Rectangle HorizontalAlignment="Left" VerticalAlignment="Center"
Width="{Binding ElementName=SurveyorStackPanel, Path=Width}"
Height="{Binding ElementName=SurveyorStackPanel, Path=Height}">
<Rectangle.Fill>
<VisualBrush AutoLayoutContent="True" AlignmentX="Left"
Visual="{Binding}" Stretch="None"/>
</Rectangle.Fill>
</Rectangle>
</ToolTip>
</StackPanel.ToolTip>
</StackPanel>
</HierarchicalDataTemplate>
As you can see, i'm trying to use Visualbrush. but this doesnt work. it only shows what you see on the screen.
I have tried with static resource and binding on a new stackpanel thats in the tooltip, but that only leaves with a blanc tooltip.
Do i something wrong? do i have to use alternatives?
i'm pretty new in WPF. i know the basics, but binding/resources is kinda new for me
EDIT
here is the static source i tried:
<ToolTip x:Key="reflectingTooltip" DataContext="{Binding Path=PlacementTarget, RelativeSource={x:Static RelativeSource.Self}}" Placement="RelativePoint" Padding="0" HasDropShadow="False">
<Rectangle Width="{Binding ActualWidth}" Height="{Binding Path=ActualHeight}" Margin="0"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Rectangle.Fill>
<VisualBrush Visual="{Binding}" Stretch="None" AlignmentX="Left" />
</Rectangle.Fill>
</Rectangle>
</ToolTip>
EDIT 2
Here are a few pics from the situation i have now:
the whole element must be shown when tooltip shows.
before tooltip: http://desmond.imageshack.us/Himg832/scaled.php?server=832&filename=beforedo.png&res=landing
when tooltip is shown: http://desmond.imageshack.us/Himg842/scaled.php?server=842&filename=afterbl.png&res=landing
tooltip has too large height and only shows what screens shows. only problem is to 'fiil in' the hidden text.
VisualBrush renders as a bitmap exactly the same thing you are providing by the 'Visual' property, and it does so without any modification to that thing: it renders them exactly as they are now.
If you want to display something else, you have to provide that something else.. Could you try with something like that: ?
<Window x:Class="UncutTooltip.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<StackPanel Orientation="Horizontal">
<ListBox ItemsSource="{Binding}">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalAlignment" Value="Stretch" />
<Setter Property="Width" Value="250" />
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Background="Transparent">
<TextBlock Text="{Binding TheText}"
TextTrimming="CharacterEllipsis">
</TextBlock>
<Grid.ToolTip>
<TextBlock Text="{Binding TheText}"
TextTrimming="CharacterEllipsis">
</TextBlock>
</Grid.ToolTip>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Border Background="Red" >
<TextBlock Margin="5" Foreground="WhiteSmoke" FontSize="18"
Text="The end of window:)" TextAlignment="Center">
<TextBlock.LayoutTransform>
<RotateTransform Angle="-90" />
</TextBlock.LayoutTransform>
</TextBlock>
</Border>
</StackPanel>
</Window>
---
using System.Collections.Generic;
using System.Windows;
namespace UncutTooltip
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new List<Item>
{
new Item { TheText = "its not that hard what i want, but i'm pulling my hairs for days!" },
new Item { TheText = "i just want the same tooltip behaviour like the WIndows Explorer: overlay a partially hidden tree/list element with the tooltip that displays the full element" },
new Item { TheText = "i use the following datatemplate in my treeview" },
new Item { TheText = "As you can see, i'm trying to use Visualbrush. but this doesnt work. it only shows what you see on the screen." },
new Item { TheText = "I have tried with static resource and binding on a new stackpanel thats in the tooltip, but that only leaves with a blanc tooltip." },
new Item { TheText = "Do i something wrong? do i have to use alternatives? i'm pretty new in WPF. i know the basics, but binding/resources is kinda new for me" },
};
}
}
public class Item
{
public string TheText { get; set; }
}
}
Edit:
Now, change the tooltip contents to i.e.:
<Grid.ToolTip>
<ListBox ItemsSource="{Binding TheWholeList}">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalAlignment" Value="Stretch" />
<!--<Setter Property="Width" Value="250" />-->
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Background="Transparent">
<TextBlock Text="{Binding TheText}" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid.ToolTip>
and also change the data definition to:
public class Item
{
public string TheText { get; set; }
public IList<Item> TheWholeList { get; set; }
}
var tmp = new List<Item>
{
.........
};
foreach (var it in tmp)
it.TheWholeList = tmp;
this.DataContext = tmp;
Note that I've commented out the width constraint in the tooltip's listbox, it will present an untruncated list of untruncated elements..
Edit #2:
<StackPanel Orientation="Horizontal">
<ListBox x:Name="listbox" ItemsSource="{DynamicResource blah}"> // <---- HERE
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalAlignment" Value="Stretch" />
<Setter Property="Width" Value="250" />
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Background="Transparent">
<TextBlock Text="{Binding TheText}" TextTrimming="CharacterEllipsis" />
<Grid.ToolTip>
<ToolTip DataContext="{DynamicResource blah}"> // <---- HERE
<TextBlock Text="{Binding [2].TheText}" /> // <---- just example of binding to a one specific item
<!-- <ListBox ItemsSource="{Binding}"> another eaxmple: bind to whole list.. -->
</ToolTip>
</Grid.ToolTip>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
public class Item
{
public string TheText { get; set; }
}
public MainWindow()
{
InitializeComponent();
Resources["blah"] = new List<Item> // <---- HERE
{
new Item { TheText = ........
........
In the last example, I've changed the window.DataContext binding, to a binding to a DynamicResource. In the window init, I've also changed the way the data is passed to the window. I've changed the tooltip template to include the Tooltip explicitely, and bound it to the same resource. This way, the inner tooltip's textblock is able to read the 3rd row of the datasource directly - this proves it is bound to the list, not to the Item.
However, this is crappy approach. It will work only with explicit Tooltip, only with Tooltip.DataContext=resource, and probably, it is the only working shape of such approach.. Probably it'd be possible to hack into the tooltip with attached behaviours and search it's parent window and get the bindings to work, but usually, it's not worth.. Could you try binding to the Item's properties like in the second sample?

Prevent empty tooltips at a wpf datagrid

I am working on a calendar program, which consists mainly of a WPF DataGrid. As there is not always enough space to display all the entries of a day (which is a DataGridCell), a tooltip with all the entries of the day shell appear at mouse over. This works so far with the code snippet shown below. And now the (little) problem: If there are no entries for a day, no tooltip shell pop up. With the code below an empty tooltip pops up.
<DataGridTemplateColumn x:Name="Entry"
IsReadOnly="True">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Grid>
<TextBlock Text="{Binding EntryText}"
Foreground="{Binding EntryForeground}"
FontWeight="{Binding EntryFontWeight}">
</TextBlock>
<TextBlock Text="{Binding RightAlignedText}"
Foreground="Gray"
Background="Transparent">
<TextBlock.ToolTip>
<TextBlock Text="{Binding AllEntriesText}"/>
</TextBlock.ToolTip>
</TextBlock>
</Grid>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
The Databinding is made via
myCalDataGrid.Itemssource = _listOfDays;
in code behind, where a 'Day' is the view model for a single calendar row.
As H.B. suggested bind directly to the ToolTip property instead of using TextBlock and in case AllEntriesText is empty string you can apply a trigger on your TextBlock to disable your tooltip by setting the property ToolTipService.IsEnabled like this -
<TextBlock Text="{Binding RightAlignedText}"
Foreground="Gray"
Background="Transparent"
ToolTip="{Binding AllEntriesText}">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<Trigger Property="ToolTip"
Value="{x:Static system:String.Empty}">
<Setter Property="ToolTipService.IsEnabled" Value="False" />
</Trigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
Make sure to add namespace system in your xaml -
xmlns:system="clr-namespace:System;assembly=mscorlib"
Bind directly to the ToolTip property (do not create a TextBlock for it) and set AllEntriesText to null if there are no entries, then the ToolTip itself is also null and should not show.
Thanks for the solutions, they may work, no question. But I need a TextBlock for the tooltip to format and align the text. So I found this solution:
<TextBlock Text="{Binding RightAlignedText}"
HorizontalAlignment="Stretch"
TextAlignment="Right" Padding="2,0"
Foreground="Gray"
Background="Transparent"
ToolTipService.ShowDuration="60000"
ToolTipService.BetweenShowDelay="0"
ToolTipService.InitialShowDelay="0"
>
<TextBlock.ToolTip>
<ToolTip Visibility="{Binding EntryToolTipVisibility}">
<TextBlock Text="{Binding ToolTipText}"
TextAlignment="Left"
FontFamily="Courier New"/>
</ToolTip>
</TextBlock.ToolTip>
</TextBlock>
I bound the propertie "Visibility" of tooltip to a propertie "EntryToolTipVisibility" (of type Visibility) in the view model. See code snippet below.
public Visibility EntryToolTipVisibility
{
get
{
return _entries.Count > 0 ? Visibility.Visible : Visibility.Collapsed;
}
}
Another option is to use an own Converter.
I prefer this way for example for a TextBlock tooltip that displays TextBlock's text, but for the case there is no text, the empty tooltip is not desired.
XAML code:
//step #1
xmlns:local="clr-namespace:MyNamespace"
//step #2 - into Window.Resources or other
<local:StringToVisibleTooltip x:Key="StringToVis" />
//step #3 - example of use
<TextBlock ...other attributes... TextTrimming="CharacterEllipsis">
<TextBlock.ToolTip>
<ToolTip Visibility="{Binding Path=Text, Converter={StaticResource StringToVis}}">
<TextBlock Text="{Binding Text}"/>
</ToolTip>
</TextBlock.ToolTip>
</TextBlock>
And code behind
namespace MyNamespace
{
public class StringToVisibleTooltip : IValueConverter
{
public StringToVisibleTooltip() { }
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value != null && ((string)value).Length > 0) //empty string does not need tooltip
return Visibility.Visible;
else
return Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return null;
}
}
}

Resources