Run long operation when Property Changed - c#

How can I run some long operation, when my property raises PropertyChanged event?
Example:
class SomeClass : INotifyPropertyChanged
{
public ObservableConllection<Item> Items { get; set; }
public string Path
{
get => _path;
if (_path != value) {
_path = value;
OnPropertyChanged(nameof(Path));
// await Task.Run(()=> long operation, for example load some Items use "_path");
}
}
Property can't be async and I think it is "normal". But what should I do?
I think that such a case occurs often.

You should read Stephen Cleary's MSDN article about asynchronous data-bound properties where he defines a "task watcher" class called NotifyTaskCompletion<T> that implements the INotifyPropertyChanged interface and has a Result property that you can bind to:
Async Programming : Patterns for Asynchronous MVVM Applications: Data Binding: https://msdn.microsoft.com/en-us/magazine/dn605875.aspx
public class MainViewModel : INotifyPropertyChanged
{
public string Path
{
get { return _path; }
set
{
if (_path != value)
{
_path = value;
OnPropertyChanged(nameof(Path));
AsyncProperty = new NotifyTaskCompletion<int>(YourAsyncMethod());
}
}
}
public NotifyTaskCompletion<string> AsyncProperty { get; private set; }
}
<Label Content="{Binding AsyncProperty.Result}"/>

You can use Task.Run without await. It may or may not work well depending on the case, there is no universal answer regarding that.
You can create a method like this to set the value, it is a more sure way to do this. However if you need a databinding this can't work:
public async Task SetPath(string value)
{
if (_path != value) {
_path = value;
OnPropertyChanged(nameof(Path));
await Task.Run(()=> ...);
}
If you need data binding and want to use await, then it is usually the best practice to use debouncing with Timer - you don't want some expensive operation to be available without restriction to the user so that he can freeze or crash the app.

Related

Using Mediator to call async method from within a data-bound property setter

I need to call an async method ConnectToLibrary once a book is selected, meaning once SelectedBook property is set. I saw many solutions. But all of them require hacks or starting a task/thread from a setter. My question, what's wrong with using a mediator like Messenger from Microsoft Toolkit.Mvvm?
I can just register a callback to ConnectToLibrary with Messenger.Register<...BookSelectedMessage>(...ConnectToLibrary...) and then call Messenger.Send(new BookSelectedMessage()) from the SelectedBook's setter.
Is this the correct way to do this in MVVM?
public MyViewModel : ObservableRecipient
{
public ObservableCollection<BookInfo> Books { get; } = new();
private BookInfo _selectedBook;
public BookInfo SelectedBook
{
get => _selectedBook;
set => SetProperty(ref _selectedBook, value);
}
private async Task ConnectToLibrary(BookInfo info)
{
await START_SOME_ASYNC_METHOD();
}
}
Nothing. In either case any Exception is swallowed. And since you are asking, what's wrong with starting a thread from a Setter? It sounds to me like the requirements of your app require this anyway, no?

MVVM c# how to load async data into a property?

I wonder if there's a better approach to load async data into a property. now I create an async function and raise a Task in the Get part of the property like this:
private ObservableCollection<CProyecto> prope;
public ObservableCollection<CProyecto> Prope
{
get
{
if (prope == null)
{
Task.Run(()=> LoadData()).Wait();
}
return proyectos;
}
set
{
prope = value;
RaisePropertyChanged();
}
}
async private Task LoadData()
{
Prope = await clsStaticClassDataLoader.GetDataFromWebService();
}
This approach works, but I don't like the use of .Wait, because that can freeze the screen if the service doesn´t respond fast.
Can you please guide me on this matter?
thanks in advance
The way I handled this was to start the process of loading the property when the object was constructed, but I did not await the result. Since the property notifies when it is populated, the bindings worked just fine. Essentially it works like this:
public class MyClass : INotifyPropertyChanged
{
private ObservableCollection<CProyecto> prope;
public ObservableCollection<CProyecto> Prope
{
get { return prope; }
set { prope = value; RaisePropertyChanged(nameof(Prope)); }
}
public MyClass()
{
// Don't wait or await. When it's ready
// the UI will get notified.
LoadData();
}
async private Task LoadData()
{
Prope = await clsStaticClassDataLoader.GetDataFromWebService();
}
}
This works very well, and does not cause any delays or stuttering in the UI. If you want the collection to never be null (a good practice IMO), you can pre-initialize the prope field with an empty collection.
I suggest you read my MSDN article on async MVVM data-binding. I have a library (github link) that provides a NotifyTask<T> type, which can be used as such:
public class MyClass : INotifyPropertyChanged
{
public NotifyTask<ObservableCollection<CProyecto>> Prope { get; private set; }
public MyClass()
{
// Synchronously *start* the operation.
Prope = NotifyTask.Create(LoadDataAsync());
}
async private Task<ObservableCollection<CProyecto>> LoadDataAsync()
{
return await clsStaticClassDataLoader.GetDataFromWebService();
}
}
Then your databinding would operate on Prope.Result.
The advantage of this approach is that you can also use databinding to hide/show busy indicators (Prope.IsNotCompleted), show controls when the data is available (Prope.IsSuccessfullyCompleted), and error notifications (Prope.IsFaulted / Prope.ErrorMessage).
Also, you can specify a non-null default value, if you wish:
Prope = NotifyTask.Create(LoadDataAsync(), new ObservableCollection<CProyecto>());
Your current implementation of the Prope property doesn't make much sense. It is pointless to execute the LoadData method on a background thread since you block the main thread anyway when you call Wait(). You might as well call Wait() directly on the task returned by the LoadData() method:
//BAD IMPLEMENTATION!
private ObservableCollection<CProyecto> prope;
public ObservableCollection<CProyecto> Prope
{
get
{
if (prope == null)
LoadData().Wait();
return proyectos;
}
set { prope = value; RaisePropertyChanged(); }
}
The above implementation is still a bad one. The getter of a property is not supposed to perform an asynchronous operation. You should read #Stephen Cleary's blog post on the subject: https://blog.stephencleary.com/2013/01/async-oop-3-properties.html
...and look into his NotifyTaskCompletion type in his AsyncEx library: https://github.com/StephenCleary/AsyncEx

Property Change Event for C# ..Parent child interaction

I have a class Step that stores a list of Tasks :
public class Step
{
public string StepName { get; set; }
private string _stepStatus
public List<Task> Tasks { get; set; }
public string StepStatus
{
get
{
return _stepStatus;
}
set
{
_stepStatus = value;
}
}
public Step(String name)
{
StepName = name;
Tasks = new List<Task>();
}
I have a class of Task :
public class Task
{
public string TaskName { get; set; }
private string _taskStatus
public string TaskStatus
{
get
{
return _taskStatus;
}
set
{
// raise an event here
_taskStatus = value;
}
}
public Task(String name)
{
TaskName = name;
}
Whenever an individual updates a Task , I want to raise an event . this event should be caught by the parent i.e Step .. Step should check the status of all the tasks .
If all tasks' status are New , then Step status should be automatically set to New .
If all tasks' status are Finished , then Step status should be automatically set to Finished.
If even on of the task's status is In Progress , the Step Status should be set to In Progress.
I shall call this method TripleLogic , say.
I can do the logic for the above three since this is logical. I am not sure how to raise an event from Task Status setter and ensure that Step class object picks it up since I am new to C#. If someone could help me with this event raising part with a small sample code , and also show me how I cna get my parent Step object to capture this event and call TripleLogic at that instance.
I looked at INotifyPropertyChanged but am not sure how to implement.
Any other code design related pointers will be helpful.
First suggestion is not not expose your Tasks property on the Step class so that consumers are not able to modify the collection whenever/however they want. Instead add a method called AddTask(Task t) to Step and in there you can set up your event handling.
The Task class should expose an event, perhaps called StatusChanged. When the setter on the TaskStatus property is called, fire that event (after checking to ensure there are listeners). This event's arguments should include the updated status and the task's name. If you have a finite set of statuses, I'd suggest defining an enum for them, instead of relying on strings.
Example:
public class Task
{
public event EventHandler<TaskStatusEventArgs> StatusChanged;
//...
}
public enum TaskStatus
{
New,
InProgress,
Done
}
public class TaskStatusEventArgs: EventArgs
{
public string TaskName { get; private set; }
public TaskStatus Status { get; private set; }
public TaskStatusEventArgs(string taskName, TaskStatus status)
{
TaskName = taskName;
Status = status;
}
}
Back in Step and the AddTask() method, subscribe to the Task's StatusChanged event and keep track of the number of tasks in the Step. In the handler for the StatusChanged event, you can then grab the task's new status and do whatever logic you need in your TripleLogic() method.
Example:
public class Step
{
List<Task> tasks;
public void AddTask(Task t)
{
t.StatusChanged += HandleStatusChanged;
}
private void HandleStatusChanged(object sender, TaskStatusEventArgs args)
{
string taskName = args.TaskName;
TaskStatus status = args.Status;
TripleLogic(taskName, status);
}
}
Given that you've mentioned you are new to C#, perhaps go through the MSDN guide for events, specifically the sections on subscribing and publishing. The publishing section mentions the generic EventHandler<TEventArgs> delegate, I highly suggest using that method.
Your question is vague and large-enough that, when coupled with your lack of experience in the language, this may take you a while to get through.

Using IObservable<T> to keep track of current state

Suppose I have an object that observes an IObservable so that it's always aware of the current state of some external source. Internally my object has a method that uses that external value as part of the operation:
public class MyObject
{
public MyObject(IObservable<T> externalSource) { ... }
public void DoSomething()
{
DoSomethingWith(CurrentT);
}
}
What's the idomatic 'reactive' way of using IObservable for 'tracking current state' instead of 'responding to stream of events'.
Idea #1 is to just monitor the observable and write down values as they come in.
public class MyObject
{
private T CurrentT;
public MyObject(IObservable<T> externalSource)
{
externalSource.Subscribe((t) => { CurrentT = t; });
}
public void DoSomething()
{
DoSomethingWith(CurrentT);
}
}
And that's fine, but keeping track of the state in a class member seems very un-reactive-y.
Idea #2 is to use a BehaviorSubject
public class MyObject
{
private readonly BehaviorSubject<T> bs;
public MyObject(BehvaiorSubject<T> externalSource)
{
this.bs = externalSource
}
public void DoSomething()
{
DoSomethingWith(bs.Value);
}
}
But using subjects directly seems to be frowned upon. But at least in this case I have the ability to use a readonly field to store the behaviorsubject.
The BehaviorSubject (or ReplaySubject) does seem like it was made for this purpose, but is there some other better way here? And if I should use the subject, would it make more sense to take the subject as an injected parameter, or take the original observable and build the subject locally in the constructor?
(by the way I'm aware about the need to deal with the 1st value if the source observable hasn't fired yet. Don't get hung up on that, that's not what I'm asking about)
I'd go with a generic solution utilizing the ReactiveUI library. RUI has a standard way of mapping IObservable<T> to an INotifyPropertyChanged stateful property.
public class ObservableToINPCObject<T> : ReactiveObject, IDisposable
{
ObservableAsPropertyHelper<T> _ValueHelper;
public T Value {
get { return _ValueHelper.Value; }
}
public ObservableToINPCObject(IObservable<T> source, T initial = default(T))
{
_ValueHelper = source.ToProperty(this, p=>p.Value, initial);
}
public Dispose(){
_ValueHelper.Dispose();
}
}
ValueHelper is contains both the current state of the observable and automatically triggers the correct INPC notification when the state changes. That's quite a bit of boiler plate handled for you.
and an extension method
public static class ObservableToINPCObject {
public static ObservableToINPCObject<T> ToINPC<T>
( this IObservable<T> source, T init = default(T) )
{
return new ObservableToINPCObject(source, init);
}
}
now given an
IObservable<int> observable;
you can do
var obj = observable.ToINPC(10);
and to get the latest value
Console.WriteLine(obj.Value);
also given that Value is an INPC supporting property you can use it in databinding. I use ToProperty all the time for exposing my observables as properties for WPF databinding.
To be Rx-ish I'd suggest avoiding the second option and go with your first, but modified in one of two ways.
Either (1) make your class disposable so that you can cleanly close off the subscription to the observables or (2) make a method that lets you clean up individual observables.
(1)
public class MyObject : IDisposable
{
private T CurrentT;
private IDisposable Subscription;
public MyObject(IObservable<T> externalSource)
{
Subscription = externalSource
.Subscribe((t) => { CurrentT = t; });
}
public void Dispose()
{
Subscription.Dispose();
}
public void DoSomething()
{
DoSomethingWith(CurrentT);
}
}
(2)
public class MyObject
{
private T CurrentT;
public IDisposable Observe(IObservable<T> externalSource)
{
return externalSource
.Subscribe((t) => { CurrentT = t; });
}
public void DoSomething()
{
DoSomethingWith(CurrentT);
}
}
Both allow proper clean-up and both don't use a subject.

Design pattern for reporting/monitoring progress of long processes

Anyone can suggest a good Design Pattern for reporting/monitoring status/progress of long processes.
Basically, I have a codebase that receives a "data-context" object:
public class DataContext : IDataContext
{
pulbic Dictionary<string, objects> Properties { get; private set; }
// Additional properties removed for simplicity...
}
Based on the provided context, a Task (not TPL-Task) object is created, with various subtasks.
During execution, the DataContext object is passed to the various sub-tasks, which can retrieve or update it.
For example, let's say that the main task is a "Copy files" task. The DataContext will have properties like the SourceFolder and TargetFolder, and perhaps a FilterFiles property (e.g. *.docx). Our main task will be a CopyFilesTasks, and it will have a "pipeline" of subtasks - Scan Folders, Scan Files, Filter Files, Copy Files, etc....
What I am looking for, is the best way to allow the task/sub-tasks to report their progress to the caller/executer.
In our example above, the changes in progress might be just "Copied file ABC.docx...", or perhaps something a bit more "complex", like "Scanning folder XYZ..."
I have considered the following options:
INotifyPropertyChanged: add a "Progress" property to DataContext
public string Progress { get; set { _progress = value; RaisePropertyChanged("Progress"); }
and have the code that created the DataContext object register to the PropertyChanged event. However, this seems like a too-simplistic approach...
ILog (using whatever logging framework you prefer): use an ILog instance in the various tasks/sub-tasks, and have the main-task executioner add it's own listener to the logging framework.
However this seemed like bending the logging mechanism to do things it was not supposed to do.
Udi Dahan's DomainEvents: The executioner of the task can regard the DataContext as a "domain", and therefore we can try to implement an "EventHandler" for a "ProgressChanged" event. In theory, this can be even used for more refined events, that happen in specific sub-tasks... But once again, it feels like forcing the concept...
My concerns include things like:
Progress might not be the only "event" that needs to be monitored - in our example above, we might want things more defined, like FolderHandled, FileCopied, etc., but we might not know the exact events when executing the tasks (remember - the subtasks are created based on the DataContext, and might result in different tasks being executed).
The context of running the tasks is not yet defined. For now, I'm just planning to run the tasks from the command-line application, so outputting to the command-line is needed for debugging. Later on, when I move this to a service, I might want to have a "listener" update a database with the task's progress (for example).
You can declare arguments for each possible operation type, say FileOperationEventArgs for file operation, DatabaseUpdateEventArgs for database operation etc.
public class FileOperationEventArgs : EventArgs
{
public readonly string SourceFolder;
public readonly string TargetFolder;
public FileOperationEventArgs(string sourceFolder, string targetFolder)
{
SourceFolder = sourceFolder;
TargetFolder = targetFolder;
}
}
public class DatabaseUpdateEventArgs : EventArgs
{
public readonly int RowsUpdated;
public DatabaseUpdateEventArgs(int rowsUpdated)
{
RowsUpdated = rowsUpdated;
}
}
OperationProgress class declares events for each operation type.
public class OperationProgress
{
public event EventHandler<FileOperationEventArgs> FileCopied;
public event EventHandler<DatabaseUpdateEventArgs> DatabaseUpdated;
public void OnFileCopied(FileOperationEventArgs a)
{
if(FileCopied != null)
FileCopied(this, a);
}
public void OnDatabaseUpdated(DatabaseUpdateEventArgs a)
{
if (DatabaseUpdated != null)
DatabaseUpdated(this, a);
}
}
OperationProgress will be specified when DataContext is created.
public class DataContext : IDataContext
{
public Dictionary<string, object> Properties { get; private set; }
public OperationProgress Progress { get; private set; }
public DataContext(OperationProgress progress)
{
Progress = progress;
}
}
Subtask implementation can update the progress.
public class FileCopySubTask
{
public void Execute(DataContext context)
{
context.Progress.OnFileCopied(new FileOperationEventArgs("c:/temp1", "c:/temp2"));
}
}
Consider BackgroundWorkers.
http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx
They have their own reportprogress event on a separate UI thread.

Categories