ContentControl change ContentTemplate on GotFocus - wpf

I have a UserControl which contains a ContentControl. When the user clicks this ContentControl I want to change its ContentTemplate, to make it "editable" (instead of labels display textboxes for example).
What I have is this:
<StackPanel>
<ContentControl Style="{DynamicResource ContainerStyleEditable}" GotFocus="ContentControl_GotFocus"></ContentControl>
</StackPanel>
and in userControl resources i have
<Style TargetType="{x:Type ContentControl}" x:Key="ContainerStyleEditable">
<Setter Property="ContentTemplate" Value="{DynamicResource ItemTemplateReadOnly}" />
<Style.Triggers>
<Trigger Property="IsFocused" Value="True">
<Setter Property="ContentTemplate" Value="{DynamicResource ItemTemplateEditable}" />
</Trigger>
</Style.Triggers>
</Style>
This doe not work, it seems the GotFocus event never fires. What is the way to to this?

I usually base my triggers of IsKeyboardFocusWithin instead of IsFocused because often the focused element usually isn't the actual ContentControl, but rather a control inside it's Content.
Also, be sure that at least one control inside the ContentControl can accept focus so the control can get focus. If nothing inside the control can accept focus, your trigger will never fire.

Related

ElementName binding doesn't work for IsMouseOver in ControlTemplate?

I have a control template and I want to trigger some actions only if mouse is over a certain part of it. Here is the core of my template (simplified for demonstration):
<ControlTemplate TargetType="{x:Type graphicElements:MyTabItem}">
<Grid x:Name="templateRoot">
<Grid x:Name="templateChild" />
</Grid>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding IsMouseOver, ElementName=templateChild}" Value="True">
<Setter Property="Background" TargetName="templateRoot" Value="Red" />
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
When I put ElementName as templateRoot it works and turns red. When I put it as templateChild it doesn't work... Why not?
In the simplified version of your code the binding to the templateRoot grid also won't work. The problem is, that WPF needs to perform Hit Tests on the elements to raise certain events or to update the IsMouseOver property. Since you don't have a background brush set for the grids, they will never receive mouse inputs, hence your trigger will never execute. Try this:
<Grid x:Name="templateRoot">
<Grid x:Name="templateChild" Background="Transparent"/>
</Grid>

Customize Expander to expand on mouse enter

I am using Expander in WPF to display my data. The default style for the Expander control contains a toggle button which shows/hides my content when I click on it.
How can I modify the style so that it expands when I hovers the mouse over the header and collapse when I move away?
Barebone setup should be this:
<Style TargetType="{x:Type Expander}">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="IsExpanded" Value="True" />
</Trigger>
</Style.Triggers>
</Style>
(Applies to the whole expander, not just the header. That would probably require messing with the template.)
It is possible to use databinding between isExpanded property an ismouseover:
IsExpanded="{Binding IsMouseOver, Mode=OneWay, RelativeSource={RelativeSource Self}}"

changing background color of container when textbox is in focus

