none
WPF TextBox Textausgabe via Code

    Frage

  • Hallo,

    ich arbeite momentan an einem Projekt das über die Klasse Net.NetworkInformation.Ping  eben genau das macht: einen Ping absetzen. Das funktioniert auch und ist auch nicht das Problem. Die Ausgaben sollen in einer Textbox ausgegeben werden der über den Code Behind die Mitteilungen übergeben werden. Über die Methode ScrollToEnd wird die Ausgabe immer an das Ende gescrollt.

    Der Aufruf sieht wie folgt aus:

      Private Sub BtnStartPingClick()
        If RadSinglePing.IsChecked = True Then
          TxtAnswer.Text = TxtAnswer.Text & " ---- Abfrage gestartet" & vbCrLf
          TxtAnswer.ScrollToEnd()
          StartPing(TxtIpAdress.Text)
          TxtAnswer.Text = TxtAnswer.Text & " ---- Abfrage beendet" & vbCrLf & vbCrLf
          TxtAnswer.ScrollToEnd()
        Else
          Dim SortedCounter(1) As Integer
          Dim Hostname As New Text.StringBuilder
          SortedCounter = SortAdresses(Tools.ConvertValue(TxtStartAdress.Text), Tools.ConvertValue(TxtEndAdress.Text))
          TxtAnswer.Text = TxtAnswer.Text & " ---- Abfrage gestartet" & vbCrLf
          TxtAnswer.ScrollToEnd()
          For Counter As Integer = SortedCounter(0) To SortedCounter(1)
            Hostname.Clear()
            Hostname.Append(TxtBaseAdress.Text.Trim)
            Hostname.Append(".")
            Hostname.Append(Counter.ToString)
            StartPing(Hostname.ToString)
          Next
          TxtAnswer.Text = TxtAnswer.Text & " ---- Abfrage beendet" & vbCrLf & vbCrLf
          TxtAnswer.ScrollToEnd()
        End If
      End Sub

    In der Methode "StartPing(.....)" wird die Abfrage aufgerufen und der Cursor auf "Wait" umgeschaltet (z.B. Timeout etc.)

    Ich bleibe jetzt mal bei dem ersten IF - Zweig:

    Ich hätte jetzt folgendes erwartet:

    • Die Ausgabe "Abfrage gestartet" wird ausgegeben
    • Die Abfrage beginnt (Cursor Wait)
    • Die Ausgabe "Abfrage beendet" wird ausgegeben

    Was aber geschieht ist Folgendes:

    • Cursor Wait
    • Cursor Normal
    • Ausgabe ALLER Meldungen

    Vielleicht kann mir jemand erklären, warum das so ist?

    Besten Dank für die Hilfe,

    fro

    Mittwoch, 11. Oktober 2017 18:16

Antworten

  • Hi,
    die Erklärung für das Verhalten ist die fehlende CPU-Zeit im UI-Thread. Du führst alle Arbeiten im Thread aus, der auch für die Aktualisierung der Anzeige CPU-Zeit benötigt. Und diese Zeit steht nicht zur Verfügung. Abhilfe schafft die Auslagerung der Ping-Abfrage in einen separaten Thread, z.B. mit dem BackGroundWorker, Delegate/Invoke, Threading.Thread oder Task.

    --
    Viele Grüsse
    Peter Fleischer (ehem. MVP)
    Meine Homepage mit Tipps und Tricks


    • Als Antwort markiert frorog Donnerstag, 12. Oktober 2017 15:10
    • Bearbeitet Peter Fleischer Freitag, 13. Oktober 2017 08:55 Grammatikfehler
    Donnerstag, 12. Oktober 2017 07:23

