WPF Expander to expand on far right side of application - c#

I would like to alter my program so that when an image is clicked it reveals a sidebar on the far right side of the application.
I have currently been trying to achieve this using an Expander. This is what it looks like so far:
<Expander>
<Expander.Header>
<Image Width="200" Height="300" Source="{Binding image}"/>
</Expander.Header>
<i:Interaction.Behaviors>
<local:UniqueNameBehavior ID="{Binding id}"/>
</i:Interaction.Behaviors>
<StackPanel Margin="10,4,0,0">
<ToggleButton Margin="4" Content="Option 1" Template="{StaticResource SimpleExpanderButtonTemp}"/>
<ListView>
<TextBox Text="Search" />
</ListView>
</StackPanel>
</Expander>
The idea is that whenever any movie is clicked, a sidebar on the far right side of the app is revealed containing info about the movie. If another movie is subsequently clicked, the sidebar will now contain info about the new movie.
I know that it is possible to change the ExpandDirection, but this only results in the content appearing beside the image as opposed to the far right of the application.
I've also tried adjusting the Margin of StackPanel but this results in the surrounding movies being pushed aside.
Could someone please help me out with how I can achieve this?
Thank you for your help!

I don't believe this kind of behaviour can be achieved using expanders the way you are using.
Why don't you create a grid on the far right side of your app with its width set to 0 and when you click/select a movie it sets the grid width to a new value and change its content based on the movie?
<Grid HorizontalAlignment="Right">
<Grid.Style>
<Style TargetType="Grid">
<Setter Property="Width" Value="0"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ShowMovieForm}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Duration="0:0:0.1" Storyboard.TargetProperty="Width" To="400" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Duration="0:0:0.1" Storyboard.TargetProperty="Width" To="0" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Style>
</Grid>

Related

WPF - System.InvalidOperationException on ContextMenu.IsOpen

I was given the following exception:
An unhandled exception of type 'System.InvalidOperationException' occurred in PresentationFramework.dll
Additional information: Cannot resolve all property references in the property path 'ContextMenu.IsOpen'. Verify that applicable objects support the properties.
I guess the exception is quite self-explanatory, however I have no idea on how to fix it.
Here is my code:
XAML
<Button
x:Name="btnNotifications"
Height="50px"
Width="auto"
Padding="15 0"
Click="btnNotifications_Click"
ToolTip="Notifications & Agenda"
HorizontalAlignment="Right"
DockPanel.Dock="Right"
BorderThickness="0">
<Button.Style>
<Style TargetType="Button">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="{DynamicResource AccentColorBrush}"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="False">
<Setter Property="Background" Value="Transparent"/>
</Trigger>
<EventTrigger RoutedEvent="Click">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<BooleanAnimationUsingKeyFrames Storyboard.TargetProperty="ContextMenu.IsOpen">
<DiscreteBooleanKeyFrame KeyTime="0:0:0" Value="True"/>
</BooleanAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</Style.Triggers>
</Style>
</Button.Style>
<Button.Content>
<StackPanel
Orientation="Horizontal">
<Image
Source="/Resources/Icons/Notifications.ico"
Width="25px"
Height="25px"/>
<Label
x:Name="lblNotifications"
FontFamily="Century Gothic"
FontSize="25px"
Foreground="Maroon"
Visibility="Collapsed"/>
</StackPanel>
</Button.Content>
<Button.ContextMenu>
<ContextMenu
x:Name="btnNotificationsMenu">
<MenuItem
x:Name="btnNotificationsNoNew"
Header="No New Notifications."/>
<MenuItem
x:Name="btnNotificationsSeperator"
Background="{DynamicResource AccentColorBrush}"
Height="2px"
Focusable="False"
IsHitTestVisible="False"/>
<MenuItem
x:Name="btnNotificationsNoAgenda"
Header="Your Agenda is Empty."/>
</ContextMenu>
</Button.ContextMenu>
</Button>
Code Behind
public static void NewAppointmentForm()
{
MainWindow appointment = new MainWindow(new AppointmentForm(true));
appointment.btnNotificationsMenu.IsOpen = false;
appointment.ShowDialog();
}
Obviously wrapping the above code in a try catch and calling Close() on appointment fixes the issue. However, it is more of a workaround than a clean solution.
This issue occurs whenever I try to close the window through another Button. I tried closing the window using Close() method within an EventHandler and also as a command through XAML - Command="{Binding CloseCommand}".
I would be really grateful if someone could shed some light upon this issue.
May I point out that the Button containing the ContextMenu is wrapped inside a Border which is placed directly in the MainWindow.
If more detail is needed, please do ask. Thanks :)
Try surrounding the property with parenthesis:
Storyboard.TargetProperty="(ContextMenu.IsOpen)"
See PropertyPath XAML Syntax. The XAML parser doesn't know how to resolve the property because it can be attached to any DependencyObject.
This was a typical Isaac silly mistake.
Basically, I copied the Button which had the ContextMenu and pasted the code to create two ContextMenu-less buttons. Having the following applied to the ContextMenu-less buttons caused the program to crash:
<EventTrigger RoutedEvent="Click">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<BooleanAnimationUsingKeyFrames Storyboard.TargetProperty="ContextMenu.IsOpen">
<DiscreteBooleanKeyFrame KeyTime="0:0:0" Value="True"/>
</BooleanAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>

