WPF action on LostFocus textbox - wpf

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

Related

WPF: DataTrigger for Topmost property of Prism Window XAML

I am trying to control the Topmost property of a prism popup window with a DataTrigger, so that when a button within the XAML file is pressed, the Topmost property is set to false. I am pretty new to WPF and am not sure what I need to do to get my binding to work.
Note that the IsModal property is set to false and I'd like to keep it that way, unless the IsModal feature could also be styled with a DataTrigger from the button.
Here's the prism Popup with the style and attempted trigger
<prism:InteractionRequestTrigger SourceObject="{Binding DataBarChartRequest, Mode=OneWay}">
<prism:PopupWindowAction IsModal="False" WindowStartupLocation="CenterScreen">
<prism:PopupWindowAction.WindowStyle>
<Style TargetType="{x:Type Window}">
<Setter Property="Topmost" Value="True"/>
<Setter Property="Height" Value="650"/>
<Setter Property="Width" Value="900"/>
<Setter Property="ResizeMode" Value="CanResize"/>
<Setter Property="ShowInTaskbar" Value="True"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=StopButton, Path=IsPressed}" Value="True">
<Setter Property="Window.Topmost" Value="False" />
</DataTrigger>
</Style.Triggers>
</Style>
</prism:PopupWindowAction.WindowStyle>
<prism:PopupWindowAction.WindowContent>
<local:DataBarChartNotificationView Width="NaN" Height="NaN" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
</prism:PopupWindowAction.WindowContent>
</prism:PopupWindowAction>
</prism:InteractionRequestTrigger>
and here's the button
<RibbonButton Label="Stop"
x:Name="StopButton"
Command="{Binding StopSessionCommand}"
LargeImageSource ="{StaticResource SessionStopGray}"
FontSize="12"
FontWeight="Regular"
Height="60"
Margin="0,10,0,0"
Foreground="White"/>
It seems as though, despite this InteractionRequestTrigger being in the same xaml file as my button, the prism popup cannot find this local source, or it simply won't work. Would like to know if I can style either the IsModal or Topmost property with the button in the XAML file as a DataTrigger or how to fix my binding to do so.

FocusElement does not change focus when I set it using a DataTrigger

I am making a window that shows the user multiple options, with each a radiobutton and a textbox.
When opening this window, the focus should be on the textbox corresponding to the radiobutton that is checked when opening the window (this is all preset, no need to worry about this).
It is important that it gets fixed either in xaml or in the code-behind.
I have used a DataTrigger to change the FocusedElement to the right textbox, however it gets overwritten after it is set.
The relevant code is in the DataTrigger below. The color gets changed correctly, however the FocusElement does not.
I have tried all the options that I have found on stackoverflow and google. The issue does not lie in the DataTrigger or the setting of the FocusedElement. I think it lies in the fact that it gets overridden at the end. I have used Snoop to see the changes in the Keyboard.FocusedElement and it does not show any change.
<DataTemplate x:Uid="DataTemplate_1" >
<RadioButton x:Uid="RadioButton_1" GroupName="Options" IsChecked="{Binding IsSelected}" Margin="4,0,0,0" Name="RadioBtn">
<StackPanel x:Uid="StackPanel_1" Orientation="Horizontal" >
<Label x:Uid="Label_1" Visibility="{Binding IsUserInput, Converter={cs:OppositeVisibilityConverter}}" Content="{Binding Description, Mode=OneWay}" />
<Label x:Uid="Label_2" Visibility="{Binding IsUserInput, Converter={cs:VisibilityConverter}}" Content="Anders, nl: " />
<TextBox x:Uid="MemAnders" Visibility="{Binding IsUserInput, Converter={cs:VisibilityConverter}}" Text="{Binding AlternativeText}"
Name="MemAnders" MinWidth="400" IsTabStop="False"
>
<TextBox.Style>
<Style TargetType="{x:Type TextBox}">
<Style.Triggers>
<DataTrigger Binding="{Binding IsSelected}" Value="True">
<Setter Property="FocusManager.FocusedElement" Value="{Binding ElementName=MemAnders}"/>
<Setter Property="Background" Value="LightGoldenrodYellow" />
</DataTrigger>
<DataTrigger Binding="{Binding IsSelected}" Value="False">
<Setter Property="FocusManager.FocusedElement" Value="{Binding ElementName=RadioBtn}"/>
<Setter Property="Background" Value="LightBlue" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
</StackPanel >
</RadioButton >
</DataTemplate >
The textbox corresponding to the checked radiobutton should be focused. Instead another (parent?) object is focused.
Anyone know a workaround for this?
The work-around turned out to be setting focus to the textbox in the Window_Loaded function.
I used an algorithm provided by Find a WPF element inside DataTemplate in the code-behind to find the correct textbox element. Then I did TextBox.Focus(); and that fixed it.

