Reputation: 4665
I have this working stub:
XAML
<Window x:Class="WpfApp1.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:WpfApp1"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<DataGrid ItemsSource="{Binding MyList}" AutoGenerateColumns="False" CanUserAddRows="False" GridLinesVisibility="None" HeadersVisibility="None" BorderThickness="0" ScrollViewer.VerticalScrollBarVisibility="Visible">
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Label>
<TextBlock Text="{Binding Id}" />
</Label>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Label>
<TextBlock Text="{Binding TheData}" />
</Label>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</Window>
Code-behind
using System.Windows;
using WpfApp1.ViewModels;
namespace WpfApp1
{
public partial class MainWindow : Window
{
public MainWindow()
{
DataContext = new MainWindowVM();
InitializeComponent();
}
}
}
ViewModel
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace WpfApp1.ViewModels
{
public class MainWindowVM
{
Random random = new Random();
public List<ListItemVM> MyList { get; set; } = new List<ListItemVM>
{
new ListItemVM(1, "Data1"),
new ListItemVM(2, "Data2"),
new ListItemVM(3, "Data3"),
};
public MainWindowVM()
{
// Start an infinite task that updates the data every 2 second
// This emulates an external process that sends new data that must be displayed
Task.Factory.StartNew(() =>
{
while (true)
{
Task.Delay(2000).Wait();
var nextData = new string(Enumerable.Repeat("ABCDEFG", 10).Select(s => s[random.Next(s.Length)]).ToArray());
MyList[1].SetNewData(nextData);
}
});
}
}
}
Item ViewModel
using System.ComponentModel;
namespace WpfApp1.ViewModels
{
public class ListItemVM : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public int Id { get; set; }
public string TheData { get; set; }
public ListItemVM(int id, string theData)
{
Id = id;
TheData = theData;
}
internal void SetNewData(string nextData)
{
// Change data
TheData = nextData;
// Notify the UI of the change
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(TheData)));
}
}
}
Every 2 seconds, I see the data update in the UI for the second item in the DataGrid.
The question
I would like the DataGridRow to get highlighted and fade out in 1 second on every update. Can someone help me achieve that please?
Upvotes: 0
Views: 596
Reputation: 169400
You could create an attached behaviour that performs the animation:
public static class Animator { private static readonly HashSet _rows = new HashSet();
public static readonly DependencyProperty ValueProperty = DependencyProperty.RegisterAttached("Value", typeof(object),
typeof(Animator), new PropertyMetadata(new PropertyChangedCallback(OnValuePropertyChanged)));
public static object GetValue(DataGridRow d) => d.GetValue(ValueProperty);
public static void SetValue(DataGridRow d, object value) => d.SetValue(ValueProperty, value);
private static void OnValuePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
DataGridRow row = (DataGridRow)d;
if (!_rows.Contains(row))
{
_rows.Add(row);
row.Unloaded += Row_Unloaded;
}
else
{
ColorAnimation animation = new ColorAnimation();
animation.From = Colors.Gray;
animation.To = Colors.White;
animation.Duration = new Duration(TimeSpan.FromSeconds(1));
Storyboard.SetTarget(animation, row);
Storyboard.SetTargetProperty(animation, new PropertyPath("Background.Color"));
Storyboard sb = new Storyboard();
sb.Children.Add(animation);
sb.Begin();
}
}
private static void Row_Unloaded(object sender, RoutedEventArgs e)
{
DataGridRow row = (DataGridRow)sender;
_rows.Remove(row);
row.Unloaded -= Row_Unloaded;
}
}
Usage:
<DataGrid.ItemContainerStyle>
<Style TargetType="DataGridRow">
<Setter Property="local:Animator.Value" Value="{Binding TheData}" />
</Style>
</DataGrid.ItemContainerStyle>
Upvotes: 1
Reputation: 108
You'll need to do something like:
DataGrid.RowStyle
to a new Style
- probably override the default ControlTemplate
(see this answer) to add a background element you can "highlight"Trigger
in Style.Triggers
- maybe a DataTrigger
based on TheData
not being emptyYou might need to do something to defer the application of this style - presumably you don't want all the rows to highlight on initial load. To do this you could look at using a Xaml behavior to do set the RowStyle
after Loaded
has fired
Upvotes: 0