Inherited WPF Style with triggers doesn't work - wpf

I have a style that fades out a control with animation:
<Style x:Key="ExpireFadeStyle">
<Style.Resources>
<!--Change this in a derived style if required-->
<sys:Double x:Key="FinalVal">0.25</sys:Double>
</Style.Resources>
<Style.Triggers>
<DataTrigger Binding="{Binding IsExpired}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard x:Name="ExpireAnimation">
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity"
From="1" To="{StaticResource FinalVal}" Duration="0:0:3" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<StopStoryboard BeginStoryboardName="ExpireAnimation" />
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
(The IsExpired property always exists in the DataContext of the control where the style is supposed to be used.)
When I use this style directly in a control, it all works fine:
<StackPanel Style="{StaticResource ExpireFadeStyle}">
...etc
But when I derive from this style, just as simple as
<Style x:Key="ExpireTextFadeStyle" BasedOn="{StaticResource ExpireFadeStyle}"/>
...and then use the derived style on the same control the same way, it doesn't work. (The intention is, of course, to change it a bit, particularly the FinalVal, but it has to work in a trivial case first).
The inherited style itself does seem to work: if I add some Setter to it, I see its effect. It just seems that Triggers are not inherited or simply don't work. How to work around it?

That's really a good question. I created an example test application to check this out and I have faced exactly the same problem. In debug mode I checked the value of the Style property and it was correct (BasedOn was set correctly and BasedOn.Triggers contains your Trigger). Nevertheless, the Trigger doesn't fire up when needed.
I have found a solution that make this work, however it is not a perfect one...
I defined an attached dependency property which copies the triggers when is set to true.
public class StyleTriggersInheritanceHelper
{
public static readonly DependencyProperty AddInheritedTriggersProperty =
DependencyProperty.RegisterAttached("AddInheritedTriggers", typeof(bool), typeof(FrameworkElement), new FrameworkPropertyMetadata(new PropertyChangedCallback(OnAddInheritedTriggers)));
private static void OnAddInheritedTriggers(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
var b = e.NewValue as bool?;
if (b.HasValue && b.Value)
{
FrameworkElement element = sender as FrameworkElement;
if(element != null)
{
Style style = element.Style;
if(style != null)
{
Style baseStyle = element.Style.BasedOn;
Style newStyle = new Style() { BasedOn = style };
if(baseStyle != null)
{
foreach (var tr in style.Triggers)
newStyle.Triggers.Add(tr);
foreach (var tr in baseStyle.Triggers)
newStyle.Triggers.Add(tr);
}
element.Style = newStyle;
}
}
}
}
public static bool GetAddInheritedTriggers(DependencyObject obj)
{
return (bool)obj.GetValue(AddInheritedTriggersProperty);
}
public static void SetAddInheritedTriggers(DependencyObject obj, bool value)
{
obj.SetValue(AddInheritedTriggersProperty, value);
}
}
And xaml looks as follows:
<local:ExpirdedControl IsExpired="{Binding IsExpired}" Style="{StaticResource InheritedStyle}" x:Name="ExpiredName" local:StyleTriggersInheritanceHelper.AddInheritedTriggers="True"/>
Note that AddInheritedTriggers attached property is set to true and then Triggers are copied. One important thing is that you have to set AddInheritedTriggers after applying the Style, otherwise it wouldn't work!
For me it works, however the solution is not elegant, so I would like to see a better try of solving this issue.

Related

Can I use data-binding on the Duration of an Animation in a ControlTemplate?

