2012-04-23 12 views
5

EDIT2: Cuando el cuadro se llena, ya no puedo cambiar los valores. Incluso cuando cambio los valores en la lista (donde los controles de elemento obtienen los valores), el gráfico no parece actualizarse con los nuevos valores.Datablinding ItemsControl (DataTemplate) no actualiza/solo recibe valores en el inicio del programa

Llamo al método GetDataGrafiek() en el temporizador para actualizar mi gráfico cada x segundos.

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

Se debe esto el temporizador de roscado que se ejecuta en un hilo separado (IsAsynchronous método en el ObjectDataProvider) o tengo que acceder a la DataContext de ItemsControl en mi método de temporizador?

EDIT: he podido capaces de poblar el gráfico cuando el programa ya estaba funcionando, así que hizo el ObservableCollection<GrafiekBar> estática (lista que tiene el valor de relleno y de las barras) y se inicializa de la forma siguiente:

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

De MSDN: ObjectDataProvider: "Sin embargo, si está vinculando a un objeto que ya se ha creado, debe configurar el DataContext en el código, como en el ejemplo siguiente".


I tienen un ItemsControl que se muestra como un gráfico de barras simple. Cuando asigno valores (codificados en mi código detrás de), el cuadro se rellena con éxito.

Lo que hago es básicamente obtener el mayor valor establecido en 100% y calcular la longitud del resto de las barras a través de eso.

Problema: no quiero que los valores de la carta de barra hardcoded pero las barras tienen que cambiar en tiempo de ejecución. Para esto utilizo un Threading.Timer que se ejecuta cada segundo mientras mi programa se está ejecutando (otros cálculos también suceden en este temporizador).

Los valores de la barra de gráfico obtienen actualizaciones basadas en los cálculos que suceden en este temporizador cada x segundos.

He intentado todo y no puedo obtener los valores que se muestran cuando mi programa se está ejecutando. Solo puedo ver barras cuando las codifico (vea la región de GetDataGrafiek() al final del hilo). ¿Qué estoy haciendo exactamente mal/perdido?

El GetDataGrafiek() (cálculos para completar mi gráfico) se llama en un ObjectDataProvider.

Este método toma TimeSpan como entrada y luego realiza cálculos para obtener un valor doble (basado en el valor 100% explicado anteriormente) que luego se coloca en el valor de la barra (= ancho de la plantilla de fecha).

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

DataTemplate para mi ItemsControl (esto utiliza la anchura para el valor de las barras de mi carta)

<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> 

ItemsControl:

<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() La región contiene valores codificados, cuando esto se hace mi tabla muestra 6 barras con éxito. Cuando comento la región, ya no obtengo ninguna barra visible.

Este método devuelve una lista con Double valores. Cada valor representa una barra que se representa como Ancho en el DataTemplate, y el Relleno simplemente le da un cierto color.

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

El temporizador de roscado que sale cada segundo (que tiene la lógica de cálculo y la getDataGrafiek() llama no hace una invocación al hilo de interfaz gráfica de usuario para las actualizaciones.

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

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

Saludos Pedro.

+0

No estoy seguro de seguir lo que estás haciendo aquí, pero tener un temporizador funcionando suena muy raro. ¿No puedes responder a los eventos en su lugar? – Oliver

+0

El gráfico tiene que cambiar (o no cambiar si no es necesario) en función de los cálculos que se realizan cada segundo en ese temporizador. No estoy seguro de cómo resolver esto. – PeterP

+0

Solo una suposición aproximada: ha intentado usar DispatcherTimer en lugar de un temporizador normal. Este temporizador se ha optimizado para el envío de WPF – SvenG

Respuesta

0

Es un tiro en la oscuridad, porque no he leído todo su código, pero aquí hay una idea: usted está vinculando a un recurso estático ¿verdad? Se lee una vez y esto es todo, ¿eh? Pruebe DynamicResource en su lugar.

+0

Ah, no importa, fue una idea estúpida. ¿Qué hace exactamente CaculateTimeBetweenWegingen(); ¿hacer? –

0

Si está accediendo al siguiente código desde otro hilo, esto causará un problema:

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

Como el otro hilo va a provocar un evento que hará que el hilo despachador (si está suscrito al evento) al error

Procurad, más bien

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

Además, asegúrese de que está utilizando UpdateSourceMethod = PropertyChanged en WPF.

+0

Además, una cosa que realmente no entendí del todo es cuál es el propósito del controlador. Si está vinculando directamente desde WPF, no necesita manejador, solo UpdateSourceMethod = PropertyChanged en el enlace de datos en XAML. – William

Cuestiones relacionadas