WPF Event Trigger Change Other UI element

I defined ListBox in my XAML which uses ItemTemplate.
Inside the ItemTemplate I placed Image.
<ListBox.ItemTemplate>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel x:Name="itmTempPanel" IsItemsHost="True" ItemWidth="60" ItemHeight="60" Width="{Binding ElementName=lstFilesDropped, Path=Width}"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
...
<Image>
<Image.Triggers>
<EventTrigger RoutedEvent="MouseEnter">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Height" To="71" Duration="0:0:0.3" />
<DoubleAnimation Storyboard.TargetName="itmTempPanel" Storyboard.TargetProperty="Height" To="71" Duration="0:0:0.3" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Image.Triggers>
</Image>
</ListBox.ItemTemplate>
When the mouse enter the image I want to begin storyboard on that image height and on the WrapPanel which I defined inside the ItemsPanelTemplate.
When the mouse enter to this image I got the following exception:
"'itmTempPanel' name cannot be found in the name scope of 'System.Windows.Controls.Image'."
How can I change other element property from element that begin the storyboard.
Thank you for your help !!
There are 2 ways to solve this. The first is using {x:Reference} a feature in .NET 4.0 for WPF. You should follow this approach if your application targets .NET 4.0. The idea is setting the Storyboard.Target to the object you want to animate (in this case is the WrapPanel). Although we can use Binding for Storyboard.Target but we cannot use RelativeSource or ElementName to set the binding source because Storyboard (or Timeline) is not a FrameworkElement (or FrameworkContentElement). The only way to specify the source is setting the Source property. However we can normally set this to a StaticResource or DynamicResource or directly (using element syntax). It's fortunate that the {x:Reference} was introduced in .NET 4.0 and this can help you reference any named object inside XAML (the way it works is not like ElementName). Here is the code for the first approach:
<DoubleAnimation Storyboard.Target="{Binding Source={x:Reference itmTempPanel}}"
Storyboard.TargetProperty="Height"
To="71" Duration="0:0:0.3" />
The second approach is based on DataTrigger. However this trigger is not for Image, it's exactly for the WrapPanel. But now the ElementName can be used to bind the Trigger source to the Image. So this approach is usable when {x:Reference} is not supported.
<WrapPanel x:Name="itmTempPanel" IsItemsHost="True" ItemWidth="60" ItemHeight="60"
Width="{Binding ElementName=lstFilesDropped, Path=Width}">
<WrapPanel.Style>
<Style TargetType="WrapPanel">
<Style.Triggers>
<DataTrigger Binding="{Binding IsMouseOver,ElementName=image}"
Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Height"
To="71" Duration="0:0:0.3" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>
</WrapPanel.Style>
</WrapPanel>
<Image Name="image">
<Image.Triggers>
<EventTrigger RoutedEvent="MouseEnter">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Height" To="71"
Duration="0:0:0.3" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Image.Triggers>
</Image>
Note that you have to give the Image a name (such as image). The <DoubleAnimation> for the WrapPanel is removed. Instead of using EventTrigger, we used DataTrigger listening to IsMouseOver property. You can also specify the DataTrigger.ExitActions to start animating when IsMouseOver is false (equal to MouseLeave).