I found a good explanation here on SO of how to bind the Duration property of a ColorAnimation to the Value property of a Slider. One uses a converter to convert the Double value from the slider to a Duration, and a Binding to have that set the Duration of the ColorAnimation. Here, abbreviated, is how that works:
<Window.Resources>
<local:DoubleToDurationConverter x:Key="DoubleToDurationConverter" />
</Window.Resources>
<Slider x:Name="slider" />
<Button Content="Click me for an animation">
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<BeginStoryboard>
<Storyboard>
<ColorAnimation To="Green"
Storyboard.TargetProperty="(Button.Background).(SolidColorBrush.Color)"
FillBehavior="Stop"
Duration="{Binding ElementName=slider,
Path=Value,
Mode=OneWay,
Converter={StaticResource DoubleToDurationConverter}}" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Button.Triggers>
</Button>
I tried that and it worked fine for me. But what I want to do is bind the Duration to a dependency property called FadeTime I've added to my custom control. So, in that control's ControlTemplate I have this:
<ControlTemplate.Triggers>
<Trigger Property="IsLit" Value="true">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="glow"
Storyboard.TargetProperty="Opacity"
To="1"
Duration="{Binding FadeTime, Mode=OneWay,
RelativeSource={RelativeSource TemplatedParent}}"/>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
</Trigger>
</ControlTemplate.Triggers>
This compiles, but gives me an error message at run-time:
InvalidOperationException: Cannot freeze this Storyboard timeline tree
for use across threads.
How can I bind my DoubleAnimation's Duration to a dependency variable in a custom control's ControlTemplate?
Thanks!
UPDATE
Data-binding is actually gross overkill for what I want to do. Real data-binding would allow for the property's value to change at run-time. All I really want is a way for the developer who is using my custom control to be able to set the Duration of the DoubleAnimation at design time, without having to edit the ControlTemplate. It's okay if the value the developer chooses never changes at run time.
Instead of defining the animation in your XAML markup, you could define it programmatically in the PropertyChangedCallback for the IsLit property.
You could simply define another property that lets the consumer of the control specify the duration of the animation.
Here is an example for you.
Control:
public class MyCustomControl : Control
{
private UIElement glow;
public static readonly DependencyProperty DurationProperty =
DependencyProperty.Register("Duration", typeof(TimeSpan),
typeof(MyCustomControl), new PropertyMetadata(TimeSpan.FromSeconds(1)));
public TimeSpan Duration
{
get { return (TimeSpan)GetValue(DurationProperty); }
set { SetValue(DurationProperty, value); }
}
public static readonly DependencyProperty IsLitProperty =
DependencyProperty.Register("IsLit", typeof(bool),
typeof(MyCustomControl), new PropertyMetadata(false, new PropertyChangedCallback(OnIsLitChanged)));
public bool IsLit
{
get { return (bool)GetValue(IsLitProperty); }
set { SetValue(IsLitProperty, value); }
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
glow = Template.FindName("glow", this) as UIElement;
if (glow != null && IsLit)
Animate(glow);
}
private static void OnIsLitChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
bool newValue = (bool)e.NewValue;
if(newValue)
{
MyCustomControl c = d as MyCustomControl;
if(c != null && c.glow != null)
{
c.Animate(c.glow);
}
}
}
private void Animate(UIElement glow)
{
DoubleAnimation animation = new DoubleAnimation();
animation.To = 1;
animation.Duration = Duration;
glow.BeginAnimation(OpacityProperty, animation);
}
}
Template:
<ControlTemplate x:Key="ct" TargetType="local:MyCustomControl">
<Border x:Name="glow" Width="100" Height="100" Background="Red" Opacity="0.1">
</Border>
</ControlTemplate>
Usage:
<local:MyCustomControl Template="{StaticResource ct}" Duration="0:0:5" IsLit="True" />
Basically, you can't use normal bindings inside the storyboard of a control template. Since you just want a way for developers to change the value, one of the following options might work for you:
(1) Use StaticResource: Place a duration object somewhere outside the control template, where it's easier to change for developers. However, it needs to be somewhere statically accessible to the control template, since DynamicResource won't work in this place.
<Duration x:Key="MyCustomDuration">0:0:1</Duration>
... then later
Duration="{StaticResource MyCustomDuration}"
(2) Use a static code behind field with x:Static:
public static class SettingsClass
{
public static Duration MyCustomDuration = new Duration(new TimeSpan(0, 0, 1));
}
and use:
Duration="{x:Static local:SettingsClass.MyCustomDuration}"

ToolKit DataGrid deselect all on LostFocus

