WPF ToolTip Text Alignment - c#

I have the following ListViewItem:
<GridViewColumn Width="{Binding ActualWidth,
RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Grid}},
Converter={StaticResource MathConverter}, ConverterParameter=(x/10)*1}">
<GridViewColumn.Header>
<GridViewColumnHeader Content=" Total Fees "
HorizontalContentAlignment="Right" />
</GridViewColumn.Header>
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding GarnishmentTotals.TotalFees, StringFormat={}{0:c}}"
TextAlignment="Right" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
The Style for this item is as follows:
<Style x:Key="MultipleGarnishmentsStyle" TargetType="ContentControl">
<Style.Triggers>
<DataTrigger Binding="{Binding Garnishments.Count, Converter={StaticResource GreaterThanEqualToBooleanConverter}, ConverterParameter=2}" Value="True"
h:TriggerTracing.TriggerName="MultipleGarnishmentsStyle_Trigger1"
h:TriggerTracing.TraceEnabled="True">
<!-- Leave the content alone and just change the format string -->
<Setter Property="TextBlock.FontWeight" Value="UltraBold" />
<Setter Property="ToolTip" Value="Employee has multiple garnishments. Double click to view details." />
</DataTrigger>
</Style.Triggers>
</Style>
I am having an issue with the alignment of the text within the ToolTip.
The text seems to be using the ListViewItems text alignment (Right). I have tried adding the following properties to teh style but nothing changes:
<Setter Property="TootlTip.HorizontalAlignment" Value="Left" />
<Setter Property="TootlTip.HorizontalContentAlignment" Value="Left" />
<Setter Property="TootlTip.Width" Value="300" />
Is there a way to either increase the ToolTip width so there is no wrapping of the text or make the ToolTip text left aligned.

I got the same problem and after some digging (RibbonToolTip is inheriting alignment from a textbox), I found a solution. It maybe late, but it would benefit others.
What you need to do is to setup you ToolTip style like this:
<Style TargetType="ToolTip">
<Setter Property="TextBlock.TextAlignment" Value="Left"/>
</Style>

Since Tooltip is a ContentControl you can customize its content as you want. Consider the following example:
<Grid>
<Button Content="Hi there!">
<Button.Style>
<Style TargetType="Button">
<Setter Property="ToolTip">
<Setter.Value>
<ToolTip MaxWidth="100" HorizontalContentAlignment="Center">
<TextBlock Text="fsdfasdfasdfasdfasdfasdfadsfasdf fadsfadsf adfa fdasfasdfasdfa adfasd" TextAlignment="Center" HorizontalAlignment="Center" TextWrapping="Wrap"/>
</ToolTip>
</Setter.Value>
</Setter>
</Style>
</Button.Style>
</Button>
</Grid>
You can play around with the content's properties (in this case the TextBlock) in order to achieve the final look you want.

Related

Command binding to top-level MenuItem of Menu doesn't work

I'm learning WPF and developing a dynamic Menu which is driven by data binding of it's ItemsSource to an ObservableCollection. To do this I have a simple MenuItemViewModel and a HierarchicalDataTemplate for automatic binding of MenuItems to it.
The problem I have is that Command property doesn't work for top level menu items. Despite it is set, a MenuItem doesn't react on mouse click and doesn't get disabled if Command cannot be executed. Simply it's like it's not getting bound.
For lower level menu items though, it works as intended. I think this should be a problem of my HierarchicalDataTemplate but I can't find it, because as I see there's no code in template which might affect command binding of only top-level MenuItems.
MenuItemViewModel implements INotifyPropertyChanged and contains following public properties:
string Text
Uri ImageSource
ICommand Command
ObservableCollection<MenuItemViewModel> Children
HierarchicalDataTemplate for MenuItem in my Window.Resources is as follows:
<HierarchicalDataTemplate DataType="{x:Type common:MenuItemViewModel}"
ItemsSource="{Binding Path=Children}">
<HierarchicalDataTemplate.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Command"
Value="{Binding Command}" />
</Style>
</HierarchicalDataTemplate.ItemContainerStyle>
<StackPanel Orientation="Horizontal">
<Image Source="{Binding ImageSource}" />
<TextBlock Text="{Binding Text}" VerticalAlignment="Center"/>
</StackPanel>
</HierarchicalDataTemplate>
Can you please point me on my mistake?
EDIT: Top-level MenuItem doesn't contain any children (i.e. Children collection of associated ViewModel is empty).
Thanks to #sTrenat comment I've come to solution below.
<Menu.Resources>
<!-- cancel sharing of image so Icon will work properly -->
<Image x:Key="MenuIcon" Source="{Binding ImageSource}" x:Shared="False"/>
</Menu.Resources>
<Menu.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}"
BasedOn="{StaticResource {x:Type MenuItem}}">
<Setter Property="Header" Value="{Binding Text}" />
<Setter Property="Icon" Value="{StaticResource MenuIcon}"/>
<Setter Property="Command" Value="{Binding Command}"/>
<Setter Property="ItemsSource" Value="{Binding Children}" />
<!-- centering MenuItem's Header -->
<Setter Property="HeaderTemplate">
<Setter.Value>
<DataTemplate>
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" Content="{Binding}" />
</DataTemplate>
</Setter.Value>
</Setter>
<!-- setting Icon to null when ImageSource isn't specified -->
<Style.Triggers>
<DataTrigger Binding="{Binding ImageSource}"
Value="{x:Null}">
<Setter Property="Icon" Value="{x:Null}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Menu.ItemContainerStyle>

