Listbox selected item from usercontrol - wpf

I have a listbox populate with controls , the control is populated with textboxes and comboboxes. I need to select the underlying listitem when i edit the textbox and select in the combos. Cant seem to find the solutio. Anyone?
<ListBox.ItemTemplate>
<DataTemplate>
<Controls:ComponentEditItem Background="Transparent"/>
</DataTemplate>
</ListBox.ItemTemplate>

You can add an EventTrigger that selects underlying ListBoxItem when one of its controls is focused. Something like this:
<ListBox.ItemTemplate>
<DataTemplate>
<Controls:ComponentEditItem Background="Transparent">
<Controls:ComponentEditItem.Triggers>
<EventTrigger RoutedEvent="GotFocus">
<BeginStoryboard>
<Storyboard>
<BooleanAnimationUsingKeyFrames Duration="00:00:00" Storyboard.Target="{Binding Path=., RelativeSource={RelativeSource FindAncestor, AncestorType=ListBoxItem}}" Storyboard.TargetProperty="IsSelected">
<DiscreteBooleanKeyFrame Value="True" />
</BooleanAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Controls:ComponentEditItem.Triggers>
</Controls:ComponentEditItem>
</DataTemplate>
</ListBox.ItemTemplate>

Related

How to set DataContext of Button.ContextMenu same as the Button?

<Button Content="{Binding Type}" Name="Ellipsis" Tag="{Binding ElementName=Ellipsis, Path=DataContext}">
<Button.ContextMenu>
<ContextMenu x:Name="MainContextMenu" DataContext="{Binding Path=PlacementTarget.Tag, RelativeSource={RelativeSource Self}}">
<MenuItem Header="{Binding Type}"/>
</ContextMenu>
</Button.ContextMenu>
<Button.Triggers>
<EventTrigger SourceName="Ellipsis" RoutedEvent="Button.Click">
<BeginStoryboard>
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="MainContextMenu" Storyboard.TargetProperty="(ContextMenu.IsOpen)">
<DiscreteObjectKeyFrame KeyTime="0:0:0">
<DiscreteObjectKeyFrame.Value>
<sys:Boolean>True</sys:Boolean>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Button.Triggers>
</Button>
As shown above, Button.Content="{Binding Type}" works perfectly but when it goes in its ContextMenu, the DataContext changes which causing MenuItem Header="{Binding Type}" to not work. I researched online and someone said saving the outer DataContext in a tag and use it as the inner DataContext. I tried that in my code but ContextMenu still not reading the correct DataContext. The MenuItem Header should be the same as the Button.Content in this case but it is not. What should I do?
To answer the datacontext part of the question.
One way would be to ensure your contextmenu would have the same datacontext as a button would be to define the contextmenu as a resource.
<Grid.Resources>
<ContextMenu x:Key="MainContextMenu">
<MenuItem Header="{Binding Type}"/>
</ContextMenu>
</Grid.Resources>
<Button ContextMenu="{StaticResource MainContextMenu}"
Because the contextmenu is instantiated in the grid, it inherits the grid's datacontext.
The left click will not work without code though. Not with a button.
Not sure why you'd want left click to show a context menu but maybe you could consider a popup instead of contextmenu.
You could animate isopen using a booleananimation
<BooleanAnimationUsingKeyFrames Storyboard.TargetName="MyPopup" Storyboard.TargetProperty="(Popup.IsOpen)">
<DiscreteBooleanKeyFrame Value="True" />
</BooleanAnimationUsingKeyFrames>
Or you could instead use a togglebutton which has an ischecked boolean:
<ToggleButton Content="{Binding Type}"
x:Name="Ellipsis" />
<Popup IsOpen="{Binding IsChecked, ElementName=Ellipsis, Mode=TwoWay}"
PlacementTarget="{Binding ElementName=Ellipsis}"
DataContext="{Binding DataContext, ElementName=Ellipsis}"
StaysOpen="False">
<ListBox>
<MenuItem Header="{Binding Type}"/>
</ListBox>
</Popup>
OK, probably needs a bit more work but my popup shows up and has the correct datacontext:

Closing Popup after clicking child button using no code-behind

