Using ObservableCollection firing Binding.TargetUpdated only on single item - wpf

I have three Textblocks with the Text Property bound to the items of an ObservableCollection:
<TextBlock Style="{StaticResource FadeInTextBlock}" Text="{Binding Path=CurrentAnswers[0], NotifyOnTargetUpdated=True}" />
<TextBlock Style="{StaticResource FadeInTextBlock}" Text="{Binding Path=CurrentAnswers[1], NotifyOnTargetUpdated=True}" />
<TextBlock Style="{StaticResource FadeInTextBlock}" Text="{Binding Path=CurrentAnswers[2], NotifyOnTargetUpdated=True}" />
The Property with INotifyPropertyChanged implemented:
public ObservableCollection<Answer> CurrentAnswers
{
get { return currentAnswers; }
set { currentAnswers = value; RaisePropertyChanged("CurrentAnswers"); }
}
Each Textblock uses the same style containing a trigger for the Binding.TargetUpdated event which fades in the actual text:
<Style x:Key="FadeInTextBlockTwo" TargetType="TextBlock">
<Style.Triggers>
<EventTrigger RoutedEvent="Binding.TargetUpdated">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity" Duration="0:0:0" To="0.0"/>
<DoubleAnimation Storyboard.TargetProperty="Opacity" Duration="0:0:1" From="0.0" To="1.0" BeginTime="0:0:0"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Style.Triggers>
</Style>
When I change ONE item in the ObservableCollection ALL Textblocks are firing the event and do the fade in:
CurrentAnswer[1] = "New Text";
// Textblock 1 - 3 do the fade in animation,
// even if only Textblock 2 has been updated
How can I limit the animation to only the Textblock whose bound value has been updated?

When you're using indexes you bind to indexer property of your collection and UI does not know which index has changed it is just notified that indexer property has changed without specifying which index so, in your case, it refreshes all 3 TextBlocks raising TargetUpdated event. What happens is ObservableCollection raises PropertyChanged event with Binding.IndexerName as property name. To solve your problem instead of using 3 TextBlocks you can use ItemsControl
<ItemsControl ItemsSource="{Binding CurrentAnswers}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Style="{StaticResource FadeInTextBlock}" Text="{Binding NotifyOnTargetUpdated=True}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
this will repeat TextBlock as many time as many items you have in CurrentAnswers

Related

WPF TextBlock animation not firing when source is updated

I am simply trying to have some text flash red momentarily when the source for this TextBlock is updated. The text that the TextBlock is bound to works just fine, but for some reason the animation won't fire. I'm at a bit of a loss.
Any ideas?
<Border BorderBrush="{StaticResource Button.BackgroundBrush}"
Background="{StaticResource Screener.Background}"
BorderThickness="0,1,0,0"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<TextBlock Text="{Binding AlertBoxMessage, Mode=OneWay, NotifyOnSourceUpdated=True}"
Name="AlertBox"
MinHeight="55"
FontWeight="Normal"
FontSize="16"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Foreground="Black">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Style.Triggers>
<EventTrigger RoutedEvent="Binding.SourceUpdated" >
<BeginStoryboard>
<Storyboard Storyboard.TargetProperty="Foreground">
<ColorAnimation From="Black"
To="Red"
AutoReverse="True"
RepeatBehavior="3"
Duration="0:0:2"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</Border>
First of all I think you misinterpreted the Binding.SourceUpdated event.
Each binding consists of two endpoints - a source and a target. The target is the object on which the binding is set, target property is the DependencyProperty whose value will be bound, the source is the object against which Binding.Path will be resolved, and source property is the property to which the target property is bound. In your case the target is the TextBlock, target property is Text, source is the (inherited) data context (a view-model I presume), and source property is AlertBoxMessage.
As you probably know bindings can work in several modes. TwoWay will allow updating in both source-to-target and target-to-source directions, whereas OneWay will only do source-to-target updates.
Key information here is that the Binding.SourceUpdated will be raised whenever a target-to-source transfer occurs (and Binding.TargetUpdated for source-to-target transfer, see MSDN). In your case however the value is always updated in source-to-target direction, because you set the binding mode to OneWay, so Binding.SourceUpdated is never raised. To achieve your goal you should use Binding.TargetUpdated event instead (with Binding.NotifyOnTargetUpdated set to true).
There are however several other issues with your code that will prevent it from working, so let's go over them:
You're trying to animate the TextBlock.Foreground property (of type Brush) with a ColorAnimation, which can only be used to animate a property of type Color, and you'll get an InvalidOperationException. You should set StoryBoard.TargetProperty="Foreground.Color"
It may seem a bit counter-intuitive but setting RepeatBehavior="3" will make the animation repeat for 3 days, and not 3 times (see this question). You should use RepeatBehavior="3x"
To sum things up, here's the code that should do what you expect:
<TextBlock Text="{Binding AlertBoxMessage, Mode=OneWay, NotifyOnTargetUpdated=True}"
Name="AlertBox"
MinHeight="55"
FontWeight="Normal"
FontSize="16"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Foreground="Black">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Style.Triggers>
<EventTrigger RoutedEvent="Binding.TargetUpdated">
<BeginStoryboard>
<Storyboard Storyboard.TargetProperty="Foreground.Color">
<ColorAnimation From="Black"
To="Red"
AutoReverse="True"
RepeatBehavior="3x"
Duration="0:0:2"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>