Exception during ChangeLogicalParent in TabControl

In my window I have several generic DataTemplates which use DataTriggers to determine data sources and such. This cuts down heavily on xaml duplication in a window with a lot of similar pages. There are TabControls for navigation, with each TabItem housing a ContentControl using one of said templates.
The exception I'm getting is happening while two TabItems on the same TabControl have the same content. I've isolated the problem at the style for a DevExpress GridControl inside the template. The style has DataTriggers which use the tag of the currently selected TabItem to determine both the ItemsSource and ColumnsSource.
The exception: Name:ChangeLogicalParent MSG:Specified element is already the logical child of another element. Disconnect it first. STACK: at System.Windows.FrameworkContentElement.ChangeLogicalParent(DependencyObject newParent)
Below is an abbreviated version of the xaml:
<DataTemplate x:Key="GridControlTemplate">
...
<dxg:GridControl>
<dxg:GridControl.Style>
<Style TargetType="{x:Type dxg:GridControl}">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type TabControl}}, Path=SelectedItem.Tag}"
Value="{StaticResource FirstTag}">
<Setter Property="ColumnsSource" Value="{StaticResource FirstColumns}" />
<Setter Property="ItemsSource" Value="{Binding FirstDataSource}" />
</DataTrigger>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type TabControl}}, Path=SelectedItem.Tag}"
Value="{StaticResource SecondTag}">
<Setter Property="ColumnsSource" Value="{StaticResource SecondColumns}" />
<Setter Property="ItemsSource" Value="{Binding SecondDataSource}" />
</DataTrigger>
...
<TabControl>
<TabItem Header="header 1" Tag="{StaticResource FirstTag}">
<ContentControl ContentTemplate="{StaticResource GridControlTemplate}" Content="{Binding}" />
</TabItem>
<TabItem Header="header 2" Tag="{StaticResource SecondTag}">
<ContentControl ContentTemplate="{StaticResource GridControlTemplate}" Content="{Binding}" />
</TabItem>
</TabControl>
Mind you, this works fine for other groups of TabItems sharing identical ContentTemplates. This TabControl scheme works fine when the GridControl's style doesn't have these DataTriggers. With that in mind, might this be a DevExpress-specific issue?
Your GridControl is part of a style held as a static resource. By default resources are shared, that is, the same instance is used by anything that uses the resource. Since both your tabs are using it as their control template, the same GridControl is trying to be made a child of each tab.
You can tell WPF to provide different instances via the x:Shared attribute:
<DataTemplate x:Key="GridControlTemplate" x:Shared="false">

Code behind for UI? MouseOver issues

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"?

How can I indicate in an Expander header that collapsed contents have an error

I have expanders that contain text boxes, the text boxes use the wpf validation stuff to draw a red box around them ( text boxes are wrapped in Adorner Decorators to make sure I don't get empty red boxes everywhere when the expanders are collapsed)
I want to indicate in the header of the expander that it has contents that have errors (in case it is in a collapsed state) - an icon or red exclamation mark or something. I think I see a way to do this in code from my validation function (not ideal) but is there a way to do it in xaml? Can I use a style for the expander with a trigger somehow pointing to the Validation.HasError of all children?
thanks for any thoughts..
Trev
If you know the contents of your expander, you can use a MultiDataTrigger to do this:
<Expander>
<Expander.Header>
<TextBlock>
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Text" Value="ERROR"/>
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding ElementName=txtWidth, Path=(Validation.HasError)}" Value="False"/>
<Condition Binding="{Binding ElementName=txtHeight, Path=(Validation.HasError)}" Value="False"/>
</MultiDataTrigger.Conditions>
<Setter Property="Text" Value="NO ERROR"/>
</MultiDataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</Expander.Header>
<StackPanel>
<TextBox x:Name="txtWidth" Text="{Binding Width, ElementName=rect, UpdateSourceTrigger=PropertyChanged, ValidatesOnExceptions=True}"/>
<TextBox x:Name="txtHeight" Text="{Binding Height, ElementName=rect, UpdateSourceTrigger=PropertyChanged, ValidatesOnExceptions=True}"/>
<Rectangle x:Name="rect" Width="100" Height="100" Margin="10" Fill="Green"/>
</StackPanel>
</Expander>
If the contents of the expander aren't known, then you'll probably have to set Binding.NotifyOnValidationError on the TextBoxes and handle the Error attached event.

Resources