1
votes

The following code is an example to explain my issue.

I have a textbox in binding. When I click on a button, executes a function. (In this case it's a for loop).

Now I want, the text box updated with the content of the i variable. (public void MyAction())

So, I made a thread, but this doesn't work.

Why ?

Thanks in advance

XAML code

<TextBox Text ="{Binding MyValue}" HorizontalAlignment="Left" Height="47" Margin="4,4,4,4" VerticalAlignment="Top" Width="342"/>
<Button Command="{Binding ClickCommand}" Content="Run" Margin="114,69,283,216"/>

C# code

    class Vm_Main : ViewModelBase
{
    public string _MyValue { get; set; }  //String in my XAML
    public string MyValue
    {
        get { return _MyValue; }
        set
        {
            _MyValue = value;
            base.OnPropertyChanged("MyValue");
        }
    }

    private bool _canExecute=true;
    private ICommand _clickCommand;
    public ICommand ClickCommand
    {
        get
        {
            return _clickCommand ?? (_clickCommand = new CommandHandler(() => MyAction(), _canExecute));
        }
    }


    public Vm_Main()
    {
        MyValue = "Hallo"; //Default value
    }

    public void MyAction() // This is the function where I want update the TextBox in Binding
    {
        Worker workerObject = new Worker();
        Thread workerThread = new Thread(workerObject.DoWork);
        workerThread.Start();

        for (int i = 1; i <= 10; i++)
        {
            MyValue = i.ToString();
            workerObject.Value = MyValue;
            Thread.Sleep(500);
        }
        workerObject.RequestStop();
        workerThread.Join();
        MessageBox.Show("End");
    }
}

// The THREAD
public class Worker : ViewModelBase 
{
    // This method will be called when the thread is started.
    public string _Value { get; set; }
    public string Value
    {
        get { return _Value; }
        set
        {
            _Value = value;
            base.OnPropertyChanged("Value");
        }
    }
    public void DoWork()
    {
        while (!_shouldStop)
        {
            Console.WriteLine("Value is..." + _Value);
        }
        Console.WriteLine("End.");
    }
    public void RequestStop()
    {
        _shouldStop = true;
    }
    // Volatile is used as hint to the compiler that this data
    // member will be accessed by multiple threads.
    private volatile bool _shouldStop;
}

And Class ViewModelBase and Class Command

    public abstract class ViewModelBase : INotifyPropertyChanged
{
    #region INotifyPropertyChanged
    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged(string strPropertyName)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(strPropertyName));
    }
    #endregion



    #region Messages and Progressbar
    private string _errorMessage;
    public string ErrorMessage
    {
        get { return _errorMessage; }
        set
        {
            if (_errorMessage == value) return;
            _errorMessage = value;
            OnPropertyChanged("ErrorMessage");
        }
    }

    private string _errorTooltip;
    public string ErrorTooltip
    {
        get { return _errorTooltip; }
        set
        {
            if (_errorTooltip == value) return;
            _errorTooltip = value;
            this.OnPropertyChanged("ErrorTooltip");
        }
    }

    private string _statusMessage;
    public string StatusMessage
    {
        get { return _statusMessage; }
        set
        {
            if (_statusMessage == value) return;
            _statusMessage = value;
            //pulizia dei messaggi di errore
            ErrorMessage = string.Empty;
            ErrorTooltip = string.Empty;
            OnPropertyChanged("StatusMessage");
        }
    }

    protected void ClearMessage()
    {
        ErrorMessage = string.Empty;
        StatusMessage = string.Empty;
    }

    private int _currentProgress;
    public int CurrentProgress
    {
        get { return _currentProgress; }
        set
        {
            _currentProgress = value;
            OnPropertyChanged("CurrentProgress");
        }
    }
    #endregion

    protected ViewModelBase()
    {

    }
}


    public class CommandHandler : ICommand
{
    private Action _action;
    private bool _canExecute;
    public CommandHandler(Action action, bool canExecute)
    {
        _action = action;
        _canExecute = canExecute;
    }

    public bool CanExecute(object parameter)
    {
        return _canExecute;
    }

    public event EventHandler CanExecuteChanged;

    public void Execute(object parameter)
    {
        _action();
    }
}
1
'Cause you are totally blocking the UI Thread in your MyAction method.Rand Random

1 Answers

1
votes

So I think you have to issues.

First use async await pattern.

It goes like this - to not block ur UI thread.

public async Task<object> MyAsyncMethod()
{
    return await Task.Run<object>(() =>
    {
        return null;
    });
}

or for ICommand just:

public async void MyAsyncMethod()
{
    await Task.Run(() =>
    {

    });
}

Second is that you may want to update your UI - while processing async. This is a common problem for updating progress for example. You can solve this with SynchronizationContext.

public interface IUpdateProgess
{
    void SendMessage(string val);
}



public async Task<object> MyAsyncMethod(IUpdateProgess progress)
{
    //UI thread
    SynchronizationContext context = SynchronizationContext.Current;

    return await Task.Run<object>(() =>
    {
        //other thread


        if (context != null && progress != null)
        {
            context.Post(new SendOrPostCallback((o) =>
            {
                progress.SendMessage("Progress");
            }), null);
        }

        return null;
    });
}

You can use this obviously to not just update progress - i think you get the idea.