WPF - problem with DataTrigger which work only once - wpf

I have problem with DataTrigger which work only once.
Button 'btnLokaty' fired Storyboard 'PlikZLokatamiStory' only first time during running program.
I've installed 'PropertyChanged.Fody' in my project and it's work fine. Property 'PlikZLokatamiFileNameCheck' work correctly. Checkbox 'test' show value of 'PlikZLokatamiFileNameCheck'.
Can anyone help me resolve problem?
Many thx
Best regards
XAML
<Window x:Class="LokatyNegocjowane.View.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="clr-namespace:LokatyNegocjowane.Viemodel"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
mc:Ignorable="d"
x:Name="wndMain" Title="MainWindow" Height="600" Width="800" WindowStyle="None" WindowStartupLocation="CenterScreen">
<Window.Resources>
<Storyboard x:Key="PlikZLokatamiStory">
<ThicknessAnimationUsingKeyFrames Storyboard.TargetProperty="(FrameworkElement.Margin)" Storyboard.TargetName="btnLokaty">
<EasingThicknessKeyFrame KeyTime="0" Value="0,0,10,0"/>
<EasingThicknessKeyFrame KeyTime="0:0:1" Value="-100,0,10,0"/>
</ThicknessAnimationUsingKeyFrames>
</Storyboard>
<Storyboard x:Key="ControlOpacity">
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)">
<EasingDoubleKeyFrame KeyTime="0" Value="0"/>
<EasingDoubleKeyFrame KeyTime="0:0:1" Value="1"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
<Storyboard x:Key="PlikZLokatamiStoryBack">
<ThicknessAnimationUsingKeyFrames Storyboard.TargetProperty="(FrameworkElement.Margin)" Storyboard.Target="{x:Reference btnLokaty}">
<EasingThicknessKeyFrame KeyTime="0" Value="-100,0,10,0"/>
<EasingThicknessKeyFrame KeyTime="0:0:1" Value="0,0,10,0"/>
</ThicknessAnimationUsingKeyFrames>
</Storyboard>
<Storyboard x:Key="ControlOpacityBack">
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)">
<EasingDoubleKeyFrame KeyTime="0" Value="1"/>
<EasingDoubleKeyFrame KeyTime="0:0:1" Value="0"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</Window.Resources>
<Window.DataContext>
<vm:ViewModel/>
</Window.DataContext>
<Grid Background="#FF3580BF">
<StackPanel Height="150" VerticalAlignment="Top">
<Image Height="150" Source="..\Resources\baner.jpg" HorizontalAlignment="Center" Stretch="Fill"/>
</StackPanel>
<StackPanel>
<CheckBox x:Name="test" IsChecked="{Binding PlikZLokatamiFileNameCheck}" Margin="0 150"/>
</StackPanel>
<StackPanel x:Name="StackPanelMenu" Width="600" HorizontalAlignment="Center" Margin="0 180 0 0">
<StackPanel x:Name="spLokaty" VerticalAlignment="Center" Orientation="Horizontal" Height="60">
<StackPanel.Style>
<Style TargetType="{x:Type StackPanel}">
<Style.Triggers>
<Trigger Property="Control.IsMouseOver" Value="True">
<Setter Property="Background" Value="#557080BF"/>
<Setter Property="Opacity" Value="5"/>
</Trigger>
</Style.Triggers>
</Style>
</StackPanel.Style>
<StackPanel x:Name="spPlikZLokatami" VerticalAlignment="Center" Margin="250 0 10 0">
<Button x:Name="btnLokaty" Background="Transparent" HorizontalAlignment="Center" BorderThickness="0" Command="{Binding WybierzPlikZLokatami}" Foreground="White">
<Button.Style>
<Style TargetType="{x:Type Button}">
<Style.Triggers>
<DataTrigger Binding="{Binding PlikZLokatamiFileNameCheck}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard Storyboard="{StaticResource PlikZLokatamiStory}"/>
</DataTrigger.EnterActions>
</DataTrigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="Background" Value="Transparent"/>
</Trigger>
</Style.Triggers>
</Style>
</Button.Style>
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
<materialDesign:PackIcon Kind="File" Width="20" Height="20" VerticalAlignment="Center"/>
<TextBlock Text="Plik z lokatami" FontSize="10" VerticalAlignment="Center"/>
</StackPanel>
</Button>
</StackPanel>
<StackPanel x:Name="spFileName" VerticalAlignment="Center" Orientation="Horizontal">
<TextBlock x:Name="tbLokaty" Foreground="White" Text="{Binding PlikZLokatamiFileName}" Opacity="0">
<TextBlock.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding PlikZLokatamiFileNameCheck}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard Storyboard="{StaticResource ControlOpacity}"/>
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
<Button Background="Transparent" BorderBrush="{x:Null}" Opacity="0" Command="{Binding UsunPlikLokat}">
<Button.Style>
<Style TargetType="{x:Type Button}">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Cursor" Value="Hand"/>
</Trigger>
<DataTrigger Binding="{Binding PlikZLokatamiFileNameCheck}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard Storyboard="{StaticResource ControlOpacity}"/>
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<BeginStoryboard Storyboard="{StaticResource PlikZLokatamiStoryBack}"/>
<BeginStoryboard Storyboard="{StaticResource ControlOpacityBack}"/>
</EventTrigger>
</Button.Triggers>
<materialDesign:PackIcon Kind="RemoveCircle" Foreground="Red"/>
</Button>
</StackPanel>
</StackPanel>
<ListView Height="60">
<Button Background="Transparent" BorderThickness="0" Command="{Binding WybierzPlikZWnioskami}">
<Button.Content>
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
<materialDesign:PackIcon Kind="File" Width="20" Height="20" VerticalAlignment="Center"/>
<TextBlock Text="Plik z wnioskami" FontSize="10" VerticalAlignment="Center"/>
</StackPanel>
</Button.Content>
</Button>
</ListView>
<ListView Height="50">
<Button Background="Transparent" BorderThickness="0" Command="{Binding WybierzPlikZPracownikamiBOKZ}">
<Button.Content>
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
<materialDesign:PackIcon Kind="Worker" Width="20" Height="20" VerticalAlignment="Center"/>
<TextBlock Text="Pracownicy BOKZ" FontSize="10" VerticalAlignment="Center"/>
</StackPanel>
</Button.Content>
</Button>
</ListView>
<ListView Height="60">
<Button Background="Transparent" BorderThickness="0" Command="{Binding WyjdzCommand}">
<Button.Content>
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
<materialDesign:PackIcon Kind="ExitToApp" Width="20" Height="20" VerticalAlignment="Center"/>
<TextBlock Text="Wyście" FontSize="10" VerticalAlignment="Center"/>
</StackPanel>
</Button.Content>
</Button>
</ListView>
</StackPanel>
<StackPanel x:Name="stackPanel" Orientation="Horizontal" VerticalAlignment="Top" Height="40" HorizontalAlignment="Left" Margin="0 150">
<Button x:Name="btnZamknijMenu" ToolTip="Zamknij menu" Width="30" Height="30" Padding="0" Visibility="Collapsed" Background="{x:Null}" BorderBrush="{x:Null}">
<materialDesign:PackIcon Kind="Menu"/>
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<BeginStoryboard Storyboard="{StaticResource ZamknijMenu}"/>
</EventTrigger>
</Button.Triggers>
</Button>
</StackPanel>
<StackPanel>
<Button Content="START" Margin="200, 450" Height="50" Command="{Binding Start}"/>
</StackPanel>
</Grid>
My ViewModel
using GalaSoft.MvvmLight.Command;
using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
namespace LokatyNegocjowane.Viemodel
{
public class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string plikZLokatamiFullPath;
private string plikZWnioskamiFullPath;
private string plikZPracownikamiBOKZFullPath;
public ICommand WyjdzCommand { get; set; }
public ICommand WybierzPlikZLokatami { get; set; }
public ICommand WybierzPlikZWnioskami { get; set; }
public ICommand WybierzPlikZPracownikamiBOKZ { get; set; }
public ICommand Start { get; set; }
public ICommand UsunPlikLokat { get; set; }
public string PlikZWnioskamiFileName { get; set; }
public string PlikZPracownikamiBOKZFileName { get; set; }
public Boolean PlikZLokatamiFileNameCheck { get; set; }
public string PlikZLokatamiFileName
{
get
{
return Path.GetFileName(plikZLokatamiFullPath);
}
set
{
plikZLokatamiFullPath = value;
if (value != null && value != "") PlikZLokatamiFileNameCheck = true;
else PlikZLokatamiFileNameCheck = false;
}
}
public ViewModel()
{
WyjdzCommand = new RelayCommand(exitApp);
WybierzPlikZLokatami = new RelayCommand(wybierzPlikZLokatami);
WybierzPlikZWnioskami = new RelayCommand(wybierzPlikZWnioskami);
WybierzPlikZPracownikamiBOKZ = new RelayCommand(wybierzPlikZPracownikamiBOKZ);
UsunPlikLokat = new RelayCommand(usunPlikLokat);
Start = new RelayCommand(start);
}
private void exitApp()
{
System.Windows.Application.Current.Shutdown();
}
private void wybierzPlikZLokatami()
{
OpenFileDialog open = new OpenFileDialog();
if (open.ShowDialog() == true)
{
plikZLokatamiFullPath = open.FileName;
PlikZLokatamiFileName = Path.GetFileName(open.FileName);
}
//PlikZLokatamiFileNameCheck = true;
}
private void wybierzPlikZWnioskami()
{
OpenFileDialog open = new OpenFileDialog();
if (open.ShowDialog() == true)
{
plikZWnioskamiFullPath = open.FileName;
PlikZWnioskamiFileName = Path.GetFileName(open.FileName);
}
}
private void wybierzPlikZPracownikamiBOKZ()
{
OpenFileDialog open = new OpenFileDialog();
if (open.ShowDialog() == true)
{
plikZPracownikamiBOKZFullPath = open.FileName;
PlikZPracownikamiBOKZFileName = Path.GetFileName(open.FileName);
}
}
private void start()
{
if (walidacja())
MessageBox.Show("OK");
else MessageBox.Show("Brak wymaganych raportów");
}
private Boolean walidacja()
{
if ((plikZLokatamiFullPath != null && plikZLokatamiFullPath != "") && (plikZWnioskamiFullPath != null && plikZWnioskamiFullPath != "") && (plikZPracownikamiBOKZFullPath != null && plikZPracownikamiBOKZFullPath != ""))
return true;
return false;
}
private void usunPlikLokat()
{
PlikZLokatamiFileName = null;
}
}
}

