C# WPF: Changing PlacementTarget of a ToolTip - c#

I'm trying to change the PlacementTarget of a ToolTip to a window further up the visual tree in order to have custom ToolTip clipping effects in that window. I've hooked everything up except for the PlacementTarget. Here's an example from XAML and in code...neither work. This style is currently being used for a single tooltip attached to a TextBox.
<Style TargetType="ToolTip">
<Setter Property="ToolTipService.PlacementTarget"
Value="{Binding RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType={x:Type Grid }} }" />
</Style>
If I go into code and look at the tooltip.PlacementTarget once it's attached to something...it's always set to the TextBox. I've tried multiple ways of using the VisualTree to get different UIElements. Nothing seems to work...so I'm assuming I'm not understanding or missing something.
The thing that really gets me is that if I go into my code and look at the PlacementTarget of the tooltip, it won't let me set it to anything else. For instance:
var ancestors = toolTip.PlacementTarget.GetSelfAndAncestors();
foreach(var ancestor in ancestors)
{
if(var ancestor is Grid)
{
// Conditional always hits.
// Before this line, PlacementTarget is a TextBox.
toolTip.PlacementTarget = (UIElement)ancestor;
// After the line, PlacementTarget is still a TextBox.
}
}
What am I doing incorrectly or not understanding?
Edit for Context: The custom clipping effect is to basically just find the closest ancestor window to the ToolTip's target and use that to make sure the ToolTip never goes outside the bounds of that window.

A short sample setting a Tooltip, using a property on the parent Window as PlacementTarget.
<Window x:Class="WpfApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Tag="Bar">
<Window.Resources>
<ToolTip x:Key="FooToolTip">
<StackPanel>
<TextBlock Text="{Binding PlacementTarget.Tag, RelativeSource={RelativeSource AncestorType={x:Type ToolTip}}}"/>
</StackPanel>
</ToolTip>
</Window.Resources>
<Grid>
<TextBlock
Text="Foo"
ToolTip="{StaticResource FooToolTip}"
ToolTipService.PlacementTarget="{Binding RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
HorizontalAlignment="Center" VerticalAlignment="Center" Height="20" Width="50">
</TextBlock>
</Grid>
</Window>
EDIT
To answer your questions,
the first snippet uses ToolTipService in the wrong way:
The ToolTipService class attached properties are used to determine the placement, behavior, and appearance of a tooltip. These properties are set on the element that defines the tooltip.
Applied in a style:
<Window x:Class="WpfApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525"
Tag="Bar">
<Window.Resources>
<ToolTip x:Key="FooToolTip">
<StackPanel>
<TextBlock Text="{Binding PlacementTarget.Tag, RelativeSource={RelativeSource AncestorType={x:Type ToolTip}}}"/>
</StackPanel>
</ToolTip>
<Style x:Key="ToolTipStyle">
<Setter Property="ToolTipService.ToolTip" Value="{StaticResource FooToolTip}"/>
<Setter Property="ToolTipService.PlacementTarget" Value="{Binding RelativeSource={RelativeSource AncestorType={x:Type Window}}}"/>
</Style>
</Window.Resources>
<Grid>
<TextBlock
Text="Foo" Style="{StaticResource ToolTipStyle}"
HorizontalAlignment="Center" VerticalAlignment="Center" Height="20" Width="50">
</TextBlock>
</Grid>
</Window>
As for your second snippet in code behind, you can't set the PlacementTarget once the ToolTip is open and when the ToolTip is closed the PlacementTarget is null. As #mm8 pointed out, this has to do with the ToolTip and the PlacementTarget being in different visual trees, since a ToolTip spawns
a Window of its own.

Related

How to 'Disable' a WPF UserControl?

