IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C# Discussion :

Flood de log dans TextBox


Sujet :

C#

  1. #1
    Membre habitué
    Avatar de yvesall
    Profil pro
    Développeur de jeux vidéo
    Inscrit en
    Novembre 2006
    Messages
    208
    Détails du profil
    Informations personnelles :
    Âge : 37
    Localisation : France

    Informations professionnelles :
    Activité : Développeur de jeux vidéo
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Novembre 2006
    Messages : 208
    Points : 127
    Points
    127
    Par défaut Flood de log dans TextBox
    Salut !

    Dans beaucoup de mes programmes j’intègre une fenêtre de log dans laquelle je prompt un peu tout ce que je souhaite (Info d’opération, warning, output standard et/ou erreur de certain process que je lance, etc.)

    J'utilise tout le temps un control WPF Textbox, en readonly et multiline.

    Dans 99% des cas le log fonctionne parfaitement (auto-scroll actif ou non, l'auto-scroll exploitant le ScrollViewer de la TextBox)

    Le 1% des cas restants sont principalement la saturation de log.

    En effet quand j’exécute un process externe et que celui-ci me retourne massivement du texte en stdout (boucle infini, prompt continu, ...), mon application C# freeze le temps que le log se calme.

    Me disant que mon implémentation du système de log est mauvaise, j'ai créé une solution avec 3 façon différentes de logger du texte :



    dont voici les sources :
    Le XAML
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    <Window x:Class="LogFloodTest.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="MainWindow" Height="436" Width="880"
            DataContext="{Binding RelativeSource={RelativeSource Self}}">
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="222*" />
                <ColumnDefinition Width="222*" />
                <ColumnDefinition Width="222*" />
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="340*" />
                <RowDefinition Height="76" />
            </Grid.RowDefinitions>
            <TextBox Margin="1" Name="textBox1" AcceptsReturn="True" AcceptsTab="True" VerticalScrollBarVisibility="Auto" />
            <TextBox Grid.Column="1" Margin="1" Name="textBox2" AcceptsReturn="True" AcceptsTab="True" Text="{Binding Path=LogText2, Mode=OneWay}" VerticalScrollBarVisibility="Auto" TextChanged="textBox2_TextChanged" />
            <TextBox Grid.Column="2" Margin="1" Name="textBox3" AcceptsReturn="True" AcceptsTab="True" Text="{Binding Path=LogText3, Mode=OneWay}" TextChanged="textBox3_TextChanged" VerticalScrollBarVisibility="Auto" />
            <GroupBox Grid.Row="1" Header="Full CodeBehind" Margin="0,-2,0,0" Name="groupBox1">
                <Grid>
                    <Button Content="Run Flood" Height="23" HorizontalAlignment="Left" Margin="6,0,0,15" Name="button1" VerticalAlignment="Bottom" Width="75" Click="button1_Click" />
                    <CheckBox Content="Auto-Scroll" Height="16" HorizontalAlignment="Left" Margin="87,17,0,0" Name="AutoScroll_checkBox1" VerticalAlignment="Top" IsChecked="{Binding Path=ScrollCheckBox1, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
                    <CheckBox Content="Use TextBox.Append Method" Height="16" HorizontalAlignment="Left" Margin="87,3,0,0" Name="Append_checkBox1" VerticalAlignment="Top" IsChecked="{Binding Path=AppendCheckBox1, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
                    <CheckBox Content="Use Invoke (not BeginInvoke)" Height="16" HorizontalAlignment="Left" IsChecked="{Binding Path=InvokeCheckBox1, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Margin="87,38,0,0" Name="checkBox1" VerticalAlignment="Top" />
                </Grid>
            </GroupBox>
            <GroupBox Header="Text DataBounded (Not the scroll)" Margin="0,-2,0,0" Name="groupBox2" Grid.Column="1" Grid.Row="1">
                <Grid>
                    <Button Content="Run Flood" Height="23" HorizontalAlignment="Left" Margin="6,0,0,15" Name="button2" VerticalAlignment="Bottom" Width="75" Click="button2_Click" />
                    <CheckBox Content="Auto-Scroll" Height="16" HorizontalAlignment="Left" Margin="87,17,0,0" Name="AutoScroll_checkBox2" VerticalAlignment="Top" IsChecked="{Binding Path=ScrollCheckBox2, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
                    <CheckBox Content="Use StringBuilder to Append" Height="16" HorizontalAlignment="Left" Margin="87,3,0,0" Name="Append_checkBox2" VerticalAlignment="Top" IsChecked="{Binding Path=AppendCheckBox2, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
                    <CheckBox Content="Use Invoke (not BeginInvoke)" Height="16" HorizontalAlignment="Left" IsChecked="{Binding Path=InvokeCheckBox2, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Margin="87,38,0,0" Name="checkBox2" VerticalAlignment="Top" />
                </Grid>
            </GroupBox>
            <GroupBox Header="DataBounded + Event" Margin="0,-2,0,0" Name="groupBox3" Grid.Column="2" Grid.Row="1">
                <Grid>
                    <Button Content="Run Flood" Height="23" HorizontalAlignment="Left" Margin="6,0,0,15" Name="button3" VerticalAlignment="Bottom" Width="75" Click="button3_Click" />
                    <CheckBox Content="Auto-Scroll" Height="16" HorizontalAlignment="Left" Margin="87,17,0,0" Name="AutoScroll_checkBox3" VerticalAlignment="Top" IsChecked="{Binding Path=ScrollCheckBox3, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
                    <CheckBox Content="Use StringBuilder to Append" Height="16" HorizontalAlignment="Left" Margin="87,3,0,0" Name="Append_checkBox3" VerticalAlignment="Top" IsChecked="{Binding Path=AppendCheckBox3, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
                </Grid>
            </GroupBox>
        </Grid>
    </Window>
    et le CS:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    294
    295
    296
    297
    298
    299
    300
    301
    302
    303
    304
    305
    306
    307
    308
    309
    310
    311
    312
    313
    314
    315
    316
    317
    318
    319
    320
    321
    322
    323
    324
    325
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Imaging;
    using System.Windows.Navigation;
    using System.Windows.Shapes;
    using System.Threading;
    using System.ComponentModel;
     
    namespace LogFloodTest
    {
        /// <summary>
        /// Interaction logic for MainWindow.xaml
        /// </summary>
        public partial class MainWindow : Window, INotifyPropertyChanged
        {
    #region r_INotifyPropertyChanged
     
            public event PropertyChangedEventHandler PropertyChanged;
            public void NotifyChange(String _PropertyName)
            {
                if (PropertyChanged != null)
                    PropertyChanged(this, new PropertyChangedEventArgs(_PropertyName));
            }
     
    #endregion
     
            public bool InvokeRequired
            {
                get { return (this.Dispatcher.Thread != System.Threading.Thread.CurrentThread); }
            }
     
            enum e_FloodType
            {
                ONE = 0,
                TWO,
                THREE,
     
                MAX
            }
     
            private bool  m_ScrollCheckBox1 = false;
            public bool     ScrollCheckBox1
            {get { return m_ScrollCheckBox1; }
             set {        m_ScrollCheckBox1 = value;
              NotifyChange("ScrollCheckBox1"); }}
     
            private bool  m_ScrollCheckBox2 = false;
            public bool     ScrollCheckBox2
            {get { return m_ScrollCheckBox2; }
             set {        m_ScrollCheckBox2 = value;
              NotifyChange("ScrollCheckBox2"); }}
     
            private bool  m_ScrollCheckBox3 = false;
            public bool     ScrollCheckBox3
            {get { return m_ScrollCheckBox3; }
             set {        m_ScrollCheckBox3 = value;
              NotifyChange("ScrollCheckBox3"); }}
     
            private bool  m_AppendCheckBox1 = false;
            public bool     AppendCheckBox1
            {get { return m_AppendCheckBox1; }
             set {        m_AppendCheckBox1 = value;
              NotifyChange("AppendCheckBox1"); }}
     
            private bool  m_AppendCheckBox2 = false;
            public bool     AppendCheckBox2
            {get { return m_AppendCheckBox2; }
             set {        m_AppendCheckBox2 = value;
              NotifyChange("AppendCheckBox2"); }}
     
            private bool  m_AppendCheckBox3 = false;
            public bool     AppendCheckBox3
            {get { return m_AppendCheckBox3; }
             set {        m_AppendCheckBox3 = value;
              NotifyChange("AppendCheckBox3"); }}
     
            private bool  m_InvokeCheckBox1 = false;
            public bool     InvokeCheckBox1
            {get { return m_InvokeCheckBox1; }
             set {        m_InvokeCheckBox1 = value;
              NotifyChange("InvokeCheckBox1"); }}
     
            private bool  m_InvokeCheckBox2 = false;
            public bool     InvokeCheckBox2
            {get { return m_InvokeCheckBox2; }
             set {        m_InvokeCheckBox2 = value;
              NotifyChange("InvokeCheckBox2"); }}
     
            private bool  m_InvokeCheckBox3 = false;
            public bool     InvokeCheckBox3
            {get { return m_InvokeCheckBox3; }
             set {        m_InvokeCheckBox3 = value;
              NotifyChange("InvokeCheckBox3"); }}
     
            private String m_LogText2 = "";
            public String    LogText2
            {get { return  m_LogText2; }
             set {         m_LogText2 = value;
               NotifyChange("LogText2"); }}
     
            private String m_LogText3 = "";
            public String    LogText3
            {get { return  m_LogText3; }
             set {         m_LogText3 = value;
               NotifyChange("LogText3"); }}
     
            private e_FloodType m_LastRunningType = e_FloodType.MAX;
            private Thread m_FloodThread = null;
     
            public MainWindow()
            {
                InitializeComponent();
            }
     
            private void button1_Click(object sender, RoutedEventArgs e)
            {
                if (button1.Content == "Cancel")
                    KillMe(e_FloodType.ONE);
                else
                    RunFlood(e_FloodType.ONE);
            }
     
            private void button2_Click(object sender, RoutedEventArgs e)
            {
                if (button2.Content == "Cancel")
                    KillMe(e_FloodType.TWO);
                else
                    RunFlood(e_FloodType.TWO);
            }
     
            private void button3_Click(object sender, RoutedEventArgs e)
            {
                if (button3.Content == "Cancel")
                    KillMe(e_FloodType.THREE);
                else
                    RunFlood(e_FloodType.THREE);
            }
     
            private void RunFlood(e_FloodType _Type)
            {
                if (m_LastRunningType != e_FloodType.MAX)
                {
                    KillRunning();
                }
                Button btn = null;
                if (_Type == e_FloodType.ONE)
                    btn = button1;
                else if (_Type == e_FloodType.TWO)
                    btn = button2;
                else if (_Type == e_FloodType.THREE)
                    btn = button3;
     
                if (btn != null)
                    btn.Content = "Cancel";
     
                m_LastRunningType = _Type;
                m_FloodThread = new Thread(new ThreadStart(delegate
                {
                    try
                    {
                        DateTime startTime = DateTime.Now;
                        for (int i = 0; ; ++i)
                        {
                            bool haveToBreak = false;
     
                            String strToAppend = " - " + i + " New line of flood log that will increase each time: " + i + "\r\n";
                            // Check timeout
                            TimeSpan ts = DateTime.Now - startTime;
                            int maxToWait = 2000;
                            if (ts.TotalMilliseconds > maxToWait)
                            {
                                haveToBreak = true;
                                strToAppend = "Auto flood auto ended because of time > " + maxToWait + " millisecond\r\n";
                            }
                            if (_Type == e_FloodType.ONE)
                                FloodOne(strToAppend);
                            else if (_Type == e_FloodType.TWO)
                                FloodTwo(strToAppend);
                            else if (_Type == e_FloodType.THREE)
                                FloodThree(strToAppend);
     
                            if (haveToBreak == true)
                                break;
                        }
                    }
                    catch (System.Exception ex)
                    {
                        MessageBox.Show("Exception raised: " + ex.Message);
                    }
                    SyncKill();
                })) { IsBackground = true };
                m_FloodThread.Start();
            }
     
            private void KillRunning()
            {
                if (m_LastRunningType == e_FloodType.MAX)
                    return;
                KillMe(m_LastRunningType);
            }
     
            private void SyncKill()
            {
                if (this.InvokeRequired == true)
                {
                    this.Dispatcher.Invoke(new Action(SyncKill));
                    return;
                }
                KillRunning();
            }
     
            private void KillMe(e_FloodType _Type)
            {
                Button btn = null;
                if (_Type == e_FloodType.ONE)
                    btn = button1;
                else if (_Type == e_FloodType.TWO)
                    btn = button2;
                else if (_Type == e_FloodType.THREE)
                    btn = button3;
     
                if (btn != null)
                    btn.Content = "Run Flood";
     
                try
                {
                    if (m_FloodThread != null)
                        m_FloodThread.Abort();
                }
                catch { }
     
                m_FloodThread = null;
                m_LastRunningType = e_FloodType.MAX;
            }
     
            private void FloodOne(String _ToAdd)
            {
                if (this.InvokeRequired == true)
                {
                    if (InvokeCheckBox1 == true)
                        this.Dispatcher.Invoke(new Action<String>(FloodOne), new Object[] {_ToAdd});
                    else
                        this.Dispatcher.BeginInvoke(new Action<String>(FloodOne), new Object[] { _ToAdd });
                    return;
                }
     
                if (AppendCheckBox1 == true)
                    textBox1.AppendText(_ToAdd);
                else
                    textBox1.Text += _ToAdd;
     
                if (ScrollCheckBox1 == true)
                {
                    textBox1.CaretIndex = textBox1.Text.Length;
                    textBox1.ScrollToEnd();
                }
            }
     
            private StringBuilder m_Log2StringBuilder = new StringBuilder();
            private void FloodTwo(String _ToAdd)
            {
                m_Log2StringBuilder.Append(_ToAdd);
     
                if (AppendCheckBox1 == true)
                    LogText2 = m_Log2StringBuilder.ToString();
                else
                    LogText2 += _ToAdd;
     
                if (ScrollCheckBox2 == true)
                {
                    ScrollLog2ToEnd();
                }
            }
            private void ScrollLog2ToEnd()
            {
                if (this.InvokeRequired == true)
                {
                    if (InvokeCheckBox2 == true)
                        this.Dispatcher.Invoke(new Action(ScrollLog2ToEnd));
                    else
                        this.Dispatcher.BeginInvoke(new Action(ScrollLog2ToEnd));
                    return;
                }
                textBox2.CaretIndex = textBox2.Text.Length;
                textBox2.ScrollToEnd();
            }
     
     
            private StringBuilder m_Log3StringBuilder = new StringBuilder();
            private void FloodThree(String _ToAdd)
            {
                m_Log3StringBuilder.Append(_ToAdd);
     
                if (AppendCheckBox1 == true)
                    LogText3 = m_Log2StringBuilder.ToString();
                else
                    LogText3 += _ToAdd;
            }
     
            private void textBox3_TextChanged(object sender, TextChangedEventArgs e)
            {
                if (ScrollCheckBox3 == true)
                {
                    textBox3.CaretIndex = textBox3.Text.Length;
                    textBox3.ScrollToEnd();
                }
                if (textBox3.Text.Length == 0)
                    m_Log3StringBuilder.Clear();
            }
     
            private void textBox2_TextChanged(object sender, TextChangedEventArgs e)
            {
                if (textBox2.Text.Length == 0)
                    m_Log2StringBuilder.Clear();
            }
        }
    }
    Dans ce code il y a un thread qui est créé et qui se charge de flooder une des TextBox (en fonction de celle sélectionnée) durant 2000 ms.

    Cas numéro 1 :
    Utilisation des Properties public des TextBox pour mettre à jour le contenu.
    Cette solution nécessite une re-syncro du thread appelant (Thread de Flood) avec le Thread du control WPF (Main Thread).
    3 options sont disponibles :
    - Utilisation de la fonction AppendText de la TextBox (à la place de l'operateur += de textBox1.Text)
    - Utilisation de this.Dispatcher.Invoke à la place de this.Dispatcher.BeginInvoke (force l'appel synchrone de l'update du control)
    - l'Auto-scroll (qui déplace le Caret à la fin, puis scroll la TextBox)

    Cas numéro 2 :
    Le texte de la TextBox est Databindée : Text="{Binding Path=LogText2, Mode=OneWay}"
    La mise à jour du texte se fait donc par un PropertyChangedEventHandler.
    Cette solution nécessite une re-syncro du thread appelant (Thread de Flood) avec le Thread du control WPF (Main Thread) dans le cas de la mise à jour de la position du Caret.
    3 options sont disponibles :
    - Utilisation d'un StringBuilder (et de ses méthodes Append + ToString) (à la place de l'operateur += de textBox2.Text)
    - Utilisation de this.Dispatcher.Invoke à la place de this.Dispatcher.BeginInvoke lors de l'auto-scroll du text (force l'appel synchrone de l'update du control)
    - l'Auto-scroll (qui déplace le Caret à la fin, puis scroll la TextBox)

    Cas numéro 3 :
    Le texte de la TextBox est Databindée : Text="{Binding Path=LogText3, Mode=OneWay}"
    La mise à jour du texte se fait donc par un PropertyChangedEventHandler.
    La mise à jours de la position du Caret lors de l'AutoScroll se fait grâce à l'event TextChanged, celui-ci étant synchrone avec le MainThread, je n'ai donc pas à faire d'Invoke par moi-même
    2 options sont disponibles :
    - Utilisation d'un StringBuilder (et de ses méthodes Append + ToString) (à la place de l'operateur += de textBox3.Text)
    - l'Auto-scroll (qui déplace le Caret à la fin, puis scroll la TextBox)

    Résultats :
    Dans les 3 cas les résultats ne sont pas bons.
    Le flood de log à le dessus sur le rafraichissement de la fenêtre WPF, et donc mon application freeze le temps que le flood s’arrête (durant 2000 ms)

    Une différence notable existe entre le Invoke et le BeginInvoke :
    Ce dernier étant asynchrone, il rend la main immédiatement après son appel (son exécution se fera lorsque la frame de sync du MainThread reprendra la main)
    De ce fait, les appels se font plus rapidement, et les BeginInvoke s'empile (et se dépile quand le MainThread est actif).
    C'est là qu'est le problème, l'empilement se fait suffisamment rapidement pour que le MainThread soit saturé d'Invoke dès qu'il reprend la main, et il se retrouve noyer : Une exception sera sans doute levée du type System.OutOfMemoryException
    Pour catcher cette exception il faut la gérer dans le App.xaml.cs :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    public partial class App : Application
        {
            public App() : base()
            {
                this.Dispatcher.UnhandledException += OnDispatcherUnhandledException;
            }
     
            private bool m_MsgBoxShown = false;
            void OnDispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e)
            {
                if (m_MsgBoxShown == false)
                {
                    m_MsgBoxShown = true;
                    String errorMessage = string.Format("An unhandled exception occurred: {0}", e.Exception.Message);
                    MessageBox.Show(errorMessage, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
                    m_MsgBoxShown = false;
                }
                e.Handled = true;
            }
     
        }
    Une bonne différence se ressent lors de l'utilisation du AppendText d'une TextBox, mais quasi aucune lors de l'utilisation d'un StringBuilder

    Au vu des résultats, je dois mal m'y prendre pour gérer le flood, et malgré mes recherches, je n'ai rien trouvé de concret sur le sujet.

    Si quelqu'un sait quoi faire, je suis preneur

    Merci !
    i = i++;

  2. #2
    Rédacteur
    Avatar de Nathanael Marchand
    Homme Profil pro
    Expert .Net So@t
    Inscrit en
    Octobre 2008
    Messages
    3 615
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Expert .Net So@t
    Secteur : Conseil

    Informations forums :
    Inscription : Octobre 2008
    Messages : 3 615
    Points : 8 080
    Points
    8 080
    Par défaut
    Pourquoi ne pas utiliser un ItemsControl avec un IEnumerable<String> ?
    Ainsi, chaque message serait un élément de l'énumérable.

  3. #3
    Membre habitué
    Avatar de yvesall
    Profil pro
    Développeur de jeux vidéo
    Inscrit en
    Novembre 2006
    Messages
    208
    Détails du profil
    Informations personnelles :
    Âge : 37
    Localisation : France

    Informations professionnelles :
    Activité : Développeur de jeux vidéo
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Novembre 2006
    Messages : 208
    Points : 127
    Points
    127
    Par défaut
    Salut, merci pour ta réponse !
    pour le coup l'utilisation d'ItemsControl me permet de générer une liste de "ligne de texte" mais le comportement d'une TextBox tel que la sélection de texte, n'existe plus.
    De plus l'autoscroll nécessite aussi la re-syncro du MainThread et donc le freeze existera aussi

    Le comportement que je souhaite approcher et celui d'une "Cmd MS-DOS" ou d'un "Cygwin", où le flood de log est parfaitement géré, sauf que je souhaite le faire en WPF

    Je suis parfaitement conscient de la différence entre un control WPF et un prompt cygwin, le prompt cygwin gère nettement moins d'event, l'affichage ne se souci pas de directX, la gestion du caret est minime,...
    Mais je me dis que même si on est très loin du TTY de 80*25 char, il doit bien y avoir moyen de faire de même en WPF !
    i = i++;

  4. #4
    Membre habitué
    Avatar de yvesall
    Profil pro
    Développeur de jeux vidéo
    Inscrit en
    Novembre 2006
    Messages
    208
    Détails du profil
    Informations personnelles :
    Âge : 37
    Localisation : France

    Informations professionnelles :
    Activité : Développeur de jeux vidéo
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Novembre 2006
    Messages : 208
    Points : 127
    Points
    127
    Par défaut
    Après une multitude de testes, le seul résultat qui commence à être potable est le suivant :
    - Ajouter le texte dans un StringBuilder
    - Lancer un nouveau Thread qui lock le StringBuilder et Update la TextBox en Invoke

    En code ça donne ça :

    Le Timer qui boucle toute les 10ms pour update la TextBox
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
            bool m_SixContentChanged = false;
            private System.Timers.Timer m_SixUpdateTimer = null;
            private Object m_SixToLock = new Object();
            private void button6_Click(object sender, RoutedEventArgs e)
            {
                if (button6.Content == "Cancel")
                    KillMe(e_FloodType.SIX);
                else
                {
                    m_SixUpdateTimer = new System.Timers.Timer();
                    m_SixUpdateTimer.Elapsed += delegate
                    {
                        lock (m_SixToLock)
                        {
                            if (m_SixContentChanged == true)
                            {
                                m_SixContentChanged = false;
                                String toDisp = m_SixStringBuilder.ToString();
                                SixDisplayText(toDisp);
                                m_SixStringBuilder.Clear();
                            }
                        }
                    };
                    m_SixUpdateTimer.Interval = 10;
                    m_SixUpdateTimer.Start();
                    RunFlood(e_FloodType.SIX);
                }
            }
     
            private void SixDisplayText(String _ToDisplay)
            {
                if (InvokeRequired == true)
                {
                    this.Dispatcher.Invoke(new Action<String>(SixDisplayText), new Object[] { _ToDisplay });
                    return;
                }
                this.textBox6.AppendText(_ToDisplay);
            }
    Dans la méthode KillMe()
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    if (_Type == e_FloodType.SIX && m_SixUpdateTimer != null)
                {
                    Console.WriteLine("total {0}", m_SixStringBuilder.Length);
                    try { m_SixUpdateTimer.Stop(); } catch { }
                    m_SixUpdateTimer = null;
                }
    et la mise à jour du StringBuilder
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
            private void FloodSix(String _ToAdd)
            {
                lock (m_SixToLock)
                {
                    m_SixContentChanged = true;
                    m_SixStringBuilder.Append(_ToAdd);
                }
            }
    Cette solution rame, mais ne freeze plus (Performances qui dépendent énormément du PC).

    Pour le coups je n'ai toujours pas trouvé comment afficher mon Log de manière fluide.

    Dans tout les cas, j'ai l'impression que faire un affichage temps-réel et massif ne peut se faire.

    La mise à jours de la TextBox est trop lente pour ça.

    Donc serait-il possible d'afficher progressivement le Log (au rythme de l'Update de la TextBox) ?
    Par là j'entends l’affichage de l'intégralité du texte, mais ce coups-ci, en différé.
    i = i++;

  5. #5
    Membre habitué
    Avatar de yvesall
    Profil pro
    Développeur de jeux vidéo
    Inscrit en
    Novembre 2006
    Messages
    208
    Détails du profil
    Informations personnelles :
    Âge : 37
    Localisation : France

    Informations professionnelles :
    Activité : Développeur de jeux vidéo
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Novembre 2006
    Messages : 208
    Points : 127
    Points
    127
    Par défaut
    Dans le but de faire une affichage progressif ne saturant pas le MainThread j'ai tenté de modifier la priorité de l'Invoke du Dispatcher
    en utilisant
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    System.Windows.Threading.DispatcherPriority.Input
    (ou priorité inférieur)
    le MainThread n'est pas saturé
    par contre le Thread de flood va s’exécuter plus lentement

    Cette solution fonctionne pour ne pas ruiner les performances de mon application, mais je ne suis pas satisfait pour autant, je ne vais donc pas noter ce sujet comme "Résolu", je posterais en fonction de mes résultats futurs.

    Le sujet reste donc ouvert (et le mystère entier)
    i = i++;

  6. #6
    Candidat au Club
    Homme Profil pro
    Développeur .NET
    Inscrit en
    Septembre 2012
    Messages
    2
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2012
    Messages : 2
    Points : 2
    Points
    2
    Par défaut
    Quel est le volume de texte dans le log à partir duquel tu trouves que les performances sont dégradées?

  7. #7
    Membre confirmé
    Homme Profil pro
    Développeur .NET
    Inscrit en
    Juin 2005
    Messages
    700
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 48
    Localisation : France

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : Tourisme - Loisirs

    Informations forums :
    Inscription : Juin 2005
    Messages : 700
    Points : 488
    Points
    488
    Par défaut
    Salut, je débarque peut etre, et j'avous ne pas avoir tout lu de ton topic.

    Ca a clairement l'air d'etre le thread de ta fenetre qui sature non?

    donc tu pourrai peut etre passer par un buffer, une Queue<String> par exemple à qui tu envoi les logs via un thread different du mainthread.

    Puis tu peux utiliser l'evenement Application.Idle qui va décharger la queue par paquet de 10, ainsi je pense que ca ne freezera pas ton affichage.

    Tu peux peut etre plus simplement balancer des DoEvent tous les 10 logs, mais je ne suis pas sur que ca retire totalement l'aspect "freeze"

  8. #8
    Membre habitué
    Avatar de yvesall
    Profil pro
    Développeur de jeux vidéo
    Inscrit en
    Novembre 2006
    Messages
    208
    Détails du profil
    Informations personnelles :
    Âge : 37
    Localisation : France

    Informations professionnelles :
    Activité : Développeur de jeux vidéo
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Novembre 2006
    Messages : 208
    Points : 127
    Points
    127
    Par défaut
    Hello
    @christian-31 : le volume importe peu, c'est la fréquence de log qui fait la différence, plus le process log rapidement moins le Dispatcher arrive à s'en sortir. L'idée étant de diminuer la priorité de l'Invoke sur le Dispatcher pour Logger moins rapidement (ceci induit une baisse de la réactivité de la fenêtre de Log)

    @giova_fr : l'idée d'un thread intermédiaire qui décharge une stack de log est décrite dans le post #4 (Solution 6). Ce système décharge le Dispatcher des appels multiples, mais les résultats dépendent clairement de la performance du PC. Sur le mien, l’application ne freeze plus, mais lag. Il faut donc tweaker la fréquence d'actualisation du Log et la densité des données à dépiler en fonction des performance du PC.
    i = i++;

Discussions similaires

  1. [VB6]Afficher du texte dans Textbox desactivé + scrollbar?
    Par toninlg dans le forum VB 6 et antérieur
    Réponses: 3
    Dernier message: 21/03/2006, 18h40
  2. Que signifie log dans un algo ?
    Par cryptorchild dans le forum Algorithmes et structures de données
    Réponses: 6
    Dernier message: 16/03/2006, 11h09
  3. Réponses: 2
    Dernier message: 27/12/2005, 13h52
  4. Echelle log-log dans mschart
    Par mb95 dans le forum VB 6 et antérieur
    Réponses: 3
    Dernier message: 27/11/2005, 22h35
  5. [VB.NET] Sauvegarde dans TextBox des logons utilisés
    Par stephane93fr dans le forum ASP.NET
    Réponses: 3
    Dernier message: 27/10/2005, 11h00

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo