i have implemented a custom CodeFixProvider that adds some XML documentation to members.
Example:
public void MyMethod() { }
will be transformed to
/// <summary></summary>
public void MyMethod() { }
The CodeFixProvider is implemented like this:
public class MyCodeFixProvider : CodeFixProvider
{
...
public async override Task RegisterCodeFixesAsync(CodeFixContext context)
{
await Task.Run(() =>
{
Diagnostics diagnostics = context.Diagnostics.First();
CodeAction codeFix = CodeAction.Create("Title", c => CreateXmlDocs(...));
context.RegisterCodeFix(codeFix, diagnostics);
}
).ConfigureAwait(false);
}
...
}
Everything is working like expected.
Now i want to add some extra functionality: After applying the code fix, the caret should be moved inside the empty summary tag.
I discovered the DocumentNavigationOperation class included in Microsoft.CodeAnalysis.Features NuGet package. This class should be able to move the caret to a specified position. But I can't find any instructions how to use this class. If i call it from inside my CreateXmlDocs method, an exception is thrown:
Navigation must be performed on the foreground thread.
Code:
private static async Task<Solution> CreateXmlDocs()
{
...
new DocumentNavigationOperation(newDocument.Id, 42)
.Apply(newDocument.Project.Solution.Workspace, cancellationToken);
...
}
I'm not sure if it makes sense to use this class inside my CreateXmlDocs method, because the new solution created inside this method isn't yet applied by Visual Studio when DocumentNavigationOperation is called.
Does anybody knows a solution to move the caret after applying a code fix?
Ok, in the meantime i found a solution for this.
A custom CodeAction is required to get it working:
internal class NavigateAfterCodeChangeAction : CodeAction
{
private readonly Func<CancellationToken, Task<Solution>> codeChangeOperation;
private readonly Func<Solution, CancellationToken, Task<NavigationTarget>> navigationTargetCalculation;
public NavigateAfterCodeChangeAction(
string title,
Func<CancellationToken, Task<Solution>> codeChangeOperation,
Func<Solution, CancellationToken, Task<NavigationTarget>> navigationTargetCalculation)
{
this.Title = title;
this.codeChangeOperation = codeChangeOperation;
this.navigationTargetCalculation = navigationTargetCalculation;
}
public override string Title { get; }
protected override async Task<IEnumerable<CodeActionOperation>> ComputeOperationsAsync(CancellationToken cancellationToken)
{
var operations = new List<CodeActionOperation>();
Solution changedSolution = await this.codeChangeOperation(cancellationToken);
NavigationTarget navigationTarget = await this.navigationTargetCalculation(changedSolution, cancellationToken);
operations.Add(new ApplyChangesOperation(changedSolution));
if (navigationTarget != null)
{
operations.Add(new DocumentNavigationOperation(navigationTarget.DocumentId, navigationTarget.Position));
}
return operations;
}
}
internal class NavigationTarget
{
public NavigationTarget(DocumentId documentId, int position)
{
this.DocumentId = documentId;
this.Position = position;
}
public DocumentId DocumentId { get; }
public int Position { get; }
}
The new CodeAction can be used in the CodeFixProvider instead of CodeAction.Create():
public class MyCodeFixProvider : CodeFixProvider
{
...
public async override Task RegisterCodeFixesAsync(CodeFixContext context)
{
await Task.Run(() =>
{
Diagnostics diagnostics = context.Diagnostics.First();
CodeAction codeFix = new NavigateAfterCodeChangeAction(
"Title",
c => CreateXmlDocs(...)
(s, c) => CalculateNavigationTarget(context.Document));
context.RegisterCodeFix(codeFix, diagnostics);
}
).ConfigureAwait(false);
}
private static NavigationTarget CalculateNavigationTarget(Document doc)
{
// Calculate the navigation target here...
// Example: Navigate to position 42 of the document
return new NavigationTarget(doc.Id, 42);
}
...
}
Related
I have a custom NLog log target class that looks like this:
public class MyTarget : AsyncTaskTarget
{
public MyTarget() {}
public MyTarget(INeedThisThingToFunction thing)
{
Thing = thing;
}
public INeedThisThingToFunction Thing { get; set; }
public override Task WriteAsyncTask(LogEventInfo logEvent, CancellationToken cancellationToken)
{
Thing.Use();
return null;
}
}
I cannot figure out how to ensure that the second constructor gets called. I've done this in Program.cs:
public static void Main(string[] args)
{
var host = CreateWebHostBuilder(args).Build();
ConfigureLogging(host.Services.GetAutofacRoot());
LogManager.GetCurrentClassLogger().Info("Hi mom");
host.Run();
}
private static void ConfigureLogging(IComponentContext container) {
ConfigurationItemFactory.Default.CreateInstance = type =>
{
if (type != typeof(MyTarget) return Activator.CreateInstance(type);
var thing = new ThingTheTargetNeedsToFunction();
return new MyTarget(thing);
}
LogManager.Configuration.Reload();
}
I've tried a number of other things, too, but that comes the closest to doing something. When LogManager.Configuration.Reload() is called, the CreateInstance code fires; but when the Info method fires, the Thing property on the target is null.
Is there a better way to do this? Like, a way that works?
Using .NET Core 3, NLog, Autofac.
If the Thing is only available after having built the host, then you can do it like this:
public static void Main(string[] args)
{
var host = CreateWebHostBuilder(args).UseNLog().Build();
ConfigureLogging(host.Services.GetAutofacRoot());
LogManager.GetCurrentClassLogger().Info("Hi mom");
host.Run();
}
private static void ConfigureLogging(IComponentContext container)
{
var defaultConstructor = ConfigurationItemFactory.Default.CreateInstance;
ConfigurationItemFactory.Default.CreateInstance.CreateInstance = type =>
{
if (type == typeof(MyTarget))
{
var thing = new ThingTheTargetNeedsToFunction();
return new MyTarget(thing);
}
return defaultConstructor(type);
};
// Reload config and assign the newly reloaded config
LogManager.Configuration = LogManager.Configuration?.Reload();
}
Then make sure that your custom MyTarget can handle that it is running in "disabled mode" where Thing is unassigned:
[Target("MyTarget")]
public class MyTarget : AsyncTaskTarget
{
public MyTarget() {}
public MyTarget(INeedThisThingToFunction thing)
{
Thing = thing;
}
public INeedThisThingToFunction Thing { get; set; }
public override await Task WriteAsyncTask(LogEventInfo logEvent, CancellationToken cancellationToken)
{
if (Thing == null)
return null; // Handle that `Thing` is unassigned by default-constructor
await Thing.UseAsync().ConfigureAwait(false);
}
}
I am getting the error
Task>
__generated__Index.SearchFilms(string)' has the wrong return type
On my code below and I don't know why. The line in question is
<BlazoredTypeahead SearchMethod="SearchFilms"
Code:
#page "/"
#using Blazored.Typeahead
<BlazoredTypeahead SearchMethod="SearchFilms"
#bind-Value="SelectedFilm">
<SelectedTemplate>
#context.Title
</SelectedTemplate>
<ResultTemplate>
#context.Title (#context.Year)
</ResultTemplate>
</BlazoredTypeahead>
#if (SelectedFilm != null)
{
<p>Selected Film is: #SelectedFilm.Title</p>
}
#code {
private List<Film> Films;
private Film SelectedFilm;
protected override void OnInitialized()
{
Films = new List<Film> {
new Film("The Matrix", 1999),
new Film("Hackers", 1995),
new Film("War Games", 1983) };
}
private async Task<List<Film>> SearchFilms(string searchText)
{
return await Task.FromResult(Films.Where(x => x.Title.ToLower().Contains(searchText.ToLower())).ToList());
}
class Film
{
public string Title { get; set; }
public int Year { get; set; }
public Film(string title, int year)
{
Title = title;
Year = year;
}
}
}
Introduction
On BlazoredTypeahead the SearchMethod is async because this delegate is intended to call backend for data and we can't block the UI thread waiting for result. Typically:
private async Task<IEnumerable<Film>> SearchFilms(string searchText)
{
try
{
var result = await BackendService.SearchFilms(searchText);
return result;
}
catch ( ... )
{
UiShowError( ... )
return empty_list;
}
}
But in your case, no backend operations are involved and your whole method is sync. Then, compiler will raise warning because they are not any async operation into your method.
Also, quoting docs:
You must provide a method which has the following signature Task<IEnumerable<T> MethodName(string searchText), to the SearchMethod parameter.
Solution
One easy and readable solution is to make an async call in your method:
private async Task<IEnumerable<Film>> SearchFilms(string searchText)
{
var result = Films
.Where(x => x.Title.ToLower().Contains(searchText.ToLower()))
.ToList();
await Task.CompletedTask; // avoid warning (*1)
return result;
}
But they are other more ways to do it: you can micro-optimized and avoid async and convert result into a task, see aguas's answer.
(*1) Also you can avoid warning writing #pragma warning disable CS1998 above the method (thanks #Henk Holterman )
You are not using an asynchronous method in your private method. The code should be :
private Task<List<Film>> SearchFilms(string searchText)
{
return Task.FromResult(Films.Where(x => x.Title.ToLower().Contains(searchText.ToLower())).ToList());
}
I fixed it by changing the SearchFilms method to the one below, weird.
private async Task<IEnumerable<Film>> SearchFilms(string searchText)
{
return await Task.FromResult(Films.Where(x => x.Title.ToLower().Contains(searchText.ToLower())).ToList());
}
This is a confusing matter for me, hope to describe it correctly.
This is in a Xamarin.Android project:
I have a class like this (simplified):
public class FinishedListener : Java.Lang.Object, IabHelper.IOnIabSetupFinishedListener
{
public IabResult Data { get; internal set; } = null;
public void OnIabSetupFinished(IabResult res)
{
if (res != null) { Data = res; }
}
}
and a calling method:
public class Class1
{
public void Method1()
{
FinishedListener listner = new FinishedListener();
SomeClass.Init(listner );
// Do something with "listner.Data.Response"
}
}
Because Init class works asynchronously, listner.Data will not be available at once.
What's the best way to implement waiting for this scenario?
I am not sure if your Init method is awaitable, assuming it is you can do something like this;
public async void Method1()
{
FinishedListener listner = new FinishedListener();
await SomeClass.Init(listner);
// Do something with "listner.Data.Response"
}
In case it is non-awaitable do this:
public async void Method1()
{
FinishedListener listner = new FinishedListener();
await Task.Run(()=>{ SomeClass.Init(listner); });
// Do something with "listner.Data.Response"
}
When this executes what will happen is that your existing compiler will wait for execution of await SomeClass.Init(listner); and then the next line shall execute.
I need to spawn individual actions that are not aware of each other but can be grouped into something like an execution group. The individual actions are also cascaded so that there is also more than one hierarchy level.
I can only show a small example to make things a bit clearer and open this example as a starting point for discussions.The example does not make complete sense, but it is to show what my intentions are.
In the example I want to start several algorithms that spawn other algorithms. Before I encapsulate the algorithms in an some sort of a context that binds all algorithms that were allocated in this context and adds them to the list of this context. At a certain time it could happen that someone wants to terminate all algorithms the Terminate() method can be called which calls the Termimnate() method on all algorithms in that context. Of course to make this work the algorithms need knowledge of the AlgorithmContext structure.
Another question is how do I transport the AlgorithmContext to the next thread or task? It also need to be threadsafe.
In the case of this example you can argue that I could hand over the CancellationToken down in the hierarchy of the algorithms but please keep in mind this is only an example. If was thinking in the direction of an ExecutionContext but maybe there are better approaches?
public class Algorithm
{
public readonly AlgorithmContext Context;
public Algorithm()
{
Context = AlgorithmContext.Current;
}
public void Terminate() { }
}
public class SmoothImage : Algorithm { }
public class SharpenImage : Algorithm { }
public class Example
{
public void Process(float[] imagedata)
{
// Everyone below should see the AlgorithmContext.Current until the end of the using.
// No one shall be able to interfere the Current inside the braces for example if another
// thread creates another instance of AlgorithmContext.
var Task1 = Task.Run(() =>
{
// The AlgorithmContext.Current shall be Context 1 until the end of the using braces.
using (var ctx = new AlgorithmContext("Context 1"))
{
CancellationTokenSource ct = new CancellationTokenSource();
ct.Token.Register(() => ctx.Terminate());
SmoothImage smoothImage = new SmoothImage();
SharpenImage sharpenImage = new SharpenImage();
}
});
var Task2 = Task.Run(() =>
{
// The AlgorithmContext.Current shall be Context 2 until the end of the using braces.
using (var ctx = new AlgorithmContext("Context 2"))
{
CancellationTokenSource ct = new CancellationTokenSource();
ct.Token.Register(() => ctx.Terminate());
SmoothImage smoothImage = new SmoothImage();
SharpenImage sharpenImage = new SharpenImage();
}
});
var Task3 = Task.Run(() =>
{
// The AlgorithmContext.Current shall be Context 2 until the end of the using braces.
using (var ctx = new AlgorithmContext("Context 3"))
{
var Task4 = Task.Run(() =>
{
// The AlgorithmContext.Current shall be Context 3
CancellationTokenSource ct = new CancellationTokenSource();
ct.Token.Register(() => ctx.Terminate());
SmoothImage smoothImage = new SmoothImage();
SharpenImage sharpenImage = new SharpenImage();
});
}
});
// There is no current context outside here at all times.
Task.WaitAll(Task1, Task2, Task3);
}
}
public class AlgorithmContext : IDisposable
{
List<Algorithm> Algorithms = new List<Algorithm>();
public readonly string Name;
[ThreadStatic]
public static AlgorithmContext Current;
public AlgorithmContext(string name)
{
Name = name;
Current = this;
}
public void Add(Algorithm algorithm) { Algorithms.Add(algorithm); }
public void Terminate()
{
foreach (var algo in Algorithms) algo.Terminate();
Algorithms.Clear();
}
public void Dispose()
{
Current = null;
}
}
I guess it's not as complicated as you described.
You need a context for multiple algorithm. One of the main jobs of this context is to handle termination of algorithms.
So, you need events! I rewrite your entire sample code.
public abstract class Algorithm
{
protected readonly AlgorithmContext _context;
public Algorithm(AlgorithmContext context)
{
_context = context;
_context.Terminated += (sender, e) =>
{
Terminate();
};
}
public abstract void Terminate();
}
public class SmoothImage : Algorithm
{
public SmoothImage(AlgorithmContext context) : base(context)
{
}
public override void Terminate()
{
// do whatever you want
}
}
public class SharpenImage : Algorithm
{
public SharpenImage(AlgorithmContext context) : base(context)
{
}
public override void Terminate()
{
// do whatever you want
}
}
public class Example
{
public void Process(float[] imagedata)
{
var Task1 = Task.Run(() =>
{
using (var ctx = new AlgorithmContext("Context 1"))
{
SmoothImage smoothImage = new SmoothImage(ctx);
SharpenImage sharpenImage = new SharpenImage(ctx);
}
});
// ...
Task.WaitAll(Task1, Task2, Task3);
}
}
public class AlgorithmContext : IDisposable
{
public AlgorithmContext(string name)
{
Name = name;
}
public event EventHandler Terminated;
public string Name { get; }
public void Dispose()
{
Terminate();
}
public void Terminate()
{
Terminated?.Invoke(this, EventArgs.Empty);
}
}
As you said, because algorithms are not aware of each other, so maybe they don't need to be in context.
If you need to terminate every other algorithms within a context, so just call _context.Terminate() somewhere in an algorithm
Anyway, hope it helps
Consider the following example. I have three view models, ViewModel_A, ViewModel_B, and ViewModel_Values.
I want to be able to navigate to ViewModel_Values from either ViewModel_A or ViewModel_B, select a value from ViewModel_Values, then return that value to the calling view model.
Is there a way of passing arguments to previous view models in the navigation stack so that I can simply call ViewModel_Values.Close(this), thereby ensuring that the ViewModels_Values is decoupled from any other view models and can be used with arbitrary "parent" view models?
MvvmCross 5 onwards
From MvvmCross 5 you can use the new IMvxNavigationService that allows you to have a much richer navigation. One of the new features is the possibility to await a value from another ViewModel after navigating to it and should be the approach to take after MvvmCross 5 instead of Messenger, e.g.:
public class ViewModel_A : MvxViewModel
{
private readonly IMvxNavigationService _navigationService;
public ViewModel_A(IMvxNavigationService navigation)
{
_navigationService = navigationService;
}
public override async Task Initialize()
{
//Do heavy work and data loading here
}
public async Task SomeMethod()
{
var result = await _navigationService.Navigate<ViewModel_Values, MyObject, MyReturnObject>(new MyObject());
//Do something with the result MyReturnObject that you get back
}
}
public class ViewModel_Values : MvxViewModel<MyObject, MyReturnObject>
{
private readonly IMvxNavigationService _navigationService;
public ViewModel_Values(IMvxNavigationService navigation)
{
_navigationService = navigationService;
}
public override void Prepare(MyObject parameter)
{
//Do anything before navigating to the view
//Save the parameter to a property if you want to use it later
}
public override async Task Initialize()
{
//Do heavy work and data loading here
}
public async Task SomeMethodToClose()
{
// here you returned the value
await _navigationService.Close(this, new MyReturnObject());
}
}
More info here
HIH
Use messaging center. Here is the sample code.
//for trigger
MessagingCenter.Send<object> (this, "Hi");
//put this where you want to receive your data
MessagingCenter.Subscribe<object> (this, "Hi", (sender) => {
// do something whenever the "Hi" message is sent
});
Installing & using the MvxMessenger plugin is a great way to decouple view model communication in MvvmCross -
In your case, you could set up a new message -
public class ValuesChangedMessage : MvxMessage
{
public ValuesChangedMessage(object sender, int valuea, string valueb)
: base(sender)
{
Valuea = valuea;
Valueb = valueb;
}
public int Valuea { get; private set; }
public string Valueb { get; private set; }
}
In ViewModel_Values, you would act on / publish your UX changes with -
_mvxMessenger.Publish<ValuesChangedMessage>(new ValuesChangedMessage(this, 1, "boo!"));
And in ViewModel_A, ViewModel_B you would subscribe and act on them (as your ViewModel A / B would be still in the navigation stack when you pushed ViewModel_Values from them, so they could receive the message) -
private MvxSubscriptionToken _messageToken;
_messageToken = _mvxMessenger.Subscribe<ValuesChangedMessage>(async message =>
{
// use message.Valuea etc ..
});
More infos here -
https://www.mvvmcross.com/documentation/plugins/messenger?scroll=644
https://www.youtube.com/watch?feature=player_embedded&v=HQdvrWWzkIk
In my case of trying to navigate in this pattern:
//pseudo code
"ModelA" => "ModelB<List<MyObject>>" => "ModelC<MyObject>"
OR
//pseudo code
"ModelA" => "ModelC<MyObject>"
I used the following work around in my ViewDestroy() override of ModelB<List>:
private bool destroyView = true;
public bool DestroyView
{
get => destroyView;
set
{
destroyView = value;
RaisePropertyChanged(() => DestroyView);
}
}
public override void ViewDestroy(bool viewFinishing)
{
viewFinishing = DestroyView;
base.ViewDestroy(viewFinishing);
}
private async Task ModifySelectedObject()
{
DestroyView = false;
MyObject obj = SelectedObject;
MyObject modifiedObj = await _navigationService.Navigate<ModifySingleViewModel, MyObject, MyObject>(new MyObject());
if (modifiedObj != null)
{
obj = modifiedObj;
}
else
{
await Application.Current.MainPage.DisplayAlert("", "No changes made.", "OK");
}
DestroyView = true;
}
This keeps the original
"await _navigationService.Navigate<ModifyMultipleViewModel,
List, List>(new MyObject);"
from ModelA open when navigating to ModelC from ModelB, but still allows the ViewDestroy Method to close otherwise.