I'm trying to create a style which will make all my DataGrids select row -1 when they lose focus. I'm doing:
<Style TargetType="{x:Type DataGrid}">
<Style.Triggers>
<EventTrigger RoutedEvent="DataGrid.LostFocus">
<BeginStoryboard>
<Storyboard>
<Int32AnimationUsingKeyFrames Storyboard.TargetProperty="(DataGrid.SelectedIndex)">
<DiscreteInt32KeyFrame KeyTime="00:00:00" Value="-1" />
</Int32AnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Style.Triggers>
</Style>
It works only for the first time the focus is lost, but on the second time program crashes because of type conversion ecxeption. Is it possible without code behind?
According to my researches, attached behavior is the only acceptable solution for me. Hope this will help somebody more:
public class DataGridBehavior
{
public static readonly DependencyProperty IsDeselectOnLostFocusProperty =
DependencyProperty.RegisterAttached("IsDeselectOnLostFocus", typeof(bool), typeof(DataGridBehavior), new UIPropertyMetadata(false, PropertyChangedCallback));
private static void PropertyChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
{
var dg = dependencyObject as DataGrid;
if (dg == null)
return;
if (e.NewValue is bool == false)
return;
if ((bool)e.NewValue)
dg.LostFocus += dg_LostFocus;
}
static void dg_LostFocus(object sender, RoutedEventArgs e)
{
(sender as DataGrid).SelectedIndex = -1;
}
public static bool GetIsDeselectOnLostFocus(DataGrid dg)
{
return(bool)dg.GetValue(IsDeselectOnLostFocusProperty);
}
public static void SetIsDeselectOnLostFocus(DataGrid dg, bool value)
{
dg.SetValue(IsDeselectOnLostFocusProperty, value);
}
}
Then:
<Style TargetType="{x:Type DataGrid}">
<Setter Property="helpers:DataGridBehavior.IsDeselectOnLostFocus" Value="True"/>
</Style>
A better way to achieve your actual goal of de-selecting the selected item is to bind an object of the same type as those in the collection data bound to the DataGrid.ItemsSource property to the DataGrid.SelectedItem property. When you want to deselect the selected item, you simply set this object to null:
<DataGrid ItemsSource="{Binding Items}" SelectedItem="{Binding Item}" />
In view model:
Item = null; // de-selects the selected item

WPF Animate property of child without using Name