I have a wpf application with a toggle window which opens a popup control. I want to be able to close it after the user clicks it's child button.
My preference would be to do it in the xaml via style triggers. But for some reason I can't set my popup IsOpen property inside of the event trigger. I receive an error saying
A value of type setter cannot be added to a collection or dictionary of type TriggerActionCollection
here's how I have the xaml set up
<ToggleButton x:Name="ShowAvailableOptionsToggleButton"
Content="Add Options" />
<Popup IsOpen="{Binding IsChecked, ElementName=ShowAvailableOptionsToggleButton}">
<StackPanel>
<ListView ItemsSource="{Binding AvailableOptions}"
IsSynchronizedWithCurrentItem="True"
DisplayMemberPath="Name"/>
<Button Content="Add"
Name="AddOptionBtn"
Command="{Binding AddOptionCommand}"/>
</StackPanel>
<Popup.Style>
<Style>
<Style.Triggers>
<EventTrigger SourceName="AddOptionBtn" RoutedEvent="Button.Click">
//ERROR HAPPENS HERE
<Setter Property="IsOpen" Value="False"/>
</EventTrigger>
</Style.Triggers>
</Style>
</Popup.Style>
</Popup>
Can someone see what I am doing wrong?
EventTrigger has to be used with animations:
<Popup IsOpen="True">
<Button x:Name="AddOptionBtn"
Content="Add" />
<Popup.Triggers>
<EventTrigger SourceName="AddOptionBtn"
RoutedEvent="Button.Click">
<BeginStoryboard>
<Storyboard>
<BooleanAnimationUsingKeyFrames Storyboard.Target="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=Popup}}"
Storyboard.TargetProperty="IsOpen">
<DiscreteBooleanKeyFrame KeyTime="0"
Value="False" />
</BooleanAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Popup.Triggers>
</Popup>

Styling WPF Popup (like popups iTunes has)

I'm trying to create a popup that resembles what iTunes has when you click the arrow button or when you click the upnext button. can anyone help me style this in WPF? I could use some code samples, Thanks
<DataTemplate x:Key="popupStyle">
<Canvas>
<ToggleButton Content="test" IsChecked="{Binding IsPopupVisible, Mode=TwoWay}"/>
<AdornerDecorator Opacity="0" x:Name="adorner" Margin="20,0,0,0">
<AdornerDecorator.Effect>
<DropShadowEffect BlurRadius="10" ShadowDepth="0"/>
</AdornerDecorator.Effect>
<Grid>
<Polygon Stroke="Silver" StrokeThickness="2" Fill="#AFFF" Points="0 10 20 0 150 0 150 150 20 150 20 20"/>
<StackPanel Margin="30,10,10,10">
<MenuItem Header="asfsd"/>
<MenuItem Header="asfsd"/>
<MenuItem Header="asfsd"/>
<MenuItem Header="asfsd"/>
</StackPanel>
</Grid>
</AdornerDecorator>
</Canvas>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding IsPopupVisible}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard Duration="0:0:0.3">
<DoubleAnimation
Storyboard.TargetName="adorner"
Storyboard.TargetProperty="Opacity"
Duration="0:0:0.3" To="1"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard Duration="0:0:0.3">
<DoubleAnimation
Storyboard.TargetName="adorner"
Storyboard.TargetProperty="Opacity"
Duration="0:0:0.3" To="0"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
This DataTemplate is using a boolean variable in its ViewModel, named IsPopupVisible.
You can use the DataTemplate in a Window like below:
<Window... MouseDown="Window_MouseDown">
<Window.Resources>
<DataTemplate x:Key="popupStyle">
...
</DataTemplate>
</Window.Resources>
<ContentPresenter ContentTemplate="{StaticResource popupStyle}" Content="{Binding}"/>
</Window>
and add the mouseDown event to the whole Window:
private void Window_MouseDown(object sender, MouseButtonEventArgs e)
{
IsPopupVisible = false;
}
so that when user clicks anywhere else, popup goes hidden.

WPF DataTemplate: Location of inflated storyboard

I have a DataTemplate for an ItemsControl which is working fine. There is a DataTrigger in the DataTemplate which contains a BeginStoryboard EnterAction. I am trying to wire up the Completed event of the storyboard to something in code behind, specifically a method on the data object, but I can be flexible about that - at the moment I just want it to run any piece of C# code when the animation has completed.
Specifying a value for the Completed XAML attribute does not compile as the attribute is defined inside a template so there is no specific method to wire up to. So I will need to use code behind to wire up the event manually.
To this end I have looked at the application with Snoop to try to find where in the logical or visual tree the inflated template Storyboards end up. So far all I can see is a ContentControl created for each item, with its ContentTemplate set. The Content property of each ContentControl is set to its corresponding data object. The ContentTemplate property contains the Triggers collection which contain the EnterActions and ultimately the Storyboard. My question is, do all the items share a single template instance for their ContentTemplate property, or do they each get their own copy? If they share one, then where are the inflated triggers and storyboards created?
I've extracted the pertinent parts of my XAML:
<Style TargetType="{x:Type m:MyControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type m:MyControl}">
<Grid Name="ControlRoot" DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}}">
<!-- ... -->
<ItemsControl ItemsSource="...">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type m:MyDataType}">
<Grid>
<Ellipse Name="IconHighlight1" Fill="{DynamicResource GoldRadialFade}" Width="70" Height="70" StrokeThickness="0" Opacity="0"/>
</Grid>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Highlighted}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard HandoffBehavior="Compose">
<Storyboard Name="ConnectToMe" Duration="0:0:2.5" FillBehavior="Stop">
<DoubleAnimation To="400" Duration="0:0:1.5" Storyboard.TargetName="IconHighlight1" Storyboard.TargetProperty="Height" FillBehavior="Stop" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
In such cases, I'd normally prefer to have a bool in the DataContext of the Item that your Storyboard is applying to and say call it AnimationCompleted
Now by modifying your Storyboard to
<Storyboard x:Key="ConnectToMe" Duration="0:0:2.5" FillBehavior="Stop">
<DoubleAnimation To="400" Duration="0:0:1.5" Storyboard.TargetName="IconHighlight1" Storyboard.TargetProperty="Height" FillBehavior="Stop" />
<BooleanAnimationUsingKeyFrames Storyboard.TargetProperty="DataContext.AnimationCompleted" FillBehavior="HoldEnd">
<DiscreteBooleanKeyFrame Value="False" KeyTime="0:0:0" />
</BooleanAnimationUsingKeyFrames>
<BooleanAnimationUsingKeyFrames Storyboard.TargetProperty="DataContext.AnimationCompleted" FillBehavior="HoldEnd">
<DiscreteBooleanKeyFrame Value="True" KeyTime="0:0:2.5" />
</BooleanAnimationUsingKeyFrames>
</Storyboard>
We toggle the bool AnimationCompleted to true at the end point of the animation. Hence in the property setter of AnimationCompleted check if the incoming value is True and trigger your corresponding function/method from there

