2012-04-23 10 views
5

EDIT2: Wenn das Diagramm ausgefüllt wird, kann ich die Werte nicht mehr ändern. Selbst wenn ich die Werte in der Liste ändere (woher die ItemControls die Werte beziehen), scheint das Diagramm nicht mit den neuen Werten zu aktualisieren.Datenbindung ItemsControl (DataTemplate) wird nicht aktualisiert/erhält nur Werte beim Programmstart

Ich rufe die GetDataGrafiek() Methode im Timer auf, um mein Diagramm alle x Sekunden zu aktualisieren.

 Grafiek graf = new Grafiek(); 
     graf.GetDataGrafiek(); 

Ist dies aufgrund des Threading-Timer in einem separaten Thread ausgeführt wird (IsAsynchronous Methode im ObjectDataProvider) oder muß ich zugreifen müssen, um die DataContext des Itemscontrol in meiner Timer-Methode?

EDIT: Ich konnte nicht der Lage, die Grafik zu füllen, wenn das Programm bereits ausgeführt wird, so habe ich die ObservableCollection<GrafiekBar> statische (Liste, die die Fill und Wert der Stäbe halten) und initialisiert ist, wie folgend:

public static ObservableCollection<GrafiekBar> listGrafiek = new ObservableCollection<GrafiekBar>() 
     { 
      new GrafiekBar() {Value = 0, Fill = (Brush)convertor.ConvertFromString(kleuren[0])}, 
      new GrafiekBar() {Value = 0, Fill = (Brush)convertor.ConvertFromString(kleuren[1])}, 
      new GrafiekBar() {Value = 0, Fill = (Brush)convertor.ConvertFromString(kleuren[2])} 
     }; 

Von MSDN: ObjectDataProvider: "Wenn Sie jedoch an ein Objekt binden, das bereits erstellt wurde, müssen Sie den DataContext im Code wie im folgenden Beispiel festlegen."


Ich habe eine Items, die als einfache Balkendiagramm angezeigt wird. Wenn ich Werte (hartcoded in meinem CodeBehind) zuweisen, wird das Diagramm erfolgreich ausgefüllt.

Was ich tue, ist im Grunde, den größten Wert zu erhalten, stellen Sie es auf 100% und berechnen Sie die Länge des Rests der Bars durch das.

Problem: Ich möchte nicht, dass die Balkenwerte hartcodierte, aber die Balken müssen runTime ändern. Dafür verwende ich eine Threading.Timer, die jede Sekunde läuft, solange mein Programm läuft (andere Berechnungen passieren auch in diesem Timer).

Die Werte der Diagrammleiste erhalten Aktualisierungen basierend auf den Berechnungen, die innerhalb dieses Timers alle x Sekunden stattfinden.

Ich habe alles versucht und ich kann Werte nicht angezeigt bekommen, wenn mein Programm läuft. Ich kann nur Balken sehen, wenn ich sie fest codiere (siehe Region von GetDataGrafiek() am Ende des Threads). Was genau mache ich falsch/fehlt?

Die GetDataGrafiek() (Berechnungen, um mein Diagramm zu füllen) wird in einem ObjectDataProvider aufgerufen.

Diese Methode verwendet TimeSpan als Eingabe und führt dann Berechnungen durch, sodass ich einen Double Value (basierend auf dem oben erläuterten 100% -Wert) erhält, der dann in den Wert des Balkens (= Breite des dateTemplate) gesetzt wird.

<ObjectDataProvider x:Key="odpLbGrafiek" ObjectType="{x:Type myClasses:GrafiekBar}" MethodName="GetDataGrafiek"/> 

Datatemplate für meine Items (dies verwendet die Breite für den Wert der Stäbe meiner Grafik)

<DataTemplate x:Key="GrafiekItemTemplate"> 
     <Border Width="Auto" Height="Auto"> 
      <Grid> 
       <Rectangle StrokeThickness="0" Height="30" 
          Margin="15" 
          HorizontalAlignment="Right" 
          VerticalAlignment="Bottom" 
          Width="{Binding Value}" 
          Fill="{Binding Fill}"> 
        <Rectangle.LayoutTransform> 
         <ScaleTransform ScaleX="20" /> 
        </Rectangle.LayoutTransform> 
       </Rectangle> 
      </Grid> 
     </Border> 
    </DataTemplate> 

Items:

<ItemsControl x:Name="icGrafiek" 
      Margin="50,3,0,0" 
      ItemsSource="{Binding Source={StaticResource odpLbGrafiek}}" 
      ItemTemplate="{DynamicResource GrafiekItemTemplate}" 
      RenderTransformOrigin="1,0.5" HorizontalAlignment="Left" VerticalAlignment="Top" Grid.RowSpan="6"> 
      <ItemsControl.RenderTransform> 
       <TransformGroup> 
        <ScaleTransform ScaleY="-1" ScaleX="1"/> 
        <SkewTransform AngleY="0" AngleX="0"/> 
        <RotateTransform Angle="180"/> 
        <TranslateTransform/> 
       </TransformGroup> 
      </ItemsControl.RenderTransform> 
     </ItemsControl> 

GetDataGrafiek() Die Region enthält fest codierte Werte, wenn dies erfolgt ist Mein Diagramm zeigt 6 Balken erfolgreich an. Wenn ich die Region kommentiere, bekomme ich keine sichtbaren Balken mehr.

Diese Methode gibt eine Liste mit Double Werten zurück. Jeder Wert repräsentiert einen Balken, der als Breite in DataTemplate dargestellt wird, und die Füllung gibt ihm nur eine bestimmte Farbe.