I'm trying to create a storyboard in XAML that animates a property of one of the child elements of an element which raises an event. But I can't seem to get it to work without using Names, which is something I can't really do in this specific situation.
I'm basically trying something like this (much simplified of course):
<Canvas>
<Canvas.Triggers>
<EventTrigger RoutedEvent="FrameworkElement.Loaded">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Children[0].(Canvas.Left)" From="0" To="400" />
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</Canvas.Triggers>
<Button Canvas.Left="20" Canvas.Top="20">A</Button>
<Button Canvas.Left="40" Canvas.Top="20">B</Button>
</Canvas>
Any ideas on how this could be achieved?
Providing that the UIElement you are indexing in the animation exists (i.e. already present on the Canvas) then you can do the following:
<Canvas x:Name="MyCanvas">
<Button x:Name="btn" Canvas.Left="20" Canvas.Top="20">A</Button>
<Button Canvas.Left="40" Canvas.Top="20">B</Button>
<Canvas.Triggers>
<EventTrigger RoutedEvent="FrameworkElement.Loaded">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.Target="{Binding ElementName=MyCanvas, Path=Children[0]}"
Storyboard.TargetProperty="(Canvas.Left)" From="0" To="400" />
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</Canvas.Triggers>
</Canvas>
Notice how I have moved the addition of the Buttons above the Trigger. If the Buttons are below the Trigger as in your question, trying to access Children[0] will throw an ArgumentOutOfRangeException because there are no children at this point.
To use the Storyboard.TargetProperty in the animation, it should always be a dependency property. Children property gets a UIElementCollection of child elements of this Panel (Canvas). Therefore, the following construction Children [n] return UIElement, which should lead to a certain type, to access its dependency property.
This can be done in the code as follows:
Button MyButton = (Button)MyCanvas.Children[0];
MessageBox.Show(MyButton.Width.ToString());
All of these actions missing in the animation by default, this is your construction will not work.
I propose to create animations in the code where this conversion possible.
To demonstrate this, I created a Canvas, in the event Loaded having registered animation. Element number is set via an attached dependency property (of course, the example can be implemented in various ways). Below is my example:
XAML
<Grid>
<local:MyCanvas x:Name="MyCanvas" local:ClassForAnimation.Children="1">
<Button Canvas.Left="20" Canvas.Top="20">A</Button>
<Button Canvas.Left="40" Canvas.Top="20">B</Button>
</local:MyCanvas>
</Grid>
Code behind
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
public class MyCanvas : Canvas
{
public MyCanvas()
{
this.Loaded += new RoutedEventHandler(MyCanvas_Loaded);
}
private void MyCanvas_Loaded(object sender, RoutedEventArgs e)
{
MyCanvas myCanvas = sender as MyCanvas;
// Get No. of children
int children = ClassForAnimation.GetChildren(myCanvas);
// Get current Button for animation
Button MyButton = (Button)myCanvas.Children[children];
if (myCanvas != null)
{
DoubleAnimation doubleAnimation = new DoubleAnimation();
doubleAnimation.From = 0;
doubleAnimation.To = 400;
MyButton.BeginAnimation(Button.WidthProperty, doubleAnimation);
}
}
}
public class ClassForAnimation : DependencyObject
{
public static readonly DependencyProperty ChildrenProperty;
public static void SetChildren(DependencyObject DepObject, int value)
{
DepObject.SetValue(ChildrenProperty, value);
}
public static int GetChildren(DependencyObject DepObject)
{
return (int)DepObject.GetValue(ChildrenProperty);
}
static ClassForAnimation()
{
PropertyMetadata MyPropertyMetadata = new PropertyMetadata(0);
ChildrenProperty = DependencyProperty.RegisterAttached("Children",
typeof(int),
typeof(ClassForAnimation),
MyPropertyMetadata);
}
}
Note: Access to the items in the Canvas should only be done in the event Loaded, or when it ended. Otherwise, the items are not available because they are not loaded.

Highlighting cells in WPF DataGrid when the bound value changes

