Hide tooltip if binding is null - c#

Currently i've got the following code to show a tooltip.
<Border BorderBrush="Black"
BorderThickness="{Binding Border}"
Height="23"
Background="{Binding Color}">
<ToolTipService.ToolTip>
<TextBlock Text="{Binding TooltipInformation}" />
</ToolTipService.ToolTip>
This is presented in a ItemsControl with about 25 items. Only a few of these have a value set to TooltipInformation
If TooltipInforation is an empty string, it still shows the tooltipbox containing the textblock as a very small window (about 5px high and 20px wide). Even if I set the textblock visbility to collapsed.
Is there a way to completely remove the tooltip if the value of TooltipInformation is null or a empty string?

One way to hide an empty tooltip for all controls is to create a style in a resource dictionary that is included in your App.xaml.
This style sets the visibility to collapsed when the tooltip is an empty string or null:
<!-- Style to hide tool tips that have an empty content. -->
<Style TargetType="ToolTip">
<Style.Triggers>
<Trigger Property="Content"
Value="{x:Static sys:String.Empty}">
<Setter Property="Visibility"
Value="Collapsed" />
</Trigger>
<Trigger Property="Content"
Value="{x:Null}">
<Setter Property="Visibility"
Value="Collapsed" />
</Trigger>
</Style.Triggers>
</Style>
Also include sys namespace (for String.Empty):
xmlns:sys="clr-namespace:System;assembly=mscorlib"

One way you can do that is wrap the ToolTip in a Rectangle and give it a Transparent color. Then you just set the Visibility to Collapsed on this Rectangle.
Update:
<Border Background="#FFE45F5F">
<Grid>
<TextBlock Text="{Binding Property1}"/>
<Rectangle Fill="Transparent" Visibility="{Binding Property2, Converter={StaticResource BooleanToVisibilityConverter}}" ToolTipService.ToolTip="{Binding TooltipInformation}"/>
</Grid>
</Border>