I have a UserControl, and I need to change its appearance when 'IsEnabled' is false. I know this is pretty basic WPF styling, but I can't seem to put the pieces together. I assume that I need to create a Style, and add a trigger for "Property="IsEnabled" Value="False". But where do I put the Style definition? (<UserControl.Resources>?) How do I apply it? ... in the UserControl or in the Parent window? Does my trigger need to be inside a Setter element? Does the style need to be applied to the UserControl or to its children? I don't know what other questions I need to ask!
If you feel this is a duplicate of another question, please direct me to it.
If you know of a good, simple tutorial on WPF styling that would answer my questions, I would be very grateful to hear of it.
My code looks like this:
<UserControl x:Class="UserControls.UCDemo"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:UserControls"
mc:Ignorable="d"
d:DesignHeight="240" d:DesignWidth="200">
<UserControl.Resources>
</UserControl.Resources>
<Grid Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="5*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid Grid.Row="0" Background="{Binding BG}" >
<Ellipse Grid.Row="0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="5" Fill="DeepSkyBlue" />
</Grid>
<Grid Grid.Row="1" Background="Tomato" />
</Grid>
<uc:UCDemo x:Name="Demo" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="300,100,0,0" Width="80" Height="100" Style="{StaticResource uc:DemoStyle}"
Visibility="{Binding LightVisible, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Converter={StaticResource BooleanToVisibilityConverter}}" />
Thank you for your help!
It depends on how exactly the appearance should change when IsEnabled is false.
A simple Style with a Trigger could be assigned directly to the UserControl's Style property. The one below just sets the Opacity to 0.5.
<UserControl ...>
<UserControl.Style>
<Style TargetType="UserControl">
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Opacity" Value="0.5"/>
</Trigger>
</Style.Triggers>
</Style>
</UserControl.Style>
<Grid Background="Transparent">
...
</Grid>
</UserControl>
I'm not sure I get what your question is. However in order to change the IsEnabled property of a control, depending on an other controls is:
Let's suppose that the control which its IsEnabled must be changed, is A and the control which A is depends on, is B.
First, choose a name for B: x:Name = "AccessCheckBox".
Then Write a binding like this for control B:
< ... IsEnabled = {Binding ElementName = "AccessCheckBox", Path="IsChecked".../>
Done. I hope I got what you mean.

Accidently instaniating an viewmodel twice in XAML? [duplicate]

<UserControl x:Class="WatermarkTextBox"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="30"
d:DesignWidth="250">
<UserControl.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
</UserControl.Resources>
<Border>
<Grid x:Name="grid">
<TextBlock Text="{Binding Watermark, FallbackValue=This prompt dissappears as you type...}"
Visibility="{Binding ElementName=txtUserEntry, Path=Text.IsEmpty, Converter={StaticResource BooleanToVisibilityConverter}}" />
<TextBox Name="txtUserEntry"
Text="{Binding Text, UpdateSourceTrigger=PropertyChanged}" />
</Grid>
</Border>
</UserControl>
The above code shows my WatermarkTextBox control. In the code behind file I have set the DataContext. I left out all the code for the DP's of the control.
public WatermarkTextBox()
{
InitializeComponent();
grid.DataContext = this;
}
I had to bind the DataContext to the grid because otherwise the Text properties of both the watermark and actual text wouldn't display. The problem now is that I can't set the Background of the Border outside of the Grid.
I tried the code below but then only the Background of the Border is set and not the watermark and actual text.
public WatermarkTextBox()
{
InitializeComponent();
this.DataContext = this;
grid.DataContext = this;
}
In a UserControl like this you should never exlicitly set the DataContext to this or anyting else, because the DataContext is usually set externally when you use the UserControl somewhere in your application. The externally applied DataContext is typically (part of) the application's view model.
You should instead change your internal bindings so that they use an explicit RelativeSource:
<TextBlock
Text="{Binding Path=Watermark,
RelativeSource={RelativeSource AncestorType=UserControl},
FallbackValue=This prompt dissappears as you type...}"
Visibility="{Binding ElementName=txtUserEntry,
Path=Text.IsEmpty,
Converter={StaticResource BooleanToVisibilityConverter}}" />
<TextBox
Name="txtUserEntry"
Text="{Binding Path=Text,
UpdateSourceTrigger=PropertyChanged,
RelativeSource={RelativeSource AncestorType=UserControl}}" />
and then remove any DataContext assignment from the UserControl's constructor.
See e.g. this answer (and many other similar) that discuss this topic in detail.

Use Style in TreeView HierarchicalDataTemplate

