locked
Problem mit ObservableCollection und Tollkit Chart RRS feed

  • Frage

  • Hallo,
    ich habe eine ObservableCollection an ein Toolkit:Chart gebunden und ändere die Werte im Hintergrund.
    Funktioniert ganz gut, eine Zeit lang. Dann beginnen die Balken zu fliegen. Ich denke es ist ein Paint Fehler, denn wenn ich das Browserfenster nur ein Pixel in der Größe ändere, werden die Balken wieder richtig gemalt.
    Was mache ich den falsch?

    Hier ist das Sample hier
    Man muss eine Zeit lang warten oder das Browserfenster maximieren und wieder verkleinern.
    XAML
    <UserControl 
        xmlns:ctk="clr-namespace:System.Windows.Controls.DataVisualization.Charting;assembly=System.Windows.Controls.DataVisualization.Toolkit"  
        xmlns:datvis="clr-namespace:System.Windows.Controls.DataVisualization;assembly=System.Windows.Controls.DataVisualization.Toolkit" 
        x:Class="SilverlightMiniApp.MainPage"
        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" 
        mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480">
            <Grid x:Name="LayoutRoot">
    
            <ctk:Chart Name="MainChart" >
               
                <ctk:Chart.Series>
                    <ctk:ColumnSeries
                                 DependentValueBinding="{Binding Path=Value}" 
                                 IndependentValueBinding="{Binding Path=Key}"/>
                </ctk:Chart.Series>
    
            </ctk:Chart>
    
        </Grid>
    </UserControl>
    
    
    C# Code
    using System.Windows.Controls;
    using System.Windows.Controls.DataVisualization.Charting;
    using System.Collections.ObjectModel;
    using System.Collections.Generic;
    using System;
    
    namespace SilverlightMiniApp
    {
        public partial class MainPage : UserControl
        {
            ObservableCollection<KeyValuePair<int, int>> myData = new ObservableCollection<KeyValuePair<int, int>>();
            System.Windows.Threading.DispatcherTimer aTimer = new System.Windows.Threading.DispatcherTimer();
    
            public MainPage()
            {
                InitializeComponent();
                for (int i = 0; i < 24; i++)
                    myData.Add(new KeyValuePair<int, int>(i, 0));
                ((ColumnSeries)MainChart.Series[0]).ItemsSource = myData;
                aTimer.Interval = TimeSpan.FromMilliseconds(4000);
                aTimer.Tick += aTimer_Tick;
                aTimer.Start();
            }
    
            void aTimer_Tick(object sender, EventArgs e)
            {
            Random aRandom = new Random();
                for (int i = 0; i < 24; i++)
                    myData[i]=new KeyValuePair<int, int>(i, aRandom.Next(0, 30));
            }
        }
    }
    
    
     


    Samstag, 6. Februar 2010 17:31