ObservableCollection<GrafiekBar> listGrafiek = new ObservableCollection<GrafiekBar>(); 

    public ObservableCollection<GrafiekBar> GetDataGrafiek() 
    { 
     var converter = new System.Windows.Media.BrushConverter(); 

     #region ***TEST HARDCODED BAR VALUES*** 
      int[] testWaardenUren = new int[] { 2, 1, 0, 1, 2, 0 }; 
      int[] testWaardenMinuten = new int[] { 58, 2, 55, 55, 2, 20 }; 

      for (int j = 0; j < 6; j++) 
      { 
       TimeSpan ts = new TimeSpan(testWaardenUren[j], testWaardenMinuten[j], 0); 
       GlobalObservableCol.regStilstanden[j].Value = ts; 
       GlobalObservableCol.regStilstanden[j].Name = ""; 
      } 
     #endregion 

     totalMinutesMaxValue = GetLargestValueStilstanden(); //= "100%" value 

     //calculate % of stilstanden Values 
     for (int i = 0; i < GlobalObservableCol.regStilstanden.Count; i++) 
     { 
      Double totalMin = GlobalObservableCol.regStilstanden[i].Value.TotalMinutes; 
      totalMin = totalMin/totalMinutesMaxValue * 10; 
      valuesChartPercentage.Add(totalMin); 
     } 

     //the barChart (itemsControl) gets its final values here 
     for (int j = 0; j < GlobalObservableCol.regStilstanden.Count; j++) 
     { 
      GrafiekBar bar = new GrafiekBar(); 
      bar.Value = valuesChartPercentage[j]; 
      bar.Fill = converter.ConvertFromString(kleuren[j]) as Brush; 
      listGrafiek.Add(bar); 
     } 

     return listGrafiek; 
    } 

GrafiekBar.cls

public class GrafiekBar : INotifyPropertyChanged 
{ 
    private double value; 
    private Brush fill; 

    public GrafiekBar() 
    {  
    } 

    public double Value 
    { 
     get { return this.value; } 

     set 
    { 
      this.value = value; 
      NotifyPropertyChanged("Value"); 
     } 
    } 

    public Brush Fill 
    { 
     get { return this.fill; } 

     set 
     { 
      this.fill = value; 
      NotifyPropertyChanged("Fill"); 
     } 
    } 

    //interface INotifyPropertyChanged 
    public event PropertyChangedEventHandler PropertyChanged; 

    //interface INotifyPropertyChanged 
    private void NotifyPropertyChanged(String info) 
    { 
     PropertyChangedEventHandler handler = PropertyChanged; 
     if (PropertyChanged != null) 
     { 
      PropertyChanged(this, new PropertyChangedEventArgs(info)); 
     } 
    } 
} 

das Einfädeln Timer, der jede Sekunde läuft (die Berechnungslogik aufweist und der getDataGrafiek() aufgerufen Aktualisierungen eine Invoke zum GUI-Thread es tut.

private void MyTimerCallBack(object state) 
    { 
     DisplayWegingInfo(); 

     App.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background, 
      new Action(() => 
      { 
       //actions that involve updating the UI 
       CaculateTimeBetweenWegingen(); 
      })); 
    } 

Mit freundlichen Grüßen Peter.

+0

Ich bin mir nicht sicher, ob ich dem, was du hier machst, folge, aber das Laufen eines Timers klingt super komisch. Kannst du nicht auf Ereignisse reagieren? – Oliver

+0

Das Diagramm muss basierend auf Berechnungen, die jede Sekunde in diesem Timer ausgeführt werden, geändert werden (oder nicht geändert werden, wenn dies nicht erforderlich ist). Ich bin mir nicht sicher, wie ich das lösen soll. – PeterP

+0

Nur eine ungefähre Vermutung: Haben Sie versucht, DispatcherTimer anstelle eines normalen Timers zu verwenden. Dieser Timer wurde für die WPF-Verteilung – SvenG

Antwort

0

Es ist ein Schuss in die Dunkelheit, weil ich nicht Ihren ganzen Code gelesen habe, aber hier ist ein Gedanke: Sie binden an eine statische Ressource, richtig? Es wird einmal gelesen und das ist es, oder? Versuchen Sie stattdessen DynamicResource.

+0

Ah, vergiss es, das war eine dumme Idee. Was genau bewirkt CarcateTimeBetweenWegingen(); machen? –

0

Wenn Sie den folgenden Code aus einem anderen Thread zugreifen, wird dies ein Problem verursachen:

private void NotifyPropertyChanged(String info) 
{ 
    PropertyChangedEventHandler handler = PropertyChanged; 
    if (PropertyChanged != null) 
    { 
     PropertyChanged(this, new PropertyChangedEventArgs(info)); 
    } 
} 

Wie der anderen Thread wird ein Ereignis auslösen, die den Dispatcher-Thread verursachen (wenn es auf das Ereignis abonniert hat) zum Fehler.

Versuchen Sie stattdessen

private void NotifyPropertyChanged(String info) 
{ 
    PropertyChangedEventHandler handler = PropertyChanged; 
    if (PropertyChanged != null) 
    { 
     if (System.Threading.Thread.CurrentThread == System.Windows.Application.Current.Dispatcher.Thread) 
      PropertyChanged(this, new PropertyChangedEventArgs(info)); 
     else 
      System.Windows.Application.Current.Dispatcher.BeginInvoke(new Action(() => PropertyChanged(this,new PropertyChangedEventArgs(info)); 
    } 
} 

Also, stellen Sie sicher, dass Sie UpdateSourceMethod = Property in WPF verwenden.

+0

Auch eine Sache, die ich nicht wirklich ganz verstanden habe, ist der Zweck des Handlers. Wenn Sie direkt aus WPF binden, benötigen Sie keinen Handler, nur UpdateSourceMethod = PropertyChanged in der Datenbindung in XAML. – William

Verwandte Themen