I'm new to this and can't quit get the correct syntax. This works correctly to capture the Left Mouse click on the textbox within the treeview:
<HierarchicalDataTemplate
DataType="{x:Type r:NetworkViewModel}"
ItemsSource="{Binding Children}"
>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding NetworkIP}" Width="110" >
<TextBlock.InputBindings>
<MouseBinding MouseAction="LeftClick"
Command="{Binding DataContext.SelectItem, RelativeSource={RelativeSource FindAncestor, AncestorType=TreeView}}"
CommandParameter="{Binding}" />
</TextBlock.InputBindings>
</TextBlock>
</StackPanel>
</HierarchicalDataTemplate>
How can this be done using a Style block in the Resources?
The goal being to use the same style for all TextBoxes in the TreeView. Something that would sit in the Usercontrol.Resources and be refrenced by the HierarchicalDataTemplate.
If I understand you correctly, you could define a template in the controls or windows resources with a target type (opposed to key x:Key=...) to have it automatically applied to all items in the tree view.
Here is a small example with a definition of a template in the window resources, which contains the InputBindings definition. This template will be automatically applied to all objects of type ItemViewModel if no other template is explicitly defined by the ItemsControl or TreeView. In this example, the items are displayed in a simple ItemsControl but it works for the TreeView just the same.
Note that for this to work, all items in the TreeView need to be of the same type. It is not sufficient if they are derived from the same base type. The template will only be applied, if the type defined in Template.DataType is exactly the same as the type of the ViewModel. If your TreeViews ItemsScources contain mixed type, you would need to specify the template for every type separately.
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:loc="clr-namespace:WpfApplication2"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<DataTemplate DataType="{x:Type loc:ItemViewModel}">
<TextBlock Text="{Binding Name}" Width="110" >
<TextBlock.InputBindings>
<MouseBinding
MouseAction="LeftClick"
Command="{Binding SelectItem}" />
</TextBlock.InputBindings>
</TextBlock>
</DataTemplate>
</Window.Resources>
<Grid>
<ItemsControl ItemsSource="{Binding Items}" />
</Grid>
</Window>

How do I bind opacity of specific GridView-Items with a double property of its items-objects Win8.1 App, C#

I have a scedule with subjects in it. When I turn on a specific mode I want to change the opacity of certain subjects.
Therefore I have declared the property:
private double _sceduleOpacity;
public virtual double SceduleOpacity
{
get
{
if (_sceduleOpacity == 0) return 0.9;
else return _sceduleOpacity;
}
set { _sceduleOpacity = value; }
}
inside the Subject-Object.
When the mode is set, the SceduleOpacity-Property of the specific Subject is changed to the desired value (I've checked that).
Now all I need is a binding to that Property so that it is visible on the UI. But that is where the problem is. I've tried several approaches:
Bind it to the opacity of the DataTemplate:
GridView:
ItemTemplate="{Binding Mode=TwoWay, Source={StaticResource SceduleGridViewTemplate}}"
Resourcedictionary:
<DataTemplate x:Key="SceduleGridViewTemplate">
<Grid Background="{Binding ColorHash, Converter={StaticResource HexToSolidColorBrushConverter}}"
[...]
Opacity="{Binding SceduleOpacity}">
<TextBlock HorizontalAlignment="Center"
VerticalAlignment="Center"
TextAlignment="Center"
FontSize="28"
Text="{Binding Name}"/>
</Grid>
This does not have any effect at all. But since the following binding:
<Grid [...]>
<TextBlock HorizontalAlignment="Center"
VerticalAlignment="Center"
TextAlignment="Center"
FontSize="28"
Text="{Binding Name}"
Opacity="{Binding SceduleOpacity}"/>
</Grid>
sets the opacity of the displayed text successfully, I went on looking what the problem might be.
Bind it to the opacity of the Style or ItemContainerStyle:
I found out that the opacity of each GridView-Item is set by either in the Styles- or in the
ItemContainerStyle-Templates. But still the bindings do not work at all:
GridView:
Style="{Binding Source={StaticResource SceduleGridViewStyle}, Mode=TwoWay}">
Resourcedictionary:
<Style x:Key="SceduleItemStyle" TargetType="GridViewItem">
[...]
<Setter Property="Opacity" Value="{Binding SceduleOpacity}"/>
[...]</Style>
GridView:
ItemContainerStyle="{Binding Source={StaticResource SceduleItemStyle}, Mode=TwoWay}">
Resourcedictionary:
<Style x:Key="SceduleGridViewStyle" TargetType="GridView">
[...]
<Setter Property="Opacity" Value="{Binding SceduleOpacity}"/>
[...]</Style>
Do you have any idea on how I could fix that? Thank you very much!
It worked now. I have had to set the Opacity of the DateTemplate-Item as follows:
<Grid.Background>
<SolidColorBrush Color="{Binding ColorHash,
Converter={StaticResource HexToSolidColorBrushConverter}}"
Opacity="{Binding SceduleOpacity}"/>
</Grid.Background>
Thanks for help!

WPF - Trouble binding ToolTip text on custom user control