Access a Control.Property inside a UserControl from Parent Control

I am trying to get an Ellipse to blink in case the icon inside another UserControl is Pause, and stop blinking if the icon is Play.
Here is the Ellipse and my attempt at binding to the PlayIcon.Opacity with a DataTrigger, which isn't working
<Ellipse.Style>
<Style TargetType="{x:Type Ellipse}">
<Style.Triggers>
<DataTrigger Binding="{Binding Opacity, Path=PlayIcon, RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type views:PlayButton}}}" Value="0">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetProperty="Opacity"
From="0"
To="3"
RepeatBehavior="Forever"
AutoReverse="True"
Duration="0:0:0.1"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Ellipse.Style>
<views:PlayButton />
Here is the Pause and Play Icons in the PlayButton UserControl
<UserControl x:Class="MyNamespace.PlayButton" ...>
...
<Path Name="PlayIcon" Fill="Blue" Opacity="0" Data="M18,12 18,38 35,25"/>
<Path Name="PauseIcon" Fill="Blue" Opacity="10" Data="M15,12 15,38 23,38 23,12z M27,12 27,38 35,38 35,12" />
...
</UserControl>
How can I make this DataTrigger fire when the Opacity of the Play Icon is 0, or if Pause icon Opacity is 10 inside the PlayButton UserControl?

Dynamically changing value of a property specified in a style in app.xaml

I am new to WPF. Recently i encountered an issue where i have to dynamically change the font size of a label.
To achieve this i created a test application and created a style with a key in my App.xaml.
Then gave this style to the label.
Style is as below:
<Style x:Key="myLbl" TargetType="Label">
<Setter Property="FontSize" Value="20"/>
</Style>
Then I gave this Style to the Label in my Window XAML like :
<Label Name="lblDemo" Content="Test" Foreground="Black" Style="{DynamicResource myLbl}"/>
I want to change the value of the Property in the Style with the click of the button. I searched for it a little bit but havent found anything useful. Can anyone suggest me a right possible direction. Any help will be appreciated.
Thanks.
If you'd like to do this using XAML only, you could do it like so:
<StackPanel>
<Label Name="lblDemo" Content="Test" Foreground="Black" FontSize="20"/>
<Button x:Name="ClickMe" Content="Enlarge" />
<StackPanel.Triggers>
<EventTrigger RoutedEvent="Button.Click" SourceName="ClickMe">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="lblDemo" Storyboard.TargetProperty="FontSize">
<DiscreteDoubleKeyFrame KeyTime="00:00:00.01" Value="48" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</StackPanel.Triggers>
</StackPanel>
OR you can use good old fashioned Click event handler in code behind with this code (using the Button above without StackPanel.Triggers section):
private void ClickMe_OnClick(object sender, RoutedEventArgs e) { lblDemo.FontSize = 48; }

ControlTemplate Storyboard color animation problem