I have a simple user control with a TextBox. I want to change the color of user control when the TextBox gets the focus. This is what I have:
<UserControl x:Class="OutLookContactList.ContactSearchControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Name="root" MinHeight="30" Loaded="UserControl_Loaded">
<UserControl.Resources>
<Style x:Key="searchTextBoxStyle" TargetType="{x:Type TextBox}">
<Style.Triggers>
<Trigger Property="IsFocused" Value="true">
<Setter TargetName="root" Property="Background" Value="{StaticResource OnMouseOverColor}" />
</Trigger>
</Style.Triggers>
</Style>
</UserControl.Resources>
But I get the errot "TargetName property cannot be set on a style Setter". How can I Set the back ground color of user control when text box gets the focus?
Thanks a bunch
Will it work to wrap the contents of your UserControl inside a Border object? If so, you can simply style the Border like so:
<UserControl x:Class="Sample2.ContactSearchControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="75" Width="300">
<Border>
<Border.Style>
<Style TargetType="Border">
<Setter Property="Background" Value="White" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsFocused, ElementName=txtSearch}" Value="true">
<Setter Property="Background" Value="Black" />
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<StackPanel>
<TextBox x:Name="txtSearch" Text="Search" />
<TextBox Text="Other" />
</StackPanel>
</Border>
</UserControl>
Update: (Answering Sheraz' Questions)
I'm not sure why ElementName doesn't work for accessing children within a UserControl. It might have something to do with the way the visual tree is constructed.
As for Trigger vs DataTrigger: Trigger is for dependency properties and DataTrigger is for databound properties (data or other controls). Since you are trying to style the Border, it makes more sense to place the DataTrigger there and have it watch the TextBox than to have the TextBox change the appearance of the Border.
As I understand it, the TargetName property of Setter is only applicable within a DataTemplate or ControlTemplate. (Info from Dr. WPF in this forum post)
If you were changing the background of the text box you need to remove the TargetName property:
<Style x:Key="searchTextBoxStyle" TargetType="{x:Type TextBox}">
<Style.Triggers>
<Trigger Property="IsFocused" Value="true">
<Setter Property="Background" Value="{StaticResource OnMouseOverColor}" />
</Trigger>
</Style.Triggers>
</Style>
and change the TextBox that wants this style to be:
<TextBox Style="{StaticResource searchTextBoxStyle}" .... />
However, as you want to change the value of the parent user control this won't give you want you want.
You could certainly do it in the code behind by adding a GotFocus event handler and putting the code to change the background colour in there.
Here's some XAML that works in Kaxaml:
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Page.Style>
<Style TargetType="Page">
<Setter Property="Background" Value="#CCCCD0" />
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=txtSearch, Path=IsFocused}"
Value="true">
<Setter Property="Background" Value="Black" />
</DataTrigger>
</Style.Triggers>
</Style>
</Page.Style>
<TextBox x:Name="txtSearch" Width="100"
HorizontalAlignment="Center" VerticalAlignment="Center" />
</Page>
You would change the Page object with your UserControl. I find it much easier to test these sorts of things out in a rapid prototyping tool such as Kaxaml before coding up the UserControl in VS.
Note that you have to set the default colour (in this case #CCCCD0) via a property setter and not via an attribute on the Page itself. This is because the attribute would override the value set by the trigger (because it's a style trigger), so even though the trigger would fire, it would always be trumpted by the local attribute specification, meaning that it wouldn't change. I only point this out because it's a fairly common gotcha.

WPF ComboBox - showing something different when no items are bound

I have a ComboBox, and i want to change its look when the ItemsSource property is null. When it is in that state, i want to show a TextPanel with the text "Retrieving data" in it, and give it a look kind of similar to the watermarked textbox.
I figure to do this i need a ControlTemplate, and a trigger. I have the ControlTemplate here:
<ControlTemplate x:Key="LoadingComboTemplate" TargetType="{x:Type ComboBox}">
<Grid>
<TextBlock x:Name="textBlock" Opacity="0.345" Text="Retrieving data..." Visibility="Hidden" />
</Grid>
<!--
<ControlTemplate.Triggers>
<Trigger Property="ComboBox.ItemsSource" Value="0">
<Setter Property="Visibility" Value="Visible" />
</Trigger>
</ControlTemplate.Triggers>
-->
</ControlTemplate>
but my issue is how do i set up the trigger to show this when the ItemsSource property is null? I have tried a couple of different ways, and each way has given me the error message "Value 'ItemsSource' cannot be assigned to property 'Property'. Invalid PropertyDescriptor value.". My ComboBox xaml is this (including the attempted trigger):
<ComboBox Margin="112,35,80,0"
Name="MyComboBox"
Height="22.723"
VerticalAlignment="Top"
DisplayMemberPath="FriendlyName"
SelectedValuePath="Path"
TabIndex="160"
>
<Trigger>
<Condition Property="ItemsSource" Value="0" />
<Setter Property="Template" Value="{StaticResource LoadingComboTemplate}" />
</Trigger>
</ComboBox>
now should the trigger go on the ComboBox, or on the ControlTemplate? How do i access the ComboBox's ItemsSource property? Should i even be using a trigger?
Thanks!
Try putting {x:Null} for the value of the condition instead of 0.
Also I got it working by moving the Trigger to a style and modifing it slightly, see below.
<Style TargetType="ComboBox" x:Key="LoadingComboStyle">
<Style.Triggers>
<Trigger Property="ItemsSource" Value="{x:Null}">
<Setter Property="Template" Value="{StaticResource LoadingComboTemplate}" />
</Trigger>
</Style.Triggers>
</Style>
<ComboBox Style="{StaticResource LoadingComboStyle}" .... >
The reason it only works in a style, is that only EventTriggers are allowed in the triggers collection directly on the Framework Element. For property triggers (like above) you need to use a style (I learn something every day).
See FrameworkElement.Triggers
Note that the collection of triggers established on an element only supports EventTrigger, not property triggers (Trigger). If you require property triggers, you must place these within a style or template and then assign that style or template to the element either directly through the Style property, or indirectly through an implicit style reference.

WPF LinkLabel implementation strategy

I am attempting to create a LinkLabel control for WPF. Essentially, I'm going to create the LinkLabel from a TextBlock and handle MouseEnter, MouseLeave, and MouseLeftButtonUp events. In the back end I have a base class that has properties that you would expect to see with a LinkLabel. With most other clickable controls in WPF, there is a default MouseEnter animation where the control becomes Ice Blue. I would like to duplicate this behavior when the mouse cursor enters over the TextBlock. I'm not sure if I'm needing to derive from ButtonBase or something along those lines. I have a I am able to change the cursor to a hand, and handle the event for when the "LinkLabel" is clicked. If accessing this seemingly default color changing animations, then I just may have to resort to a simple foreground color swap without the smooth transition. If anyone has created a custom WPF LinkLabel before or has any advice into the matter your input would be much appreciated.
You can create the equivalent of WinForms' LinkLabel right now using a combination of TextBlock and HyperLink:
<TextBlock>Here is a <Hyperlink NavigateUri="http://example.com">link</Hyperlink></TextBlock>
You won't get the "ice blue" mouse-over effect, but you will get the hand cursor. I'm sure you can introduce your mouse-over effects using a simple style trigger.
The "NavigateUri" property works in navigation-style applications where the hyperlink is inside a Frame control. In a standard WPF application you'll want to handle the Hyperlink's Click event instead and use Process.Start to kick off a web browser with the correct URL.
I just created a style for a button and apply a style to a button whenever you want LinkLabel look. Click event of button is used to perform a function when the text is clicked.
<Style x:Key="LinkLabelButtonStyle" TargetType="Button">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Foreground" Value="CornflowerBlue"></Setter>
</Trigger>
</Style.Triggers>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border Background="Transparent">
<ContentPresenter/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock TextDecorations="Underline" Text="{TemplateBinding Content}"></TextBlock>
</DataTemplate>
</Setter.Value>
</Setter>
<Setter Property="Foreground" Value="DarkBlue"></Setter>
<Setter Property="MinWidth" Value="90"></Setter>
<Setter Property="HorizontalAlignment" Value="Left"></Setter>
<Setter Property="Padding" Value="5"></Setter>
<Setter Property="Margin" Value="5"></Setter>
<Setter Property="Cursor" Value="Hand"></Setter>
</Style>
You can place above style in the Window.Resources so you can use it in entire window.
Then apply the style to a button whenever you want LinkLabel look.
<Button Name="LinkLabelLookALikeButton" Content="Text goes here" Style="{StaticResource LinkLabelButtonStyle}" Click="Event_Goes_Here">
</Button>
Hope this helps!

Resources