How to get TreeViewItems to not inherit Tooltip property from their parent - wpf

I have an application in which is a quasi IDE where a TreeView is acting as a solution explorer. What the user is designer is a screen layout which could look like this.
Root
Menus
MainMenu
MenuItem1
Button Bars
MainBar
Button1
I originally had issues with context menus. In the example above MenuItem1 doesn't have a context menu but MainMenu does. Well, MenuItem1 would inherit the context menu from MainMenu. I got by this by creating an empty context menu and assigning it to MenuItem1. I'd like something more elegant though.
I have the same issues with tooltips. If I assign one to MainMenu then MenuItem1 inherits the one assigned to MainMenu. I tried setting the MenuItem1 tooltip to null, did nothing. If I set it to "", an empty string it overrides the MainMenu tooltip but when you hover over MenuItem1 a small empty tooltip box appears. I thought the system would have been smart enough to not show the box if it was an empty string but apparently not.
How can I prevent children from inheriting context menu and tooltip properties from their parents?
Updated
Still having issues with this. I analyzed my items using Snoop and it indicates that these properties are inheirited, but I still don't see any solution to breaking the inheritance.
The only kludge I can think of is that for every tooltip to handle the ToolTipOpening event and inspect the string, if it has no length then jsut close it immediately. There must be a better way though.

I ran into the exact same problem but I found a solution that works for me. I changed the visibility of the tooltip so that it no longer appears for empty strings.
System.Windows.Controls.ToolTip tt = new System.Windows.Controls.ToolTip();
tt.Content = tooltipDescription;
if (tooltipDescription == null)
tt.Visibility = Visibility.Collapsed;
item.ToolTip = tt;

Have you tried setting ToolTipService.IsEnabled="False" this will disable your Tooltip on the desired element.

For myself, I created a style with zero Width and Height:
<Style x:Key="NullToolTip" TargetType="{x:Type ToolTip}">
<Setter Property="Width" Value="0" />
<Setter Property="Height" Value="0" />
<Setter Property="Content" Value="{x:Null}" />
</Style>
When I created ToolTip with this style and placed in resources:
<ToolTip x:Key="NoToolTip" Style="{StaticResource NullToolTip}" />
Then set this ToolTip for each item:
<TreeViewItem Header="Sample" ToolTipService.ToolTip="{StaticResource NoToolTip}">
or in style:
<Setter Property="ToolTipService.ToolTip" Value="{StaticResource NoToolTip}" />
In this case, null ToolTip for item will be default, but when you set our ToolTip, it will be defined only for him.

Firstly masking a tooltip should be done with null and not string.empty. Secondly if you had used hierarchical data template and itemssource binding for your treeview then you could have set tooltips based on your template hierachy (such as bound to a property in your object hierarchy from model or itemssource) in which case they must had taken an effect based on your specific treeview item.
As of now you can use null to mask.

The other answers here all had issues for me so here's the approach I came up with which does avoid child tree items showing the parent item's tip.
Similar to some other answers, I use a style with a setter for the Tooltip property. The key differences are:
Binding the Visibility of a ToolTip element instead of the TextBlock element showing the tip.
Wrapping the TextBlock with a Border element. This avoided occasionally seeing a tiny, empty tip block.
<local:StringToVisibilityConverter x:Key="strToVisibilityConverter"/>
<Style x:Key="MyTreeStyleKey" TargetType="TreeViewItem">
<Setter Property="ToolTip">
<Setter.Value>
<ToolTip Visibility="{Binding TipText, Converter={StaticResource strToVisibilityConverter}}">
<Border>
<TextBlock Text="{Binding TipText}"/>
</Border>
</ToolTip>
</Setter.Value>
</Setter>
</Style>
StringToVisibilityConverter is a simple converter I created which returns Visibility.Collapsed for null or emptry strings, Visibility.Visible otherwise.

<[YourControl].Resources>
<Style TargetType="ToolTip" x:Key="InvisibleToolTip">
<Setter Property="Visibility"
Value="Collapsed" />
</Style>
</[YourControl].Resources>
<[YourControl].ToolTip>
<ToolTip Style="{StaticResource InvisibleToolTip}"/>
</[YourControl].ToolTip>