WPF smooth transition for databound property updates

I have a control that has its margin bound to a property of my view model:
<Grid Margin="{Binding Path=Property1, Converter={StaticResource Converter1}}"></Grid>
How do I get a smooth animation between successive updates to the Margin property? I want the margin to slide for a short amount of time instead of a discrete jump. Preferably a xaml solution.
Edit:
This is different than the other questions on this site, because I would need the "From" in a thickness animation to be bound to the previous value, and "To" to be bound to the updated value. It seems like a hack to just add another property to the view model for this.
Found the solution; the animation only needs to bind to the "From" and it will animate the way I want.
<Grid Margin="{Binding Path=Property1,
NotifyOnTargetUpdated=True,
Converter={StaticResource Converter1}}">
<Grid.Triggers><EventTrigger RoutedEvent="Binding.TargetUpdated">
<BeginStoryboard><StoryBoard>
<ThicknessAnimation Storyboard.TargetProperty="Margin"
Duration="00:00:00.5"
From="{Binding Path="Property1" Converter={StaticResource Converter1}}"/>
</StoryBoard></BeginStoryboard>
</EventTrigger></Grid.Triggers>
</Grid>
You can use ThicknessAnimation:
<BeginStoryboard>
<Storyboard>
<!-- BorderThickness animates from left=1, right=1, top=1, and bottom=1 to
left=28, right=28, top=14, and bottom=14 over one second. -->
<ThicknessAnimation
Storyboard.TargetProperty="Margin"
Duration="0:0:1.5" FillBehavior="HoldEnd" From="1,1,1,1" To="28,14,28,14" />
</Storyboard>
</BeginStoryboard>
You just need to bind the properties From and To

Animate DependencyProperty on a UserControl displayed through a ContentPresenter