Remove:
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<BeginStoryboard Storyboard="{StaticResource PlikZLokatamiStoryBack}"/>
<BeginStoryboard Storyboard="{StaticResource ControlOpacityBack}"/>
</EventTrigger>
</Button.Triggers>
and change:
<DataTrigger Binding="{Binding PlikZLokatamiFileNameCheck}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard Storyboard="{StaticResource PlikZLokatamiStory}"/>
</DataTrigger.EnterActions>
</DataTrigger>
to:
<DataTrigger Binding="{Binding PlikZLokatamiFileNameCheck}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard Storyboard="{StaticResource PlikZLokatamiStory}"/>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard Storyboard="{StaticResource PlikZLokatamiStoryBack}"/>
</DataTrigger.ExitActions>
</DataTrigger>
and similarly for ControlOpacity and ControlOpacityBack storyboard.
Btw. I saw you are using GalaSoft.MvvmLight.Command; but for wpf better to use GalaSoft.MvvmLight.CommandWpf;. Unless you want to manually raise canexecute changed.

Related

How can I stop a custom storyboard with a data trigger?

I have a storyboard for a canvas and I want to stop it as soon as the ShowError property has changed.
<UserControl.Resources>
<Storyboard x:Key="LoaderAnimation" Name="LoaderAnimation" >
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(RotateTransform.Angle)"
Storyboard.TargetName="canvas"
RepeatBehavior="Forever">
<EasingDoubleKeyFrame KeyTime="0" Value="0"/>
<EasingDoubleKeyFrame KeyTime="0:0:3" Value="360"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</UserControl.Resources>
<Viewbox x:Name="LayoutRoot" HorizontalAlignment="Center" VerticalAlignment="Top" Grid.Row="0" Height="150" Width="250">
<Canvas Height="323" Width="308" RenderTransformOrigin="0.5,0.5" x:Name="canvas">
<Canvas.RenderTransform>
<RotateTransform/>
</Canvas.RenderTransform>
<Ellipse Fill="{Binding Foreground, ElementName=userControl}" Height="71" Canvas.Left="121" Canvas.Top="21" Width="69" Opacity="0.3"/>
<Ellipse Fill="{Binding Foreground, ElementName=userControl}" Height="71" Width="69" Canvas.Left="194" Canvas.Top="52" Opacity="0.4"/>
<Ellipse Fill="{Binding Foreground, ElementName=userControl}" Width="69" Height="71"
</Canvas>
</Viewbox>
<TextBlock Grid.Row="1" TextWrapping="Wrap" FontSize="20" Margin="0 20 0 0">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="{Binding ShowError}" Value="false">
<Setter Property="Text" Value="{loc:Translate LogIn.TestSpsConnection}" />
<Setter Property="Foreground" Value="Black" />
</DataTrigger>
<DataTrigger Binding="{Binding ShowError}" Value="true">
<Setter Property="Text" Value="{loc:Translate LogIn.FailSpsConnection}" />
<Setter Property="Foreground" Value="Red" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</Grid>
I've tried every idea but I can't access the storyboard from the style of the TextBlock.
Is there any way I can stop the animation when ShowError goes true ?
thank you for your help
Try applying a style trigger onto your Canvas that stops the storyboard when ShowError goes true, instead of doing it from your TextBlock.
Example:
<UserControl.Resources>
<Storyboard x:Key="LoaderAnimation" Name="LoaderAnimation" >
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(RotateTransform.Angle)"
RepeatBehavior="Forever">
<EasingDoubleKeyFrame KeyTime="0" Value="0"/>
<EasingDoubleKeyFrame KeyTime="0:0:3" Value="360"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</UserControl.Resources>
...
<Canvas Height="323" Width="308" RenderTransformOrigin="0.5,0.5" x:Name="canvas">
...
<Canvas.Style>
<Style TargetType="Canvas">
<Style.Triggers>
<EventTrigger RoutedEvent="Loaded">
<EventTrigger.Actions>
<BeginStoryboard x:Name="beginStoryboard" Storyboard="{StaticResource LoaderAnimation}"/>
</EventTrigger.Actions>
</EventTrigger>
<DataTrigger Binding="{Binding ShowError}" Value="True">
<DataTrigger.EnterActions>
<StopStoryboard BeginStoryboardName="beginStoryboard"/>
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Canvas.Style>
</Canvas>
Also, notice that I removed the Storyboard.TargetName from your resource, or else it violates some XAML rules when used the way that I did it. You'll get runtime errors if you keep it there.