Alle Antworten

  • Hi,
    die Erklärung für das Verhalten ist die fehlende CPU-Zeit im UI-Thread. Du führst alle Arbeiten im Thread aus, der auch für die Aktualisierung der Anzeige CPU-Zeit benötigt. Und diese Zeit steht nicht zur Verfügung. Abhilfe schafft die Auslagerung der Ping-Abfrage in einen separaten Thread, z.B. mit dem BackGroundWorker, Delegate/Invoke, Threading.Thread oder Task.

    --
    Viele Grüsse
    Peter Fleischer (ehem. MVP)
    Meine Homepage mit Tipps und Tricks


    • Als Antwort markiert frorog Donnerstag, 12. Oktober 2017 15:10
    • Bearbeitet Peter Fleischer Freitag, 13. Oktober 2017 08:55 Grammatikfehler
    Donnerstag, 12. Oktober 2017 07:23
  • Hallo,

    besten Dank für die Antwort und die Erklärung. Meine Vermutung ging schon in die Richtung. Ich werde dann die SendAsync Methode der Ping Klasse verwenden (auch wenn ich das erst aus verschiedenen Gründen nicht wollte).

    Einen schönen Abend noch,

    fro

    Donnerstag, 12. Oktober 2017 15:13
  • Hi,
    zu berücksichtigen ist aber, dass ein unpassendes await auch die Aktualisierung der Oberfläche blockieren kann. Hier mal ein Demo, wie man das asynchron mit MVVM lösen kann:

    <Window x:Class="Window16"
            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:WpfApp1"
            mc:Ignorable="d"
            Title="Window16" Height="300" Width="300">
      <Window.Resources>
        <local:Window16VM x:Key="vm"/>
        <Style TargetType="Button">
          <Setter Property="Margin" Value="5"/>
        </Style>
        <Style TargetType="CheckBox">
          <Setter Property="Margin" Value="5"/>
        </Style>
        <Style TargetType="ListBox">
          <Setter Property="Margin" Value="5"/>
        </Style>
      </Window.Resources>
      <Grid DataContext="{StaticResource vm}">
        <Grid.RowDefinitions>
          <RowDefinition Height="Auto"/>
          <RowDefinition Height="Auto"/>
          <RowDefinition/>
        </Grid.RowDefinitions>
        <Button Content="Start Ping" Command="{Binding Cmd}"/>
        <CheckBox Grid.Row="1" Content="für Test der Bedienbarkeit"/>
        <ListBox Grid.Row="2" ItemsSource="{Binding Anzeige}"/>
      </Grid>
    </Window>
    

    Und der ViewModel dazu (incl. Model-Funktionalität)

    Imports System.ComponentModel
    Imports System.Runtime.CompilerServices
    Imports System.Net.NetworkInformation
    Imports System.Collections.ObjectModel
    Imports System.Net
    
    Public Class Window16VM
      Implements INotifyPropertyChanged
    
      ''' <summary>
      ''' Konstruktor 
      ''' </summary>
      Public Sub New()
        ' Command-Bindung vorbereiten
        Cmd = New RelayCommand(AddressOf CmdExec, AddressOf CanCmdExec)
      End Sub
    
      ''' <summary>
      ''' Liste der anzuzeigenden Textzeilen
      ''' </summary>
      ''' <returns></returns>
      Public Property Anzeige As New ObservableCollection(Of String)
    
      ''' <summary>
      ''' In Arbeit-Zustand merken
      ''' </summary>
      Private _isWorking As Boolean = False
    
      ''' <summary>
      ''' In Arbeit-Eigenschaft verwalten
      ''' </summary>
      ''' <returns></returns>
      Private Property Isworking As Boolean
        Get
          Return Me._isWorking
        End Get
        Set(value As Boolean)
          If Me._isWorking <> value Then
            Me._isWorking = value
            If value Then
              Anzeige.Insert(0, " ---- Abfrage gestartet")
            Else
              Anzeige.Insert(0, " ---- Abfrage beendet")
            End If
            OnPropertyChanged("Cmd")
          End If
        End Set
      End Property
    
    #Region " Ping abarbeiten"
    
      ''' <summary>
      ''' Ping Instanz
      ''' </summary>
      Private WithEvents p As New Ping
    
      ''' <summary>
      ''' Ping-Routine aufrufen
      ''' </summary>
      Private Sub BtnStartPingClick()
        Startping("192.168.0.1")
      End Sub
    
      ''' <summary>
      ''' Ping asynchron starten
      ''' </summary>
      ''' <param name="adr"></param>
      Private Sub Startping(adr As String)
        Isworking = True
        Dim byteAddress = (From itm In adr.Split("."c) Select CType(itm, Byte)).ToArray
        p.SendPingAsync(New IPAddress(byteAddress))
      End Sub
    
      ''' <summary>
      ''' asynchrones Ping-Ende
      ''' </summary>
      ''' <param name="sender"></param>
      ''' <param name="e"></param>
      Private Sub PingCompleted(sender As Object, e As PingCompletedEventArgs) Handles p.PingCompleted
        Anzeige.Insert(0, e.Reply.Status.ToString)
        Isworking = False
      End Sub
    #End Region
    
    #Region " Command Bindung"
      ''' <summary>
      ''' Eigenschaft für Command-Bindung
      ''' </summary>
      ''' <returns></returns>
      Public ReadOnly Property Cmd As ICommand
    
      ''' <summary>
      ''' Gebundenes Command ausführen
      ''' </summary>
      ''' <param name="obj">CommandParameter</param>
      Private Sub CmdExec(obj As Object)
        BtnStartPingClick()
      End Sub
    
      ''' <summary>
      ''' Command-Befehlsschaltfläche enable/disable setzen
      ''' </summary>
      ''' <param name="obj"></param>
      ''' <returns></returns>
      Private Function CanCmdExec(obj As Object) As Boolean
        Return Not Isworking
      End Function
    #End Region
    
    #Region " PropertyChanged"
      ''' <summary>
      ''' INotifyPropertyChanged für die Anzeige von Veränderungen, damit sich die Anzeige aktualisiert
      ''' </summary>
      Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
      Friend Sub OnPropertyChanged(<CallerMemberName> Optional propName As String = "")
        RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propName))
      End Sub
    #End Region
    
    End Class


    --
    Viele Grüsse
    Peter Fleischer (ehem. MVP)
    Meine Homepage mit Tipps und Tricks

    Freitag, 13. Oktober 2017 10:42
  • Hallo,

    besten Dank für die Mühe mit dem Beispiel! Bin aber jetzt schon dabei auf den BackGroundWorker umzustellen.

    Ein schönes Wochenende noch,

    fro

    Samstag, 14. Oktober 2017 14:34