In WPF, I have a ListBox with the list made up of UserControls. The controls are meant to navigate to different screens in the application. Each UserControl (called NavigationButton) has an icon and text. The icons are mostly combinations of multiple Path objects, so each icon is it's own UserControl, and they are being displayed using a ContentPresenter. I want to be able to animate the color of the icon depending on different states of the screen, but have tried a lot of options and have been unable to do this.
Here is a stripped down version of NavigationButton:
<DockPanel Margin="12,0,12,0">
<!-- Icon -->
<ContentPresenter x:Name="Content_Icon" Content="{Binding}" Width="20"/>
<!-- Text -->
<Grid Margin="9,0,0,0">
<TextBlock x:Name="TextBlock_Text" Text="{Binding ScreenName, Converter={StaticResource StringToStringUpperConverter}}" VerticalAlignment="Center"
FontSize="15" Foreground="#FFF2F2F2" />
</Grid>
Basically, I need to animate a property on the ContentPresenter, but don't know how to access it.
Here is the ListBox hosting the NavigationButtons:
<ListBox DockPanel.Dock="Top" ItemsSource="{Binding ScreenViewModels}"
SelectedItem="{Binding SelectedScreenViewModel}">
<ListBox.ItemTemplate>
<DataTemplate>
<my:NavigationButton/>
</DataTemplate>
</ListBox.ItemTemplate>
I have created a base UserControl (called IconBaseControl) that all of these icon UserConrols can inherit. The base control has a Brush DependencyProperty, called IconFill. The parts of the paths on the icon that can change are bound to this property:
<Path Data="<data>" Fill="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type my:IconBaseControl}}, Path=IconFill}"
I know the binding is working correctly because the colors change when I change the default color on the UserControl. Ideally I want to use a VisualStateManager, because there will be many different states. So, I have a VisualStateManager on NavigationButton, the UserControl containing the ContentPresenter that hosts the icon (all UserControls that inherit IconBaseControl), called Content_Icon. I tried something like this in one of the states:
<VisualState x:Name="Deselected">
<Storyboard>
<ColorAnimation Storyboard.TargetName="TextBlock_Text" Storyboard.TargetProperty="Foreground.Color"
To="#FF5e5e5e" Duration="0"/>
<ColorAnimation Storyboard.TargetName="Content_Icon" Storyboard.TargetProperty="IconFill"
To="#FF5e5e5e" Duration="0"/>
</Storyboard>
</VisualState>
But I get the following error:
InvalidOperationException: Cannot resolve all property references in the property path 'IconFill'. Verify that applicable objects support the properties.
I also tried binding the property of the storyboard with something like this:
Storyboard.TargetProperty="(IconBaseControl.IconFill)
But get this error:
IconBaseControl is not supported in a Windows Presentation Foundation (WPF) project.
I have also tried messing around in code behind but cannot figure out how to convert the ContentPresenter to an IconBaseControl. I figured the ContentTemplate property would be the way to go but it's Nothing.
Any suggestions on how to animate this property? Open to pretty much anything :) I'm coding in VB.Net but any C# suggestions are fine too.
Thanks in advance.
EDIT: Included code for NavigationButton
I find that creating sub-classes of WPF controls can get messy and isn't necessary unless it is a very advanced problem. In my opinion creating the IconBaseControl as a child of UserControl is overkill in your scenario.
Here's my suggestion assuming you are using MVVM: create the IconBaseControl as a normal UserControl. Just create a IconControl.xaml with IconControl.xaml.cs code behind file just like you would any other view.
Here is an example of what you would have inside IconControl:
<UserControl>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding IsSelected}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<ColorAnimation Storyboard.TargetProperty="(Background).(SolidColorBrush.Color)" To="#FF5e5e5e" Duration="0:0:0" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<ColorAnimation Storyboard.TargetProperty="(Background).(SolidColorBrush.Color)" To="White" Duration="0:0:0" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Style>
<Image Source="Icon.jpeg" />
<TextBlock Text="{Binding PageName}" Grid.Column="1" />
</Grid>
</UserControl>`
Notice that the background of the surrounding grid will change based on a binding to a value called IsSelected on the DataContext. So at this point you need to create a ViewModel called IconControlViewModel.cs that has the IsSelected boolean exposed as a dependency property.
Finally the view that contains these navigation buttons:
<UserControl>
<ItemsControl ItemsSource="{Binding ListOf_IconControlViewModels}">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type IconControlViewModel}">
<local:IconView />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</UserControl>
Notice the DataTemplate that tells the ItemsControl what to render when it sees a IconControlViewModel in the ItemsSource list. This is how I would design it using the MVVM pattern. I hope this helps and let me know if you need clarification on my answer, or it's way off.
Cheers,
Eric

WPF: Show and persist ToolTip for a Textbox based on the cursor

The purpose of this tooltip is to show, the format of the string which must be entered.
The features I would like to achieve are:
The tooltip should be shown when the user places the cursor in the textbox, i.e. when the user tabs into the control.
The tooltip should update based on user input into the textbox (this can be achieved by binding).
The tooltip must persist until the user tabs out of the control.
I wanted to know if the standard tooltip as provided has configuration settings, properties, that can be used to achieve this,... in my research thus far I haven't found any. If the existing tooltip is not up to the task, which is very likely, I'd like some pointers, sample code to achieve this...
Thanks
Hasanain
Using a combination of event triggers, bindings, and minimal code-behind I managed to implement a behavior which would update the ToolTip while the user types into textbox; when the keyboard focus is lost the tooltip disappears.
Here is the xaml for the textbox:
<TextBox Grid.Column="0" x:Name="txtBxQckTkt" Margin="5,5,0,0" Width="250" ToolTipService.IsEnabled="True"
Text="{Binding QuickTicketText}">
<TextBox.Triggers>
<EventTrigger RoutedEvent="TextBox.GotKeyboardFocus">
<BeginStoryboard>
<Storyboard>
<BooleanAnimationUsingKeyFrames
Storyboard.TargetName="txtBxQckTktToolTip"
Storyboard.TargetProperty="IsOpen">
<DiscreteBooleanKeyFrame KeyTime="0:0:0" Value="False"/>
<DiscreteBooleanKeyFrame KeyTime="0:0:0.0001" Value="True" />
</BooleanAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="txtBxQckTktToolTip"
Storyboard.TargetProperty="Placement">
<DiscreteObjectKeyFrame Value="{x:Static PlacementMode.Bottom}"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="TextBox.LostKeyboardFocus">
<BeginStoryboard>
<Storyboard>
<BooleanAnimationUsingKeyFrames
Storyboard.TargetName="txtBxQckTktToolTip"
Storyboard.TargetProperty="IsOpen">
<DiscreteBooleanKeyFrame KeyTime="0:0:0" Value="True"/>
<DiscreteBooleanKeyFrame KeyTime="0:0:0.0001" Value="False" />
</BooleanAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</TextBox.Triggers>
<TextBox.ToolTip>
<ToolTip x:Name="txtBxQckTktToolTip" Placement="Bottom" Content="{Binding ToolTip}">
</ToolTip>
</TextBox.ToolTip>
</TextBox>
Here is the code-behind:
txtBxQckTktToolTip.PlacementTarget = txtBxQckTkt;
_handler = (s, e) =>
{
var viewModel = DataContext as SingleTradeEntryViewModel;
if (viewModel == null) return;
viewModel.OnKeyup.Execute(txtBxQckTkt.Text);
};
txtBxQckTkt.KeyUp -= _handler;
txtBxQckTkt.KeyUp += _handler;
When the command (OnKeyup) executes, it raises a change notification for the ToolTip property bound as seen in the xaml.
Thanks
Hasanain
You might have to implement your own using the Popup Control. Here is some sample XAML to get you started:
<Button Width="120" Height="30" Name="btn">
<Popup IsOpen="True" StaysOpen="True" PlacementTarget="{Binding ElementName=btn}" Placement="Bottom">
<Button Width="120" Height="30" Content="Button In ToolTip"/>
</Popup>
</Button>
And here is some example code to get you started:
http://social.msdn.microsoft.com/forums/en-US/wpf/thread/845ffad0-4abf-4830-b206-03f7fe53f74b
2. ToolTip="{Binding Text, ElementName=textBox1, UpdateSourceTrigger=PropertyChanged}"
Here textBox1 is your textbox name and I have changed UpdateSourceTrigger to PropertyChanged so it updates your tooltip as you type.
3. ToolTipService.ShowDuration="12000"
Give this property a random time which is long enough to suit your needs.
I don't fully understand your first point but I think you need the tooltip to show in your gotfocus eventhandler. This can be achieved by something like in the gotfocus event.
ToolTip toolTip = ToolTipService.GetToolTip(textBox1) as ToolTip;
toolTip.IsOpen = true;
You could create a trigger that sets the ToolTip based on if the control has focus or not

Animate WPF Datatemplate when item added to Listbox?

In my project I have a WPF Listbox bound to an ObservableCollection. Every time I add a new item to the Collection the same item is added to the Listbox automaticly.
To display the items in the Listbox I use a XAML Datatemplate.
What I want to do is animate an item once when it is added to the Collection/Listbox.
Can this be done?
As animation in the datatemplate maybe? I guess I need a trigger somehow to start this animate but what trigger is fired when a new item/datatemplate is added?
I think an event trigger for the FrameworkElement.Loaded routed event could work. For example:
<DataTemplate DataType="{x:Type l:Foo}">
<Button x:Name="Button" Content="{Binding Path=Bar}">
<Button.Background>
<SolidColorBrush x:Name="ButtonBrush" Color="Tan" />
</Button.Background>
</Button>
<DataTemplate.Triggers>
<EventTrigger RoutedEvent="FrameworkElement.Loaded" SourceName="Button">
<BeginStoryboard>
<Storyboard>
<ColorAnimation Storyboard.TargetName="ButtonBrush" Storyboard.TargetProperty="Color" To="Red" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</DataTemplate.Triggers>
</DataTemplate>

Resources