This is a WPF answer (haven't tried it in Silverlight).
Use ToolTipService.IsEnabled, and bind it to the tooltip property. Then use a converter to convert the tooltip string to a bool.
For example, I have the following:
<TextBlock x:Name="textBlock" ToolTipService.IsEnabled="{Binding EntryToolTip, Converter={StaticResource StringNullOrEmptyToBoolConverter}}">
...
</TextBlock>
Or in code-behind
ToolTipService.SetIsEnabled(textBlock, false);

I was having the same issue as I was setting value to String.Empty. Setting it to null solves the problem.
WinRT/Windows 8 App XAML

If just using the default tooltip I would otherwise recommend either setting the bound value to null in the viewmodel or using a converter whenever the item is empty.
In my case I've got a:
public string Name { get; }
Bound using:
<TextBlock Text="{Binding Name}" TextTrimming="CharacterEllipsis" Tooltip="{Binding Name}" />
Where the idea is to show the full name in the tooltip if cut of due to lack of width. In my viewmodel I simply:
if (string.IsNullOrEmpty(Name)) Name = null;
At least in .Net 4.0 this will not show a tooltip for me.

Strangely, none of these answers worked in my case. A reply to the top answer alludes to it - if you're ToolTip is related to a TextBlock then that solution won't work. I have a TextBlock within a DataGridTemplateColumn.CellTemplate element, and I just bound the text directly to the ToolTip property of the TextBlock like this:
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock
HorizontalAlignment="Center"
VerticalAlignment="Center"
ToolTip="{Binding Path=SomeTextProperty}"
Style="{StaticResource TextBlockOverflowStyle}"
Text="{Binding Path=SomeTextProperty, NotifyOnSourceUpdated=True}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
And I ended up getting the desired behavior (hidden tooltip when text is empty) "for free".

You could create a converter from string to bool that returns false if the string length is 0 and true otherwise, then bind ToolTip.Active to TooltipInformation with that converter.

Related

C# WPF DataGrid Column Header fitting TextBox into width

I've a DataGrid in my C# WPF application and styled the Columns Header with a Label/TextBlock and a TextBox as an instant filter beneath of it.
My target is to fit the width of the TextBox into the width of the DataGridColumn, but I didn't found an acceptable way for me.
Currently I'm using a Viewbox, but this crashes all size measurements of the elements in the header.
Here is an example what it looks like with the Viewbox:
As you can see, I've either a too big TextBox and FontSize or a too small TextBox.
My currently code looks like that:
<DataGridTemplateColumn.Header>
<Viewbox StretchDirection="DownOnly">
<StackPanel>
<TextBlock Text="Mitarbeiter"/>
<TextBox x:Name="txtInstantSearchPersonio" HorizontalAlignment="Stretch" TextChanged="evh_InstantFilterChanged"/>
</StackPanel>
</Viewbox>
</DataGridTemplateColumn.Header>
Is there a way to fit the TextBox into full wifth of the column but don't stretch the height or fontsize?
You could use a Style for DataGridColumnHeader to fit the Textbox inside of a column
<DataGridTemplateColumn Width="200">
<DataGridTemplateColumn.HeaderStyle>
<Style TargetType="{x:Type DataGridColumnHeader}">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
</Style>
</DataGridTemplateColumn.HeaderStyle>
<DataGridTemplateColumn.Header>
<StackPanel>
<TextBlock Text="Mitarbeiter"/>
<TextBox x:Name="txtInstantSearchPersonio" HorizontalAlignment="Stretch" TextChanged="evh_InstantFilterChanged"/>
</StackPanel>
</DataGridTemplateColumn.Header>
Tried by desperation results in an really simple solution.
I put both elements into a StackPanel:
<DataGridTemplateColumn.Header>
<StackPanel>
<TextBlock Margin="0" Padding="0" Text="Mitarbeiter"/>
<TextBox HorizontalAlignment="Stretch" TextChanged="evh_InstantFilterChanged"/>
</StackPanel>
</DataGridTemplateColumn.Header>
and set the DataGrid.ColumnHeaderStyle simply to this:
<Style x:Key="style_DataGridColumnHeader" TargetType="DataGridColumnHeader">
<Style.Setters>
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
</Style.Setters>
</Style>
Works fine for me, the TextBox has always full width of the column header.

Xceed Datagrid Cell Colouring

I have an xceed WPF Datagrid that I want to color a particular cell in each row a particular way.
The grid is bound to a Collection of Bid objects. The column I want to apply to color is BidValue.
<xcdg:DataGridCollectionViewSource x:Key="BidViewSource" Source="{Binding Bids}"
d:DesignSource="{d:DesignInstance {x:Type models:Bid}, CreateList=True}">...
<xcdg:DataGridControl Name="BidGrid" DockPanel.Dock="Bottom" VerticalAlignment="Top" AutoCreateColumns="False"
ReadOnly="True" ItemsSource="{Binding Source={StaticResource BidViewSource}}"...
In order to simply the process, Bid.BackgroundColor and Bid.ForegroundColor exist for the purpose of supplying getters that determine the correct Color that BidValue should be displayed in.
Basically what I'm trying to do should begin something like this:
<xcdg:Column FieldName="BidValue" Title="Bid" CellHorizontalContentAlignment="Center" MaxWidth="75" AllowSort="False">
<xcdg:Column.CellContentTemplate>
<DataTemplate>
<DataTemplate.Triggers>
The remaining part that connects it to my the color fields in the Bid object is proving difficult. I've tried to implement the coloring logic in XAML (which is more common) with something like this:
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Path=BidValue}" Value="X" >
<Setter Property="Background" Value="Red"/>
<Setter Property="Foreground" Value="White"/>
</DataTrigger>
but when I do I get the following:
error MC4109: Cannot find the Template Property 'Background' on the type 'System.Windows.Controls.ContentPresenter
This code actually gets the data from one column (BidText) to use set the Color of another (BidValue) column -- which is its own mean feat using xceed DataGrids.
As alluded to above, a control (a textblock in this case) has to be set in the column's template and bound to the data that was already being displayed. The XAML for referring to another xceed Datagrid column's content to pass into the ColorConverter is shown in the Background and Foreground property assignments. That reference column doesn't need to be visible as is done here, with the Visibility property set to False.
<xcdg:Column FieldName="BidText" Visible="False" AllowSort="False"/>
<xcdg:Column FieldName="BidValue" Title="Bid" CellHorizontalContentAlignment="Center" MaxWidth="50" AllowSort="False">
<xcdg:Column.CellContentTemplate>
<DataTemplate>
<TextBlock Name="TextBlock" Width="50" HorizontalAlignment="Stretch" VerticalAlignment="Top" TextAlignment="Center"
Text="{Binding}" FontWeight="Bold"
Foreground="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type xcdg:Cell}}, Path=ParentRow.DataContext.BidText, Converter={StaticResource FGColorConverter}}"
Background="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type xcdg:Cell}}, Path=ParentRow.DataContext.BidText, Converter={StaticResource BGColorConverter}}"/>
</DataTemplate>
</xcdg:Column.CellContentTemplate>
</xcdg:Column>

Cannot show quickly list of images in WPF ItemsControl

