2012-06-01 10 views
13

Tengo un DataGrid que tiene sus datos actualizados por un proceso en segundo plano cada 15 segundos. Si alguno de los datos cambia, quiero ejecutar una animación que resalte la celda con el valor modificado en amarillo y luego volver a fundir el blanco. En cierto modo me-lo tengo de trabajar de la siguiente manera:Resaltar celdas en WPF DataGrid cuando el valor enlazado cambia

he creado un estilo con disparador de evento en 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> 

Y entonces se aplica a las columnas que quería poner de relieve si se modifica un valor

<DataGridTextColumn Header="Status" 
    Binding="{Binding Path=Status, NotifyOnTargetUpdated=True}" 
    CellStyle="{StaticResource ChangedCellStyle}" /> 

Si el valor del campo de estado en la base de datos cambia, la celda se resalta en amarillo como yo quiero. Pero hay algunos problemas.

Primero, cuando la cuadrícula de datos se carga inicialmente, toda la columna se resalta en amarillo. Esto tiene sentido, porque todos los valores se están cargando por primera vez, por lo que esperaría que TargetUpdated se disparara. Estoy seguro de que hay alguna forma en que puedo detener esto, pero es un punto relativamente menor.

El problema real es que toda la columna se resalta en amarillo si la cuadrícula está ordenada o filtrada de alguna manera. Supongo que no entiendo por qué un tipo causaría que TargetUpdated se activara ya que los datos no cambiaron, solo la forma en que se muestra.

Así que mi pregunta es (1) ¿cómo puedo detener este comportamiento en la carga inicial y ordenar/filtrar, y (2) estoy en el camino correcto y esto es incluso una buena manera de hacerlo? Debo mencionar que esto es MVVM.

+0

Para organizar una solución alternativa a la que propone ... 1) ¿espera que la lista sea grande? (en este caso significa grande> = 100 elementos); y 2) ¿espera que el número de elementos en la lista cambie a menudo? –

+0

Se trata esencialmente de una aplicación de cola de ayuda que enumera los errores en las transacciones y permite que las personas asuman un error específico y lo marquen como resuelto. Los valores no deberían cambiar tan a menudo, y espero que obtengan menos de 100 errores por día en producción. –

+0

Muy bien, la idea de mostrar las diferentes celdas cambiando su fondo sin problemas cuando su contenido cambia, por ejemplo, cuando cambia el estado de la tarea o el asignado de la tarea es interesante ..., pero investigué un poco sobre eso y No encuentro la forma de que puedas hacer esto solo escribiendo Xaml. Lo que haría es escribir una colección de objetos de dominio en la memoria, y cada vez que recupere la lista del servidor, implemente métodos de utilidad que realicen la comparación de los datos recién recuperados con el que ya está en la cuadrícula de datos y que realicen el estilo cambia –

Respuesta

0

Desde TargetUpdated es realmente solo un evento basado en la actualización de UI. No importa cómo esté sucediendo la actualización. Mientras clasifica todos los DataGridCells permanecen en sus lugares, solo se cambian los datos según el resultado de clasificación, por lo tanto, se genera TargetUpdated. por lo tanto, debemos depender de la capa de datos de la aplicación WPF. Para lograr esto, reinicié el Enlace de DataGridCell basado en una variable de ese tipo de rastreo si la actualización está sucediendo en la capa de datos.

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> 

Código Atrás (DataContext objeto de la ventana):

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); 
    } 

Clase Modelo:SourceUpdating la propiedad se establece en verdadero (que establece el enlace para notificar TargetUpdate a través de DataTrigger) cuando hay una notificación en progreso para MyClass en el método updateProperty() y después de que se notifique a UI, SourceUpdating se establece en falso (que a continuación restablece el enlace para no notificar TargetUpdate a través de 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;     
     } 
    } 

} 

Salidas:

dos actualizaciones simultáneas/botón se hace clic una vez:

update1

muchas actualizaciones simultáneas/botón se pulsa varias veces:

update2

así que después de la actualización, al ordenar o filtrado está sucediendo los enlaces saber que no tiene que distribuir el evento TargetUpdated . Solo cuando la actualización de la recopilación de fuentes está en progreso, el enlace se restablece para invocar el evento TargetUpdated. También el problema inicial de coloración también se maneja con esto.

Sin embargo, como la lógica todavía tiene algunas idas ordenar como para el editor TextBox la lógica se basa en la mayor complejidad de los tipos de datos y la lógica de la interfaz de usuario del código será más compleja también para toda la fila de restablecimiento de unión inicial está animado como TargetUpdated se genera para todas las celdas de una fila.

0

Mis ideas para el punto (1) serían manejar esto en el código. Una forma sería manejar el evento TargetUpdated para DataGridTextColumn y hacer una verificación adicional del valor anterior frente al nuevo, y aplicar el estilo solo si los valores son diferentes, y quizás otra forma sería crear y eliminar el enlace. programáticamente basado en diferentes eventos en su código (como carga inicial, actualización, etc.).

0

Sugiero usar OnPropertyChanged para cada utilería en su viewmodel y actualizar el UIElement relacionado (animación de inicio o lo que sea), para que su problema se resuelva (en carga, clasificación, filtro, ...) y también los usuarios puedan ver qué celda cambiado!

Cuestiones relacionadas