How to get Current ActiveDocument in Visual Studio Extension using MEF? - visual-studio

I'm working on Visual Studio 2013 Extension using MEF while trying to read Active Document Content Type and Code. Presently it only reads at Opening Time of the Document/ProjectItem in the Editor. Once these are opened, it doesn't read these again whenever we switch between opened Document Tabs.
Requirement: I want this extension to read the Content Type and Code Text of current Active Document.
Updated:
Problem: I know, using EnvDTE80.DTE2.ActiveWindow, I can get currently focused document, but I'm confused here that how to call this code to read the Currently Active Document/Window things? Let's say if we have 10 Documents, the active document (which got current focus) needs to be read by the this extension. And here VsTextViewCreated is only called whenever we open a new document or the one closed before i.e Text View is Created. It won't be called upon already open documents (i.e. Text View already created) and so we won't be able to get updated wpfTextView object upon moving the focus on other already open documents. And I'm confused here how to call this using DTE2.ActiveDocument or DTE2.ActiveWindow event handlers.
Question:
Is this possible in MEF, without using DTE?
Is there any Interface dealing with TextViews already present in VS editor?
Here is my code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.VisualStudio.Text.Tagging;
using Microsoft.VisualStudio.Text.Editor;
using System.ComponentModel.Composition;
using Microsoft.VisualStudio.Utilities;
using Microsoft.VisualStudio.Editor;
using Microsoft.VisualStudio.TextManager.Interop;
using Microsoft.VisualStudio.OLE.Interop;
using System.Diagnostics;
namespace VSIXProject_Test
{
[Export(typeof(IVsTextViewCreationListener))]
[ContentType("code")]
[TextViewRole(PredefinedTextViewRoles.Editable)]
class VsTextViewCreationListener : IVsTextViewCreationListener
{
[Import]
IVsEditorAdaptersFactoryService AdaptersFactory = null;
public void VsTextViewCreated(IVsTextView textViewAdapter)
{
var wpfTextView = AdaptersFactory.GetWpfTextView(textViewAdapter);
if (wpfTextView == null)
{
Debug.Fail("Unable to get IWpfTextView from text view adapter");
return;
}
Debug.Write(wpfTextView.TextBuffer.ContentType.TypeName);
}
}
}

Fortunately, I got what I was trying to achieve. A helper solution is already posted here:
I used the helper method in dte2.Events.WindowsEvents.WindowActived and getting IVsTextView object to retrieve the Text Buffer. Here is my Code snippet of WindowActivated event:
void WindowEvents_WindowActivated(EnvDTE.Window GotFocus, EnvDTE.Window LostFocus)
{
if (null != GotFocus.Document)
{
Document curDoc = GotFocus.Document;
Debug.Write("Activated : " + curDoc.FullName);
this.IvsTextView=GetIVsTextView(curDoc.FullName); //Calling the helper method to retrieve IVsTextView object.
if (IvsTextView != null)
{
IvsTextView.GetBuffer(out curDocTextLines); //Getting Current Text Lines
//Getting Buffer Adapter to get ITextBuffer which holds the current Snapshots as wel..
Microsoft.VisualStudio.Text.ITextBuffer curDocTextBuffer = AdaptersFactory.GetDocumentBuffer(curDocTextLines as IVsTextBuffer);
Debug.Write("\r\nContentType: "+curDocTextBuffer.ContentType.TypeName+"\nTest: " + curDocTextBuffer.CurrentSnapshot.GetText());
}
}
}
This is now working with all the code documents opened in VS Editor. Hope this would help others like me.

Related

Show tooltip on hover over text