Animation inside DataTrigger won't run a second time

This is the code:
<Grid>
<Ellipse Fill="Turquoise" HorizontalAlignment="Left" Height="100" Stroke="Black" VerticalAlignment="Top" Width="100">
<Ellipse.Style>
<Style TargetType="Ellipse">
<Style.Setters>
<Setter Property="Ellipse.RenderTransform">
<Setter.Value>
<TranslateTransform X="0" Y="50"/>
</Setter.Value>
</Setter>
</Style.Setters>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=rectRight, Path=IsMouseOver}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard TargetProperty="RenderTransform.(TranslateTransform.X)">
<!--<DoubleAnimation To="{Binding Path=Width}" Duration="0:0:1"/>--> <!--Doesn't work inside a Style Trigger-->
<DoubleAnimation To="250" Duration="0:0:1"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
<DataTrigger Binding="{Binding ElementName=rectLeft, Path=IsMouseOver}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard TargetProperty="RenderTransform.(TranslateTransform.X)">
<DoubleAnimation To="45" Duration="0:0:1"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Ellipse.Style>
</Ellipse>
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Bottom" Orientation="Horizontal">
<Rectangle Name="rectLeft" Fill="Gray" Height="100" Stroke="Black" Width="100" Margin="10"/>
<Rectangle Name="rectRight" Fill="Gray" Height="100" Stroke="Black" Width="100" Margin="10"/>
</StackPanel>
</Grid>
The goal is to animate the Ellipse right when hovering over the right rectangle and left when hovering over the left rectangle. What happens is that after hovering over the left one the Ellipse will no longer move right.
What's also weird is that after changing the order of the DataTrigger declarations around, the reverse is the case.
What's going on that prevents the animation from running again?
I can do this a different way using EventTriggers but in my larger scenario I am using DataTriggers and this is where I am flummoxed.
Another thing is that I wanted to Bind the DoubleAnimation.To property to the Width of the Ellipse but apparently you can't do that in Style DataTriggers and I've yet to find a good workaround. Any help is appreciated.
You need to stop the storyboard before you start other one(add names to both the storyboard & stop each of them before the other runs):
<Grid>
<Ellipse Fill="Turquoise" HorizontalAlignment="Left" Height="100" Stroke="Black" VerticalAlignment="Top" Width="100">
<Ellipse.Style>
<Style TargetType="Ellipse">
<Style.Setters>
<Setter Property="Ellipse.RenderTransform">
<Setter.Value>
<TranslateTransform X="0" Y="50"/>
</Setter.Value>
</Setter>
</Style.Setters>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=rectRight, Path=IsMouseOver}" Value="True">
<DataTrigger.EnterActions>
<StopStoryBoard BeginStoryBoardName="Second"/>
<BeginStoryboard x:Name="First">
<Storyboard TargetProperty="RenderTransform.(TranslateTransform.X)">
<!--<DoubleAnimation To="{Binding Path=Width}" Duration="0:0:1"/>--> <!--Doesn't work inside a Style Trigger-->
<DoubleAnimation To="250" Duration="0:0:1"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
<DataTrigger Binding="{Binding ElementName=rectLeft, Path=IsMouseOver}" Value="True">
<DataTrigger.EnterActions>
<StopStoryBoard BeginStoryBoardName="First"/>
<BeginStoryboard x:Name="Second">
<Storyboard TargetProperty="RenderTransform.(TranslateTransform.X)">
<DoubleAnimation To="45" Duration="0:0:1"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Ellipse.Style>
</Ellipse>
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Bottom" Orientation="Horizontal">
<Rectangle Name="rectLeft" Fill="Gray" Height="100" Stroke="Black" Width="100" Margin="10"/>
<Rectangle Name="rectRight" Fill="Gray" Height="100" Stroke="Black" Width="100" Margin="10"/>
</StackPanel>
</Grid>

WPF Button with ContextMenu under left click

I have small problem with ContextMenu.
<Window.Resources>
<Style x:Key="ButtonWithContextMenuStyle" TargetType="Button">
<Setter Property="Background" Value="#cbeb00" />
<Setter Property="Foreground" Value="#505050" />
<Setter Property="FontSize" Value="12" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border CornerRadius="4" Background="{TemplateBinding Background}"
BorderThickness="0">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" Margin="{TemplateBinding Padding}" />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#aaaaaa"/>
<Setter Property="Foreground" Value="#dcff00"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu>
<MenuItem Command="{Binding EditUserCommand}" Header="{DynamicResource EditUserContextMenuMW}"/>
<MenuItem Command="{Binding OpenHistoryCommand}" Header="{DynamicResource ConversationHistoryContextMenuMW}"/>
<MenuItem Command="{Binding OpenHistoryCommand}" Header="{DynamicResource BuyCreditContextMenuMW}"/>
<Separator />
<MenuItem Command="{Binding LogoutCommand}" Header="{DynamicResource LogoutContextMenuMW}"/>
</ContextMenu>
</Setter.Value>
</Setter>
<Style.Triggers>
<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>
</Window.Resources>
When I click right mouse button, everything work correctly. ContextMenu opens and i can invoke Command on MenuItem.
I would like to see the same behavior when I click the left mouse button. However, only the ContextMenu opens, and I can not call Command.
Can someone tell me what am I doing wrong?
<Window x:Class="ContextMenuMouseDownStack.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<StackPanel Margin="10">
<StackPanel.Resources>
<Style TargetType="Button">
<Style.Triggers>
<EventTrigger RoutedEvent="Click">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<BooleanAnimationUsingKeyFrames Storyboard.TargetProperty="ContextMenu.IsOpen">
<DiscreteBooleanKeyFrame KeyTime="0:0:2" Value="True"/>
</BooleanAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</Style.Triggers>
</Style>
</StackPanel.Resources>
<Button Content="Click!" VerticalAlignment="Center" HorizontalAlignment="Center">
<Button.ContextMenu>
<ContextMenu>
<MenuItem Header="Menu item 1" Click="MenuItem_Click"/>
<MenuItem Header="Menu item 2" />
<Separator />
<MenuItem Header="Menu item 3" />
</ContextMenu>
</Button.ContextMenu>
</Button>
</StackPanel>
In this example, which resembles yours, the Click event does the job as it should. Indeed, i don't have a Command there but a simple code behind handler. It reaches it in both ways, right or left mouse click. Just for testing, can you try a simple handler there?
Update 1:
It also works in both ways for me with :
<EventTrigger RoutedEvent="PreviewMouseDown">
Update 2
It seems that your Storyboard is ruining the things:
I've changed that with a Popup like this:
<Popup x:Name="btnMenuPopup" Placement="Mouse" StaysOpen="False">
<Border BorderBrush="Black" BorderThickness="2" CornerRadius="2">
<Menu>
<MenuItem Header="Menu item 1" Command="{Binding EditCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}"/>
<MenuItem Header="Menu item 2" />
<Separator />
<MenuItem Header="Menu item 3" />
<Menu.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel Orientation="Vertical"/>
</ItemsPanelTemplate>
</Menu.ItemsPanel>
</Menu>
</Border>
</Popup>
For this to be fast i've added a click event on the button which will open the Popup.
private void Button_Click(object sender, RoutedEventArgs e)
{
btnMenuPopup.IsOpen = true;
}
And now it should work.
Here is the Command for the Popup:
Command="{Binding CommandShowPopup}" CommandParameter="{Binding ElementName=btnMenuPopup}"
Command implementation:
public class CommandShowPopup : ICommand
{
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
(parameter as Popup).IsOpen = true;
}
}
You need to use the MouseRightButtonDown routed event.
<EventTrigger RoutedEvent="MouseRightButtonDown">

