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.
Related
I have a WPF app with the follow XAML
<CheckBox IsChecked="{Binding IsTime}" />
<TextBlock Text="Time" />
What I'd like is if the user clicks the textbox, it will toggle the CheckBox. In the same way we can use label and a checkbox in MVC.NET (well, HTML)
I could do this with events in the code behind but I'm using MVVM and as such, don't want to use those events.
The difference between my question and Change a Label's behavior to support toggling by click in WPF is that I'm already binding my CheckBox to something...
I hope this effort provides a clearer idea on what I'm trying to do
<CheckBox IsChecked="{Binding IsTime}" x:Name="Checky" />
<TextBlock Text="Time">
<TextBlock.Style>
<Style>
<Style.Triggers>
<Trigger Property="TextBlock.MouseDown" Value="True">
<Setter TargetName="Checky" Property="IsChecked" Value=Not IsTime> //WHAT TO DO HERE
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
You can put the TextBox and other controls inside the CheckBox. This way clicking the TextBox will also toggle the CheckBox. Here's an example with multiple items inside the CheckBox, but you could of course have only the TextBox inside it.
<CheckBox Padding="2" IsChecked="{Binding IsTime}">
<StackPanel Orientation="Horizontal">
<Image Width="24" Height="24" Source="someimage"/>
<TextBlock Text="Time"/>
</StackPanel>
</CheckBox>
Use the code in the link you have provided and bind IsChecked to the same property:
<StackPanel>
<CheckBox x:Name="checkbox" IsChecked="{Binding IsTime}"/>
<CheckBox IsChecked="{Binding IsTime}" Content="Hello">
<CheckBox.Template>
<ControlTemplate TargetType="CheckBox">
<ContentPresenter/>
</ControlTemplate>
</CheckBox.Template>
</CheckBox>
</StackPanel>
You know that you have a Content property on the CheckBox which can be templated to achieve the desired effect without having to use multiple CheckBox-es.
I'm learning WPF/MVVM and I have a combo box that updates several text boxes based on the selected combo box value. What I would like to do is highlight some of the thext boxes if they contain a certain value. The text boxes do not receive focus and are read only. What is the best way to go about something like this using MVVM?
EDIT:
Thank you for the idea of using a Trigger. I'm actually trying to set the border color of the text box based on the value of the same text box. Based on the information provided below, this is what my text box looks like, but when the word "Transaction" shows up in the textbox, the border doesn't change. Am I referencing something incorrectly?
<TextBox Height="23" HorizontalAlignment="Left" VerticalAlignment="Top" Width="170" Grid.Column="4" Grid.Row="2" Margin="4,2,0,0" IsEnabled="False" DataContext="{Binding SelectedTDetails}" Text="{Binding CType}" Background="WhiteSmoke" Padding="1,0,0,0" >
<TextBox.Style>
<Style TargetType="TextBox">
<Style.Triggers>
<Trigger Property="Text" Value="Transaction">
<Setter Property="BorderBrush" Value="LightGreen" />
</Trigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
here is a sample using Triggers in TextBox
while using this sample trigger the same by typing item 2 in TextBox or select it from ComboBox
<StackPanel>
<ComboBox x:Name="combo">
<ComboBoxItem>item 1</ComboBoxItem>
<ComboBoxItem>item 2</ComboBoxItem>
</ComboBox>
<TextBox Text="{Binding SelectedItem.Content,ElementName=combo}">
<TextBox.Style>
<Style TargetType="TextBox">
<Style.Triggers>
<Trigger Property="Text"
Value="item 2">
<Setter Property="Background"
Value="LightGreen" />
</Trigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
</StackPanel>
in above example the TextBox is bound to ComboBox's Selected Item (just to simulate your app's behavior)
The Trigger in TextBox's style look if the Text property contains item 2 and it changes the Background color of TextBox to simulate the highlight
This is just a basic idea on triggers, you may use your creativity to implement the desired highlight behavior perhaps involving flashing color, animated sizing etc.
I have a ListView inside a DockPanel. I need the ListView to be stretched horizontally and centered vertically inside the DockPanel. When the ListView is disabled (IsEnabled=false) I need to change the DockPanel's background to match the disabled ListView's background color.
So, basically I want to avoid the situation that can be seen on the picture below.
<Window>
<DockPanel IsEnabled="False">
<ListView IsEnabled="False" VerticalAlignment="Center" HorizontalContentAlignment="Center">
<ListViewItem Content="AAA"/>
<ListViewItem Content="BBB"/>
<ListViewItem Content="CCC"/>
</ListView>
</DockPanel>
</Window>
However, I don't want to explicitly declare colors anywhere in my code, because I don't know what kind of background color will a ListView use in different Windows environments (I'm not sure, but I guess different Windows theme/color settings can alter the default background color of the ListView - which potentially could be different from the color that I don't want to explicitly declare).
Binding the ListView's background color to the DockPanel's background color doesn't work.
So, right now I'm using the following workaround.
<DockPanel.Style>
<Style TargetType="{x:Type DockPanel}">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=parametersListView, Path=IsEnabled}" Value="False">
<Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</DockPanel.Style>
I've extracted the resource key that my ListView's using in its ControlTemplate. On the template there is a Trigger declared on the IsEnabled property and it sets the control's background color to the color represented by the SystemColors.ControlBrushKey key.
This seems to work, but I'm not sure if this is the right way to do this.
Is there a way to do this in a more robust fashion, or this is the best that I could do?
(On a side note: using SystemColors.ControlBrush instead of SystemColors.ControlBrushKey produces a different shade of grey, I'm not sure why.)
Why not use LstFilledChild =true
Edit:
If you name your element and use the ElementName in the binding it should work.
<DockPanel HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="{Binding Background, ElementName=myList}">
<ListView Name="myList" IsEnabled="False" HorizontalAlignment="Center" HorizontalContentAlignment="Center" >
<ListViewItem>AAA</ListViewItem>
</ListView>
</DockPanel>
Since I've asked my question the application I was working on is live and it's using the solution I've originally come up which is the following. It works well for the moment.
<Window>
<DockPanel IsEnabled="False">
<DockPanel.Style>
<Style TargetType="{x:Type DockPanel}">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=myListView, Path=IsEnabled}"
Value="False">
<Setter Property="Background"
Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</DockPanel.Style>
<ListView IsEnabled="False"
VerticalAlignment="Center"
HorizontalContentAlignment="Center"
x:Name="myListView"
x:FieldModifier="private">
<ListViewItem Content="AAA"/>
<ListViewItem Content="BBB"/>
<ListViewItem Content="CCC"/>
</ListView>
</DockPanel>
</Window>
We have a custom canvas which has specialized nodes that behave a lot like a standard MDI application's windows. The desired behavior is that if any of the child controls of the "window" have the focus, then that "window" is said to be active.
Now the IsFocused property doesn't seem to cascade, meaning if a child control has the focus, it's container is not also set to 'focused' so we can't use that. For the same reason, we can't set the IsFocused property on the container as I believe that would steal it from the child.
My only thought is to create a new DP called HasChildWithFocus or something like that, then in the code-behind, listen for the bubbled events and set that flag. Not sure that's the best way to go. (We may implement that as a combination attached property/attached behavior kinda thing.)
But of course it would be much better if we could simply ask a control 'Hey... do you or any of your children have the focus?'
So can you?
You can use UIElement.IsKeyboardFocusWithin directly like this:
<Grid>
<Grid.Resources>
<Style x:Key="panelStyle" TargetType="Border">
<Setter Property="BorderBrush" Value="PaleGoldenrod"/>
<Style.Triggers>
<Trigger Property="IsKeyboardFocusWithin" Value="True">
<Setter Property="BorderBrush" Value="PaleGreen"/>
</Trigger>
</Style.Triggers>
</Style>
</Grid.Resources>
<UniformGrid Columns="2">
<Border BorderThickness="10" Style="{StaticResource panelStyle}">
<StackPanel>
<TextBox Text="TextBox1"/>
<TextBox Text="TextBox2"/>
</StackPanel>
</Border>
<Border BorderThickness="10" Style="{StaticResource panelStyle}">
<StackPanel>
<TextBox Text="TextBox3"/>
<TextBox Text="TextBox4"/>
</StackPanel>
</Border>
</UniformGrid>
</Grid>
In this example the border that contains the element with the keyboard focus is styled with a different border brush.
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?