Code behind for UI? MouseOver issues - wpf

Is code behind in WPF for UI related things really ugly?
I'm trying to achieve similar effect to Visual Studio Panels (something like sample in WPF Unleashed book).
I want to change Grid Visibility to Visible when mouse enter into the button "solutionManagerPanel". It works however when my mouse enter into this Grid, it's visibility changes to hidden.
Below is code in xaml:
<Grid Grid.Column="2" Background="Gray" Visibility="{Binding ElementName=solutionManagerPanel, Path=IsMouseOver, Converter={StaticResource BooleanToVisibilityConverter}}">
<Grid.Resources>
<Style TargetType="{x:Type Grid}">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Visibility" Value="Visible"/>
</Trigger>
</Style.Triggers>
</Style>
</Grid.Resources>
Is there any simple way to do this in XAML or can I write event handling code-behind for this? Wouldn't this violate "clean, MVVM code rules"?

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>

WPF action on LostFocus textbox

I'm working on a WPF application and i have a little problem.
I have 1 ToggleButton and 1 TextBox. When i click on the ToggleButton, the TextBox apears and gets focus. This is good. But now i want that when i click on another textbox or just somewhere else, that the textbox loses his focus and disapears. I tried this with Differnet triggers and setters, but can't get it to work.
My code now:
<ToggleButton x:Name="SearchButton" Width="100" Height="100" BorderThickness="0" Margin="580,0,0,0" Template="{DynamicResource ButtonBaseControlTemplate1}" Panel.ZIndex="1">
<ToggleButton.Style>
<Style TargetType="ToggleButton">
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsChecked, ElementName=SearchButton}" Value="True" />
<Condition Binding="{Binding Visibility, ElementName=SearchBox}" Value="Visible"/>
</MultiDataTrigger.Conditions>
<Setter Property="FocusManager.FocusedElement" Value="{Binding ElementName=SearchBox}" />
</MultiDataTrigger>
</Style.Triggers>
</Style>
</ToggleButton.Style>
</ToggleButton><TextBox x:Name="SearchBox" Margin="100,33,0,34" Visibility="{Binding IsChecked, ElementName=SearchButton, Converter={StaticResource BoolVisibilityConverter}}" Opacity="0" FontSize="24" FontFamily="Arial" Background="{x:Null}" Foreground="#FF7F7F7F" BorderThickness="0">
<TextBox.Style>
<Style TargetType="TextBox">
<Style.Triggers>
<Trigger Property="TextBox.IsFocused" Value="False">
<Setter Property="ToggleButton.IsChecked" Value="False" TargetName="SearchButton" />
</Trigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
The fact, that the styles in the WPF separated from each other, it's just a bunch of settings setters. We can say, that two different styles for controls, with two different visual trees. So when you're trying to style TextBox to access ToggleButton it does not work, because of its visual tree no ToggleButtons.
In WPF for editing elements in the visual tree, and control in particular, uses a template control or controls placed within view of one Style (but this is usually done with the help of templates, such as DataTemplate, or with ControlTemplate).
I think it will suit you to control the Expander. It already has ToggleButton and content. Example:
XAML
<Expander Header="SearchButton">
<TextBox Text="SearchBox: Opened" Background="Gainsboro" />
</Expander>
Output
To change the view of Expander, you need to change his Style. With it, you can set any form and view of control.
For more information see:
Expander in MSDN
Styling and Templating in MSDN
Customizing the Appearance of an Existing Control by Using a ControlTemplate
Data Templating Overview

ControlTemplate and validation - How to position items?

I created ControlTemplate which is shown if there are validation error on my textbox. My controltemplate looks like that
<ControlTemplate x:Key="TextBoxErrorTemplate">
<TextBlock Foreground="Orange" FontSize="12pt">Field can't be empty</TextBlock>
</ControlTemplate>
However if validation errors occure textBlock appears on textBox - and the user can't enter proper value. Is there any way to set the position of TextBlock - the one which shows error info?
ErrorTemplates are for adorning the control and not for changing its internal properties, to do this you should use a style with the respective trigger:
<Style TargetType="TextBox">
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="True">
<Setter Property="Foreground" Value="Orange"/>
<Setter Property="FontSize" Value="12"/>
</Trigger>
</Style.Triggers>
</Style>
If you want to display some text you could use a template like this:
<ControlTemplate x:Key="TextBoxErrorTemplate">
<StackPanel Orientation="Horizontal">
<AdornedElementPlaceholder/>
<TextBlock Foreground="Orange" FontSize="12pt">Field can't be empty</TextBlock>
</StackPanel>
</ControlTemplate>
The TextBlock will be displayed on the right of the TextBox.
If you just want to show error messages i'd suggest you set the tooltip of the TextBox and bind it to the validation errors.

WPF Expander still shows Validation Error adorner when shrunk

I've got a style for a TextBox to show a validation error message as follows:
<Style TargetType="{x:Type TextBox}">
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip"
Value="{Binding RelativeSource={x:Static RelativeSource.Self},
Path=(Validation.Errors)[0].ErrorContent}"/>
</Trigger>
</Style.Triggers>
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<StackPanel Orientation="Horizontal">
<Border BorderBrush="{Binding Path=ErrorContent,
Converter={StaticResource ValidationErrorToBrushConverter}}" BorderThickness="2">
<AdornedElementPlaceholder />
</Border>
<Image Name="image1" Height="14" Width="14" Stretch="Fill" Margin="1,1,1,1"
Source="{Binding Path=ErrorContent,
Converter={StaticResource ValidationErrorToImageSourceConverter}}"
ToolTip="{Binding Path=ErrorContent}"/>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The TextBox lives in an Expander. When I open the Expander, the TextBox allows for input, but will fail validation if the input is NullorEmpty, or contains special characters.
My problem is that when I trigger a validation error, the TextBox lights up in red and shows an icon with the message as a tooltip. All good so far. BUT when i close the Expander without passing validation, the red outline and icon with tooltip are still there! Even with the Expander shrunk down! Just floating there... This is not good behavior.
Any ideas on how to get the Validation stuff to hide along with all the other controls in the Expander? Also, the Style for validation is declared in the resources of the UserControl, not in the Expander itself.
I ended up simply clearing the TextBox upon closing the Expander. That way, the validation error goes away and the box is clear and ready for another input when the Expander is opened back up.
I had the same problem. I fixed it by putting an AdornerDecorator as the first child object of the expander. The AdornerDecorator is collapsed when the Expander is collapsed, so the Adorners should all disappear too.
I've resolved this same problem by setting the Validation.ErrorTemplate property to null when the TextBox is hidden
<Style TargetType="TextBox">
<Style.Triggers>
<Trigger Property="IsHitTestVisible" Value="False">
<Setter Property="Validation.ErrorTemplate" Value="{x:Null}"/>
</Trigger>
</Style.Triggers>
</Style>

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.

Resources