Benutzer mit den meisten Antworten
Performante Darstellung in WPF

Frage
-
Hallo, hab folgendes Problem:
Ich empfange über einen UDP-Client in sehr kurzen Abständen (100-500ms) Datenpaket, welche Ich Tabellarisch visualisieren soll. Da Problem ist, dass durch meine View der Workerthread für die Kommunikation zu lange blockiert wird, und sich dieser dann verabschiedet. Die Darstellung erfolgt in einer Listview...
<ListView Grid.Row="1" x:Name="listBox" ItemsSource="{Binding ItemList, Mode=OneWay,NotifyOnSourceUpdated=False}" ScrollViewer.VerticalScrollBarVisibility="Auto" Width="Auto" ScrollViewer.IsDeferredScrollingEnabled="False" ScrollViewer.CanContentScroll="True" VirtualizingStackPanel.IsVirtualizing="True" VirtualizingStackPanel.VirtualizationMode="Recycling" Grid.ColumnSpan="4"> <ListView.ItemTemplate> <DataTemplate> <VirtualizingStackPanel Orientation="Horizontal" VirtualizationMode="Recycling"> <Label Content="{Binding Position, Mode=OneWay,NotifyOnSourceUpdated=False}" Width="70"> <Label.Resources> <Style TargetType="Label"> <Style.Triggers> <DataTrigger Binding="{Binding GetPositionError,NotifyOnSourceUpdated=False, Mode=OneWay}" Value="True"> <Setter Property="BorderBrush" Value="Red"></Setter> <Setter Property="BorderThickness" Value="3" /> </DataTrigger> </Style.Triggers> </Style> </Label.Resources> </Label> <ListView ItemsSource="{Binding AnalogOutputs,Mode=OneWay,NotifyOnSourceUpdated=False}" VirtualizingStackPanel.IsVirtualizing="True" VirtualizingStackPanel.VirtualizationMode="Recycling"> <ListView.ItemTemplate> <DataTemplate> <Border Visibility="{Binding IsEnabled,NotifyOnSourceUpdated=False,Converter={StaticResource BooleanToVisibilityConverter}}"> <Border.Resources> <Style TargetType="Border"> <Style.Triggers> <DataTrigger Binding="{Binding ErrorCode, Mode=OneWay,NotifyOnSourceUpdated=False}" Value="{x:Static local:ErrorCodes.OutOfTolerance100}"> <Setter Property="BorderBrush" Value="Red"></Setter> <Setter Property="BorderThickness" Value="3" /> </DataTrigger> <DataTrigger Binding="{Binding ErrorCode,Mode=OneWay,NotifyOnSourceUpdated=False}" Value="{x:Static local:ErrorCodes.OutOfTolerance50}"> <Setter Property="BorderBrush" Value="Orange"></Setter> <Setter Property="BorderThickness" Value="3" /> </DataTrigger> </Style.Triggers> </Style> </Border.Resources> <Grid Visibility="{Binding IsEnabled,Converter={StaticResource BooleanToVisibilityConverter}}"> <Grid.RowDefinitions> <RowDefinition /> <RowDefinition /> </Grid.RowDefinitions> <TextBox Grid.Row="0" Text="{Binding MeasValue1, Mode=OneWay,NotifyOnSourceUpdated=False}" Style="{StaticResource BaseLabelStyle}" Visibility="{Binding IsEnabled,Converter={StaticResource BooleanToVisibilityConverter}}" ></TextBox> <TextBox Grid.Row="1" Text="{Binding MeasValue2,Mode=OneWay,NotifyOnSourceUpdated=False}" Style="{StaticResource BaseLabelStyle}" Visibility="{Binding IsEnabled,Converter={StaticResource BooleanToVisibilityConverter}}"> </TextBox> </Grid> </Border> </DataTemplate> </ListView.ItemTemplate> <ListView.ItemsPanel> <ItemsPanelTemplate> <VirtualizingStackPanel Orientation="Horizontal" Width="Auto" VirtualizationMode="Recycling"> </VirtualizingStackPanel> </ItemsPanelTemplate> </ListView.ItemsPanel> </ListView> </VirtualizingStackPanel> </DataTemplate> </ListView.ItemTemplate> </ListView>
Mein Objekte werden in einer ObservableCollection "ItemList" gespeichert. Dass Problem scheint die Methode:
MS.Win32.UnsafeNativeMethods.GetMessageW (ist angeblich für das Updaten der Dependency Properties zuständig) welche im Mainthread aufgerufen wird, zu sein.
Gibt es eine performantere Darstellung als die ListView?
Antworten
-
Hi,
nachfolgend mal eine kleine Demo, die annähernd Deinen Anforderungen entspricht. Ich habe den Takt des Datenempfangs auf 50 Millisekunden gesetzt und bei meinem Prozessor (5 Jahre alter Intel 2,67 GHz) ruckelt die Oberfläche nicht.XAML:
<Window x:Class="Window06" 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" xmlns:local="clr-namespace:WpfApplication1VB" mc:Ignorable="d" Title="Window06" Height="300" Width="400"> <Window.Resources> <local:Window06VM x:Key="vm"/> <local:Window06Converter x:Key="conv"/> <DataTemplate x:Key="DataTemplate"> <StackPanel Orientation="Horizontal" DataContext="{Binding}"> <Border BorderBrush="{Binding ErrorCode, Converter={StaticResource conv}}" BorderThickness="3" DataContext="{Binding AnalogOutputs[0]}" Visibility="{Binding IsEnabled, Converter={StaticResource conv}}"> <StackPanel Width="25" > <TextBlock Text="{Binding MeasValue1}" /> <TextBlock Text="{Binding MeasValue2}" /> </StackPanel> </Border> <Border BorderBrush="{Binding ErrorCode, Converter={StaticResource conv}}" BorderThickness="3" DataContext="{Binding AnalogOutputs[1]}" Visibility="{Binding IsEnabled, Converter={StaticResource conv}}"> <StackPanel Width="25" > <TextBlock Text="{Binding MeasValue1}" /> <TextBlock Text="{Binding MeasValue2}" /> </StackPanel> </Border> <Border BorderBrush="{Binding ErrorCode, Converter={StaticResource conv}}" BorderThickness="3" DataContext="{Binding AnalogOutputs[2]}" Visibility="{Binding IsEnabled, Converter={StaticResource conv}}"> <StackPanel Width="25" > <TextBlock Text="{Binding MeasValue1}" /> <TextBlock Text="{Binding MeasValue2}" /> </StackPanel> </Border> <Border BorderBrush="{Binding ErrorCode, Converter={StaticResource conv}}" BorderThickness="3" DataContext="{Binding AnalogOutputs[3]}" Visibility="{Binding IsEnabled, Converter={StaticResource conv}}"> <StackPanel Width="25" > <TextBlock Text="{Binding MeasValue1}" /> <TextBlock Text="{Binding MeasValue2}" /> </StackPanel> </Border> <Border BorderBrush="{Binding ErrorCode, Converter={StaticResource conv}}" BorderThickness="3" DataContext="{Binding AnalogOutputs[4]}" Visibility="{Binding IsEnabled, Converter={StaticResource conv}}"> <StackPanel Width="25" > <TextBlock Text="{Binding MeasValue1}" /> <TextBlock Text="{Binding MeasValue2}" /> </StackPanel> </Border> <Border BorderBrush="{Binding ErrorCode, Converter={StaticResource conv}}" BorderThickness="3" DataContext="{Binding AnalogOutputs[5]}" Visibility="{Binding IsEnabled, Converter={StaticResource conv}}"> <StackPanel Width="25" > <TextBlock Text="{Binding MeasValue1}" /> <TextBlock Text="{Binding MeasValue2}" /> </StackPanel> </Border> <Border BorderBrush="{Binding ErrorCode, Converter={StaticResource conv}}" BorderThickness="3" DataContext="{Binding AnalogOutputs[6]}" Visibility="{Binding IsEnabled, Converter={StaticResource conv}}"> <StackPanel Width="25" > <TextBlock Text="{Binding MeasValue1}" /> <TextBlock Text="{Binding MeasValue2}" /> </StackPanel> </Border> <Border BorderBrush="{Binding ErrorCode, Converter={StaticResource conv}}" BorderThickness="3" DataContext="{Binding AnalogOutputs[7]}" Visibility="{Binding IsEnabled, Converter={StaticResource conv}}"> <StackPanel Width="25" > <TextBlock Text="{Binding MeasValue1}" /> <TextBlock Text="{Binding MeasValue2}" /> </StackPanel> </Border> <Border BorderBrush="{Binding ErrorCode, Converter={StaticResource conv}}" BorderThickness="3" DataContext="{Binding AnalogOutputs[8]}" Visibility="{Binding IsEnabled, Converter={StaticResource conv}}"> <StackPanel Width="25" > <TextBlock Text="{Binding MeasValue1}" /> <TextBlock Text="{Binding MeasValue2}" /> </StackPanel> </Border> <Border BorderBrush="{Binding ErrorCode, Converter={StaticResource conv}}" BorderThickness="3" DataContext="{Binding AnalogOutputs[9]}" Visibility="{Binding IsEnabled, Converter={StaticResource conv}}"> <StackPanel Width="25" > <TextBlock Text="{Binding MeasValue1}" /> <TextBlock Text="{Binding MeasValue2}" /> </StackPanel> </Border> </StackPanel> </DataTemplate> </Window.Resources> <StackPanel DataContext="{Binding Source={StaticResource vm}}"> <Label Content="{Binding Info}"/> <DataGrid ItemsSource="{Binding ItemList}" ScrollViewer.VerticalScrollBarVisibility="Auto" AutoGenerateColumns="False"> <DataGrid.Columns> <DataGridTextColumn Binding="{Binding Position}"/> <DataGridTemplateColumn CellTemplate="{StaticResource DataTemplate}" Width="250"/> </DataGrid.Columns> </DataGrid> </StackPanel> </Window>
Code dazu:
Public Class Window06VM Implements INotifyPropertyChanged Public Sub New() 'Call (New Threading.Thread(AddressOf RefreshThread)).Start() If Not DesignerProperties.GetIsInDesignMode(New DependencyObject) Then Call (New Threading.Thread(AddressOf LoadData)).Start() End If End Sub Private Sub RefreshThread() Do Threading.Thread.Sleep(1000) Application.Current.Dispatcher.Invoke(Sub() cvs.View.Refresh() End Sub) Loop End Sub Private zeit1 As Date = Now Private Sub LoadData() Dim rnd As New Random Do Threading.Thread.Sleep(50) Dim zeit2 = Now If col.Count < 20 Then Application.Current.Dispatcher.Invoke(Sub() Dim item1 As New Window06Data1 With {.Position = rnd.Next(1, 100).ToString} col.Add(item1) For index = 1 To 10 item1.AnalogOutputs.Add(New Window06Data2()) Next End Sub) Else Dim i = rnd.Next(0, col.Count) col(i).Position = rnd.Next(1, 100).ToString Dim k = rnd.Next(0, col(i).AnalogOutputs.Count) Dim spalte As Window06Data2 = col(i).AnalogOutputs(k) spalte.MeasValue1 = rnd.Next(1, 100).ToString spalte.MeasValue2 = rnd.Next(1, 100).ToString spalte.ErrorCode = rnd.Next(1, 101) spalte.IsEnabled = rnd.NextDouble > 0.2 Info = String.Format("Zeitintervall {0}", (zeit2 - zeit1).Milliseconds) zeit1 = zeit2 End If Loop End Sub Private col As New ObservableCollection(Of Window06Data1) Private cvs As CollectionViewSource Public ReadOnly Property ItemList As ICollectionView Get If cvs Is Nothing Then cvs = New CollectionViewSource cvs.Source = col End If Return cvs.View End Get End Property Private _info As String Public Property Info As String Get Return Me._info End Get Set(value As String) If Me._info <> value Then Me._info = value OnPropertyChanged() End If End Set End Property #Region " PropertyChanged" Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged Private Sub OnPropertyChanged(<CallerMemberName> Optional propname As String = "") RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propname)) End Sub #End Region End Class Public Class Window06Data1 Implements INotifyPropertyChanged Private _position As String Public Property Position() As String Get Return Me._position End Get Set(value As String) Me._position = value OnPropertyChanged() End Set End Property Public Property AnalogOutputs As List(Of Window06Data2) = New List(Of Window06Data2) #Region " PropertyChanged" Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged Private Sub OnPropertyChanged(<CallerMemberName> Optional propname As String = "") RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propname)) End Sub #End Region End Class Public Class Window06Data2 Implements INotifyPropertyChanged Private _measValue1 As String Public Property MeasValue1() As String Get Return Me._measValue1 End Get Set(value As String) If Me._measValue1 <> value Then Me._measValue1 = value OnPropertyChanged() End If End Set End Property Private _measValue2 As String Public Property MeasValue2() As String Get Return Me._measValue2 End Get Set(value As String) If Me._measValue2 <> value Then Me._measValue2 = value OnPropertyChanged() End If End Set End Property Private _isEnabled As Boolean Public Property IsEnabled() As Boolean Get Return Me._isEnabled End Get Set(value As Boolean) If Me._isEnabled <> value Then Me._isEnabled = value OnPropertyChanged() End If End Set End Property Private _errorCode As Integer Public Property ErrorCode() As Integer Get Return Me._errorCode End Get Set(value As Integer) If Me._errorCode <> value Then Me._errorCode = value OnPropertyChanged() End If End Set End Property #Region " PropertyChanged" Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged Private Sub OnPropertyChanged(<CallerMemberName> Optional propname As String = "") RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propname)) End Sub #End Region End Class Public Class Window06Converter Implements IValueConverter Public Function Convert(value As Object, targetType As Type, parameter As Object, culture As CultureInfo) As Object Implements IValueConverter.Convert If targetType Is GetType(Visibility) AndAlso value.GetType Is GetType(Boolean) Then Return If(CType(value, Boolean), Visibility.Visible, Visibility.Hidden) End If If targetType Is GetType(Brush) AndAlso value.GetType Is GetType(Integer) Then Select Case CType(value, Integer) Case < 50 Return Brushes.White Case < 100 Return Brushes.Orange Case 100 Return Brushes.Red End Select End If Return Nothing End Function Public Function ConvertBack(value As Object, targetType As Type, parameter As Object, culture As CultureInfo) As Object Implements IValueConverter.ConvertBack Throw New NotImplementedException() End Function End Class
--
Viele Grüsse
Peter Fleischer (MVP, Partner)
Meine Homepage mit Tipps und Tricks
Kommas richtig setzen!
Schüler sagen, Lehrer haben es gut.
Schüler, sagen Lehrer, haben es gut- Als Antwort markiert Hartl_D Montag, 16. November 2015 13:11
-
Hallo Hartl_D,
Ein Paar Inputs von meiner Seite:
Ab .NET 4.5 kannst Du bei der WPF einen Delay eintragen. D.h. du kannst sagen, dass das Databinding nur z.B. alle 500ms ausgelöst werden soll. Damit kannst Du den Dispatcher entlasten:
<TextBlock Text="{Binding Name, Delay=500}"/>
http://www.jonathanantoine.com/2011/09/21/wpf-4-5-part-4-the-new-bindings-delay-property/
Es gibt starke Performance Unterschiede zwischen verschiedenen Grids. Probier doch mal die Grids von Telerik, DevExpress, etc aus. Die sind oft schon out-of-the-box viel besser als das "normale" DataGrid.
Schau Dir mal diese Anwendung an:
https://wpfrealtime.codeplex.com/
Der Artikel dazu geht voll in die Details und am Schluss kommt ein "fast Echtzeit" Dashboard in WPF raus. Ich glaube, das ist genau das, was Du brauchst:
http://www.codeproject.com/Articles/326641/WPF-MVVM-Real-Time-Trading-Application
Die .NET Reactive Extensions (.NET Rx) könnten Dir auch weiterhelfen:
http://stackoverflow.com/questions/19452935/wpf-realtime-chart-application-architecture
PS: Please mark as answer if helpful. Thanks!
Blog: http://www.manuelmeyer.net
Twitter: https://twitter.com/manumeyer1- Als Antwort markiert Hartl_D Mittwoch, 25. November 2015 13:37
Alle Antworten
-
Hallo,
eine GUI ist immer langsam. Und wenn du 2 bis 10mal in der Sekunde ein Update machen willst ist das natürlich heftig. Ich würde dazu tendieren jede Sekunde nur einmal alle Elemente zu übertragen die man wärend dessen zwischen gespeichert hat.
Alternative wäre vielleicht ein Blick auf die BeginInvoke-Methode einen Blick wert, sofern du Invoke benutzt. Die von mir genannte blockiert den UI Thread nicht sofort sondern kann es auch verzögert tun.
Viel performanter durch ein anderes Control dürfte es aber nicht werden.
PS: GetMessageW ist eine native Methode, welche durchaus beim aktualisieren einer DP aufgerufen werden kann, hängt aber nicht zwingend direkt damit zusammen.
Tom Lambert - .NET (C#) MVP
Wozu Antworten markieren und für Beiträge abstimmen? Klicke hier.
Nützliche Links: .NET Quellcode | C# ↔ VB.NET Konverter | Account bestätigen (Verify Your Account)
Ich: Webseite | Code Beispiele | Facebook | Twitter | Snippets -
Hi,
wenn die Anzahl der Elemente in der ItemList konstant ist, dann wäre auch ein Grid Denbar, welches in jeder Zelle konkrete Eigenschaften anzeigt. Das dürfte schneller sein als ein ListView.--
Viele Grüsse
Peter Fleischer (MVP, Partner)
Meine Homepage mit Tipps und Tricks
Kommas richtig setzen!
Schüler sagen, Lehrer haben es gut.
Schüler, sagen Lehrer, haben es gut -
Vielen Dank für eure Antworten,
Das Problem ist, dass dieses Programm auf einer Maschine zum Einsatz kommt, wo der Benutzer sofort jede Abweichung visualisiert bekommen soll. Habs schon mal probiert mit 1s probiert, leider dasselbe Ergebnis. Das andere Problem ist ja auch noch, dass das ganze auf einem PC mit Intel DualCore und Standard Intel HD Grafikkarte laufen soll, also von vornerein nicht die besten Vorraussetzungen,....
Das grundsätzliche Problem ist eigentlich, dass ohne Exception auf einmal der "Standard" Microsoft UDP-Client kein Receive Event mehr feuert...
Die ItemList wächst auf 20 Elemente und danach wird das letzte Element gelöscht und das neue am Anfang eingefügt.... Es wird eigentlich nicht viel dargestellt, nur upgedatet.
-
Hallo Hartl_D,
ich weiß nicht, ob dieser Vorschlag genau ist, was du suchst, aber kannst ihn ja mal wenigstens im Hinterkopf behalten:
Wenn du das erhalten der Daten in einem anderen (Hintergrund-) Thread laufen lässt und in deinem Haupt-Thread die UI hast, könnten diese unabhängig arbeiten.
Ferner bräuchtest du noch einen geteilten speicher (static volatile ?). Wenn die Verbindung nun neue Daten hat, gibt diese die Daten an den Speicher weiter.
Dein Haupt-Thread indes fragt immer wieder, ob es Änderungen gab (nimm einfach einen Zähler). Sofern die Daten aktualisiert wurden, kann dieser Thread sich diese Daten nehmen und aufbereiten und anzeigen.
Der Vorteil wäre, dass sich beide Threads nicht blocken könnten. Bzw. es währe dann die Aufgabe des Systems zu sorgen, dass beide Threads aktiv bleiben.
© 2015 Thomas Roskop
Germany // Deutschland -
Hi,
ich habe mal eine kleine Demo getestet mit Thomas' Anregungen. Das ListView-Steuerelement verbraucht enorm viel Zeit bei der Aktualisierung, insbesondere, weil im DataTemplate wieder ein ListView eingebaut ist. Wenn aller 100 msec neue Daten kommen und jede Sekunde die Oberfläche mit dem ListView aktualisiert wird, dann ruckelt es auch bei meinem recht flotten Rechner. Dabei arbeiten neben dem Haupt-Thread der Datenempfang in einem separaten Thread und die Aktualisierung wird auch in einem separaten Thread angestoßen.Man kann das Flackern nur verhindern, wenn man auf eine Neuformatierung der Oberfläche verzichtet und nur den anzuzeigenden Inhalt aktualisiert. Das bedeutet, dass die Anzeige einmalig vollständig aufgebaut wird und dann bezüglich Größen, Lagen und Abständen unverändert bleibt.
--
Viele Grüsse
Peter Fleischer (MVP, Partner)
Meine Homepage mit Tipps und Tricks
Kommas richtig setzen!
Schüler sagen, Lehrer haben es gut.
Schüler, sagen Lehrer, haben es gut
- Bearbeitet Peter Fleischer Sonntag, 15. November 2015 08:13 Ergänzung
-
Hi,
Du strukturierst die Oberfläche (alle Container, Steuerelemente) mit festen Größen (Höhen, Breiten, Abständen) und aktualisierst nur die Anzeigen direkt in den Steuerelementen (Label, TextBlock). Das läuft bei mir recht flüssig. Diese Aktualisierung kann man auch noch verzögern, indem nicht alle Änderungen sofort aktualisiert werden, sondern nur regelmäßig z.B. nach 1 Sekunde. Das bedeutet aber etwas mehr Aufwand im ViewModel.--
Viele Grüsse
Peter Fleischer (MVP, Partner)
Meine Homepage mit Tipps und Tricks
Kommas richtig setzen!
Schüler sagen, Lehrer haben es gut.
Schüler, sagen Lehrer, haben es gut -
Hi,
nachfolgend mal eine kleine Demo, die annähernd Deinen Anforderungen entspricht. Ich habe den Takt des Datenempfangs auf 50 Millisekunden gesetzt und bei meinem Prozessor (5 Jahre alter Intel 2,67 GHz) ruckelt die Oberfläche nicht.XAML:
<Window x:Class="Window06" 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" xmlns:local="clr-namespace:WpfApplication1VB" mc:Ignorable="d" Title="Window06" Height="300" Width="400"> <Window.Resources> <local:Window06VM x:Key="vm"/> <local:Window06Converter x:Key="conv"/> <DataTemplate x:Key="DataTemplate"> <StackPanel Orientation="Horizontal" DataContext="{Binding}"> <Border BorderBrush="{Binding ErrorCode, Converter={StaticResource conv}}" BorderThickness="3" DataContext="{Binding AnalogOutputs[0]}" Visibility="{Binding IsEnabled, Converter={StaticResource conv}}"> <StackPanel Width="25" > <TextBlock Text="{Binding MeasValue1}" /> <TextBlock Text="{Binding MeasValue2}" /> </StackPanel> </Border> <Border BorderBrush="{Binding ErrorCode, Converter={StaticResource conv}}" BorderThickness="3" DataContext="{Binding AnalogOutputs[1]}" Visibility="{Binding IsEnabled, Converter={StaticResource conv}}"> <StackPanel Width="25" > <TextBlock Text="{Binding MeasValue1}" /> <TextBlock Text="{Binding MeasValue2}" /> </StackPanel> </Border> <Border BorderBrush="{Binding ErrorCode, Converter={StaticResource conv}}" BorderThickness="3" DataContext="{Binding AnalogOutputs[2]}" Visibility="{Binding IsEnabled, Converter={StaticResource conv}}"> <StackPanel Width="25" > <TextBlock Text="{Binding MeasValue1}" /> <TextBlock Text="{Binding MeasValue2}" /> </StackPanel> </Border> <Border BorderBrush="{Binding ErrorCode, Converter={StaticResource conv}}" BorderThickness="3" DataContext="{Binding AnalogOutputs[3]}" Visibility="{Binding IsEnabled, Converter={StaticResource conv}}"> <StackPanel Width="25" > <TextBlock Text="{Binding MeasValue1}" /> <TextBlock Text="{Binding MeasValue2}" /> </StackPanel> </Border> <Border BorderBrush="{Binding ErrorCode, Converter={StaticResource conv}}" BorderThickness="3" DataContext="{Binding AnalogOutputs[4]}" Visibility="{Binding IsEnabled, Converter={StaticResource conv}}"> <StackPanel Width="25" > <TextBlock Text="{Binding MeasValue1}" /> <TextBlock Text="{Binding MeasValue2}" /> </StackPanel> </Border> <Border BorderBrush="{Binding ErrorCode, Converter={StaticResource conv}}" BorderThickness="3" DataContext="{Binding AnalogOutputs[5]}" Visibility="{Binding IsEnabled, Converter={StaticResource conv}}"> <StackPanel Width="25" > <TextBlock Text="{Binding MeasValue1}" /> <TextBlock Text="{Binding MeasValue2}" /> </StackPanel> </Border> <Border BorderBrush="{Binding ErrorCode, Converter={StaticResource conv}}" BorderThickness="3" DataContext="{Binding AnalogOutputs[6]}" Visibility="{Binding IsEnabled, Converter={StaticResource conv}}"> <StackPanel Width="25" > <TextBlock Text="{Binding MeasValue1}" /> <TextBlock Text="{Binding MeasValue2}" /> </StackPanel> </Border> <Border BorderBrush="{Binding ErrorCode, Converter={StaticResource conv}}" BorderThickness="3" DataContext="{Binding AnalogOutputs[7]}" Visibility="{Binding IsEnabled, Converter={StaticResource conv}}"> <StackPanel Width="25" > <TextBlock Text="{Binding MeasValue1}" /> <TextBlock Text="{Binding MeasValue2}" /> </StackPanel> </Border> <Border BorderBrush="{Binding ErrorCode, Converter={StaticResource conv}}" BorderThickness="3" DataContext="{Binding AnalogOutputs[8]}" Visibility="{Binding IsEnabled, Converter={StaticResource conv}}"> <StackPanel Width="25" > <TextBlock Text="{Binding MeasValue1}" /> <TextBlock Text="{Binding MeasValue2}" /> </StackPanel> </Border> <Border BorderBrush="{Binding ErrorCode, Converter={StaticResource conv}}" BorderThickness="3" DataContext="{Binding AnalogOutputs[9]}" Visibility="{Binding IsEnabled, Converter={StaticResource conv}}"> <StackPanel Width="25" > <TextBlock Text="{Binding MeasValue1}" /> <TextBlock Text="{Binding MeasValue2}" /> </StackPanel> </Border> </StackPanel> </DataTemplate> </Window.Resources> <StackPanel DataContext="{Binding Source={StaticResource vm}}"> <Label Content="{Binding Info}"/> <DataGrid ItemsSource="{Binding ItemList}" ScrollViewer.VerticalScrollBarVisibility="Auto" AutoGenerateColumns="False"> <DataGrid.Columns> <DataGridTextColumn Binding="{Binding Position}"/> <DataGridTemplateColumn CellTemplate="{StaticResource DataTemplate}" Width="250"/> </DataGrid.Columns> </DataGrid> </StackPanel> </Window>
Code dazu:
Public Class Window06VM Implements INotifyPropertyChanged Public Sub New() 'Call (New Threading.Thread(AddressOf RefreshThread)).Start() If Not DesignerProperties.GetIsInDesignMode(New DependencyObject) Then Call (New Threading.Thread(AddressOf LoadData)).Start() End If End Sub Private Sub RefreshThread() Do Threading.Thread.Sleep(1000) Application.Current.Dispatcher.Invoke(Sub() cvs.View.Refresh() End Sub) Loop End Sub Private zeit1 As Date = Now Private Sub LoadData() Dim rnd As New Random Do Threading.Thread.Sleep(50) Dim zeit2 = Now If col.Count < 20 Then Application.Current.Dispatcher.Invoke(Sub() Dim item1 As New Window06Data1 With {.Position = rnd.Next(1, 100).ToString} col.Add(item1) For index = 1 To 10 item1.AnalogOutputs.Add(New Window06Data2()) Next End Sub) Else Dim i = rnd.Next(0, col.Count) col(i).Position = rnd.Next(1, 100).ToString Dim k = rnd.Next(0, col(i).AnalogOutputs.Count) Dim spalte As Window06Data2 = col(i).AnalogOutputs(k) spalte.MeasValue1 = rnd.Next(1, 100).ToString spalte.MeasValue2 = rnd.Next(1, 100).ToString spalte.ErrorCode = rnd.Next(1, 101) spalte.IsEnabled = rnd.NextDouble > 0.2 Info = String.Format("Zeitintervall {0}", (zeit2 - zeit1).Milliseconds) zeit1 = zeit2 End If Loop End Sub Private col As New ObservableCollection(Of Window06Data1) Private cvs As CollectionViewSource Public ReadOnly Property ItemList As ICollectionView Get If cvs Is Nothing Then cvs = New CollectionViewSource cvs.Source = col End If Return cvs.View End Get End Property Private _info As String Public Property Info As String Get Return Me._info End Get Set(value As String) If Me._info <> value Then Me._info = value OnPropertyChanged() End If End Set End Property #Region " PropertyChanged" Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged Private Sub OnPropertyChanged(<CallerMemberName> Optional propname As String = "") RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propname)) End Sub #End Region End Class Public Class Window06Data1 Implements INotifyPropertyChanged Private _position As String Public Property Position() As String Get Return Me._position End Get Set(value As String) Me._position = value OnPropertyChanged() End Set End Property Public Property AnalogOutputs As List(Of Window06Data2) = New List(Of Window06Data2) #Region " PropertyChanged" Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged Private Sub OnPropertyChanged(<CallerMemberName> Optional propname As String = "") RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propname)) End Sub #End Region End Class Public Class Window06Data2 Implements INotifyPropertyChanged Private _measValue1 As String Public Property MeasValue1() As String Get Return Me._measValue1 End Get Set(value As String) If Me._measValue1 <> value Then Me._measValue1 = value OnPropertyChanged() End If End Set End Property Private _measValue2 As String Public Property MeasValue2() As String Get Return Me._measValue2 End Get Set(value As String) If Me._measValue2 <> value Then Me._measValue2 = value OnPropertyChanged() End If End Set End Property Private _isEnabled As Boolean Public Property IsEnabled() As Boolean Get Return Me._isEnabled End Get Set(value As Boolean) If Me._isEnabled <> value Then Me._isEnabled = value OnPropertyChanged() End If End Set End Property Private _errorCode As Integer Public Property ErrorCode() As Integer Get Return Me._errorCode End Get Set(value As Integer) If Me._errorCode <> value Then Me._errorCode = value OnPropertyChanged() End If End Set End Property #Region " PropertyChanged" Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged Private Sub OnPropertyChanged(<CallerMemberName> Optional propname As String = "") RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propname)) End Sub #End Region End Class Public Class Window06Converter Implements IValueConverter Public Function Convert(value As Object, targetType As Type, parameter As Object, culture As CultureInfo) As Object Implements IValueConverter.Convert If targetType Is GetType(Visibility) AndAlso value.GetType Is GetType(Boolean) Then Return If(CType(value, Boolean), Visibility.Visible, Visibility.Hidden) End If If targetType Is GetType(Brush) AndAlso value.GetType Is GetType(Integer) Then Select Case CType(value, Integer) Case < 50 Return Brushes.White Case < 100 Return Brushes.Orange Case 100 Return Brushes.Red End Select End If Return Nothing End Function Public Function ConvertBack(value As Object, targetType As Type, parameter As Object, culture As CultureInfo) As Object Implements IValueConverter.ConvertBack Throw New NotImplementedException() End Function End Class
--
Viele Grüsse
Peter Fleischer (MVP, Partner)
Meine Homepage mit Tipps und Tricks
Kommas richtig setzen!
Schüler sagen, Lehrer haben es gut.
Schüler, sagen Lehrer, haben es gut- Als Antwort markiert Hartl_D Montag, 16. November 2015 13:11
-
Vielen Dank,
hab jetzt zwar eine flüssige Darstellung und sehr geringe CPU Auslastung auch auf meinem Dual Core,
aber ab und zu verabschiedet sich der UDP Client (Keine Exception,etc....). Bzw. die ReceiveCallBack Funktion wird nicht mehr aufgerufen !? Hat jemand Erfahrung mit einem ähnlichen Fehler?
private void ReceiveCallback(IAsyncResult ar) { try { if (ar == currentAsyncResult) { MulticastListener receiver = (MulticastListener)(ar.AsyncState); UdpClient udpClient = receiver.UdpClient; IPEndPoint ipEndPoint = receiver.LocalIPEndPoint; if (udpClient == null || ipEndPoint == null) { log.FatalFormat("udpClient = {0}, ipEndPoint = {1}", udpClient, ipEndPoint); } else { byte[] receiveBytes = udpClient.EndReceive(ar, ref ipEndPoint); EventHandler<ReceivedEventArgs> handler = DataReceived; if (handler != null) { handler(this, new ReceivedEventArgs(receiveBytes)); } } } lock (syncRoot) { if (udpClient != null && udpClient.Client != null && udpClient.Client.IsBound) { BeginReceive(); } } } catch (ObjectDisposedException) { // expected exception fired when we close - swallow it up } catch (ArgumentException) { // expected exception fired when we close - swallow it up } catch (Exception ex) { log.FatalFormat("ReceiveCallback exception: {0}", ex); throw; } } private void BeginReceive() { AsyncCallback receiveCallback = new AsyncCallback(ReceiveCallback); currentAsyncResult = UdpClient.BeginReceive(receiveCallback, this); }
-
Hi,
mit dem Fragment kann ich nichts anfangen. Es sieht recht chaotisch aus und weit weg von typischer Kapselung. Nicht zu erkennen ist die Empfangs-Schleife.--
Viele Grüsse
Peter Fleischer (MVP, Partner)
Meine Homepage mit Tipps und Tricks
Kommas richtig setzen!
Schüler sagen, Lehrer haben es gut.
Schüler, sagen Lehrer, haben es gut -
Hallo Hartl_D,
Ein Paar Inputs von meiner Seite:
Ab .NET 4.5 kannst Du bei der WPF einen Delay eintragen. D.h. du kannst sagen, dass das Databinding nur z.B. alle 500ms ausgelöst werden soll. Damit kannst Du den Dispatcher entlasten:
<TextBlock Text="{Binding Name, Delay=500}"/>
http://www.jonathanantoine.com/2011/09/21/wpf-4-5-part-4-the-new-bindings-delay-property/
Es gibt starke Performance Unterschiede zwischen verschiedenen Grids. Probier doch mal die Grids von Telerik, DevExpress, etc aus. Die sind oft schon out-of-the-box viel besser als das "normale" DataGrid.
Schau Dir mal diese Anwendung an:
https://wpfrealtime.codeplex.com/
Der Artikel dazu geht voll in die Details und am Schluss kommt ein "fast Echtzeit" Dashboard in WPF raus. Ich glaube, das ist genau das, was Du brauchst:
http://www.codeproject.com/Articles/326641/WPF-MVVM-Real-Time-Trading-Application
Die .NET Reactive Extensions (.NET Rx) könnten Dir auch weiterhelfen:
http://stackoverflow.com/questions/19452935/wpf-realtime-chart-application-architecture
PS: Please mark as answer if helpful. Thanks!
Blog: http://www.manuelmeyer.net
Twitter: https://twitter.com/manumeyer1- Als Antwort markiert Hartl_D Mittwoch, 25. November 2015 13:37