Can't do animation

I want to change size of button from 70 till 90 when mouse is over:
<Style TargetType="Button"
x:Key="RadialButton">
<Setter Property="Width"
Value="70"></Setter>
<Setter Property="Height"
Value="85"></Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<ControlTemplate.Resources>
<Storyboard x:Key="Storyboard1">
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(FrameworkElement.Width)"
Storyboard.TargetName="ExtEllipse">
<EasingDoubleKeyFrame KeyTime="0:0:1"
Value="90" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</ControlTemplate.Resources>
<Grid Name="MainGrid">
<Grid.RowDefinitions>
<RowDefinition Height="70"></RowDefinition>
<RowDefinition Height="15"></RowDefinition>
</Grid.RowDefinitions>
<Ellipse Width="70"
Height="70"
Stroke="Gray"
Grid.Row="0"
Name="ExtEllipse"
Fill="{x:Null}" />
<Ellipse Width="50"
Height="50"
Stroke="Gray"
Grid.Row="0"></Ellipse>
<TextBlock Grid.Row="1"
FontSize="13"
FontWeight="Bold"
TextAlignment="Center"
Foreground="Green">
<ContentPresenter RecognizesAccessKey="True"
Content="{TemplateBinding Button.Content}" />
</TextBlock>
</Grid>
<ControlTemplate.Triggers>
<EventTrigger RoutedEvent="FrameworkElement.Loaded" />
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
using:
<Button Content="Button"
HorizontalAlignment="Left"
Margin="36,140,0,147"
Width="151"
Style="{DynamicResource RadialButton}" />
but it does not work. Nothing happened. Why and how to solve this problem?
That's because you have Storyboard, but you don't play it.
Try add trigger to play that storyboard. Something like this:
<Trigger Property="IsMouseOver" Value="True">
<Trigger.EnterActions>
<BeginStoryboard Storyboard="{StaticResource Storyboard1}" />
</Trigger.EnterActions>
</Trigger>
Btw this is result of your animation:
You have to start your Storyboard. Your EventTrigger does nothing.
<EventTrigger RoutedEvent="MouseEnter">
<BeginStoryboard Storyboard="{StaticResource Storyboard1}" />
</EventTrigger>