how to trigger visibility change to child controls with a trigger

I have a databound listbox thet generates items in a datatemplate of type wrappanel with other controls within it. I would like to have the behavior to, when I change the visibility to affect differently the controls within the wrappanel
<WrapPanel Orientation="Horizontal" Tag="{Binding .}" HorizontalAlignment="Stretch" Visibility="{Binding editMode, Converter={StaticResource VisibilityConverter}}">
<Label Width="150" Content="{Binding Path=avaiableAttribute.Text}" Name="lblName"/>
<Label Width="150"
Content="{Binding Path=informationItem.ItemString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Initialized="Label_Initialized"
Name="lblText" />
<ContentPresenter MinWidth="200" MaxHeight="200" Content="{Binding ., Converter={StaticResource InformationItemConverter}, Mode=TwoWay}" HorizontalAlignment="Stretch"
Name="cpValue"
Initialized="ContentPresenter_Initialized"/>
<WrapPanel.Style>
<Style TargetType="{x:Type WrapPanel}">
<Style.Triggers>
<Trigger Property="Visibility" Value="Visible" >
<Trigger.Setters>
<Setter TargetName="lblText" Property="Visibility" Value="Collapsed" />
<Setter TargetName="cpValue" Property="Visibility" Value="Visible" />
</Trigger.Setters>
</Trigger>
</Style.Triggers>
</Style>
</WrapPanel.Style>
</WrapPanel>
I just get the following build error
the property 'targetname' does not represent a valid target for the 'setter' because an element
When using a Setter, the TargetName only applies to elements in a Template. This means your Trigger has to exist in a DataTemplate or ControlTemplate. The easiest way to do what you want to do is to create your own IValueConverter for the inverse of BooleanToVisibilityConverter (ie return Visibility.Collapsed when value is true).

Custom ListBox in WPF

I am trying to create a custom ListBox control in WPF for a chat Messenger. I am using an ellipse to show the online/offline user. The ellipse is to be displayed on left and some text in center of the ListBoxItem.
I want to set the ellipse fill propert to red/green based on some variable.
This is what I have done :
<ListBox Name="myList" HorizontalAlignment="Left" Height="232" Margin="117,74,0,0" VerticalAlignment="Top" Width="207">
<ListBox.ItemTemplate>
<DataTemplate>
<DockPanel>
<Ellipse Name="ellipse" Fill="Red" DockPanel.Dock="Left">
<Ellipse.Triggers>
<Trigger Property="{Binding Online}" Value="True">
<Setter TargetName="ellipse" Property="Ellipse.Fill" Value="Green"/>
</Trigger>
</Ellipse.Triggers>
</Ellipse>
<TextBlock Text="{Binding text}"></TextBlock>
</DockPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
and in the code :
myList.Items.Add(new { text="Hello",Online="True" });
I am getting an error as
Cannot find the static member 'FillProperty' on the type 'ContentPresenter'.
What am I doing wrong here?
Obviously this is wrong: Property="{Binding Online}"
Also you should use a Style for triggers, no need to set TargetName, and you need to take precedence into consideration, and use a Setter for the default value.
you are actually misleading WPF with some of these concerns.
Binding A property on trigger will not work. you have to use DataTrigger insteed of Triggers.
Implementing Trigger on the Fly for any control. most of the times not work. So go with Styles.
While you are creating Ellipse in template make sure you have created enough size for it. So that can be visible to users.
try this.
<Window.Resources>
<Style x:Key="elstyle" TargetType="Ellipse">
<Setter Property="Height" Value="5"/>
<Setter Property="Width" Value="5"/>
<Setter Property="Fill" Value="Red"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Online}" Value="true">
<Setter Property="Fill" Value="Green"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid>
<ListBox x:Name="myList" HorizontalAlignment="Left" Height="232" Margin="117,74,0,0" VerticalAlignment="Top" Width="207">
<ListBox.ItemTemplate>
<DataTemplate>
<DockPanel>
<Ellipse Name="ellipse" Margin="5" DockPanel.Dock="Left" Style="{DynamicResource elstyle}">
</Ellipse>
<TextBlock Text="{Binding Name}"></TextBlock>
</DockPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
code behind .
public MainWindow()
{
Random r = new Random();
InitializeComponent();
for (int i = 0; i < 10; i++)
{
myList.Items.Add(new { Name = "Name" + i.ToString(), Online = Convert.ToBoolean(r.Next(-1, 1)) });
}
}
You need to set the initial colour via a Setter and not in XAML. For more information and see this question: Change the color of ellipse when mouse over
there are a few issues in your XAML
set the size of your Ellipse
you need to use Style instead of Ellipse.Triggers
set your Fill color in your Style if you wane be able to change it in XAML by some condition
here an working example for your problem
<DataTemplate>
<!--<DockPanel> juste because i like StackPanel-->
<StackPanel Orientation="Horizontal">
<!--<Ellipse Name="ellipse" Fill="Red" DockPanel.Dock="Left">-->
<Ellipse Name="ellipse" Width="15" Height="15">
<!--<Ellipse.Triggers>-->
<Ellipse.Style>
<Style TargetType="Ellipse">
<Setter Property="Fill" Value="Red"/>
<Style.Triggers>
<!--<Trigger Property="{Binding Online}" Value="True">-->
<DataTrigger Binding="{Binding Online}" Value="True">
<Setter Property="Fill" Value="LimeGreen"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Ellipse.Style>
</Ellipse>
<TextBlock Text="{Binding text}"/>
</StackPanel>
</DataTemplate>