I have a problem with color animation. This is my source:
<Window.Resources>
<hedit:BrushToColorConverter x:Key="BrushToColorConverter" />
<Style x:Key="MyButtonStyle" TargetType="Button">
<Setter Property="OverridesDefaultStyle" Value="True"/>
<Setter Property="Margin" Value="5"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<ControlTemplate.Resources>
<Storyboard x:Key="buttonAnimIn">
<!-- Problem line -->
<ColorAnimation Storyboard.TargetName="bntBack" Storyboard.TargetProperty="Color" To="{Binding Path=Foreground, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}, Converter={StaticResource BrushToColorConverter}}" />
</Storyboard>
<Storyboard x:Key="buttonAnimOut">
<ColorAnimation Storyboard.TargetName="bntBack" Storyboard.TargetProperty="Color" To="Blue" />
</Storyboard>
<Storyboard x:Key="buttonAnimForegroundIn">
<ColorAnimation Storyboard.TargetName="btnFore" Storyboard.TargetProperty="Color" To="Blue" />
</Storyboard>
<Storyboard x:Key="buttonAnimForegroundOut">
<ColorAnimation Storyboard.TargetName="btnFore" Storyboard.TargetProperty="Color" To="Red" />
</Storyboard>
</ControlTemplate.Resources>
<Border Name="border"
BorderThickness="1"
Padding="4,2"
BorderBrush="DarkGray"
CornerRadius="3">
<Border.Background>
<SolidColorBrush Color="Blue" x:Name="bntBack" />
</Border.Background>
<ContentControl HorizontalAlignment="Center" VerticalAlignment="Center" Content="{TemplateBinding Content}">
<ContentControl.Foreground>
<SolidColorBrush Color="Red" x:Name="btnFore" />
</ContentControl.Foreground>
</ContentControl >
</Border>
<ControlTemplate.Triggers>
<EventTrigger RoutedEvent="Button.MouseEnter">
<BeginStoryboard Storyboard="{StaticResource buttonAnimIn}" />
<BeginStoryboard Storyboard="{StaticResource buttonAnimForegroundIn}" />
</EventTrigger>
<EventTrigger RoutedEvent="Button.MouseLeave">
<BeginStoryboard Storyboard="{StaticResource buttonAnimOut}" />
<BeginStoryboard Storyboard="{StaticResource buttonAnimForegroundOut}" />
</EventTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
The problem is:
Cannot convert the value in attribute 'Style' to object of type 'System.Windows.Style'. Cannot freeze this Storyboard timeline tree for use across threads. Error at object 'System.Windows.Controls.Button' in markup file 'HLSLEditor;component/mainwindow.xaml' Line 223 Position 25.
When using fixed colors it worked, but it cannot work with the Foreground color of the parent...
How do I do an animation to the foreground or background color?
Thanks!
You cannot freeze Bindings, you probably can get around this issue by declaring a color as a resource and then bind your Control's Background to it while using StaticResource in the animation.
e.g.
<Window.Background>
<SolidColorBrush Color="{DynamicResource Background}"/>
</Window.Background>
<Window.Resources>
<Color x:Key="Background">Green</Color>
</Window.Resources>
<ColorAnimation Storyboard.TargetProperty="Foreground.Color"
Duration="0:0:1"
To="{StaticResource Background}"/>
Alternative using a resource class:
public static class MyColors
{
public static Color MyHighlightColor = Color.FromArgb(255, 0, 88, 0);
}
<ColorAnimation Storyboard.TargetProperty="Foreground.Color"
Duration="0:0:1"
To="{x:Static local:MyColors.MyHighlightColor}"/>
I think that understanding the error might give you a way of fixing the problem.
Animation requires the use of threads besides the UI thread. So storyboards have to be freezable, which means that all the animations in the storyboard must be freezable, and everything those animations use must also be freezable.
Bindings aren't freezable - pretty much by definition, as they are a mechanism whereby a dependency property can be changed. You can't use a dynamic binding in a color animation - there's the possibility that the property could change while the animation was running. The same thing happens whether you're binding to an object or you're using DynamicResource.
The thing is, this is protecting you from something that you don't really want anyway. You don't really want the colors to change while the animation is running. That's not what you're trying to accomplish. You want the color resources that the animation is using to change if the user selects a different skin.
So instead of binding storyboards to skinnable resources, add the storyboards to the dictionary of resources that get set when the skin changes (using static bindings to set the colors), and use dynamic binding in your event triggers. That should work.
When I came across this problem I worked around it by modifying my style to contain two identical elements on top of each other - one for the 'normal' state and one for the 'pressed' state. The 'pressed' one had its Opacity set to 0 by default and the other one had an Opacity of 1. My animation changed the opacities from 0 to 1 and vice versa.
This approach avoided actually animating the Color property but produced the same effect whilst keeping everything in XAML. As the colours were set in the style definition rather than the animation they could be bound as required. This will probably not be suitable for all situations but for my fairly simple style it was a very quick way to achieve the desired effect.

Categories

Resources