WPF -- it's gotta be easier than I'm making it

I'm having the darndest time figuring this out: say I've got two Button and three TextBlocks. I want either button to trigger a simple Storyboard on ALL TextBlocks. Currently I'm trying to define a generic Textblock style that contains the Storyboard, and then the trigger comes from any Button click. This is the closest I've come but the app crashes on startup...what am I don't wrong here:
<Window.Resources>
<Style TargetType="TextBlock" >
<Setter Property="Foreground" Value="Blue" />
<Style.Resources>
<Storyboard x:Key="TextBlockOpacity" Storyboard.TargetProperty="Opacity">
<DoubleAnimation From="0" To="1" />
</Storyboard>
</Style.Resources>
</Style>
<Window.Triggers>
<EventTrigger RoutedEvent="ButtonBase.Click" SourceName="button">
<BeginStoryboard Storyboard="{StaticResource TextBlockOpacity}"/>
</EventTrigger>
</Window.Triggers>
<Grid x:Name="LayoutRoot">
<Button x:Name="button" HorizontalAlignment="Left" Margin="51,54,0,0" VerticalAlignment="Top" Width="96" Height="45" Content="Button"/>
<TextBlock x:Name="textBlock1" Margin="228,54,172,0" VerticalAlignment="Top" Height="45" FontSize="26.667" Text="TextBlock" TextWrapping="Wrap" />
<TextBlock x:Name="textBlock2" Margin="228,103,172,0" VerticalAlignment="Top" Height="45" FontSize="26.667" Text="Hello" TextWrapping="Wrap"/>
</Grid>
If you "dedicate" the button to changing the opacity, you could harness its DataContext and animate it. Then simply bind your elements' Opacity to the DataContext:
(I've also refactored your xaml a bit)
<Window x:Class="SomeNamespace.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:System="clr-namespace:System;assembly=mscorlib"
Title="Window1" Height="300" Width="300">
<Window.Resources>
<Storyboard x:Key="TextBlockOpacity" Storyboard.TargetName="button1" Storyboard.TargetProperty="DataContext" >
<DoubleAnimation From="0.1" To="1"/>
</Storyboard>
<Style TargetType="TextBlock" >
<Setter Property="Foreground" Value="Blue" />
<Setter Property="Background" Value="LightGray" />
<Setter Property="FontSize" Value="26.667" />
<Setter Property="TextWrapping" Value="Wrap" />
<Setter Property="Height" Value="45" />
<Setter Property="Opacity" Value="{Binding ElementName=button1, Path=DataContext}"/>
</Style>
</Window.Resources>
<Window.Triggers>
<EventTrigger RoutedEvent="ButtonBase.Click">
<BeginStoryboard Storyboard="{StaticResource TextBlockOpacity}" >
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="ListBox.SelectionChanged">
<BeginStoryboard Storyboard="{StaticResource TextBlockOpacity}" >
</BeginStoryboard>
</EventTrigger>
</Window.Triggers>
<Grid x:Name="LayoutRoot">
<Button x:Name="button1" HorizontalAlignment="Left" Margin="51,54,0,0" VerticalAlignment="Top" Width="96" Height="45" Content="Button">
<Button.DataContext>
<System:Double>0</System:Double>
</Button.DataContext>
</Button>
<Button x:Name="button2" HorizontalAlignment="Right" Margin="0,54,29,0" VerticalAlignment="Top" Width="96" Height="45" Content="Button"/>
<ListBox x:Name="listBox1" Height="50" VerticalAlignment="Top">
<ListBox.Items>
<System:String>Text1</System:String>
<System:String>Text2</System:String>
</ListBox.Items>
</ListBox>
<TextBlock x:Name="textBlock1" Margin="51,114,61,0" Text="TextBlock" Height="45" VerticalAlignment="Top" Width="166" />
<TextBlock x:Name="textBlock2" Margin="51,0,74,42" Text="Hello" Height="45" Width="153" VerticalAlignment="Bottom" />
</Grid>
</Window>
Also note one thing - this is the approach to use if you want to minimize your code, and make it all happen in xaml. Your approach would anmate the Opacity of the whole Window. That's why in the code above, TextBlocks bind to the button's DataContext, which is itself animated.
It is of course doable without binding to a common value (the DataContext), but then you need to repeat X animations (because you need to set X TargetNames). This approach above is more easily extendable and maintainable.
EDIT
Added another Button and a ListBox for variety :)
Based on kek444's Xaml-only solution, I present a slightly improved version that doesn't rely on the DataContext of the button and can have multiple triggers.
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="WpfApplication1.MainWindow"
x:Name="Window"
Title="MainWindow"
Width="640" Height="480">
<Window.Resources>
<UIElement x:Key="OpacityCounter" Opacity="0"/>
<Style TargetType="TextBlock">
<Setter Property="Opacity" Value="{Binding Source={StaticResource OpacityCounter}, Path=Opacity}" />
</Style>
<Storyboard x:Key="OnClick1">
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.Target="{StaticResource OpacityCounter}" Storyboard.TargetProperty="(UIElement.Opacity)">
<SplineDoubleKeyFrame KeyTime="00:00:00" Value="0"/>
<SplineDoubleKeyFrame KeyTime="00:00:00.5000000" Value="1"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</Window.Resources>
<Window.Triggers>
<EventTrigger RoutedEvent="ButtonBase.Click" SourceName="button1">
<BeginStoryboard Storyboard="{StaticResource OnClick1}"/>
</EventTrigger>
<EventTrigger RoutedEvent="ButtonBase.Click" SourceName="button2">
<BeginStoryboard Storyboard="{StaticResource OnClick1}"/>
</EventTrigger>
</Window.Triggers>
<Grid x:Name="LayoutRoot">
<StackPanel>
<StackPanel Orientation="Horizontal">
<Button x:Name="button1" Width="131" Height="37" Content="Button 1" Margin="0,0,0,22"/>
<Button x:Name="button2" Width="131" Height="37" Content="Button 2" Margin="0,0,0,22"/>
</StackPanel>
<TextBlock x:Name="textBlock" Height="27" Text="TextBlock 1" TextWrapping="Wrap" />
<TextBlock x:Name="textBlock1" Height="27" Text="TextBlock 2" TextWrapping="Wrap" />
<TextBlock x:Name="textBlock2" Height="27" Text="TextBlock 3" TextWrapping="Wrap" />
<TextBlock x:Name="textBlock3" Height="27" Text="TextBlock 4" TextWrapping="Wrap" />
</StackPanel>
</Grid>
</Window>
To use a ListBox as a trigger mechanism (provided you have a ListBox named "listbox1" someplace, add the following to Window.Triggers:
<EventTrigger RoutedEvent="Selector.SelectionChanged" SourceName="listbox1">
<BeginStoryboard Storyboard="{StaticResource OnClick1}"/>
</EventTrigger>
or to trigger off a specific ListBoxItem, you'll need (where item1 is a named ListBoxItem):
<EventTrigger RoutedEvent="ListBoxItem.Selected" SourceName="item1">
<BeginStoryboard Storyboard="{StaticResource OnClick1}"/>
</EventTrigger>
In your sample, you are defining the Storyboard inside a Style as a Resource, but then you are trying to access it as a Window resource. Try moving the Storyboard declaration to Window.Resources, then reference the Storyboard in the Style.
I don't know right off if it will do what you want, but I would start there.

Resources