Antworten

  • Hier jetzt die Lösung (Work-Around) mit MVVM:

    XAML:

    <navigation:Page 
      xmlns:ctk="clr-namespace:System.Windows.Controls.DataVisualization.Charting;assembly=System.Windows.Controls.DataVisualization.Toolkit"  
      x:Class="SilverlightApplication1.Page3" 
               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"
               mc:Ignorable="d"
               xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
               d:DesignWidth="640" d:DesignHeight="480"
               Title="Page3 Page"
                xmlns:my="clr-namespace:SilverlightApplication1"
               >
      <navigation:Page.Resources>
        <my:Page3ViewModel x:Key="VM" />
      </navigation:Page.Resources>
      <Grid x:Name="LayoutRoot">
        <ctk:Chart Name="MainChart" Height="500">
          <ctk:Chart.Series>
            <ctk:ColumnSeries ItemsSource="{Binding Source={StaticResource VM}, Path=Data}"
                              DependentValueBinding="{Binding Value}" 
                              IndependentValueBinding="{Binding Key}"
                              >
              <ctk:ColumnSeries.DependentRangeAxis>
                <ctk:LinearAxis Orientation="Y" Minimum="0" Maximum="100" />
              </ctk:ColumnSeries.DependentRangeAxis>
            </ctk:ColumnSeries>
          </ctk:Chart.Series>
        </ctk:Chart>
      </Grid>
    </navigation:Page>

     

    Dazu das ViewModel und die Datenklasse:

    Public Class Page3ViewModel
      Implements INotifyPropertyChanged
    
      Public Event PropertyChanged(ByVal sender As Object, ByVal e As System.ComponentModel.PropertyChangedEventArgs) Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged
    
      Private WithEvents aTimer As New DispatcherTimer
    
      Public Sub New()
        Me._data = New List(Of DataClass)
        For i = 1 To 24
          Me._data.Add(New DataClass With {.Key = 2 * i, .Value = i})
        Next
        With aTimer
          .Interval = TimeSpan.FromMilliseconds(4000)
          .Start()
        End With
      End Sub
    
      Private Sub aTimer_Tick(ByVal sender As Object, ByVal e As EventArgs) _
          Handles aTimer.Tick
        Dim aRandom As New Random()
        For i = 0 To 23
          Me._data(i).Value = aRandom.Next(0, 30)
        Next
        RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("Data"))
      End Sub
    
      Private _data As List(Of DataClass)
      Public ReadOnly Property Data() As List(Of DataClass)
        Get
          Return New List(Of DataClass)(Me._data)
        End Get
      End Property
    
    End Class
    
    Public Class DataClass
    
      Private _key As Integer
      Public Property Key() As Integer
        Get
          Return _key
        End Get
        Set(ByVal value As Integer)
          _key = value
        End Set
      End Property
    
      Private _value As Integer
      Public Property Value() As Integer
        Get
          Return _value
        End Get
        Set(ByVal value As Integer)
          _value = value
        End Set
      End Property
    
    End Class

     

    --

    Peter

    • Als Antwort vorgeschlagen Peter Fleischer Samstag, 20. März 2010 08:20
    • Als Antwort markiert ixmac Dienstag, 30. März 2010 11:00
    Samstag, 20. März 2010 08:20