I'm in the process of creating a simple user control; just an ImageButton.
I've already successfully bound the Image to the button and so I've decided to add a tooltip. Now I'm having troubles. It seems that I can hard-code the text for the tooltip in the XAML for the control, but when it's bound it's returning an empty string.
Here's the XAML for my control:
<Button x:Class="BCOCB.DACMS.Controls.ImageButton"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
Name="this"
Style="{StaticResource DisabledButton}">
<Image Source="{Binding ElementName=this, Path=Source}" />
<Button.ToolTip>
<TextBlock Text="{Binding ElementName=this, Path=ToolTipText}" />
</Button.ToolTip>
</Button>
And here's the dependency property info for the tooltip text:
public static readonly DependencyProperty ToolTipTextProperty = DependencyProperty.Register("ToolTipText", typeof(string), typeof(ImageButton));
public string ToolTipText
{
get
{
return this.GetValue(ToolTipTextProperty) as string;
}
set
{
this.SetValue(ToolTipTextProperty, value);
}
}
And, finally, the declaration of the control in my Window:
<controls:ImageButton x:Name="btnAdd" Source="/DACMS;component/Resources/plus.png" ToolTipText="Add New Item" Click="btnAdd_Click" />
As I mentioned before, the image binds just fine and I've done it in exactly the same manner.
Any ideas?
Thanks,
Sonny
EDIT: I have it working now. I've removed the ElementName from the binding and set the TextBlock's DataContext = this in the code behind on instanciation. Still, I'd like to know how to fix this in the XAML, instead.
I'm unable to test this right now, but you can try:
<Button.ToolTip
DataContext=”{Binding Path=PlacementTarget.Parent.Parent,
RelativeSource={x:Static RelativeSource.Self}}"
>
<TextBlock Text="{Binding Path=ToolTipText}" />
</Button.ToolTip>
You may have to experiment a little with the number of "Parent" in PlacementTarget.
Hopefully this works. I don't like giving answers that I haven't tested, but I don't have VS on this computer. :)
I've had this same problem with binding to a ContextMenu. After my research I think that it is because the ToolTip and ContextMenu do not exist within the visual tree of your page/window/control. And therefore the DataContext is not inherited and makes binding troublesome.
Here is a Xaml hack I found that worked for me.
Binding to a MenuItem in a WPF Context Menu
The way to set the data context to "this" through xaml looks like this:
<Control DataContext={Binding RelativeSource={RelativeSource Self}}>
As another point, wpf buttons allow their content to be just about any (single) thing you want. If you want something other than text (ie, text and an image), it looks like this:
<Button Name="SampleButton" Click="SampleButton_Click">
<Grid Width="70" Height="62">
<Label Content="SampleText"/>
<Image Margin="3,3,3,3" Source="Graphics/sample.ico"/>
</Grid>
</Button>
Since you aren't changing anything but the Text on the tooltip TextBlock you can just use an inline declaration which will generate the TextBlock for you and doesn't require any hacking to get around the name scoping issue you're running into otherwise:
<Button ... ToolTip="{Binding RelativeSource={RelativeSource Self}, Path=ToolTipText}">...
You could alternately set the ToolTip on the Image and use the control as the DataContext, which gets around the name scoping problem. The DataContext will be passed to the ToolTip, allowing normal binding:
<Image DataContext="{Binding ElementName=this}" Source="{Binding Source}">
<Image.ToolTip>
<TextBlock FontSize="18" Text="{Binding Path=ToolTipText}" />
</Image.ToolTip>
</Image>
This way allows additional settings on the TextBlock or more complex visuals.
This fixes the Problem with the Tooltip Bindings and Dependencies Properties:
<UserControl x:Class="Extended.InputControls.TextBoxUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Extended.InputControls"
x:Name="UserControl"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<TextBox x:Name="textBox">
<TextBox.ToolTip>
<ToolTip Content="{Binding Path=CustomToolTip}" Background="Yellow"/>
</TextBox.ToolTip>
</TextBox>
</UserControl>
Instead of this ( doesnt Work ):
<UserControl x:Class="Extended.InputControls.TextBoxUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Extended.InputControls"
x:Name="UserControl">
<TextBox x:Name="textBox">
<TextBox.ToolTip>
<ToolTip Content="{Binding ElementName=UserControl, Path=CustomToolTip}" Background="Yellow"/>
</TextBox.ToolTip>
</TextBox>
</UserControl>

Categories

Resources