Search code examples
c#wpfxaml

WPF TextBox doesn't refresh despite binding and INotifyPropertyChangeduse


I did a little WPF window with a button and a textBox.

When I click on the button, there is some stuff, then I add some lines to my TextBox.

I often did this kind of apps with Winform and when I did them, the UI refreshed each time the textbox was updated, but not in my WPF application.

So I read about and did binding and used the INotifyPropertyChanged.

public partial class MainWindow : MetroWindow, INotifyPropertyChanged
{ 
    public MainWindow()
    {
        InitializeComponent();
        this.DataContext = this;
    }

    private string myValue;
    public string MyValue
    {
        get { return myValue; }
        set
        {
            myValue = value;
            RaisePropertyChanged("MyValue");
        }
    }

    private void RaisePropertyChanged(string propName)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propName));
    }
    public event PropertyChangedEventHandler PropertyChanged;

    // 
    //.......
    //
}

Then my XAML :

<TextBox  x:Name="TxtBox_result" Text="{Binding Path=MyValue}" HorizontalAlignment="Left" TextWrapping="Wrap" VerticalScrollBarVisibility="Auto" AcceptsReturn="True" Margin="30,332,0,0"  VerticalAlignment="Top" Width="868" Height="285" TextChanged="TxtBox_result_TextChanged" />

I know there is probably cleaner code to do with MVVM approach but I don't know MVVM but would like to try first to have a simple method to refresh my UI. It's a shame because it was easier in Win32.


Here is the code executed when i click on the button, i update the MyValue on these code but the TextBox only show value when the treatment is over :

private void GenererBases (string DirectoryStd, string DirectorySur)
{
int cpt = 0;

bool bSignatureBaseFound = false;
bool bFirst = true;
string strDirectoryOut = string.Empty;

var files =  Directory.GetFiles(DirectorySur, "*.*", SearchOption.TopDirectoryOnly)
.Where(s => s.EndsWith(".txt") || s.EndsWith(".dat"));

MyValue = " ";
MyValue = " > Recherche des fichiers ...." + Environment.NewLine;

foreach (string fileName in files)
{
    string line = string.Empty;
    string FileNameStandard = string.Empty;
    bSignatureBaseFound = false;

    MyValue = MyValue + Environment.NewLine + " > Extraction de la signature du fichier base pour : " + System.IO.Path.GetFileName(fileName);


    System.IO.StreamReader file = new System.IO.StreamReader(fileName);
    while ((line = file.ReadLine()) != null)
    {
        if (line.Contains("SetModuleInfo('$Id:"))
        {
            bSignatureBaseFound = true;
            int posDebFichier = line.IndexOf("'$Id:") + 6;
            int posFinFichier = line.IndexOf(" ", (line.IndexOf("'$Id:") + 6));

            FileNameStandard = line.Substring(posDebFichier, posFinFichier-posDebFichier); 

            if (File.Exists(DirectoryStd+@"\"+FileNameStandard))
            {
                MyValue = MyValue + Environment.NewLine + " > Fichier base : " + FileNameStandard + " localisé ! > Copie vers le dossier de destination !";
                
                if (bFirst)
                {
                    strDirectoryOut = DirectorySur + @"\Generated_bases_" + DateTime.Now.ToString("yyyyMMddHHmm");
                    Directory.CreateDirectory(strDirectoryOut);
                    bFirst = false;
                }

                cpt++;

                if (File.Exists(strDirectoryOut + @"\" + System.IO.Path.GetFileNameWithoutExtension(FileNameStandard) + "_base" + System.IO.Path.GetExtension(FileNameStandard)))
                {
                    File.Delete(strDirectoryOut + @"\" + System.IO.Path.GetFileNameWithoutExtension(FileNameStandard) + "_base" + System.IO.Path.GetExtension(FileNameStandard));
                }
                File.Copy(DirectoryStd + @"\" + FileNameStandard, strDirectoryOut + @"\" + System.IO.Path.GetFileNameWithoutExtension(FileNameStandard) + "_base" + System.IO.Path.GetExtension(FileNameStandard),true);
                File.SetAttributes(strDirectoryOut + @"\" + System.IO.Path.GetFileNameWithoutExtension(FileNameStandard) + "_base" + System.IO.Path.GetExtension(FileNameStandard), File.GetAttributes(strDirectoryOut + @"\" + System.IO.Path.GetFileNameWithoutExtension(FileNameStandard) + "_base" + System.IO.Path.GetExtension(FileNameStandard)) | FileAttributes.Hidden);

            }
            else
            {
                MyValue = MyValue + Environment.NewLine + "!!! WARNING!!" + Environment.NewLine + "!!! Fichier base non localisé : " + FileNameStandard + Environment.NewLine;
            }
            break;
        }
        
    }
    file.Close();

    if (bSignatureBaseFound==false)
    {
        MyValue = MyValue + Environment.NewLine + "!!! WARNING!!" + Environment.NewLine + "!!! Aucune signature de base localisée pour : " + fileName + Environment.NewLine;

    }
    
}

MyValue = MyValue + Environment.NewLine + "***************************" + Environment.NewLine + "***************************" + Environment.NewLine + " >> Fin du traitement !" + cpt + " fichiers bases générés pour " + files.Count() + " sources surchargés !" + Environment.NewLine + ">> Fichiers bases copiés dans le dossier :" + strDirectoryOut + Environment.NewLine;
}

Thanks a lot


Solution

  • A simple example.

    using System;
    using System.ComponentModel;
    using System.Runtime.CompilerServices;
    using System.Threading.Tasks;
    using System.Windows;
    using System.Windows.Controls.Primitives;
    
    namespace Core2022.SO.WalterFabioSimoni
    {
        public partial class InpcWindow : Window, INotifyPropertyChanged
        {
            protected bool Set<T>(ref T propertyField, T newValue, [CallerMemberName] string? propertyName = null)
            {
                if (string.IsNullOrWhiteSpace(propertyName))
                    throw new ArgumentNullException(nameof(propertyName));
    
                bool notEquals = !Equals(propertyField, newValue);
                if (notEquals)
                {
                    propertyField = newValue;
                    RaisePropertyChanged(propertyName);
                }
    
                return notEquals;
            }
            protected void RaisePropertyChanged([CallerMemberName] string? propertyName = null)
            {
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName ?? string.Empty));
            }
    
            public event PropertyChangedEventHandler? PropertyChanged;
        }
        public partial class AsyncUpdateWindow : InpcWindow
        {
            public AsyncUpdateWindow()
            {
                InitializeComponent();
            }
            private string _myValue = string.Empty;
    
            public string MyValue
            {
                get => _myValue;
                set => Set(ref _myValue, value ?? string.Empty);
            }
    
            private static readonly Random random= new Random();
            private async void OnRestartAsync(object sender, RoutedEventArgs e)
            {
                UIElement button = (UIElement)sender;
                button.SetCurrentValue(IsEnabledProperty, false);
                for (int i = 0; i < 10; i++)
                {
                    MyValue = random.Next(int.MinValue, int.MaxValue).ToString();
                    await Task.Delay(500);
                }
                button.InvalidateProperty(IsEnabledProperty);
            }
        }
    }
    
    <local:InpcWindow x:Class="Core2022.SO.WalterFabioSimoni.AsyncUpdateWindow"
                      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:Core2022.SO.WalterFabioSimoni"
                      mc:Ignorable="d"
                      Title="AsyncUpdateWindow" Height="300" Width="400"
                      DataContext="{Binding RelativeSource={RelativeSource Self}}">
        <UniformGrid Columns="1">
            <Viewbox>
                <TextBlock Text="{Binding MyValue}" Margin="10"/>
            </Viewbox>
    
            <Button Content="Restart" Click="OnRestartAsync"/>
        </UniformGrid>
    </local:InpcWindow>
    

    There are no calls to UI elements in your method code - this is very good. For your code, you just need to add the following method:

        private async void GenererBasesAsync(string DirectoryStd, string DirectorySur)
            => await Task.Run(() => GenererBases(DirectoryStd, DirectorySur));
    

    In the clicker, call this async method.