I have a DataGrid that has its data refreshed by a background process every 15 seconds. If any of the data changes, I want to run an animation that highlights the cell with the changed value in yellow and then fade back to white. I sort-of have it working by doing the following:
I created a style with event trigger on Binding.TargetUpdated
<Style x:Key="ChangedCellStyle" TargetType="DataGridCell">
<Style.Triggers>
<EventTrigger RoutedEvent="Binding.TargetUpdated">
<BeginStoryboard>
<Storyboard>
<ColorAnimation Duration="00:00:15"
Storyboard.TargetProperty=
"(DataGridCell.Background).(SolidColorBrush.Color)"
From="Yellow" To="Transparent" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Style.Triggers>
</Style>
And then applied it to the columns I wanted to highlight if a value changes
<DataGridTextColumn Header="Status"
Binding="{Binding Path=Status, NotifyOnTargetUpdated=True}"
CellStyle="{StaticResource ChangedCellStyle}" />
If the value for the status field in the database changes, the cell highlights in yellow just like I want. But, there are a few problems.
First, when the data grid is initially loaded, the entire column is highlighted in yellow. This makes sense, because all of the values are being loaded for the first time so you would expect TargetUpdated to fire. I'm sure there is some way I can stop this, but it's a relatively minor point.
The real problem is the entire column is highlighted in yellow if the grid is sorted or filtered in any way. I guess I don't understand why a sort would cause TargetUpdated to fire since the data didn't change, just the way it is displayed.
So my question is (1) how can I stop this behavior on initial load and sort/filter, and (2) am I on the right track and is this even a good way to do this? I should mention this is MVVM.
Since TargetUpdated is truly only UI update based event. It doesn't matter how update is happening. While sorting all the DataGridCells remain at their places only data is changed in them according to sorting result hence TargetUpdatedis raised. hence we have to be dependent on data layer of WPF app. To achieve this I've reset the Binding of DataGridCell based on a variable that kind of trace if update is happening at data layer.
XAML:
<Window.Resources>
<Style x:Key="ChangedCellStyle" TargetType="DataGridCell">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="DataGridCell">
<ControlTemplate.Triggers>
<EventTrigger RoutedEvent="Binding.TargetUpdated">
<BeginStoryboard>
<Storyboard>
<ColorAnimation Duration="00:00:04" Storyboard.TargetName="myTxt"
Storyboard.TargetProperty="(DataGridCell.Background).(SolidColorBrush.Color)"
From="Red" To="Transparent" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</ControlTemplate.Triggers>
<TextBox HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="Transparent"
Name="myTxt" >
<TextBox.Style>
<Style TargetType="TextBox">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},Path=DataContext.SourceUpdating}" Value="True">
<Setter Property="Text" Value="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},Path=Content.Text,NotifyOnSourceUpdated=True,NotifyOnTargetUpdated=True}" />
</DataTrigger>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},Path=DataContext.SourceUpdating}" Value="False">
<Setter Property="Text" Value="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},Path=Content.Text}" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<StackPanel Orientation="Vertical">
<DataGrid ItemsSource="{Binding list}" CellStyle="{StaticResource ChangedCellStyle}" AutoGenerateColumns="False"
Name="myGrid" >
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Binding Name}" />
<DataGridTextColumn Header="ID" Binding="{Binding Id}" />
</DataGrid.Columns>
</DataGrid>
<Button Content="Change Values" Click="Button_Click" />
</StackPanel>
Code Behind(DataContext object of Window):
public MainWindow()
{
list = new ObservableCollection<MyClass>();
list.Add(new MyClass() { Id = 1, Name = "aa" });
list.Add(new MyClass() { Id = 2, Name = "bb" });
list.Add(new MyClass() { Id = 3, Name = "cc" });
list.Add(new MyClass() { Id = 4, Name = "dd" });
list.Add(new MyClass() { Id = 5, Name = "ee" });
list.Add(new MyClass() { Id = 6, Name = "ff" });
InitializeComponent();
}
private ObservableCollection<MyClass> _list;
public ObservableCollection<MyClass> list
{
get{ return _list; }
set{
_list = value;
updateProperty("list");
}
}
Random r = new Random(0);
private void Button_Click(object sender, RoutedEventArgs e)
{
int id = (int)r.Next(6);
list[id].Id += 1;
int name = (int)r.Next(6);
list[name].Name = "update " + r.Next(20000);
}
Model Class: SourceUpdating property is set to true(which set the binding to notify TargetUpdate via a DataTrigger) when any notification is in progress for MyClass in updateProperty() method and after update is notified to UI, SourceUpdating is set to false(which then reset the binding to not notify TargetUpdate via a DataTrigger).
public class MyClass : INotifyPropertyChanged
{
private string name;
public string Name
{
get { return name; }
set {
name = value;updateProperty("Name");
}
}
private int id;
public int Id
{
get { return id; }
set
{
id = value;updateProperty("Id");
}
}
//the vaiable must set to ture when update in this calss is ion progress
private bool sourceUpdating;
public bool SourceUpdating
{
get { return sourceUpdating; }
set
{
sourceUpdating = value;updateProperty("SourceUpdating");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void updateProperty(string name)
{
if (name == "SourceUpdating")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
else
{
SourceUpdating = true;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
SourceUpdating = false;
}
}
}
Outputs:
Two simultaneous Updates/ Button is clicked once :
Many simultaneous Updates/ Button is clicked many times :
SO after update, when sorting or filtering is happening the bindings know that it doesn't have to invoke the TargetUpdated
event. Only when the update of source collection is in progress the
binding is reset to invoke the TargetUpdated event. Also initial coloring problem is also get handled by this.
However as the logic still has some sort comings as for editor TextBox the logic is based on with more complexity of data types and UI logic the code will become more complex also for initial binding reset whole row is animated as TargetUpdated is raised for all cells of a row.
My ideas for point (1) would be to handle this in the code. One way would be to handle the TargetUpdated event for the DataGridTextColumn and do an extra check on the old value vs. the new value, and apply the style only if the values are different, and perhaps another way would be to create and remove the binding programmatically based on different events in your code (like initial load, refresh, etc).
I suggest to use OnPropertyChanged for every props in your viewmodel and update related UIElement (start animation or whatever), so your problem will solved (on load, sort, filter,...) and also users can saw which cell changed!

How can we activate a WPF trigger in XAML when a drag operation is occurring?

We're looking for a way to base a WPF trigger in XAML on whether or not we're in a drag-drop operation. Depending on if we are or not, we want different hovering behaviors which is why this is needed.
The only way I can think of is to handle the drag start and end events and manually track the state, but that requires a code-behind, not pure XAML. Plus it seems like complete overkill, especially since we'd have to do it on every potential drop target which is a real pain.
So is there an easy way to say 'Hey... I'm in a drag-drop operation so make this trigger active' or am I out of luck here?
Update
To clarify what we are trying to do, currently in pure XAML, you can create a style, then set a style trigger to examine the IsMouseOver property to say, draw a background highlight. Well, we want to do this, but we want to say if 'IsMouseOver' is true and if IsDragging = true then apply this trigger.
I've just had this problem, my solution consists of using an attached property that supplies the missing IsDragging:
Define the attached Property
public static readonly DependencyProperty IsDraggingProperty =
DependencyProperty.RegisterAttached
(
"IsDragging",
typeof(bool),
typeof(ClassContainingThisProperty),
new UIPropertyMetadata(false)
);
public static bool GetIsDragging(DependencyObject source)
{
return (bool)source.GetValue(IsDraggingProperty);
}
public static void SetIsDragging(DependencyObject target, bool value)
{
target.SetValue(IsDraggingProperty, value);
}
Create this extension methods to help you set the Property
public static TParent FindParent<TParent>(this DependencyObject child) where TParent : DependencyObject
{
DependencyObject current = child;
while(current != null && !(current is TParent))
{
current = VisualTreeHelper.GetParent(current);
}
return current as TParent;
}
public static void SetParentValue<TParent>(this DependencyObject child, DependencyProperty property, object value) where TParent : DependencyObject
{
TParent parent = child.FindParent<TParent>();
if(parent != null)
{
parent.SetValue(property, value);
}
}
Handle DragDrop events according to the Control used (e.g. ListView), to set the attached property on the elements.
private void OnDragEnter(object sender, DragEventArgs e)
{
DependencyObject source = e.OriginalSource as DependencyObject;
if (source != null)
{
source.SetParentValue<ListViewItem>(ClassContainingTheProperty.IsDraggingProperty, true);
}
}
private void OnDragLeave(object sender, DragEventArgs e)
{
DependencyObject source = e.OriginalSource as DependencyObject;
if(source != null)
{
source.SetParentValue<ListViewItem>(ClassContainingTheProperty.IsDraggingProperty, false);
}
}
private void OnDrop(object sender, DragEventArgs e)
{
DependencyObject source = e.OriginalSource as DependencyObject;
if(source != null)
{
source.SetParentValue<ListViewItem>(ClassContainingTheProperty.IsDraggingProperty, false);
}
}
Use the Property in your Trigger
<ControlTemplate TargetType="{x:Type ListViewItem}">
<ControlTemplate.Triggers>
<Trigger Property="namespace:ClassContainingTheProperty.IsDragging" Value="True">
<Setter Property="Background" Value="White" />
<Setter Property="BorderBrush" Value="Black" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
I've only ever seen drag-drop implemented with event handlers, so I think you're out of luck. I suggest you create your own dependency property to indicate a drag drop in progress.
if it is a real DragDrop event just watch for the DragOver event...
<EventTrigger RoutedEvent="DragOver">
<BeginStoryboard>
<Storyboard Storyboard.TargetProperty="Fill.Color">
<ColorAnimation From="White" To="Black" Duration="0:0:1" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>

Resources