I want to create extension that allows to show custom message when I hover over a text.
E.g. "test-text" should give tooltip "OK" instead of current "ITrackin..."
I tried to follow https://learn.microsoft.com/en-us/visualstudio/extensibility/walkthrough-displaying-quickinfo-tooltips?view=vs-2019
but people are stating that it is not working and it's quite long way of doing this.
I cannot find any more docs on this. I know how to display it in on-click window/get currently selected text.
The sample send by Lance Li-MSFT was really helpful, but in order to get this working I had to spend some time.
Important steps:
Import LineAsyncQuickInfoSourceProvider.cs and LineAsyncQuickInfoSource.cs
Add reference to System.ComponentModel.Composition by add reference dialog (right click on the project name)
Get missing references by installing them using NuGet Package Manager
To initialize MEF components, you’ll need to add a new Asset to source.extension.vsixmanifest.
<Assets>
...
<Asset Type = "Microsoft.VisualStudio.MefComponent" d:Source="Project" d:ProjectName="%CurrentProject%" Path="|%CurrentProject%|" />
</Assets>
LineAsyncQuickInfoSourceProvider.cs
It's just used to display quick info/tooltip.
using Microsoft.VisualStudio.Language.Intellisense;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Utilities;
using System.ComponentModel.Composition;
namespace JSONExtension
{
[Export(typeof(IAsyncQuickInfoSourceProvider))]
[Name("Line Async Quick Info Provider")]
[ContentType("any")]
[Order]
internal sealed class LineAsyncQuickInfoSourceProvider : IAsyncQuickInfoSourceProvider
{
public IAsyncQuickInfoSource TryCreateQuickInfoSource(ITextBuffer textBuffer) //creates instance of LineAsyncQuickInfoSource for displaying Quick Info
{
return textBuffer.Properties.GetOrCreateSingletonProperty(() => new LineAsyncQuickInfoSource(textBuffer)); //this ensures only one instance per textbuffer is created
}
}
}
LineAsyncQuickInfoSource.cs
Here you can customize what you want to display.
using Microsoft.VisualStudio.Language.Intellisense;
using Microsoft.VisualStudio.Language.StandardClassification;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Adornments;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace JSONExtension
{
internal sealed class LineAsyncQuickInfoSource : IAsyncQuickInfoSource
{
private ITextBuffer _textBuffer;
public LineAsyncQuickInfoSource(ITextBuffer textBuffer)
{
_textBuffer = textBuffer;
}
// This is called on a background thread.
public Task<QuickInfoItem> GetQuickInfoItemAsync(IAsyncQuickInfoSession session, CancellationToken cancellationToken)
{
var triggerPoint = session.GetTriggerPoint(_textBuffer.CurrentSnapshot);
if (triggerPoint != null)
{
var line = triggerPoint.Value.GetContainingLine();
var lineSpan = _textBuffer.CurrentSnapshot.CreateTrackingSpan(line.Extent, SpanTrackingMode.EdgeInclusive);
var text = triggerPoint.Value.GetContainingLine().GetText(); //get whole line of current cursor pos
ContainerElement dataElm = new ContainerElement(
ContainerElementStyle.Stacked,
new ClassifiedTextElement(
new ClassifiedTextRun(PredefinedClassificationTypeNames.Keyword, "MESSAGE TO EDIT: " + text.ToString())
));
return Task.FromResult(new QuickInfoItem(lineSpan, dataElm)); //add custom text from above to Quick Info
}
return Task.FromResult<QuickInfoItem>(null); //do not add anything to Quick Info
}
public void Dispose()
{
// This provider does not perform any cleanup.
}
}
}

Binding iOS Framework leads to corrupted temporary files

