This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Select ListBoxItem if TextBox in ItemTemplate gets focus
I have a ListView bound to an ObservableCollection (Listview.ItemsSource). The listview presents several textboxes that are bound to properties of the objects in the observable collection.
I would like to have the following functionality: when a user focusses a textbox the corresponding item in the listview should get selected.
I have tried things with ContainerFromElement, ContainerFromItem, etc. but can't get this "simple" functionality to work.
Any ideas...
The trick here is to use the IsKeyboardFocusWithin property on the ItemContainerStyle:
<ListView ItemsSource="{Binding}">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Style.Triggers>
<Trigger Property="IsKeyboardFocusWithin" Value="True">
<Setter Property="IsSelected" Value="True" />
</Trigger>
</Style.Triggers>
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding Path=YourPropertyValue}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
In this example we are simply stating that IsSelected should be set to true whenever a control within that item contains the keyboard focus.
Note: this does not work in the opposite direction; selecting a particular item in the list will not automatically give focus to the contained TextBox
Edit in response to comments
As Joep pointed out, this will mean that losing keyboard focus (which will happen when a control besides the TextBox gains focus) will cause the IsSelected property to be reset to false. You can work around this by replacing the Style setter with an trigger enter action, which prevents the change from being undone when the trigger is no longer valid.
For this to work in the same way as the previous example you will need to explicitly set the SelectionMode for the ListView to Single; otherwise, multiple items can become selected at once.
<ListView ItemsSource="{Binding}" SelectionMode="Single">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Style.Triggers>
<Trigger Property="IsKeyboardFocusWithin" Value="True">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<BooleanAnimationUsingKeyFrames
Storyboard.TargetProperty="IsSelected">
<DiscreteBooleanKeyFrame KeyTime="0:0:0"
Value="True" />
</BooleanAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
</Trigger>
</Style.Triggers>
</Style>
</ListView.ItemContainerStyle>
<!-- ... -->
</ListView>
The MVVM way would add extra properties to the ViewModel representing the properties that are focussed.
E.g., if the ViewModel has a property Name add a property IsNameFocussed, if it has a property Address, add a property IsAddressFocussed.
Then bind the appropriate control in the DataTemplate to the Is...Focussed property to highlight it.
All that is left is setting the Is...Focussed property in the GotFocus and LostFocus events of the textboxes. (I'd rather bind to a Focussed Property but it's not there...)
Related
My problem seems to be the opposite of the one everybody else has. My WPF DataTrigger fires sometimes even when the viewmodel property to which it is tied does not change. I've put logging code in the property itself to make sure it's not changing.
Is there some easy to way determine WHY an animation is firing? Some event I can subscribe to let me put a breakpoint in my own code and look at the call stack at the moment the animation fires?
Here's the Storyboard. Just animating a double from 0 to 1 over 1 second. The double will be stored in FrameworkElement.Tag
<Storyboard x:Key="TagZeroToOne" x:Shared="False">
<DoubleAnimation Storyboard.TargetProperty="Tag" From="0" To="1" Duration="0:0:1.0" />
</Storyboard>
And here is the relevant bit of the style that uses it in the DataTrigger. (I've left out the item template and panel)
<!-- Style for ItemsControl that draws circular handles when ShapeVm is selected -->
<Style x:Key="ShapeHandlesItemsControlStyle" TargetType="{x:Type ItemsControl}">
<d:Style.DataContext>
<x:Type Type="gci:ShapeVm" />
</d:Style.DataContext>
<!-- Default style value of ItemsControl.Tag" property is 1.0 -->
<Setter Property="Tag">
<Setter.Value>
<sys:Double>1.0</sys:Double>
</Setter.Value>
</Setter>
<!--
We cannot animate Opacity directly because its max value depends upon
a binding but we be we can make it be dependent upon another property
"Tag" that we *do* animate. This requires a Math Converter as well
Picked up this technique from the following SO question
https://stackoverflow.com/questions/2186933/wpf-animation-binding-to-the-to-attribute-of-storyboard-animation/14164245#14164245
-->
<Setter Property="Opacity">
<Setter.Value>
<MultiBinding Converter="{StaticResource CvtMath}"
ConverterParameter="1.0 - (x *(1.0-y))"> <!-- Calculation -->
<Binding Path="Tag" RelativeSource="{RelativeSource Self}" /> <!-- Animated operand (0.0 to 1.0) -->
<Binding ElementName="Root" Path="ShapeHandleOpacity" /> <!-- Configured opacity operand -->
</MultiBinding>
</Setter.Value>
</Setter>
<!-- When ShapeVm.IsSelected becomes true, animate the ItemsControl.Tag from 0 to 1.0 -->
<Style.Triggers>
<DataTrigger Binding="{Binding IsSelected}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard Storyboard="{StaticResource TagZeroToOne}" />
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
The idea is that when the user first selects the shape, my code-behind sets its IsSelected property to "true". That makes the animation fire and I see the visual effect tied to my animation. But that's supposed to be it.
Instead, it seems that almost every other time I click on the object in question, The animation fires and I see the effect. Yet I have verified (repeatedly) via logging and breakpoints that the IsSelected property is never changing after that. Nobody is setting it. Nobody is firing a PropertyChanged Notification for it and as far as I can see, nobody is even firing PropertyChanged(null).
And here is the ShapeVm.IsSelected property
private bool _isSelected;
public bool IsSelected
{
get => _isSelected;
set
{
if (value == _isSelected)
return;
_isSelected = value;
Debug.WriteLine($"Shape selected change to {value}");
}
}
So how do I determine why WPF is making this trigger fire?
I have an ObservableCollection of custom objects bound via a DataContext to a ListBox.
Next to the ListBox a have a group of TextBox that are bound to the current item's field. (i.e. Text={Binding Path=/SomeField})
How can I disable / grey out the record detail TextBoxes when my DataContext's ObservableCollection is empty?
You could do it with a style:
<Style TargetType="TextBox">
<Style.Triggers>
<!-- When the collection itself is null -->
<DataTrigger Binding="{Binding }" Value="{x:Null}">
<Setter Property="IsEnabled" Value="False" />
</DataTrigger>
<!-- When the collection has no items -->
<DataTrigger Binding="{Binding Count}" Value="0">
<Setter Property="IsEnabled" Value="False" />
</DataTrigger>
</Style.Triggers>
</Style>
This answer assumes that you are using mvvm and have a viewmodel to back up your view - I would create a readonly bool property in your viewmodel and return true if the count of your observable collection > 0.
You can then bind the bool to the isEnabled property of the necessary text boxes, thus enabling or disabling them depending upon the value(s) of your observable collection.
EDIT - As per BenjaminPaul's comment below, the change event must be handled for your observable collection and you should then call propertyChanged on your bool property to ensure is is updated and passed back to the view.
Hello WPF Pros at least I hope some of you read this!
DataContext is a property on FrameworkElement (base class for all WPF Controls) and is implemented as a DependencyProperty. That means all the descendant elements in the logical tree share the same DataContext.
So the ContentControl should do it with its descendant elements right?
I have a scenario where that is NOT the case and I would like to know WHAT is the cause of that misbehaviour ?!
That you understand a bit more about it please read this thread ( dont NOT want to copy everything here) where the trouble starts...:
WPF: Can not find the Trigger target 'cc'. The target must appear before any Setters, Triggers
and to say it in short words: My DataTemplates within the ContentControl do have a dead DataContext that means there is NOTHING to bind to it, what is actually not possible...
Every Element down the ContentControl has NOTHING set in the DataContext Property ???
DataContext is a property on
FrameworkElement (base class for all
WPF Controls) and is implemented as a
DependencyProperty. That means all the
descendant elements in the logical
tree share the same DataContext.
The fact that it's a dependency property doesn't imply inheritance... It's true for DataContext, but only because the dependency property has the FrameworkPropertyMetadataOptions.Inherits flag in its metadata.
So the ContentControl should do it
with its descendant elements right?
ContentControl is a bit special: the DataContext of its descendants (the visual tree built from the DataTemplate) is actually be the Content of the ContentControl. So if your ContentControl has no content, the DataContext inside it is null.
This worked for me:
<ContentControl ContentTemplate="{StaticResource NotesTemplate}"
Content="{Binding}"
DataContext="{Binding HeightField}"/>
Without the Content="{Binding}", the DataContext was NULL
The last answer (from VinceF) worked for me too.
I wanted to show a usercontrol depending on the value of a property in my viewmodel. So I made a ContentControl with some Style Triggers. Depending on the value of a bind property the trigger sets a specific ContentTemplate containing the specific usercontrol.
The usercontrol was shown right, but its DataContext was always null. So I had to set the Context of the ContentControl to: Content="{Binding}" After that, the UserControls worked fine and had the same DataContext as their parent.
So my XAML looks like that:
In the Resources part I defined two DataTemplates; each one for each UserControl I want to show.
<DataTemplate x:Key="ViewA">
<namespace:UserControlA/>
</DataTemplate>
<DataTemplate x:Key="ViewB">
<namespace:UserControlB/>
</DataTemplate>
The part where I show the UserControl depending on a property is the following:
<ContentControl Content="{Binding}">
<ContentControl.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Property}" Value="0">
<Setter Property="ContentControl.ContentTemplate" Value="{StaticResource ViewA}" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=Property}" Value="1">
<Setter Property="ContentControl.ContentTemplate" Value="{StaticResource ViewB}" />
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
after reading this question and previous answers, I prefer using ContentControl with data triggered Content like this:
Controls which will be set as Content of ContentControl:
<TextBox x:Key="ViewA">
...
</TextBox>
<ComboBox x:Key="ViewB">
...
</ComboBox>
ContentControl which switch own content by DataTrigger in ContentControl style:
<ContentControl>
<ContentControl.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Property}" Value="0">
<Setter Property="Content" Value="{StaticResource ViewA}" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=Property}" Value="1">
<Setter Property="Content" Value="{StaticResource ViewB}" />
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
I hope this helps to someone like previous answers to me.
What is the simplest way to get a WPF resource from code?
I need to disable a text box in a WPF form if a checkbox in the same window is checked. I have wired the checkbox to an event handler in code-behind. The event handler disables the checkbox and changes its background to a light gray, to indicate that the control is disabled:
private void OnCheckBoxChecked(object sender, RoutedEventArgs e)
{
MyTextBox.IsEnabled = false;
MyTextBox.Background = (Brush)FindResource("DisabledControlBackgroundBrush");
}
The disabled control background color is defined as a resource in a resource dictionary that is imported into the WPF window. I tested the resource by setting the textbox background in XAML, and the resource works fine.
I also know the event handler is working, because it disables the text box when the checkbox is clicked.
My problem is that the event handler isn't changing the Background property as it should. I suspect that there is a problem with my call to FindResource, but I am not getting an exception, and the Output window has nothing on it.
So, how do I get this resource from code and apply it to my text box? Thanks for your help.
David. I've put together a sample window that does this using triggers on the TextBox.Style:
<Window x:Class="WpfApplication1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1">
<Window.Resources>
<SolidColorBrush x:Key="IsCheckedColor" Color="DarkGray" />
</Window.Resources>
<StackPanel>
<TextBox x:Name="textbox" Margin="36" Height="24" >
<TextBox.Style>
<Style TargetType="TextBox">
<Setter Property="Background" Value="White" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsChecked, ElementName=checkbox}" Value="True" >
<Setter Property="Background" Value="{StaticResource IsCheckedColor}" />
<Setter Property="IsEnabled" Value="False" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
<CheckBox x:Name="checkbox" Content="Click Me" Height="24" Margin="36"/>
</StackPanel>
</Window>
You can't use a Trigger to change another control's properties, but you can use them to change a control's properties based on something else, like a DataContext or another control.
Each control can have a Triggers collection, but this can only contain EventTriggers. In a Style you can use plain Trigger which can be used to control animation, as well as DataTrigger, which I've used in this sample to control the TextBox settings based on the properties of the CheckBox.
Notice that I've also used a Setter outside of the Triggers collection to set the default value, and I don't need a second Setter for when the CheckBox is not checked -- it just goes back to the "default" state.
edit - how to change background of disabled TextBox
I do this in Blend, but if you don't have Blend you can of course put the XAML in directly. This has to do with controls states. As the TextBox transitions among Normal, MouseOver, and Disabled, you can animate changes to the appearance. In this case we use an animation of virtually zero duration, so the change is immediate.
Add the following to the StackPanel:
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="VisualStateGroup">
<VisualState x:Name="Disabled">
<Storyboard>
<ColorAnimationUsingKeyFrames BeginTime="00:00:00" Duration="00:00:00.0010000" Storyboard.TargetName="textbox" Storyboard.TargetProperty="(Panel.Background).(SolidColorBrush.Color)">
<SplineColorKeyFrame KeyTime="00:00:00" Value="{StaticResource IsCheckedColor}"/>
</ColorAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
this kind of approach is form Windows Forms which is a bad pattern for WPF. All what you want to do is can be done by triggers and styles
Patten:
<Style x:Key="BackGroundCheckBoxStyle"> < !--apply the style to checkbox -->
<Style.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="{Binding ElementName=m_txtBox, Path=IsEnabled, Mode=TwoWay}" Value="false}" />
<!-- bind your resource here with a setter as well -->
</Trigger>
</Style.Triggers>
</Style>
Background store in the Aplication.Resources scope or Window. Triggers are Freezable object so GUI will refresh itself (no repaint() needed)
Try to get some overview, since it is a new way to develop apps :)
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.