Reputation: 3461
I have a simple MainWindow with a root Grid that contains Grid and in it a cell with a Button, and the root grid also contains an overlay control (control with partially transparent background and some short text like Drop here...). This full-window overlay shows up with a fade-in animation when the user drags a file over the window. The problem is my animation does not finish, it almost does not start, and repeats itself while the user drags the file over the window.
I am not sure where to look next. The DropOverlay class and XAML looks well. I think the problem is in the d&d event handling. When I use IsHitTestVisible="False" it seems to work better when I do not have an animation.
<UserControl x:Class="cs_wpf_test_16.DropOverlay"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:cs_wpf_test_16"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
Style="{DynamicResource BorderStyle1}">
<UserControl.Resources>
<Style TargetType="UserControl" x:Key="BorderStyle1">
<Style.Triggers>
<Trigger Property="Visibility" Value="Visible">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity" From="0" To="1" Duration="0:0:0.5">
</DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
<Trigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity" To="0" Duration="0:0:0.5">
</DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</Trigger.ExitActions>
</Trigger>
</Style.Triggers>
</Style>
</UserControl.Resources>
<Border Background="#99000000">
<Viewbox>
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center"
Orientation="Horizontal" Margin="100" IsHitTestVisible="False">
<TextBlock Foreground="White" FontSize="50" Margin="15,0,0,0" Name="MyTextBlock">Drop Here...</TextBlock>
</StackPanel>
</Viewbox>
</Border>
</UserControl>
Nothing special.
<Window x:Class="cs_wpf_test_16.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:local="clr-namespace:cs_wpf_test_16"
mc:Ignorable="d"
Title="MainWindow" Height="209.375" Width="317.969"
PreviewDragEnter="MyWindow_DragEnter"
PreviewDragLeave="MyWindow_DragLeave"
PreviewDrop="MyWindow_Drop"
AllowDrop="True">
<Grid>
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Button>Test</Button>
</Grid>
<local:DropOverlay Visibility="Collapsed" x:Name="MyDropOverlay"/>
</Grid>
</Window>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
internal bool _DraggingOver = false;
internal bool DraggingOver
{
get
{
return _DraggingOver;
}
set
{
if (_DraggingOver != value)
{
_DraggingOver = value;
UpdateDropIndicator();
}
}
}
private void UpdateDropIndicator()
{
if (DraggingOver)
{
MyDropOverlay.Visibility = Visibility.Visible;
}
else
{
MyDropOverlay.Visibility = Visibility.Collapsed;
}
}
private void MyWindow_DragEnter(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(DataFormats.FileDrop) &&
!(e.Source is DropOverlay))
{
e.Effects = DragDropEffects.Link;
var paths = e.Data.
GetData(DataFormats.FileDrop) as string[];
string path = paths[0];
DraggingOver = true;
e.Handled = true;
}
else
{
DraggingOver = false;
//e.Handled = true;
}
}
private void MyWindow_Drop(object sender, DragEventArgs e)
{
if (GetVisualParent<DropOverlay>(e.Source) == null)
{
e.Handled = true;
return;
}
string[] paths = null;
if (e.Effects != DragDropEffects.None &&
e.Data.GetDataPresent(DataFormats.FileDrop))
{
paths = e.Data.
GetData(DataFormats.FileDrop) as string[];
}
DraggingOver = false;
if (paths != null)
{
MessageBox.Show(this, "Dropped.");
e.Handled = true;
}
}
private void MyWindow_DragLeave(object sender, DragEventArgs e)
{
if (GetVisualParent<DropOverlay>(e.Source) == null)
{
e.Handled = true;
return;
}
Point p = Mouse.GetPosition(this);
if (p.X < 0 || p.Y < 0 ||
p.X > ActualWidth || p.Y > ActualHeight)
{
DraggingOver = false;
e.Handled = true;
}
}
public static T GetVisualParent<T>(object childObject) where T : System.Windows.Media.Visual
{
var child = childObject as System.Windows.DependencyObject;
// iteratively traverse the visual tree
while ((child != null) && !(child is T))
{
child = System.Windows.Media.VisualTreeHelper.GetParent(child);
}
return child as T;
}
}
Expected result: the fade-in animation runs and finishes one time when the user starts dragging a file over the window.
Actual result: the fade-in animation does not finish, it starts over and over again and runs just up to some point where it is hardly visible that the overlay is there. Sometimes when I leave the window with the mouse while dragging the overlay fades-in nicely (but not in the good moment), sometimes not.
Upvotes: 0
Views: 252
Reputation: 1166
Found it just delete your else case in MyWindow_DragEnter
copy the code below and paste instead of your MyWindow_DragEnter method.
in case you are lazy :)
private void MyWindow_DragEnter(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(DataFormats.FileDrop) &&
!(e.Source is DropOverlay))
{
e.Effects = DragDropEffects.Link;
var paths = e.Data.
GetData(DataFormats.FileDrop) as string[];
string path = paths[0];
DraggingOver = true;
e.Handled = true;
}
}
FOR FADE IN/OUT ANIMATIONS
The problem is your UserControl is set to Collapsed while animating => we need "connected animations" (one animation after another)
in our case we need some independent boolen: i took Selector.IsSelected.
Change your UpdateDropIndicator method
private void UpdateDropIndicator()
{
if (DraggingOver)
{
Selector.SetIsSelected(MyDropOverlay, true);
// MyDropOverlay.Visibility = Visibility.Visible;
}
else
{
Selector.SetIsSelected(MyDropOverlay, false);
//MyDropOverlay.Visibility = Visibility.Collapsed;
}
}
and your xaml animation
<Trigger Property="Selector.IsSelected" Value="True">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity"
From="0"
To="1"
Duration="0:0:0.5"/>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Visibility"
Duration="0:0:0">
<DiscreteObjectKeyFrame Value="{x:Static Visibility.Visible}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
<Trigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity"
From="1"
To="0"
Duration="0:0:0.5"/>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Visibility"
Duration="0:0:0.5">
<DiscreteObjectKeyFrame Value="{x:Static Visibility.Collapsed}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</Trigger.ExitActions>
</Trigger>
Upvotes: 1