I have ItemsControl (listbox) with some custom complex data templates. And its take few seconds to show window with this listbox!
My listbox datatemplate contains image:
<Image Stretch="Uniform"
x:Name="image"
Grid.ColumnSpan="1"
Source="{Binding ImagePath}"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Width="Auto"
Grid.Column="0"
MinWidth="20"
MinHeight="20"
d:LayoutOverrides="GridBox"
Margin="1,0,1,1" />
Almost all ImagePaths is null. I noticed that this null generates exception but wpf engine hides it! I think this is the source of problem. Is it possible to disable image loading when i know that image path is null!?
Add this DataTrigger to your DataTemplate:
<DataTrigger Binding="{Binding ImagePath}" Value="{x:Null}">
<Setter TargetName="image" Property="Source" Value="{x:Null}"/>
</DataTrigger>
Try FallBackValue attribute which sets a value when binding exception is thrown (msdn link)
eg: FallbackValue="pack://application:,,,/Resources/Images/nocover.png" or {x:null}
There is also TargetNullValue which sets a default value when the bound data is null
Another option would be trying with a converter which returns a default value if parameter is null..
Resolved similar issue here

Is it possible to change a the converter of a binding in a trigger in WPF?

I've got the following TabItem template where the TabItem contains an image whose source depends on the IsSelected property. I accomplish this by binding the Image.Source to the TabItem.Header, with a Converter that converts the header text to a full filename. (i.e. a header of "awb" will become "images/awb-white.png" or "images/awb-black.png" depending on the converter).
This code works, but it seems to have some unnecessary redundancy. All I really need to change is the Image.Source's binding's converter; not the whole binding itself. (The RelativeSource and Path both stay the same). Is there any way to accomplish this without the redundancy?
<Window.Resources>
<local:UnselectedImageFilenameConverter x:Key="UnselectedImageFilenameConverter" />
<local:SelectedImageFilenameConverter x:Key="SelectedImageFilenameConverter" />
<ControlTemplate TargetType="TabItem" x:Key="TabItemTemplate">
<Image x:Name="TabImage" Source="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Header, Converter={StaticResource UnselectedImageFilenameConverter}}" Stretch="None"/>
<ControlTemplate.Triggers>
<Trigger Property="Selector.IsSelected" Value="True">
<Setter TargetName="TabImage" Property="Source" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Header, Converter={StaticResource SelectedImageFilenameConverter}}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Window.Resources>
<TabControl>
<TabItem Header="awb" Template="{StaticResource TabItemTemplate}">
<Grid/>
</TabItem>
<TabItem Header="av" Template="{StaticResource TabItemTemplate}">
<Grid/>
</TabItem>
</TabControl>
To my knowledge that is not possible.
But you could change the way you handle this by creating only one converter but passing both the original value and the selection status via a MultiBinding. (The converter would need to be an IMultiValueConverter). Whether that is a good idea is of course questionable...

Error: The name 'tBox' does not exist in the current context

Error: The name 'tBox' does not exist in the current context.
XAML:
<ItemsControl Name="itemsControl">
<ItemsControl.Template>
<ControlTemplate>
<WrapPenel>
<ItemsPresenter/>
</WrapPenel>
</ControlTemplate>
</ItemsControl.Template>
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Name="tBox" Text="{Binding Name}"></TextBlock>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
C#:
tBox.Background=Brushes.White; // Error: The name 'tBox' does not exist in the current context.
How to access control?
The TextBlock you named tBox is inside a DataTemplate. Controls inside a template are in a different name scope, so you can't access it in code-behind via its name. I'm not sure but you might get it via the ItemTemplate property and casting it to a TextBlock. Or you can add a property in your code-behind representing the background and use binding on the TextBlock's Background property. Hope this helps.
Set it on the TextBlock, in your DataTemplate:
<DataTemplate>
<TextBlock Name="tBox" Background="White" Text="{Binding Name}"></TextBlock>
</DataTemplate>
Or if you wish to only set the Background in certain conditions, consider using Triggers:
<DataTemplate>
<TextBlock Name="tBox" Text="{Binding Name}"></TextBlock>
<DataTemplate.Triggers>
<Trigger SourceName="tBox" Property="IsMouseOver" Value="True">
<Setter TargetName="tBox" Property="Background" Value="White" />
</Trigger>
</DataTemplate.Triggers>
</DataTemplate>
More information on how to use Triggers can be found here: A Guided Tour of WPF - Part 4 (Data Templates and Triggers)
I didn't try but maybe the answer here works:
Access a control from within a DataTemplate with its identifying name
to use something like :
var tbUserIcon= (TextBlock)checkBox.Template.FindName("tbUserIcon", checkBox);
But I think this way isn't convenient at all, especially if there's lots of controls have to do it this way, and it can't be checked by intellisense when writing code real time.
this.Background=Brushes.White; (assuming its code behind the control)?
Since Background is a dependency property, you will have to use
tBox.SetValue(BackgroundProperty, new SolidBrush(Color.White));

Categories