Roslyn : Get Current Project / Get Current Doc that is selected - c#

I'm developing a code gen (VSIX) for my team and I'm intendedly using Roslyn as a fundamental engine after I have struggling with VSIX extensibility framework.
My code gen is currently able generate a new csproj for the solution and able to churn out boilerplate code base based on Templating project from VSIX extensibility. As ambitious I am, I'm trying not to rely on static templating project and use Roslyn to churn out the code instead.
My solution has a list of folders and each folder has a list of csproj.
My Question 1 is I'm trying to use Roslyn Workspace API to detect the current document (.cs) that has been open in code editor or am trying to get the current doc id of the selected cs file I right click from the solution explorer.
I have tried to use AdhocWorkspace which so far have failed as I'm not able to get any.
Question 2: If I were to use AdhocWorkspace, am I able to change the default namespace in the csproj properties? Or was it not part of the functionalities in Roslyn Workspace API at the moment?
Thanks.

For #1 some code I have to do the same thing. I am doing stuff with the cursor so I'm going through caretPosition (cursor). There are other ways but the gist is the same, get the current textview the go from that to Roslyn.
You will need to install Microsoft.CodeAnalysis.EditorFeatures.Text which brings in allot of code analysis packages but allows you to use he GetOpenDocumentInCurrentContextWithChanges extension applied on ITextSnapshot
private IVsEditorAdaptersFactoryService GetEditorAdaptersFactoryService()
{
IComponentModel componentModel =(IComponentModel)GetService(typeof(SComponentModel));
return componentModel.GetService<IVsEditorAdaptersFactoryService>();
}
private Microsoft.VisualStudio.Text.Editor.IWpfTextView GetTextView()
{
IVsTextManager textManager = (IVsTextManager)GetService(typeof(SVsTextManager));
if (textManager == null)
return null;
IVsTextView textView = null;
textManager.GetActiveView(1, null, out textView);
if (textView == null)
return null;
return GetEditorAdaptersFactoryService().GetWpfTextView(textView);
}
//code to get the doc
Microsoft.VisualStudio.Text.Editor.IWpfTextView textView = GetTextView();
if (textView != null)
{
SnapshotPoint caretPosition = textView.Caret.Position.BufferPosition;
Document document = caretPosition.Snapshot.GetOpenDocumentInCurrentContextWithChanges();
//do stuff with Roslyn Document
}
"or am trying to get the current doc id of the selected cs file I right click from the solution explorer."
This is really ugly but what I used from a different SO post (don't remember the author) that does work well.
private static bool IsSingleProjectItemSelection(out IVsHierarchy hierarchy, out uint itemid)
{
hierarchy = null;
itemid = VSConstants.VSITEMID_NIL;
int hr = VSConstants.S_OK;
var monitorSelection = Package.GetGlobalService( typeof( SVsShellMonitorSelection ) ) as IVsMonitorSelection;
var solution = Package.GetGlobalService( typeof( SVsSolution ) ) as IVsSolution;
if (monitorSelection == null || solution == null)
return false;
IVsMultiItemSelect multiItemSelect = null;
IntPtr hierarchyPtr = IntPtr.Zero;
IntPtr selectionContainerPtr = IntPtr.Zero;
try
{
hr = monitorSelection.GetCurrentSelection( out hierarchyPtr, out itemid, out multiItemSelect, out selectionContainerPtr );
if (ErrorHandler.Failed( hr ) || hierarchyPtr == IntPtr.Zero || itemid == VSConstants.VSITEMID_NIL)
return false;
// multiple items are selected
if (multiItemSelect != null)
return false;
// there is a hierarchy root node selected, thus it is not a single item inside a project
if (itemid == VSConstants.VSITEMID_ROOT)
return false;
hierarchy = Marshal.GetObjectForIUnknown( hierarchyPtr ) as IVsHierarchy;
if (hierarchy == null)
return false;
Guid guidProjectID = Guid.Empty;
if (ErrorHandler.Failed( solution.GetGuidOfProject( hierarchy, out guidProjectID ) ))
return false;
// if we got this far then there is a single project item selected
return true;
}
finally
{
if (selectionContainerPtr != IntPtr.Zero)
Marshal.Release( selectionContainerPtr );
if (hierarchyPtr != IntPtr.Zero)
Marshal.Release( hierarchyPtr );
}
}
IVsHierarchy hierarchy = null;
uint itemid = VSConstants.VSITEMID_NIL;
if (!IsSingleProjectItemSelection(out hierarchy, out itemid))
return;
string itemFullPath = null;
((IVsProject)hierarchy).GetMkDocument(itemid, out itemFullPath);
if (itemFullPath.EndsWith(".cs"))

Related

VSIX Get Project associated with its Context Menu

I want to create a context menu command for a project. I managed to get the command to show on the right menu by setting the id in the .vsct file to "IDM_VS_CTXT_PROJNODE". And my code example is called correctly:
private FirstCommand(AsyncPackage package, OleMenuCommandService commandService)
{
this.package = package ?? throw new ArgumentNullException(nameof(package));
commandService = commandService ?? throw new ArgumentNullException(nameof(commandService));
var menuCommandID = new CommandID(CommandSet, CommandId);
var menuItem = new MenuCommand(StartNotepad, menuCommandID);
commandService.AddCommand(menuItem);
}
private void StartNotepad(object sender, EventArgs e)
{
//example code
/*var process = new Process();
process.StartInfo.FileName = "Notepad.exe";
process.Start();*/
}
I now need Information about the project (directory, name etc). But all examples on vsix projects only show how to get the current project (I don't even know whether that is the project I clicked on) or don't work for me. They are all old and I don't know if they are currently best practice.
So my question is how do I get information about the project in StartNotepad()?
Thanks for your help.
Use the following method to get an item you clicked on:
private EnvDTE.UIHierarchyItem GetSelectedSolutionExplorerItem()
{
EnvDTE.UIHierarchy solutionExplorer = dte.ToolWindows.SolutionExplorer;
object[] items = solutionExplorer.SelectedItems as object[];
if (items.Length != 1)
return null;
return items[0] as EnvDTE.UIHierarchyItem;
}
And then convert it to a project with GetSelectedSolutionExplorerItem()?.Object as EnvDTE.Project.
Sergey's answer helped me to find the solution. The only think missing from it was how to get the dte in an async way:
private EnvDTE.UIHierarchyItem GetSelectedSolutionExplorerItem()
{
ThreadHelper.ThrowIfNotOnUIThread();
var dte = ServiceProvider.GetServiceAsync(typeof(DTE)).Result as DTE2;
if (dte == null) return null;
var solutionExplorer = dte.ToolWindows.SolutionExplorer;
object[] items = solutionExplorer.SelectedItems as object[];
if (items.Length != 1)
return null;
return items[0] as UIHierarchyItem;
}

After deleting WebExtensionPart from WordprocessingDocument document get corrupted

I am new to open xml sdk and i haven't much idea on how relationship work for wordprocessing document. I want to remove webtask-pane from my existing document which contains webtask-pane and its added through programmatically.
After deleting WebExtensionPart from WordprocessingDocument using open office xml.
Result : document get corrupted.
Reason : Relationship part is exists after deleting WebExtensionPart.
Code :
public static void RemoveTaskPaneExt(WordprocessingDocument package)
{
WebExTaskpanesPart webExTaskpanesPart1 = package.GetPartsOfType<WebExTaskpanesPart>().FirstOrDefault();
if (webExTaskpanesPart1 != null)
{
WebExtensionPart aWebExtension =
webExTaskpanesPart1.GetPartsOfType<WebExtensionPart>()
.Where(
x =>
x.WebExtension.WebExtensionStoreReference.Id ==
System.Configuration.ConfigurationManager.AppSettings["PaneID"])
.FirstOrDefault();
if (aWebExtension != null)
{
bool result = package.WebExTaskpanesPart.DeletePart(aWebExtension);
}
}
}
Please help.
Thanks in advance.
I got the answer using removing all children of task pane.
public static void RemoveTaskPaneExt(WordprocessingDocument package)
{
WebExTaskpanesPart webExTaskpanesPart1 = package.GetPartsOfType<WebExTaskpanesPart>().FirstOrDefault();
if (webExTaskpanesPart1 != null)
{
WebExtensionPart aWebExtension =
webExTaskpanesPart1.GetPartsOfType<WebExtensionPart>()
.Where(
x =>
x.WebExtension.WebExtensionStoreReference.Id ==
System.Configuration.ConfigurationManager.AppSettings["PaneID"])
.FirstOrDefault();
if (aWebExtension != null)
{
webExTaskpanesPart1.Taskpanes.RemoveAllChildren();
bool result = package.WebExTaskpanesPart.DeletePart(aWebExtension);
}
}
}
Happy coding!
The following will remove all WebExtensionTaskpanes and your Word file will be valid. Call this instead of your method above.
private static void RemoveWebExtensionPart(WordprocessingDocument package)
{
WebExTaskpanesPart webExTaskpanesPart1 = package.GetPartsOfType<WebExTaskpanesPart>().FirstOrDefault();
if (webExTaskpanesPart1 != null)
{
bool result2 = package.DeletePart(webExTaskpanesPart1);
}
}

Get if "Allow management of content types" is checked using SharePoint WebServices

I need the information about SharePoint document library whether the "Allow management of content types" is checked or not. I have to use SharePoint web services.
I have looked up in GetListAndView method in Lists.asmx, but found no property in "List" Node or in "View" Node that refer to the management of Content Types .
Could anyone help me out please?
Thanks :)
You can get it from the GetList() method of lists.asmx. Look at the Flags attribute.
Better yet, here's some sample code from https://social.technet.microsoft.com/Forums/sharepoint/en-US/9d6c26a5-279e-4f4e-8dfc-b31acff81813/web-service-to-check-if-the-management-of-content-types-are-allowed?forum=sharepointgeneralprevious
public static bool GetAllowContentTypes(string listName)
{
listservice.Lists ls = new listservice.Lists();
ls.Url = "http://basesmc2008/_vti_bin/lists.asmx";
ls.UseDefaultCredentials = true;
UInt64 flags = 0;
bool contentTypesAllowed = false;
XmlNode node = ls.GetList(listName);
XElement element = XElement.Parse(node.OuterXml);
var result = from e in element.Attributes("Flags")
select e.Value;
if (result != null && UInt64.TryParse(result.First().ToString(), out flags))
contentTypesAllowed = ((flags & ((ulong)0x400000L)) != 0L);
else
return false;
return contentTypesAllowed;
}

How to get Only one acadobject by selection set

I have some trouble to select the targeted acadObject. I get the input via selectionset.SelectonScreen method.
Here i can get more number of object from modelspace based on my filter condition.But i need only one object from the user.
Here i mentioned my code below:
AcadSelectionSet selset= null;
selset=currDoc.selectionset.add("Selset");
short[] ftype=new short[1];
object[] fdata=new object[1];
ftype[0]=2;//for select the blockreference
fdata[0]=blkname;
selset.selectOnScreen ftype,fdata; // Here i can select any no. of blocks according to filter value but i need only one block reference.
Please help me to solve this problem.
That's possible using other Autocad .NET libraries (instead of Interop library). But fortunately, one does not exclude the other.
You will need to reference the libraries containing the following namespaces:
using Autodesk.Autocad.ApplicationServices
using Autodesk.Autocad.EditorInput
using Autodesk.Autocad.DatabaseServices
(you get those downloading the Object Arx libraries for free from Autodesk):
You will need to access the Editor from an autocad Document.
By the code you've shown, you're probably working with AcadDocument documents.
So, to transform an AcadDocument into a Document, do this:
//These are extension methods and must be in a static class
//Will only work if Doc is saved at least once (has full name) - if document is new, the name will be
public static Document GetAsAppServicesDoc(this IAcadDocument Doc)
{
return Application.DocumentManager.OfType<Document>().First(D => D.Name == Doc.FullOrNewName());
}
public static string FullOrNewName(this IAcadDocument Doc)
{
if (Doc.FullName == "")
return Doc.Name;
else
return Doc.FullName;
}
Once you've got a Document, get the Editor, and the GetSelection(Options, Filter)
The Options contains a property SingleOnly and a SinglePickInSpace. Setting that to true does what you want. (Try both to see wich works better)
//Seleciton options, with single selection
PromptSelectionOptions Options = new PromptSelectionOptions();
Options.SingleOnly = true;
Options.SinglePickInSpace = true;
//This is the filter for blockreferences
SelectionFilter Filter = new SelectionFilter(new TypedValue[] { new TypedValue(0, "INSERT") });
//calls the user selection
PromptSelectionResult Selection = Document.Editor.GetSelection(Options, Filter);
if (Selection.Status == PromptStatus.OK)
{
using (Transaction Trans = Document.Database.TransactionManager.StartTransaction())
{
//This line returns the selected items
AcadBlockReference SelectedRef = (AcadBlockReference)(Trans.GetObject(Selection.Value.OfType<SelectedObject>().First().ObjectId, OpenMode.ForRead).AcadObject);
}
}
this is a direct quote from autocad developer help
http://docs.autodesk.com/ACD/2013/ENU/index.html?url=files/GUID-CBECEDCF-3B4E-4DF3-99A0-47103D10DADD.htm,topicNumber=d30e724932
There is tons of documentation the AutoCAD .NET API.
you will need to have
[assembly: CommandClass(typeof(namespace.class))]
above your namespace if you want to be able to invoke this command from the command line after you NetLoad the .dll, if it is a classLibrary.
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
[CommandMethod("SelectObjectsOnscreen")]
public static void SelectObjectsOnscreen()
{
// Get the current document and database
Document acDoc = Application.DocumentManager.MdiActiveDocument;
Database acCurDb = acDoc.Database;
// Start a transaction
using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
{
// Request for objects to be selected in the drawing area
PromptSelectionResult acSSPrompt = acDoc.Editor.GetSelection();
// If the prompt status is OK, objects were selected
if (acSSPrompt.Status == PromptStatus.OK)
{
SelectionSet acSSet = acSSPrompt.Value;
// Step through the objects in the selection set
foreach (SelectedObject acSSObj in acSSet)
{
// Check to make sure a valid SelectedObject object was returned
if (acSSObj != null)
{
// Open the selected object for write
Entity acEnt = acTrans.GetObject(acSSObj.ObjectId,
OpenMode.ForWrite) as Entity;
if (acEnt != null)
{
// Change the object's color to Green
acEnt.ColorIndex = 3;
}
}
}
// Save the new object to the database
acTrans.Commit();
}
// Dispose of the transaction
}
}

Determine display mode of sharepoint page

I have this question many times and bored while trying to find good solution.
Dont understand why microsoft not include method which can easy determine mode of display page: "normal display" or in "design mode".
It have many advices of check different variables, but it cant uniquely say that page in design on different type of page(webpart page and wiki page) and on postback or not.
Is finally tired me and i write this:
public static bool IsDesignTime()
{
if (SPContext.Current.IsDesignTime) return true;
if (HttpContext.Current.Request.QueryString["DisplayMode"] != null)
return true;
var page = HttpContext.Current.Handler as Page;
if(page == null) return false;
var inDesign = page.Request.Form["MSOLayout_InDesignMode"];
var dispMode = page.Request.Form["MSOSPWebPartManager_DisplayModeName"];
var wikiMode = page.Request.Form["_wikiPageMode"];
var we = page.Request.Form["ctl00$PlaceHolderMain$btnWikiEdit"];
if (inDesign == null & dispMode == null) return false; //normal display
if (we == "edit") return true; //design on wiki pages
if (page is WikiEditPage & page.IsPostBack & inDesign == "" & dispMode == "Browse" & wikiMode == "") return false; //display wiki on postback
if (inDesign == "" & dispMode == "Browse" & (wikiMode == null | wikiMode == "")) return false; //postback in webpart pages in display mode
if (inDesign == "0" & dispMode == "Browse") return false; //exiting design on webpart pages
return true;
}
Does anybody have better solution?
you have 2 case to detect the page mode:
In case you are using a team site :
if (Microsoft.SharePoint.SPContext.Current.FormContext.FormMode == SPControlMode.Edit)
{
ltr.Text = "EditMode2";
}
else
{
ltr.Text = "ViewMode";
}
in case you are using a publishing site:
if (Microsoft.SharePoint.SPContext.Current.FormContext.FormMode == SPControlMode.Display)
{
// your code to support display mode
}
else // Microsoft.SharePoint.SPContext.Current.FormContext.FormMode = SPControlMode.Edit
{
// your code to support edit mode
}
if your work in WebpartPage than below code work for me
WebPartManager mgr = this.WebPartManager;
if (mgr.DisplayMode == WebPartManager.EditDisplayMode)
{
// logic when in Edit Mode
}
else
{
}
I had a hard time getting any of these answers to work in Sharepoint 2013 given all the scenarios. I also couldn't get EditModePanel to work consistently. I found a snippet in this article that seems to work in every scenario I've tried so far.
Note: This does not work in Page_Init but will work in Page_Load
var isPublishing = SPContext.Current.FormContext.FormMode != SPControlMode.Invalid;
var wpDMode = WebPartManager.GetCurrentWebPartManager(Page).DisplayMode.Name;
var isEditing = isPublishing
? SPContext.Current.FormContext.FormMode != SPControlMode.Display
: (wpDMode.Equals("Edit") || wpDMode.Equals("Design"));
Then you can simply check isEditing for your conditions.
please try this code ..
if (Microsoft.SharePoint.SPContext.Current.FormContext.FormMode == SPControlMode.Display)
{
// your code to support display mode
}
else // Microsoft.SharePoint.SPContext.Current.FormContext.FormMode = SPControlMode.Edit
{
// your code to support edit mode
}
Handling SharePoint Page Modes.
This works for me , I resolved my critical issue by using below lines.
if (Microsoft.SharePoint.SPContext.Current.FormContext.FormMode == SPControlMode.Edit)
{
alert.Text = "EditMode2";
}
else
{
alert.Text = "ViewMode";
}

Categories