Related

How do I create a dynamic context menu for ListBoxItems?

I have a style that doesn't seem to be working. In spite of Snoop telling me the DataContext for the ListBoxItem is correct, nothing shows up. If it was a problem with the binding for Commands I would expect to see an empty context menu appear.
The style:
<ContextMenu x:Key="CommandsContextMenu" ItemsSource="{Binding Commands}">
<Style TargetType="MenuItem">
<Setter Property="Header" Value="{Binding Name}"/>
</Style>
</ContextMenu>
<Style TargetType="ListBoxItem">
<Setter Property="ContextMenu" Value="{StaticResource CommandsContextMenu}" />
<Style.Triggers>
<DataTrigger Value="True">
<DataTrigger.Binding>
<Binding Path="DataContext.HasCommands" />
</DataTrigger.Binding>
</DataTrigger>
</Style.Triggers>
</Style>
The snoop DataContext:
The snoop properties showing the ContextMenu property isn't even set.
The idea here, was that without knowing any of the types, I could have a listbox item style where if the thing it was bound to has a property called HasCommands, and it was set to true, then it would set a context menu on that listbox item, bound to the Commands property.
I'm not getting any binding errors or warnings from PresentationTraceSources.DataBindingSource
Why doesn't the context menu get set?
So it turns out, that the problem was using something that inherited from ListBox
for reference here's my defined class:
// adapted from http://stackoverflow.com/questions/3350187/wpf-c-rearrange-items-in-listbox-via-drag-and-drop
// which was probably adapted from http://wpftutorial.net/DragAndDrop.html
type DragDropListBox() as self =
inherit ListBox()
which from there only hooks the following
self.PreviewMouseLeftButtonUp
self.PreviewMouseMove
override x.OnItemsSourceChanged
and in the intialization it overwrites the ItemContainerStyle as follows:
So it turns out, that the problem was using something that inherited from ListBox
for reference here's the top of my class:
// adapted from http://stackoverflow.com/questions/3350187/wpf-c-rearrange-items-in-listbox-via-drag-and-drop
// which was probably adapted from http://wpftutorial.net/DragAndDrop.html
type DragDropListBox() as self =
inherit ListBox()
which from there only hooks the following
self.PreviewMouseLeftButtonUp
self.PreviewMouseMove
override x.OnItemsSourceChanged
and in the intialization it overwrites the ItemContainerStyle as follows:
do
self.PreviewMouseLeftButtonUp.Add listBoxPreviewMouseLeftButtonUp
//self.PreviewMouseLeftButtonDown.Add listBoxPreviewMouseMove //.AddHandler (MouseButtonEventHandler previewMouseMove)
self.PreviewMouseMove.Add listBoxPreviewMouseMove
let style =
let s = Style(typeof<ListBoxItem>)
s.Setters.Add (Setter(ListBoxItem.AllowDropProperty, true))
s.Setters.Add (EventSetter(ListBoxItem.PreviewMouseLeftButtonDownEvent, MouseButtonEventHandler listBoxPreviewMouseLeftButtonDown))
s.Setters.Add (EventSetter(ListBoxItem.DropEvent, DragEventHandler listBoxItemDrop))
s
self.ItemContainerStyle <- style
now I've got to figure out if you can add two styles together.

Issue with WPF styles