Can't get (fairly simple) WPF animation working

I'm trying to get a message panel animated in WPF but has so far achieved no success.
This is the situation:
I have a user control with a StackPanel containing an ItemsControl bound to an (observable) collection in the control's View Model object (ViewModel.Messages).
When I need to present the user with messages I ad those (as MessageVM instances) to the observable collection.
The ItemsControl's visibility is bound to an integer property called ViewModel.CountVisibleMessages and there's a converter taking care of translating 0 to Visibility.Hidden and positive values to Visibility.Visible.
This works just fine. When a message gets added to the collection the StackPanel automatically becomes visible and as the user (or a timer) removes the last message it gets hidden. The StackPanel height is automatically adjusted to fit all messages of course.
To make everything look nicer I would prefer it if the StackPanel resized itself using an animation running for, say, 300 ms. (Ultimately I would also like it to accelerate and deccelerate but that's beyond my ambition right now.
I have experimented for a few hours now but I feel I'm not even close.
Below is my current (not even close to working) XAML at the moment:
<StackPanel Orientation="Vertical"
VerticalAlignment="Top"
Visibility="{Binding CountVisibleMessages, Converter={StaticResource IntToVisibility}}"
Height="Auto"
Background="{DynamicResource HmiBackColorLightBrush}">
<StackPanel.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding CountVisibleMessagesChanged}" Value="True" ><!-- I suppose I shopuld've used a Routed Event here but I just needed to get it triggered -->
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetProperty="Margin.Bottom"
From="100" <!-- Just a made up value to test the concept -->
To="0"
Duration="0:0:0:3"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>
</StackPanel.Style>
<ItemsControl ItemsSource="{Binding Messages}" >
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Style="{DynamicResource Message}">
<Grid >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="15" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding Text}" Margin="3" Style="{Binding MessageType, Converter={StaticResource MessageTypeToStyle}, ConverterParameter={x:Type TextBlock}}" /> <!-- using dynamic styling here -->
<RadioButton Grid.Column="1" Style="{DynamicResource HmiCloseMessageButton}" IsChecked="{Binding IsVisible, Converter={StaticResource BoolToVisibility}, ConverterParameter=true}" />
</Grid>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
(I do realize the above XAML won't get the StackPanel to auto-resize slowly. It's just an experiment to get anything happening).
This can't be too difficult I suppose (it's a pretty standard UI behavior in many programs) so I'd appreciate it if anyone could point me in the right directions.
Cheers
Are you sure you want to adjust the bottom margin? The stack panel VerticalAlignment is top. If you want to change the Height then bind your StoryBoard Property to Height. Do you know if your StoryBoard is firing?
The key point is ExitActions are necessary for EnterAction based dataTrigger animations. So the following seems to be working in my case ....
<StackPanel Grid.Row="0" Height="100" x:Name="MyGrid">
<StackPanel.Style>
<Style>
<Style.Triggers>
<DataTrigger
Binding="{Binding ElementName=MyCheckBox,
Path=IsChecked}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.Target="{Binding
RelativeSource={RelativeSource
AncestorType={x:Type
StackPanel}},
BindsDirectlyToSource=True}"
Storyboard.TargetProperty="Height"
From="100"
To="200"
Duration="0:0:1"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.Target="{Binding
RelativeSource={RelativeSource
AncestorType={x:Type
StackPanel}},
BindsDirectlyToSource=True}"
Storyboard.TargetProperty="Height"
To="100"
Duration="0:0:1"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</StackPanel.Style>
</StackPanel>
<CheckBox x:Name="MyCheckBox" Grid.Row="1" />
Let me know if this helps.

Resources