ListBox ItemTemplate based on parent binding value

Basically, I am looking to create a custom template for my listitems. One template will use checkboxes, while the other will use radioboxes. This is meant to emulate when multiselect is allowed or not. However, I have tried many different ways, with the most promising being the DataTemplateSelector, however I need to create a Dependency Property so that I can pass in the boolean IsMultiSelect value. But, I need a DependencyObject within the Selector, and the closest I can get is the contentpresenter. I know I can get the parent control based off of that, but that seems like a hack. Is there any way to accomplish what I am looking to do?
I'm not completely sure if I understood everything correctly, but this may be helpful:
<ListBox SelectionMode="Multiple">
<!--<ListBox SelectionMode="Single">-->
<ListBox.Items>
<TextBlock Text="Test 1" />
<TextBlock Text="Test 2" />
<TextBlock Text="Test 3" />
<TextBlock Text="Test 4" />
<TextBlock Text="Test 5" />
<TextBlock Text="Test 6" />
</ListBox.Items>
<ListBox.Style>
<Style TargetType="{x:Type ListBox}">
<Style.Resources>
<DataTemplate x:Key="SingleSelectionModeItemTemplate">
<RadioButton IsChecked="{Binding Path=IsSelected,
RelativeSource={RelativeSource AncestorType={x:Type ListBoxItem}},
Mode=TwoWay}"
Content="{Binding}" />
</DataTemplate>
<DataTemplate x:Key="MultiSelectionModeItemTemplate">
<CheckBox IsChecked="{Binding Path=IsSelected,
RelativeSource={RelativeSource AncestorType={x:Type ListBoxItem}},
Mode=TwoWay}"
Content="{Binding}" />
</DataTemplate>
</Style.Resources>
<Style.Triggers>
<Trigger Property="SelectionMode"
Value="Single">
<Setter Property="ItemTemplate" Value="{StaticResource SingleSelectionModeItemTemplate}" />
</Trigger>
<Trigger Property="SelectionMode"
Value="Multiple">
<Setter Property="ItemTemplate" Value="{StaticResource MultiSelectionModeItemTemplate}" />
</Trigger>
</Style.Triggers>
</Style>
</ListBox.Style>
</ListBox>

How to set vertical text in the column headers of WPF DataGrid?

Well, actually rotated -90 degrees from horizontal is what I mean.
I need to do this because the text for the header is quite long but the cell value is short, and I want to fit a lot of columns on the screen.
Is it possible to do this easily or do I need to learn about resources and templates first? I don't mind a "hack" solution!
This will rotate the whole ColumnHeaderCell:
<DataGrid.ColumnHeaderStyle>
<Style TargetType="{x:Type DataGridColumnHeader}">
<Setter Property="LayoutTransform">
<Setter.Value>
<RotateTransform Angle="270" />
</Setter.Value>
</Setter>
</Style>
</DataGrid.ColumnHeaderStyle>
Be aware: this means HorizontalContentAlignment is then a VerticalContentAlignment and vice versa.
Here is another way to do it:
<Style x:Key="soDataGrid_ColumnHeaderRotateStyle" TargetType="DataGridColumnHeader" >
<Setter Property="ContentTemplate" >
<Setter.Value>
<DataTemplate>
<TextBlock TextWrapping="Wrap" Text="{Binding}"
FontWeight="Bold" Width="60"
VerticalAlignment="Center" TextAlignment="Center"
HorizontalAlignment="Center">
<TextBlock.LayoutTransform>
<RotateTransform Angle="270" />
</TextBlock.LayoutTransform>
</TextBlock>
</DataTemplate>
</Setter.Value>
</Setter>
<Setter Property="HorizontalContentAlignment" Value="Center" />
</Style>
You can use the style as follows
<DataGridComboBoxColumn Header="Portrait /
Landscape" Width="42"
HeaderStyle="{StaticResource soDataGrid_ColumnHeaderRotateStyle}"
SelectedItemBinding="{Binding Orientation}"
ItemsSource="{Binding Mode=OneWay,
Source={StaticResource languageEnum}}" />
I find this approach gives you a lot of control. It is helpful to use the line break code in long header text.
Unfortunately I have found you need to hardcode the width of the rotated textblock - maybe there is a better way to set this width based on the text content.

Categories

Resources