I created four buttons in WPF. And I use Tooltip for each button. I give an initial delay value to Tooltip. The Initial delay is working when I first time mouse hover to any of these button but when I move one button to another then no initial delay is triggered. I want to disable Tooltip when I move my mouse cursor from one button control to another. But unfortunately it's not working.
<Button
Style="{StaticResource RoundCorner}"
Name="button1"
Width="71"
HorizontalAlignment="Left"
Margin="381,14,0,0"
Height="24"
VerticalAlignment="Top"
UseLayoutRounding="True"
RenderOptions.ClearTypeHint="Enabled"
RenderOptions.BitmapScalingMode="NearestNeighbor"
SnapsToDevicePixels="True"
ToolTipService.InitialShowDelay="1000"
ToolTipService.BetweenShowDelay="0"
ToolTipService.ShowDuration="7000">
<Button.Content>
<TextBlock FontSize="10" FontFamily="Segoe UI" UseLayoutRounding="True" TextOptions.TextFormattingMode="Display">
Help
</TextBlock>
</Button.Content>
<Button.ToolTip>
<ToolTip UseLayoutRounding="True" RenderOptions.ClearTypeHint="Enabled" RenderOptions.BitmapScalingMode="NearestNeighbor" SnapsToDevicePixels="True" TextOptions.TextFormattingMode="Display">
<StackPanel>
<TextBlock FontFamily="Segoe UI" FontSize="12" TextOptions.TextFormattingMode="Ideal" >
Help
</TextBlock>
</StackPanel>
</ToolTip>
</Button.ToolTip>
</Button>
The problem is cause by how much time the cursor spends travelling between elements which have tooltips. If there is no time or a very short time then the InitialShowDelay is ignored. This is essentially because the tooltipservice hasn't closed the first tooltip and it still has a tooltip open. It just picks up the tooltip content of the next button.
The simple solution is a gap between the buttons.
This ensures a previous tooltip has closed before the mouse is over another element with a tooltip.
Hence if you try the below:
<StackPanel>
<Button Content="Apple"
ToolTipService.InitialShowDelay="1000"
ToolTipService.BetweenShowDelay="2"
ToolTip="An Apple"
Margin="5"
/>
<Button Content="Banana"
ToolTip="A Banana"
ToolTipService.InitialShowDelay="1000"
ToolTipService.BetweenShowDelay="2"
Margin="5"
/>
<Button Content="Carrot"
ToolTip="Orange Carrot"
ToolTipService.InitialShowDelay="1000"
ToolTipService.BetweenShowDelay="2"
Margin="5"
/>
</StackPanel>
There's a gap between the buttons and there will be a delay if you move the cursor from the first to second button.
In the picture, notice there is a gap between the buttons.
If you remove that margin I have on all the buttons there will be no gap and the second tooltip will show immediately when you move the mouse from the first to second button.
If you can only have a small gap then a rather more complicated solution is necessary. You need to null or close the tooltip immediately the mouse leaves one of the buttons.
One way to do that would be using a trigger.
Triggers set properties when their logic is satisfied so set the property to null using a style so it's null when the trigger is false.
When the mouse is over the button, set the tooltip to a value.
Thus the button only has a tooltip when the mouse is over it and our first tooltip will be nulled immediately the mouse moves off the button.
You need somewhere to put the tooltip markup and you could add an attached property but the below solution uses the button Tag.
Minimal or No Gap:
<Window.Resources>
<Style TargetType="{x:Type Button}">
<Setter Property="ToolTip" Value="{x:Null}" />
<Setter Property="ToolTipService.InitialShowDelay" Value="1000"/>
<Setter Property="ToolTipService.BetweenShowDelay" Value="0"/>
<Setter Property="Margin" Value="1"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="ToolTip" Value="{Binding Tag, RelativeSource={RelativeSource Self}}" />
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid>
<StackPanel>
<Button Content="Apple">
<Button.Tag>
<TextBlock Text="This textBlock illustrates a more sophisticated tag" FontFamily="Segoe UI" FontSize="12"/>
</Button.Tag>
</Button>
<Button Content="Banana"
Tag="A Banana"
/>
<Button Content="Carrot"
Tag="Orange Carrot"
/>
</StackPanel>
</Grid>
Note that the first button uses dot notation to illustrate how you would put any markup you like inside the tag rather than just the string the second and third buttons use.
If you want a style to incorporate the settings from another style you can use BasedOn:
<Style TargetType="{x:Type Button}" x:Key="RoundCorner">
.....
</Style>
<Style TargetType="{x:Type Button}" BasedOn="{StaticResource RoundCorner}">
This style has no key so it'll be applied to all buttons within it's scope. You could give the style a key if you don't want it applied to some buttons.
I don't know what is wrong with the ToolTipService but the only thing that works for me is to set BetweenShowDelay to a small integer value and then the behavior I'm looking for works. I know the documentation says BetweenShowDelay should be in milliseconds but when I try to use a ms value, tooltips will be immediately displayed as I mouseover one control to the next after the InitialShowDelay as elapsed for one control. I even checked the .NET reference source and I could not find an issue with the code (https://referencesource.microsoft.com/#PresentationFramework/src/Framework/System/Windows/Controls/PopupControlService.cs,dd8bf024ff5d4d74)
This is what works for me:
<StackPanel>
<Button ToolTipService.InitialShowDelay="2000"
ToolTipService.BetweenShowDelay="1"
ToolTip="Say something" />
<Button ToolTipService.InitialShowDelay="2000"
ToolTipService.BetweenShowDelay="1"
ToolTip="Say something else" />
</StackPanel>
After the "Say something" tooltip displays, if I mouseover the next button, the tooltip won't display until the initial delay of 2000ms has elapsed. This isn't how the documentation and reference source says it should work but this is how I got it to work for me to get the intended behavior.
Related
I try to use the metro style from MahApps but buttons becomes not "SizeToContent" or becomes not AutoResized or something like that.
This is my button style
<Style x:Key="GrayMetroButtonStyle" TargetType="Button" BasedOn="{StaticResource LightMetroWindowButtonStyle}">
<Setter Property="Margin" Value="5"/>
<Setter Property="Background" Value="LightGray"/>
<Setter Property="Padding" Value="2"/>
</Style>
This is the style using in WPF. The width of the button's column is larger than the button needed to check autoresize.
<Grid Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="80" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Left" Text="{Binding ManifestFileName}" Margin="5" />
<Button Grid.Column="1" Content="Change" Margin="2" Visibility="{Binding LoadButtonVisibility}" Command="{Binding LoadManifest}" Style="{StaticResource GrayMetroButtonStyle}" />
</Grid>
And result is
As you see, content is out of the buttons width range.
If i clear the style refference, button get a properly behavior.
Which property are rule this behavior? How can i make this buttons autoresizable?
Thanks for replies.
According to the comments in the MahApps sources, LightMetroWindowButtonStyle is intended for the minimize, maximize, and close buttons in the title bar of a Metro-style window. For that reason, the style sets a fixed Width of 34.
It looks like the 'standard' button style for MahApps is MahApps.Metro.Styles.MetroButton, so I would suggest using that instead. It actually looks like this is the style that gets applied by default, as the second image you posted (the button with the style cleared) looks like it has this style already applied.
There are others too, like SquareButtonStyle and AccentedSquareButtonStyle, which are showcased in the MahApps example app:
If you want the 'flat' look, MetroFlatButton looks pretty similar to the button in the first image. You can see the various MahApps button styles in the MahApps docs and see the actual source code on GitHub .
Note that some of the button styles force their content to display in lowercase. If you want to avoid this, you can extend one of the MahApps styles and override a single property, e.g.:
<Style x:Key="SquareButtonStyleNormalCase"
BasedOn="{StaticResource SquareButtonStyle}"
TargetType="Button">
<Setter Property="m:ControlsHelper.ContentCharacterCasing"
Value="Normal" />
</Style>
Is it possible to display tooltip constantly, not depending on whether the control is focused ot not, but depending only on the value of the bind property.
<TextBox Name="projectTextBox"
ToolTipService.Placement="Bottom" ToolTipService.ShowDuration="12000"
MinWidth="150" Text="{Binding ProjectName}" IsEnabled="{Binding IsEnabled}">
<TextBox.ToolTip>
<ToolTip Placement="Bottom"
StaysOpen="True" Content="TEXT"
Visibility="{Binding IsNotFound, Converter={StaticResource booleanToVisibilityCollapsedConverter}}"
IsOpen="True">
</ToolTip>
</TextBox.ToolTip>
</TextBox>
you should use an adorner for the behavior you are looking for. you can use a datatrigger or what you want to show the adorner as long as you want. btw with an adorner you did not have the problems popups have, while moving the mainwindow.
Why not set the tooltip based on a trigger?
<TextBox Name="projectTextBox" ToolTipService.Placement="Bottom" ToolTipService.ShowDuration="12000" MinWidth="150" Text="{Binding ProjectName}" IsEnabled="{Binding IsEnabled}">
<TextBox.Style>
<Style TargetType="{x:Type TextBox}">
<Style.Triggers>
<DataTrigger Binding="{Binding IsNotFound}" Value="False">
<Setter Property="ToolTip">
<Setter.Value>
<ToolTip Placement="Bottom" StaysOpen="True" Content="TEXT" IsOpen="True" />
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
Basically, you cannot force the tooltip to be shown constantly, because Windows is the one who decides when the tooltip hides (usually on MouseLeave or after some amount of time) to keep the look and feel of the applications consistent (The tooltip control is made to act this way).
If you want to display some helpful information to the user in a way that differs from the standard Windows tooltip way, you should consider using something else than a ToolTip, maybe a Popup or something similar with the FormNotification control from this article.
You can consider using Popup instead. Or if you are using material design for WPF, you can consider using PopupBox.
I know I late for the party this time.
Is there a reason why the text content of a WPF button is appearing with unwanted space above the text?
I have a button in a StackPanel. This button is a simple close button so I want it to appear as a square button with an "x" centred in it. I have set my padding to zero and set both the HorizontalContentAlign and VerticalContentAlign to Center but the "x" still appears at the bottom of the button (or even truncated should I have my FontSize too big relative to my Height). It's as if there is some padding at the top of the button that prevents the text using the full vertical space.
My XAML is:
<StackPanel Orientation="Horizontal">
<Label Content="{Binding GenderFilter.Title}" FontWeight="Bold" Width="60" />
<Button Padding="0" Width="15" Height="15" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" FontSize="12">x</Button>
<Label Content="{Binding GenderFilterExpander.SelectedValue}" />
</StackPanel>
The problem is exactly the same should I set my button VerticalContentAlign property to either Stretch or even Top. If I remove the Height and Weight properties so that the button determines its own then the control does not appear as a square but an upright rectangle and the "x" is still not centred.
UPDATE:
While the button and content are now both centred perfectly the button is still being displayed far bigger than it needs to be, as if a high Padding is being applied.
My resource style definition is now as follows:
<Style x:Key="ClearFilterButtonStyle" TargetType="{x:Type Button}">
<Setter Property="Padding" Value="0" />
<Setter Property="Width" Value="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=ActualHeight}" />
<Setter Property="HorizontalContentAlignment" Value="Center" />
<Setter Property="VerticalContentAlignment" Value="Center" />
<Setter Property="Content" Value="X" />
<Setter Property="FontSize" Value="8" />
</Style>
The button itself is defined as:
<Expander.Header>
<StackPanel Orientation="Horizontal">
<Label Content="{Binding GenderFilter.Title}" FontWeight="Bold" Width="60" />
<Button Style="{StaticResource ClearFilterButtonStyle}"
Visibility="{Binding GenderFilterExpander.ClearFilterVisibility}" />
<Label Content="{Binding GenderFilterExpander.SelectedValue}"
Visibility="{Binding GenderFilterExpander.SelectedValueVisibility}" />
</StackPanel>
</Expander.Header>
The Expander being within a StackPanel within a GroupBox within a DockPanel.
In the design view the buttons are correctly sized, only just containing the X, but at run-time they become enlarged. How can I correct that?
A simple way to accomplish this with a lower case "x" is to use a TextBlock as content and play with its upper margin:
<Button Command="{Binding myCommand}">
<TextBlock Text="x" Margin="0,-3,0,0"/>
</Button>
My guess is that you see this problem because there's a conflict between the height you gave it and the space it actually needs to render itself properly.
Things you can do to solve this:
Play with the Padding property of the button. This controls the space between the text and the button borders.
Reduce the font size.
Don't put an explicit height on the button, or at least give it a height large enough to accommodate for the font size and padding you want.
Also, as mentioned by Heinzi in the comments you should of course use an uppercase X.
By the way, here's a trick you can use if you want to make the button be a proper square while making sure the button gets the size it needs.
<Button Padding="0"
Width="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=ActualHeight}"
HorizontalContentAlignment="Center" VerticalContentAlignment="Center"
FontSize="12" Content="X" />
This effectively lets the button decide what height it needs and then you set the Width to that calculated value.
I have a Button template to define what my edit button should look like:
<ControlTemplate x:Key="EditButton" TargetType="{x:Type Button}">
<Button Style="{StaticResource GrayOutButtonStyle}" >
<Image x:Name="editImage" Source="{StaticResource EditIcon}" />
</Button>
</ControlTemplate>
I want to declare the Command in the XAML where I create the button (not in the template). But when I set the Command attribute in the Button, it's being ignored:
<Button Template="{StaticResource EditButton}"
Command="{Binding Source={StaticResource ViewModel}, Path=EditCommand}"
CommandParameter="{Binding}" />
What's wrong with my syntax?
(Note: If I remove the template from this button then the Command works, so it's something about using a template that's messing it up.).
Why does your Button template include another Button? That makes no sense and would suffer from a StackOverflow if your template was applied implicitly. It should just be the Image, in which case your command should work.
To be clear, what's happening is you have an outer Button which correctly has the ICommand applied to it. However, it's being rendered as another Button with an Image inside it. Hence, when you click the Image, you're actually clicking the inner Button, which has no ICommand associated with it. The outer Button never "sees" the click, so it never executes the command.
Your only other option, which I wouldn't recommend, but should work, is to have the inner button bind to the properties on the outer button:
<ControlTemplate ...>
<Button Command="{TemplateBinding Command}" CommandParameter="{Binding CommandParameter}" ...>
<Image .../>
</Button>
</ControlTemplate>
Another option to refer to the command is relative binding as below:
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Button Command="{Binding Path=Command, RelativeSource={RelativeSource AncestorType=Button}}">
<Image... />
</Button>
</ControlTemplate>
</Setter.Value>
</Setter>
Problem
We need to efficiently display a large (>1000) number of objects in a WPF ListBox control.
We are relying on the WPF ListBox’s virtualization (via VirtualizingStackPanel) to efficiently display these items.
Bug: The WPF ListBox control does not display items correctly when using virtualization.
How to Reproduce
We have distilled the problem to the standalone xaml shown below.
Copy and paste the xaml into XAMLPad.
Initially, there is no selected item in the ListBox, so as expected, all items are the same size and they completely fill the available space.
Now, click on the first item.
As expected, because of our DataTemplate, the selected item will expand to show additional information.
As expected, this causes the horizontal scrollbar to appear, since the selected item is now wider than the available space.
Now use the mouse to click and drag the horizontal scrollbar to the right.
Bug: the non-selected visible items no longer stretch to fill the available space. All the visible items should be the same width.
Is this a known bug?
Is there any way to fix this, either via XAML or programmatically?
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" >
<Page.Resources>
<DataTemplate x:Key="MyGroupItemTemplate">
<Border Background="White"
TextElement.Foreground="Black"
BorderThickness="1"
BorderBrush="Black"
CornerRadius="10,10,10,10"
Cursor="Hand"
Padding="5,5,5,5"
Margin="2"
>
<StackPanel>
<TextBlock Text="{Binding Path=Text, FallbackValue=[Content]}" />
<TextBlock x:Name="_details" Visibility="Collapsed" Margin="0,10,0,10" Text="[xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx]" />
</StackPanel>
</Border>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type ListBoxItem}},Path=IsSelected}"
Value="True">
<Setter Property="TextElement.FontWeight"
TargetName="_details"
Value="Bold"/>
<Setter Property="Visibility"
TargetName="_details"
Value="Visible"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</Page.Resources>
<DockPanel x:Name="LayoutRoot">
<Slider x:Name="_slider"
DockPanel.Dock="Bottom"
Value="{Binding FontSize, ElementName=_list, Mode=TwoWay}"
Maximum="100"
ToolTip="Font Size"
AutoToolTipPlacement="BottomRight"/>
<!--
I want the items in this ListBox to completly fill the available space.
Therefore, I set HorizontalContentAlignment="Stretch".
By default, the WPF ListBox control uses a VirtualizingStackPanel.
This makes it possible to view large numbers of items efficiently.
You can turn on/off this feature by setting the ScrollViewer.CanContentScroll to "True"/"False".
Bug: when virtualization is enabled (ScrollViewer.CanContentScroll="True"), the unselected
ListBox items will no longer stretch to fill the available horizontal space.
The only workaround is to disable virtualization (ScrollViewer.CanContentScroll="False").
-->
<ListBox x:Name="_list"
ScrollViewer.CanContentScroll="True"
Background="Gray"
Foreground="White"
IsSynchronizedWithCurrentItem="True"
TextElement.FontSize="28"
HorizontalContentAlignment="Stretch"
ItemTemplate="{DynamicResource MyGroupItemTemplate}">
<TextBlock Text="[1] This is item 1." />
<TextBlock Text="[2] This is item 2." />
<TextBlock Text="[3] This is item 3." />
<TextBlock Text="[4] This is item 4." />
<TextBlock Text="[5] This is item 5." />
<TextBlock Text="[6] This is item 6." />
<TextBlock Text="[7] This is item 7." />
<TextBlock Text="[8] This is item 8." />
<TextBlock Text="[9] This is item 9." />
<TextBlock Text="[10] This is item 10." />
</ListBox>
</DockPanel>
</Page>
I spent more time attempting this than I probably should have, and couldn't get it to work. I understand what's going on here, but in pure XAML, I'm having trouble figuring out how to solve the issue. I think I see how to solve the problem, but it involves a converter.
Warning: Things are going to get complicated as I explain my conclusions.
The underlying problem comes from the fact that the Width of the controls stretch to the Width of their container. When virtualization is enabled, the Width will not change. In the underlying ScrollViewer inside of ListBox, the ViewportWidth property corresponds to the Width you see. When another control stretches out further (you select it), the ViewportWidth is still the same, but the ExtentWidth shows the full width. Binding the width of all controls to that of the ExtentWidth should work...
But it doesn't. I set the FontSize to 100 for quicker testing in my case. When an item is selected, ExtentWidth="4109.13. Going down the tree to your ControlTemplate's Border, I see ActualWidth="4107.13". Why the 2 pixel difference? ListBoxItem contains a Border with 2 Pixel padding, causing the ContentPresenter to render slightly smaller.
I added the following Style with help from here to allow me to directly access the ExtentWidth:
<Style x:Key="{x:Type ListBox}" TargetType="ListBox">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBox">
<Border
Name="Border"
Background="White"
BorderBrush="Black"
BorderThickness="1"
CornerRadius="2">
<ScrollViewer
Name="scrollViewer"
Margin="0"
Focusable="false">
<StackPanel IsItemsHost="True" />
</ScrollViewer>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="false">
<Setter TargetName="Border" Property="Background"
Value="White" />
<Setter TargetName="Border" Property="BorderBrush"
Value="Black" />
</Trigger>
<Trigger Property="IsGrouping" Value="true">
<Setter Property="ScrollViewer.CanContentScroll" Value="false"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Note I added a name to ScrollViewer for this purpose.
Then, I attempted to bind the Width of your Border to the ExtentWidth:
Width="{Binding ElementName=scrollViewer, Path=ExtentWidth}"
However, because of that 2 pixel padding, the controls will resize in an infinite loop, with the padding adding 2 pixels to the ExtentWidth, which resizes the Border width, which adds 2 more pixels to the ExtentWidth, etc. until you delete the code and refresh.
If you added a Converter that subtracted 2 from the ExtentWidth, I think this might work. However, when the scroll bar does not exist (you have not selected anything), ExtentWidth="0". Thus, binding to MinWidth instead of Width may work better so the items appear correctly when no scroll bar is visible:
MinWidth="{Binding ElementName=scrollViewer, Path=ExtentWidth, Converter={StaticResource PaddingSubtractor}}"
A better solution would be if you could directly databind the MinWidth of the ListBoxItem itself. You could bind directly to ExtentWidth, and no converter would be necessary. However I have no idea how to get access to that item.
Edit: For organization sake, here's the clip required to do that. Makes everything else unnecessary:
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="MinWidth" Value="{Binding Path=ExtentWidth, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ScrollViewer}}}" />
</Style>
Thanks to Will's great analysis!
Based on Will's suggestion: "A better solution would be if you could directly databind the MinWidth of the ListBoxItem itself...However I have no idea how to get access to that item", I was able to implement that using pure xaml, as follows:
<ListBox x:Name="_list"
Background="Gray"
Foreground="White"
IsSynchronizedWithCurrentItem="True"
TextElement.FontSize="28"
HorizontalContentAlignment="Stretch"
ItemTemplate="{DynamicResource MyGroupItemTemplate}">
<!-- Here is Will's suggestion, implemented in pure xaml. Seems to work.
Next problem is if you drag the Slider to the right to increase the FontSize.
This will make the horizontal scroll bar appear, as expected.
Problem: the horizontal scroll bar never goes away if you drag the Slider to the left to reduce the FontSize.
-->
<ListBox.Resources>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="MinWidth" Value="{Binding Path=ExtentWidth, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ScrollViewer}}}" />
</Style>
</ListBox.Resources>
<TextBlock Text="[1] This is item 1." />
<TextBlock Text="[2] This is item 2." />
<TextBlock Text="[3] This is item 3." />
<TextBlock Text="[4] This is item 4." />
<TextBlock Text="[5] This is item 5." />
<TextBlock Text="[6] This is item 6." />
<TextBlock Text="[7] This is item 7." />
<TextBlock Text="[8] This is item 8." />
<TextBlock Text="[9] This is item 9." />
<TextBlock Text="[10] This is item 10." />
</ListBox>
I got the idea from Adam Nathan's great book, "Windows Presentation Foundation Unleashed".
So, this seems to fix the original problem.
New Problem
You notice that there is a Slider control in the xaml that let's you increase/decrease the ListBox font. The idea here was to allow the user the ability to scale the ListBox content up or down for easier visibility.
If you first drag the Slider to the right to increase the FontSize, this will make the horizontal scroll bar appear, as expected. The new problem is that the horizontal scroll bar never goes away if you drag the Slider to the left to reduce the FontSize.
Any ideas?