Alle Antworten

  • Nimm anstelle der KeyValuePair-Objekte eigene Objekt, die INotifyPropertyChanged implemetieren, dann werden Änderungen auch sofort angezeigt.

    --
    Peter
    Mittwoch, 24. Februar 2010 06:00
  • Hallo ixmac,

    Ich gehe davon aus, dass die Antwort Dir weitergeholfen hat.
    Solltest Du noch "Rückfragen" dazu haben, so gib uns bitte Bescheid.

    Grüße,
    Robert

    Donnerstag, 4. März 2010 09:21
  • Hallo,
    leider ist das nicht die Lösung des Problems.
    Die Balken werden ja aktualisiert. 
    Nur ab einem Zeitpunkt werden die Balken nicht mehr ab der Null-Linie gezeichnet.
    Ich habe das auf mehreren Rechnern reproduzieren können.
    Das Programm hab ich trotzdem umgebaut auf eine "ObservableKeyValuePair" :)
    Der Rest ist wie oben.
        public class ObservableKeyValuePair<TKey, TValue> : INotifyPropertyChanged
        {
            public ObservableKeyValuePair(TKey k, TValue v)
            {
                Key = k;
                Value = v;
            }
    
            private TKey _key;
            public TKey Key 
            {
                get{ return(_key); }
                set
                {
                    _key = value;
                    if (PropertyChanged != null)
                        PropertyChanged(this, new PropertyChangedEventArgs("Key"));
                }
            }
    
            private TValue _value;
            public TValue Value
            {
                get{return (_value);}
                set
                {
                    _value = value;
                    if (PropertyChanged != null)
                        PropertyChanged(this, new PropertyChangedEventArgs("Value"));
                }
            }
            public event PropertyChangedEventHandler PropertyChanged;
        }

    Das Setzen der Daten im Timer hab ich nochmal vereinfacht:
           void aTimer_Tick(object sender, EventArgs e)
            {
            Random aRandom = new Random();
            for (int i = 0; i < 24; i++)
                myData[i].Value = aRandom.Next(0, 30);
            }
    Die Aktualisierung geht. Das Problem der "fliegenden Balken" bleibt.

    Beste Grüße
    Marcus
    Montag, 15. März 2010 18:52
  • Ich kann das Problem reproduzieren. Wenn der neue Wert größer als der alte Wert ist, dann entsteht der Balken. Wenn du in deinem Code die ItemsSource entbindest und neu bindest, dann gibt es das Problem nicht. Für MVVM habe ich noch keine Lösung -- Peter
    Freitag, 19. März 2010 09:09
  • Hier jetzt die Lösung (Work-Around) mit MVVM:

    XAML:

    <navigation:Page 
      xmlns:ctk="clr-namespace:System.Windows.Controls.DataVisualization.Charting;assembly=System.Windows.Controls.DataVisualization.Toolkit"  
      x:Class="SilverlightApplication1.Page3" 
               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"
               mc:Ignorable="d"
               xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
               d:DesignWidth="640" d:DesignHeight="480"
               Title="Page3 Page"
                xmlns:my="clr-namespace:SilverlightApplication1"
               >
      <navigation:Page.Resources>
        <my:Page3ViewModel x:Key="VM" />
      </navigation:Page.Resources>
      <Grid x:Name="LayoutRoot">
        <ctk:Chart Name="MainChart" Height="500">
          <ctk:Chart.Series>
            <ctk:ColumnSeries ItemsSource="{Binding Source={StaticResource VM}, Path=Data}"
                              DependentValueBinding="{Binding Value}" 
                              IndependentValueBinding="{Binding Key}"
                              >
              <ctk:ColumnSeries.DependentRangeAxis>
                <ctk:LinearAxis Orientation="Y" Minimum="0" Maximum="100" />
              </ctk:ColumnSeries.DependentRangeAxis>
            </ctk:ColumnSeries>
          </ctk:Chart.Series>
        </ctk:Chart>
      </Grid>
    </navigation:Page>

     

    Dazu das ViewModel und die Datenklasse:

    Public Class Page3ViewModel
      Implements INotifyPropertyChanged
    
      Public Event PropertyChanged(ByVal sender As Object, ByVal e As System.ComponentModel.PropertyChangedEventArgs) Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged
    
      Private WithEvents aTimer As New DispatcherTimer
    
      Public Sub New()
        Me._data = New List(Of DataClass)
        For i = 1 To 24
          Me._data.Add(New DataClass With {.Key = 2 * i, .Value = i})
        Next
        With aTimer
          .Interval = TimeSpan.FromMilliseconds(4000)
          .Start()
        End With
      End Sub
    
      Private Sub aTimer_Tick(ByVal sender As Object, ByVal e As EventArgs) _
          Handles aTimer.Tick
        Dim aRandom As New Random()
        For i = 0 To 23
          Me._data(i).Value = aRandom.Next(0, 30)
        Next
        RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("Data"))
      End Sub
    
      Private _data As List(Of DataClass)
      Public ReadOnly Property Data() As List(Of DataClass)
        Get
          Return New List(Of DataClass)(Me._data)
        End Get
      End Property
    
    End Class
    
    Public Class DataClass
    
      Private _key As Integer
      Public Property Key() As Integer
        Get
          Return _key
        End Get
        Set(ByVal value As Integer)
          _key = value
        End Set
      End Property
    
      Private _value As Integer
      Public Property Value() As Integer
        Get
          Return _value
        End Get
        Set(ByVal value As Integer)
          _value = value
        End Set
      End Property
    
    End Class

     

    --

    Peter

    • Als Antwort vorgeschlagen Peter Fleischer Samstag, 20. März 2010 08:20
    • Als Antwort markiert ixmac Dienstag, 30. März 2010 11:00
    Samstag, 20. März 2010 08:20