I have a pretty massive iOS Framework to bind.
This framework depends on two others.
I created the binding project, successfully generated the ApiDefinitions.cs file and StructsAndEnums.cs. So far so good.
Now, when I try to build it in Debug mode, I get about 30+ errors regarding syntax issues in temporary files.
These errors only occur in Trampolines.g.cs, and SupportDelegates.g.cs files.
The worst of the two is the SupportDelegates.g.cs file which looks like this :
//
// Auto-generated from generator.cs, do not edit
//
// We keep references to objects, so warning 414 is expected
#pragma warning disable 414
using System;
using System.Drawing;
using System.Diagnostics;
using System.ComponentModel;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;
using UIKit;
using GLKit;
using Metal;
using MapKit;
using Photos;
using ModelIO;
using SceneKit;
using Contacts;
using Security;
using Messages;
using AudioUnit;
using CoreVideo;
using CoreMedia;
using QuickLook;
using CoreImage;
using SpriteKit;
using Foundation;
using CoreMotion;
using ObjCRuntime;
using AddressBook;
using MediaPlayer;
using GameplayKit;
using CoreGraphics;
using CoreLocation;
using AVFoundation;
using NewsstandKit;
using FileProvider;
using CoreAnimation;
using CoreFoundation;
namespace System.Action`1[[Foundation.NSString, Xamarin.iOS, Version=0.0.0 {
public delegate void 0, Culture=neutral, PublicKeyToken=84e04ff9cfb79065]] (NSString obj);
}
The Trampolines.g.cs file is generating almost completely, but the same kind of issue appears in the middle of the generated C# code:
static internal class SDActionArity2V1 {
static internal readonly DActionArity2V1 Handler = Invoke;
[MonoPInvokeCallback (typeof (DActionArity2V1))]
static unsafe void Invoke (IntPtr block, IntPtr arg1, IntPtr arg2) {
var descriptor = (BlockLiteral *) block;
var del = (global::System.Action<NSString, global::System.Action<NSString>>) (descriptor->Target);
if (del != null)
del ( Runtime.GetNSObject<NSString> (arg1), (System.Action`1[Foundation.NSString]) Marshal.GetDelegateForFunctionPointer (arg2, typeof (System.Action`1[Foundation.NSString])));
}
}
Some other binding projects is generating the same kind of files, and eveything seems to be working fine, so I'm actually struggling to find the root cause of this.
Any ideas ?
EDIT:
To give more infos here is the generated interface definition
https://gist.github.com/Miiite/367e17335431e932acdc92c65b504bd4
EDIT2: Here is what sharpie generated as log output
https://gist.github.com/Miiite/e272622df2e853d53f1add4c8eb4eabf
I believe the issue is here:
void PresentCheckoutForSubmittingTransactionCompletionHandler(Action<OPPTransaction, NSError> completionHandler, [NullAllowed] Action<NSString, Action<NSString>> paymentBrandSelectedHandler, [NullAllowed] Action cancelHandler);
I think the Action inside the Action is causing the problem, the generator might not support this, so you might need to find a different way of binding that.
This problematic method can definitely be bound, if you need it here is how (I omitted the rest of the class members for brevity):
delegate void OPPPaymentBrandSelectedHandler (string paymentBrand, [BlockCallback] OPPPaymentCompletionHandler completionHandler);
delegate void OPPPaymentCompletionHandler ([NullAllowed] string checkoutId);
[BaseType (typeof (NSObject))]
interface OPPCheckoutProvider {
// -(void)presentCheckoutForSubmittingTransactionCompletionHandler:(void (^ _Nonnull)(OPPTransaction * _Nullable, NSError * _Nullable))completionHandler
// paymentBrandSelectedHandler:(void (^ _Nullable)(NSString * _Nonnull, void (^ _Nonnull)(NSString * _Nullable)))paymentBrandSelectedHandler
// cancelHandler:(void (^ _Nullable)(void))cancelHandler;
[Export("presentCheckoutForSubmittingTransactionCompletionHandler:paymentBrandSelectedHandler:cancelHandler:")]
void PresentCheckoutForSubmittingTransactionCompletionHandler(Action<OPPTransaction, NSError> completionHandler, [NullAllowed] OPPPaymentBrandSelectedHandler paymentBrandSelectedHandler, [NullAllowed] Action cancelHandler);
}
The trick is to instead of using nested Actions you need to break it into two delegates and use the BlockCallback attribute to instruct the generator that the parameter should be marshaled as an ObjC block, then just use the parent delegate as the type of the paymentBrandSelectedHandler parameter inside the PresentCheckoutForSubmittingTransactionCompletionHandler method.
Hope this helps to get you completely unblocked. That said we should report a much better error, I'll discuss with the team and have a look.
Cheers!

Web Api OData Controller returns blank page no error

I'm developing a library of OData queries using Web Api and ODataController. When I go to run my api from a web browser it returns nothing. I don't get an error of any kind. I can debug in Visual Studio and see clearly that the method runs and successfully returns my results as an IQueryable<>. Somewhere under the hood it's discarding my data. Has anyone else seen or encountered this? I've included my code below for reference:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using System.Web.Http.OData;
using Epm.Core.Model;
using System.Web.Http.OData.Query;
using Epm.Data.Access;
using Epm.Service.Assemblers;
namespace Epm.Service.Web.Controllers.OData
{
public class SearchActionsController : ODataController
{
private readonly EpmEntities context = new EpmEntities();
[Queryable(AllowedQueryOptions=AllowedQueryOptions.All)]
public IQueryable<ActionStepDisplay> Get(int planId, int? factorId, bool? showArchived)
{
var user = this.GetCurrentUser();
var results = (from p in context.SearchActions(user.SessionId, planId, factorId, showArchived, 1, null)
select p).ToModel().ToArray();
return results.AsQueryable();
}
protected override void Dispose(bool disposing)
{
context.Dispose();
base.Dispose(disposing);
}
}
}
My configuration:
ODataModelBuilder modelBuilder = new ODataConventionModelBuilder();
modelBuilder.EntitySet<Epm.Core.Model.ActionStep>("SearchActions");
Microsoft.Data.Edm.IEdmModel model = modelBuilder.GetEdmModel();
config.Routes.MapODataRoute("ODataRoute", "odata", model);
You are returning ActionStepDisplay from your method, but in builder you specified ActionStep as the entity.
May be Not Acceptable (406) in the response header
Possibly the problem lies with the MediaFormatter, which gets called after the controller has finished. When a media type formatter encounters a reference loop (where object A reference B and B references A) then you need to tell the media type formatter how to handle that, so in the Json Media Type Formatter you do something like...
json.SerializerSettings.PreserveReferencesHandling =
Newtonsoft.Json.PreserveReferencesHandling.All;
See Documentation Here
I'd recommend you use Fiddler to see what is actually going on. You say your not getting a response in the browser, so what HTTP code is returned? You can use Fiddler to find out...

sitecore-- how to get all the publishing items by code when a directory is being published

How to get all the publishing items by code when a directory is being published and which event should I add my handler to, publish:begin or publish:itemProcessing?
If you're looking to setup a custom event handler start with the web.config reference.
<event name="publish:begin">
<handler type="YourNamespace.YourClass, YourLibrary" method="YourHandlerMethod" />
</event>
Then create a class that will support this reference.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using Sitecore.Diagnostics;
using Sitecore.Sites;
using Sitecore.Configuration;
using Sitecore.Caching;
using Sitecore.Events;
using Sitecore.Publishing;
using Sitecore.Data.Events;
using Sitecore.Data;
using Sitecore.Data.Items;
namespace YourNamespace {
public class YourClass {
public void YourHandlerMethod(object sender, EventArgs args) {
Assert.ArgumentNotNull(sender, "sender");
Assert.ArgumentNotNull(args, "args");
//try to get the sitecore event args
if (args.GetType().ToString().Equals("Sitecore.Events.SitecoreEventArgs")) {
SitecoreEventArgs sargs = (SitecoreEventArgs)args;
foreach (object o in sargs.Parameters) {
//try to get the publisher object
if (o.GetType().ToString().Equals("Sitecore.Publishing.Publisher")) {
Publisher p = (Publisher)o;
if (p != null) {
Item root = p.Options.RootItem;
bool b = p.Options.RepublishAll;
if(p.Options.Mode.Equals(PublishMode.SingleItem)){
//only one item published
}
}
}
}
}
}
}
}
From this class you can try to access the publisher object which will give you the root item published and publish options. The publish options will tell you if there was a single item published or if it published all versions of languages.
Depending on your real needs, it might make more sense to inject a custom processor into the publishItem pipeline rather than use publish:itemProcessing event. If you take a closer look at that pipeline (search for "<publishItem") in web.config, you'll that those events (publish:itemProcessing and publish:itemProcessed) are generated by the appropriate processors of pipeline.
NOTE: the publishing process is rather complex and I would not recommend doing anything with the item being published that can influence the process in general. I can't give you an example here - only your fantasy sets the limits...
Note also, that with those events, as well as the pipeline I mentioned, you operate with 1 item at a time - it will be called for each item being published. This can become performance critical...
UPDATE: You can read more about pipeline in this blog post. Apart from being useful itself, it contains more useful links on the subject.

Renaming config files with Visual Studio setup project

My applications have a handful of config files, each one particular to the machine it's being deployed to (dev, production, etc.) I'm wanting to add them to the setup project, use radio buttons to select one and then have a script (or maybe custom action?) rename the selected files to connectionStrings.config, settings.config, etc.
Is this feasible/possible with a setup project?
To give you an idea, my configs might look like this:
DEV connectionStrings.config
PROD connectionStrings.config
After the user chooses DEV or PROD in the installer radiobutton UI, I would like the chosen config to be renamed to
connectionStrings.config
Considering it's a VS setup project, I have a feeling I'm asking for way too much and that I will get an interesting response as most setup project questions do :)
I created a setup project to set connection strings and i used the following which works perfectly for me.
Create a installer.cs file for the setup.
using System;
using System.Data.SqlClient;
using System.Xml;
using System.Configuration;
using System.Reflection;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Collections;
using System.Configuration.Install;
namespace YOURNAMESPACE
{
[RunInstaller(true)]
public partial class installer : System.Configuration.Install.Installer
{
public installer()
{
InitializeComponent();
}
public override void Install(System.Collections.IDictionary stateSaver)
{
base.Install(stateSaver);
}
public override void Commit(IDictionary savedState)
{
base.Commit(savedState);
try
{
string DatabaseString1 = "FULL NAME OF CONNECTION STRING";
ConnectionConfigure(DatabaseString1);
}
catch (Exception e)
{
base.Rollback(savedState);
}
}
public override void Rollback(IDictionary savedState)
{
base.Rollback(savedState);
}
public override void Uninstall(IDictionary savedState)
{
base.Uninstall(savedState);
}
private void ConnectionConfigure(string DatabaseString)
{
string dataSource = "";
dataSource = "Provider="+ Context.Parameters["InitialCatalog"]+ ";" + "Data Source=" + Context.Parameters["DataSource"];
ExeConfigurationFileMap map = new ExeConfigurationFileMap();
string configFile = string.Concat(Assembly.GetExecutingAssembly().Location, ".config");
map.ExeConfigFilename = configFile;
System.Configuration.Configuration config = System.Configuration.ConfigurationManager.
OpenMappedExeConfiguration(map, System.Configuration.ConfigurationUserLevel.None);
string connectionsection = config.ConnectionStrings.ConnectionStrings
[DatabaseString].ConnectionString;
ConnectionStringSettings connectionstring = null;
if (connectionsection != null)
{
config.ConnectionStrings.ConnectionStrings.Remove(DatabaseString);
}
connectionstring = new ConnectionStringSettings(DatabaseString, dataSource);
config.ConnectionStrings.ConnectionStrings.Add(connectionstring);
config.Save(ConfigurationSaveMode.Modified, true);
ConfigurationManager.RefreshSection("connectionStrings");
}
}
}
Add project output to your setup project then begin to setup this setup project.
Right click setup project
Add text boxes and create the UI
Set custom actions
Create project output actions
Custom action properties
That is how i setup mine (I have attached screenshots to help explain my process but in short. Create setup project and installer.cs file. Add project output to setup project, add a UI so that the user can input a connection string and or provider for connection string, add custom actions so that the inputs can be read by the installer.cs file and then congratulations it should change the connection string.
Hope this helps.
I've come to the conclusion that this is impossible due to VS severely lacking on setup project configuration options. Instead I added a radio button control during the setup process and assigned the choices a variable name. In the file system I added all of my config files and then set conditions to each one. They referenced values to my radio button choices in order to copy during deployment.
I've done this many times, and basically I just install both files with different names. The application can ask the user which file to use, and change it anytime they want because many users don't know enough about the application to make this choice at install time.
You get interesting answers to setup questions like this because many people want to configure the application during the installation. Why not just let the setup install the files and have the app do its own configuration?

Resources