I wonder if someone can help, I am designing a custom WPF window for an application i am working on and I have an issue with the Min, Max and Close buttons. I have designed a ControlTemplate for the 3 buttons and they are in a StackPanel with Vertical orientation. In my base style I have the following
<Style x:Key="BaseWindowButtonStyle" TargetType="{x:Type Button}">
<Setter Property="Foreground" Value={Binding RelativeSource={RelativeSource AncestorType={x:Type FrameworkElement}}, Path=(TextElement.Foreground)}" />
<Setter Property="Background" Value="Transparent" />
...
</Style>
Have also tried setting the Foreground to a specific color such as #FF000000 and nothing displays
I have a style that inherits this style for the buttons but does not change the foreground or background.
My problem is that the button content does not display, the button displays and the IsMouseOver trigger fires which changes the background but the textual content never displays.
The Min button style is
<Button x:Name="PART_Min">
<Path Fill="{Binding Path=Foreground, RelativeSource={RelativeSource AncestorType={x:Type Button}}}"
Data="F1M0,6L0,9 9,9 9,6 0,6z"
SnapsToDevicePixels=True" />
</Button>
I am at a loss as to why the content does not display so would appreciate your thoughts.
The Visual Tree is below, I have examined this and identified the Foreground values
Window (Foreground: #FF000000)
Grid
AdornerDecorator
Grid
ContentControl (Foreground: #FF000000)
StackPanel
Button (Foreground: #FF000000)
Grid
ContentControl (Foreground: #FF000000)
But like I said above I have removed the binding and specified a physical value and still do not get the content displaying
Use ContentPresenter instead of ContentControl in your button's template. (You should include the button's control template in a question like this... it's potentially highly relevant.)
As I too was a beginner with WPF, and trying to understand things was a bit of a learning curve, I would like to offer a few previous links posted out here.
First a simple style for creating simple label and having all labels as a default take on this format without explicit style assignments.
Another sample showing creation of a custom button. This is where I went step by step to create a custom class and apply a style to it to show/hide image on a button.
Maybe the button link and style declaration will help you find your button coloring issues too.

Set property after command

I have a StatusBar below the screen in SL4 (using PRISM), just a very simple Telerik RadDockPanel.
I also have a menu (Telerik RibbonView with RadRibbonGroup and RadRibbonToggleButton). When the toggle button is pressed, I want to set the text to 'ON' and 'OFF', and I want to hide the status bar, but... only in XAML (not using code behind).
I believe this is a common SL/WPF coding practice... but how ?
Have to use EventTrigger (check example bellow on page by link that I provided) and ObjectAnimationUsingKeyFrames to change properties that aren't animated (Text, Visibility so on).
Check good example in other answer on so.
You can specify a DataTrigger in your window like this -
<StatusBar.Style>
<Style>
<Style.Triggers>
<DataTrigger
Binding="{Binding ElementName=MyRadRibbonToggleButton, Path=IsChecked}"
Value="True">
<Setter Property="Grid.Visibility" Value="Collapsed" />
</DataTrigger>
</Style.Triggers>
</Style>
</StatusBar.Style>
In case you can't use ElementName binding then you can use a property in your ViewModel(corresponding to RadRibbonToggleButton state). Similar Trigger can be created for a TextBlock/Label to show On/Off text.
This is how I implement this kind of functionality in WPF/MVVM applications;
You may have to apply some hack to make this work with telerik controls.

wpf combobox trigger for Validation.HasError

I've looked around, but can't specifically find my issue. I know that the default "Error" handling within WPF puts an "Adorner" around controls in case there are any errors based on IDataErrorInfo or Validataion rules failing a given control. That's all good and fine, however, with a tabbed page interface, if any controls are so flagged as invalid, they are properly adorned in red border. However, as soon as you go from tab page 1 to 2 and back to 1, all the adorners are gone (bad). This was already asked, and solution accepted, but was looking for a better alternative.
So, I went to my "Themes" declaration, and for the textbox control, I just said to set the entire background color of the control to red and not just the border. Without any fancy forced triggering via Notify on Property Changed, if I swap between pages, the red background of the entire textbox remains constant.
Now, on to the combobox control. For those who have customized their own, or even looked into the default MS version of the control, its actually a clustered mess of controls, grids, columns, buttons, etc to make the magic of combobox work. In brief...
ControlTemplate
Grid (two columns, one for text display of chosen, second column for the drop-down arrow)
Border spanning both columns
Path ( line drawing / glyph for the drop-down image for combobox )
ControlTemplate TargetType Textbox (as part of the entire combobox set)
Border specifically "PART_ContentHost"
ControlTemplate of combobox
Grid
Toggle Button
Dropdown exposed showing list
other triggers..
Finally, the main ComboBox declaration which is templated by above components.
Anyhow, I can't for the life of me get this. In the "Toggle Button" area of the combobox declaration, I have a trigger to change the background to an OBVIOUS off color for proof of testing the trigger working and in the right location within the ControlTemplate declarations.
So, knowing this is the correct place within the combobox declarations, I want to supersede the green background color with red if there's an error with the data. I KNOW the overall "Validation.HasError" is properly getting triggered as the native error handler shows. No matter how / where within the template I try to change the background color to red, it does NOT work. I've even tried doing DataTriggers, using converters, trying multiple properties, but it appears not to be cooperating.
Any suggestions? This is getting really annoying.
FINALLY, got it... and not as obvious as I would have guessed. Anyhow, here's what I've found. If you went with a sample from Microsoft's template of the combobox, they first provide the overall two-column "ToggleButton" declaration
<ControlTemplate TargetType="ToggleButton"
x:Key="baseComboBoxToggleButton" >
... blah blah...
</ControlTemplate>
Then, the declaration for the "Display Value" of the combobox
<ControlTemplate TargetType="TextBox" x:Key="ComboBoxTextBox" >
<Border x:Name="PART_ContentHost" Focusable="False"
Background="{TemplateBinding Background}" />
</ControlTemplate>
Then, tie them together as one Combobox "wrapper" declaration
<ControlTemplate TargetType="ComboBox" x:Key="ComboBoxGridControlTemplate" >
<Grid x:Name="GridComboWrapper">
<!-- This is the dropdown button that POINTS TO THE "baseComboBoxToggleButton at the top -->
<ToggleButton Name="ToggleButton"
Template="{StaticResource baseComboBoxToggleButton}"
Grid.Column="2" Focusable="false"
IsChecked="{Binding Path=IsDropDownOpen, Mode=TwoWay,
RelativeSource={RelativeSource TemplatedParent}}"
ClickMode="Press" >
</ToggleButton>
...
rest of the content presenter,
EDIT(able) textbox area,
popup area of combobox when in drop-down mode
</Grid>
<ControlTemplate.Triggers>
<!-- PUT THE VALIDATION CHECK HERE -->
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip"
Value="{Binding RelativeSource={x:Static RelativeSource.Self},
Path=(Validation.Errors)[0].ErrorContent}"/>
<!-- THIS IS THE CRITICAL COMPONENT... I HAD TO EXPLICITLY TELL
The TagetName as the "ToggleButton" and change ITs Background property
and it now works -->
<Setter TargetName="ToggleButton" Property="Background"
Value="{StaticResource BrushDataInvalidBorder}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
So, now it works as intended and doesn't loose any adorner just because the active page on a given form changes and clears it... its static to each individual control as expected... Wow... what a PITA this one was.
Hope it helps someone ELSE in the future from excessive head banging against a wall while learning this nested level of stuff.

How to set TextAlignment property on a TextBox that is part of a Content of other element?

I have an element witch has TextBlock and TextBox in its Content property. I need to set TextAlignment for TextBox without interfering with the current style of the control, so I should make it work somehow with Setter Property and xaml.
Is this possible and how? I don't want to recreate Content with my own controls, or setting it through style, since style can be changed in runtime, and I want to preserve the styles as they are.
Edit: an example is DataForm. DataForm consists of several DataField elements. Each DataField consists of a Label and input control. Now, normally I could do this
<DataField Label="{Binding ...}">
<TextBox Text="{Binding ...}" TextAlignment="Right" />
</DataField>
But if I do that, I will loose the style of the DataField, which I want to avoid. So, is there a way I can access this TextAlignment property of DataField, so I can set it to Right, but not for all of them, just the ones that I want (ex numeric ones). This is why it should be done on a particular instance of DataField.
So the task: set DataField Text to be aligned right, without interfering with its style, and do this for a particular DataField.
I hope now is more clear.
What about create a specific TextBox Style?
<Style x:Key="AlignedTextBoxStyle" TargetType="TextBox">
<Setter Property="HorizontalAlignment" Value="Right"></Setter>
<Setter Property="VerticalAlignment" Value="Center"></Setter>
</Style>
Doing this way you can set it on xaml by the property "Style"
<TextBox Style="AlignedTextBoxStyle"></TextBox>
And if you are adding it dynamically you can also do this :
TextBox myTb = new TextBox();
myTb.Style = (Style)Application.Current.Resources["AlignedTextBoxStyle"];
I think this is the best way to do that without interfering on your "ListBoxItem" style
Hope it helps

Resources