2012-06-01 6 views
13

Ho un DataGrid che ha i suoi dati aggiornati da un processo in background ogni 15 secondi. Se uno qualsiasi dei dati cambia, voglio eseguire un'animazione che evidenzia la cella con il valore modificato in giallo e quindi tornare al bianco. I sorta-di l'ho lavorare nel modo seguente:Evidenzia celle in WPF DataGrid quando il valore associato cambia

ho creato uno stile con trigger di evento su 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> 

E poi applicata alle colonne che volevo mettere in evidenza se un valore cambia

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

Se il valore per il campo di stato nelle modifiche del database, i punti salienti delle celle in giallo, proprio come voglio. Ma ci sono alcuni problemi.

Per prima cosa, quando la griglia di dati viene inizialmente caricata, l'intera colonna viene evidenziata in giallo. Questo ha senso, perché tutti i valori vengono caricati per la prima volta, quindi ti aspetteresti che TargetUpdated sia attivato. Sono sicuro che c'è un modo in cui posso fermarlo, ma è un punto relativamente secondario.

Il vero problema è che l'intera colonna è evidenziata in giallo se la griglia è ordinata o filtrata in alcun modo. Suppongo che non capisco perché un ordinamento potrebbe causare l'attivazione di TargetUpdated poiché i dati non sono cambiati, solo nel modo in cui viene visualizzato.

Quindi la mia domanda è (1) come posso interrompere questo comportamento sul caricamento iniziale e ordinare/filtro, e (2) sono sulla strada giusta e questo è anche un buon modo per farlo? Dovrei menzionare questo è MVVM.

+0

Per organizzare una soluzione alternativa a quella che si propone ... 1) ti aspetti che la lista sia grande? (in questo caso significa grande> = 100 articoli); e 2) ti aspetti che il numero di articoli nella lista cambi spesso? –

+0

Si tratta essenzialmente di un'applicazione della coda dell'helpdesk che elenca gli errori sulle transazioni e consente alle persone di assumere la proprietà di un errore specifico e contrassegnarlo come risolto. I valori non dovrebbero cambiare così spesso, e spero che ricevano meno di 100 errori al giorno in produzione. –

+0

OK, ho trovato l'idea di mostrare le diverse celle cambiando lo sfondo senza problemi quando il loro contenuto cambia, ad esempio, quando cambia lo stato dell'attività o l'assegnatario dell'attività ..., ma ho fatto qualche ricerca su quello e Non trovo il modo in cui puoi farlo solo scrivendo Xaml. Quello che vorrei fare è scrivere una raccolta degli oggetti del dominio in memoria, e ogni volta che recuperi l'elenco dal server, implementa metodi di utilità che eseguono il confronto dei dati appena recuperati rispetto a quello che è già presente nel datagrid ed esegui lo stile cambia. –

risposta

0

Poiché TargetUpdated è davvero solo l'evento basato sull'aggiornamento dell'interfaccia utente. Non importa come si sta verificando l'aggiornamento. Mentre si ordina tutto il DataGridCells rimangono al loro posto solo i dati vengono cambiati in loro in base al risultato di ordinamento quindi TargetUpdated viene generato. quindi dobbiamo essere dipendenti dal livello dati dell'app WPF. Per raggiungere questo obiettivo ho resettato il binding di DataGridCell in base a una variabile che tipo di traccia se l'aggiornamento avviene al livello dati.

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> 

codice sottostante (DataContext oggetto della finestra):

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

Classe Modello:SourceUpdating proprietà è impostata su true (che ha fissato il legame di notificare TargetUpdate tramite un DataTrigger) quando ogni notifica è in corso per MyClass in updateProperty() metodo e dopo l'aggiornamento viene notificata al UI, SourceUpdating è impostata su false (che poi ripristinare il legame di non comunicare TargetUpdate tramite 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;     
     } 
    } 

} 

Uscite:

due aggiornamenti simultanei/pulsante viene premuto una volta:

update1

molti aggiornamenti simultanei/pulsante viene premuto più volte:

update2

SO dopo l'aggiornamento, durante l'ordinamento o il filtraggio sta accadendo le associazioni sapere che non ha bisogno di richiamare l'evento TargetUpdated . Solo quando è in corso l'aggiornamento della raccolta di origine, il binding viene ripristinato per richiamare l'evento TargetUpdated. Anche questo problema di colorazione iniziale viene gestito da questo.

Tuttavia la logica ha ancora alcune carenze ordinamento come per montaggio TextBox la logica si basa sulla con maggiore complessità dei tipi di dati e la logica UI il codice diventerà più complessa anche per iniziale fila intera resettato legame è animato come TargetUpdated viene generato per tutte le celle di una riga.

0

Le mie idee per il punto (1) sarebbero gestirlo nel codice. Un modo sarebbe gestire l'evento TargetUpdated per DataGridTextColumn ed eseguire un controllo aggiuntivo sul vecchio valore rispetto al nuovo valore e applicare lo stile solo se i valori sono diversi e forse un altro modo sarebbe quello di creare e rimuovere l'associazione a livello di codice in base a diversi eventi nel codice (come caricamento iniziale, aggiornamento, ecc.).

0

Suggerisco di utilizzare OnPropertyChanged per ogni oggetto di scena nel tuo viewmodel e aggiornare UIElement relativo (avviare l'animazione o qualsiasi altra cosa), in modo che il tuo problema venga risolto (su caricamento, ordinamento, filtro, ...) e anche gli utenti possano vedere quale cella cambiato!