Why is TextDecorationCollection always empty in WPF RichTextBox? - c#

I've seen some questions address this problem domain and the unnecessary complexity of handling underlines (mainly applying them, but I want to detect them), but none that I can recall suggesting as I am here that the default strategies for accomplishing this create illogical false negatives. Furthermore, most of the previous questions I've referred to have used a different control (e.g. TextBlock) and/or have obselete syntax.
The problem
(.NET Core 3.1) I would simply like to programatically detect if a WPF RichTextBox selection contains any TextDecorations, but debugging shows that the TextDecorationCollection is always empty, even when the selection is all underlined.
As you can see, TextDecorationCollection returns empty even when examining a fully underlined Inline (Run)
For context, this screenshot just shows the plain text representation of the FlowDocument
What I've tried
1
TextRange myrange = new TextRange(MainRtb.Selection.Start, MainRtb.Selection.End);
if (myrange.GetPropertyValue(Inline.TextDecorationsProperty).Equals(TextDecorations.Underline)) { }
2
TextRange myrange = new TextRange(MainRtb.Selection.Start, MainRtb.Selection.End);
var obj = myrange.GetPropertyValue(Inline.TextDecorationsProperty);
if (obj == DependencyProperty.UnsetValue) {
log.addLog("mix format");
}
if (obj is TextDecorationCollection) {
var objProper = obj as TextDecorationCollection;
if (objProper.Count > 0) {
log.addLog("all underlined");
} else {
log.addLog("none underlined");
}
}
3
foreach (Block block in MainRtb.Document.Blocks) {
Paragraph p = block as Paragraph;
if (p != null) {
foreach (Inline inline in p.Inlines) {
InlineUIContainer iuic = inline as InlineUIContainer;
if (iuic != null) {
Console.WriteLine("found underline");
}
}
}
}
Theory
This post https://social.msdn.microsoft.com/Forums/vstudio/en-US/3ac626cf-60aa-427f-80e9-794f3775a70e/how-to-tell-if-richtextbox-selection-is-underlined?forum=wpf suggests that
myrange.GetPropertyValue(Inline.TextDecorationsProperty)
doesn't work properly due to an issue inside the "GetPropertyValue()" method, but it's a very old post. I couldn't run Jim's solution exactly because he initialises an "IEnumerable" which now needs to be declared with a type of some kind - at least that's what VS2019 said.
Test Rtf File:
https://docs.google.com/document/d/1YQmGsPcH4hX2XsP7KBdFqTFg4XjrSv8I/edit?usp=sharing&ouid=111968029811979231347&rtpof=true&sd=true

Try the following method:
public static void GetDecorations(RichTextBox rtb)
{
TextDecorationCollection decors = rtb.Selection.GetPropertyValue(Inline.TextDecorationsProperty) as TextDecorationCollection;
if (decors == null || decors.Count == 0)
{
if (rtb.Selection.Start.Parent is Run run)
{
if (run.Parent is Span span)
{
decors = span.TextDecorations;
}
else if (run.Parent is Paragraph para)
{
decors = para.TextDecorations;
}
}
}
if (decors is TextDecorationCollection tdc)
{
// TODO: Processing decorations...
}
}
I suppose the problem you are discovered is related to the particular structure of the FlowDocument after loading your RTF document and it might be described as follow.
When the RTF document is loaded for the underlined text tBox. See TextPointer for more information on text position terminology like "insertion position" a Run inline is created for this text, but the Run.TextDecorations property doesn't contain the actual decorations for this text. Instead of that the decorations settings are stored in the parent Span object that contains this Run. In another words, these decorations property is inherited from parent to child.
Therefore, if no decorations property is set on the current Run object, then you should to check the TextDecorations property in the parent object.

Related

How to get selection and replace it with certain text using UI Automation Lib?

So I have a WinForms application running on .NET 7.0 and I need to get a selection from a focused control (and this should work in any application that supports text selection) and replace it with specific text. The problem is that I can replace all the text in the field, but I can't replace just the part of the text that I need.
private void GetSelectedText()
{
try
{
var element = AutomationElement.FocusedElement;
if (element != null)
{
object pattern;
object valuePattern;
// Here I can get the selected text from almost any application.
if (element.TryGetCurrentPattern(TextPattern.Pattern, out pattern))
{
var tp = (TextPattern)pattern;
var sb = new StringBuilder();
foreach (var r in tp.GetSelection())
{
sb.AppendLine(r.GetText(-1));
}
// Debug info:
MessageBox.Show(sb.ToString());
}
// And this code sets value of the focused control to "aaaaaaa".
if (element.TryGetCurrentPattern(ValuePattern.Pattern, out valuePattern))
{
((ValuePattern)valuePattern).SetValue("aaaaaaa");
}
}
}
catch (Exception ex)
{
// Debug info:
Console.WriteLine(ex.Message, ex.StackTrace);
}
}
I've also tried WinAPI calls for copying and pasting the value but I couldn't figure out how to replace the text without using the clipboard or writing a huge amount of code.
I would be grateful if you could help me with this.
Text pattern doesn't provide any method to modify the text, and the value pattern also allows just setting the whole value. What you can do is modifying the selection through direct keyboard input. This is what I tried and works as expected:
var element = AutomationElement.FocusedElement;
if (element != null)
{
if (element.TryGetCurrentPattern(TextPattern.Pattern, out object pattern))
{
var tp = (TextPattern)pattern;
var selection = tp.GetSelection().FirstOrDefault();
if(selection != null)
{
SendKeys.SendWait("XXXXXX");
}
}
}
Fore more information, take a look at TextPattern overview
The TextPattern classes do not provide a means to insert or modify
text. However, depending on the control, this may be accomplished by
the UI Automation ValuePattern or through direct keyboard input. See
the TextPattern Insert Text Sample for an example.

C# System.Windows.Automation get element text

I am trying to get text/labels from application controls with Automation in C#.
So far I am able to obtain AutomationElement tree of application (for example Notepad) with this function:
private void WalkControlElements(AutomationElement rootElement, TreeNode treeNode)
{
AutomationElement elementNode = TreeWalker.ContentViewWalker.GetFirstChild(rootElement);;
while (elementNode != null)
{
TreeNode childTreeNode = treeNode.Nodes.Add(elementNode.Current.ControlType.LocalizedControlType);
// here I want to get text from 'elementNode'
WalkControlElements(elementNode, childTreeNode);
elementNode = TreeWalker.ControlViewWalker.GetNextSibling(elementNode);
}
}
I tried to follow this article http://msdn.microsoft.com/en-us/library/ms788751(v=vs.110).aspx but it only can get text attributes as font name, font weight and so on.
Could anybody point me to the right procedure how to get element text with Automation?
That sample is showing you how to get text attributes, i.e. information about the display of the text in the UI, not the actual displayed text. Getting all the actual displayed text for a general application is more difficult that it might first appear.
It is made difficult by the fact that there are several ways get text and there is inconsistent support by applications and controls. There are two patterns that are of some use, ValuePattern and TextPattern. By convention the Name property contains text displayed to the user however adherence to this is inconsistent. Below is a helper method that I've used in UI automation for testing. It basically goes through those patterns checking the control for support and falls back to the Name.
public static class AutomationExtensions
{
public static string GetText(this AutomationElement element)
{
object patternObj;
if (element.TryGetCurrentPattern(ValuePattern.Pattern, out patternObj))
{
var valuePattern = (ValuePattern)patternObj;
return valuePattern.Current.Value;
}
else if (element.TryGetCurrentPattern(TextPattern.Pattern, out patternObj))
{
var textPattern = (TextPattern)patternObj;
return textPattern.DocumentRange.GetText(-1).TrimEnd('\r'); // often there is an extra '\r' hanging off the end.
}
else
{
return element.Current.Name;
}
}
}
This takes care of getting the text out of simple controls like labels, textboxes (both vanilla textbox and richtextbox), and buttons. Controls like listboxes and comboboxes (esp. in WPF) can be tricker because their items can be virtualized so they may not exist in the automation tree until the user interacts with them. You may want to filter and call this method only on certain UI Automation control types like Edit, Text, and Document which you know contain text.
Mike Zboray answer works fine. In case you have access to pattern-Matching, here is the same (condensed) code :
public static class AutomationExtensions
{
public static string GetText(this AutomationElement element)
=> element.TryGetCurrentPattern(ValuePattern.Pattern, out object patternValue) ? ((ValuePattern)patternValue).Current.Value
: element.TryGetCurrentPattern(TextPattern.Pattern, out object patternText) ? ((TextPattern)patternText).DocumentRange.GetText(-1).TrimEnd('\r') // often there is an extra '\r' hanging off the end.
: element.Current.Name;
}

FastColoredTextbox AutoWordSelection?

FastColoredTextbox is an user-control that can be downloaded in this url, it looks like this:
Its an amazing control but only can select one word when doubleclicking on the text, can't hold the mouse to select more words, so it only selects the entire current word over the mouse pointer even if you try to move the mouse cursor to left or right to select more text.
I have not found any information explaining the problem, and all of the official example projects has this problem.
Nobody means how to make an AutoWordSelection equivalent of a default TextBox for a FastcoloredTextbox control, but even the most important thing is:
How to select just more than one word with the mouse?
UPDATE:
#mostruash answer is very instructive but in all this time I could not carry out the modifications by myself.
I need a huge help from a C# programmer to carry out this task, my knowledge of C# is very low and the modifications that I made to the source did not work (don't compiled), I went back to the original user-control source to not end up spoiling more. I hate to say this but this time I need the job done, this source is too much for me.
If I'm requesting for too much then maybe with the necesary extended instructions of a C# developer, explaining how to accomplish this step by step, maybe I could carry it out by myself.
UPDATE
A video that demostrates the problem:
https://www.youtube.com/watch?v=Cs2Sh2tMvII
UPDATE
Another demo, I show what the FastColoredTextBox can't do but I would like to do like every other text-editor can do:
I've checked the source code of the project. Dragging is cancelled if a double click occurs and SelectWord is called.
You could modify the source code to include the feature that you request. (https://github.com/PavelTorgashov/FastColoredTextBox). In that case:
You must trace selections that start with double clicks.
Instead of calling SelectWord function, use the Selection class and draggedRange attribute to mark the selected word in OnMouseMove.
You also must handle deselection of words in OnMouseMove.
You must also select spaces between words in OnMouseMove.
The double click is handled in the code piece below:
if (!isLineSelect)
{
var p = PointToPlace(e.Location);
if (e.Clicks == 2)
{
mouseIsDrag = false; //Here, drag is cancelled.
mouseIsDragDrop = false;
draggedRange = null; //Drag range is nullified
SelectWord(p); //SelectWord is called to mark the word
return;
}
if (Selection.IsEmpty || !Selection.Contains(p) || this[p.iLine].Count <= p.iChar || ReadOnly)
OnMouseClickText(e);
else
{
mouseIsDragDrop = true;
mouseIsDrag = false;
}
}
EDIT:
This may require a lot of work to accomplish. So maybe you should use another tool/library. I have not studied the whole source code so there will probably be additional steps to those provided above.
For example, to trace double clicks you can do the following:
Define a class variable/property in FastColoredTextbox.cs: bool isDoubleClick.
Set it to true in OnMouseDown under if(e.Clicks == 2) condition. Set it to false in all other conditions.
Set it to false in OnMouseClick or OnMouseUp or in other relevant mouse event handlers.
That way you will know if series of mouse events had started with a double click event or not. Then you would act accordingly in OnMouseMove because that is where you (un)mark characters or (un)mark words.
LAST WORDS OF CAUTION:
The author of that project did not include any inline comments or any other means of documentation so you will be studying the code line by line to understand what each function/part does.
Add the following statement between Line 5276 and line 5277 in the class FastColoredTextBox.cs:
SelectWord(p);
mouseIsDrag = true; // here
return;
Note that implementing the ultimate behavior would require a good bunch of coding. Whereas the workaround mentioned above might satisfy your needs.
As #mostruash points out in his answer, that is the place where author cancels the mouse drag. Not sure why he deliberately prevents this feature. Only he knows.
if (e.Clicks == 2)//Line 5270
{
mouseIsDrag = false;
mouseIsDragDrop = false;
draggedRange = null;
SelectWord(p);
return;
}
I didn't read whole code, and I have no reason to do it. I just checked quickly and removed them. And it works as you expect.
if (e.Clicks == 2)//Line 5270
{
//Comment or remove completely.
//mouseIsDrag = false;
//mouseIsDragDrop = false;
//draggedRange = null;
SelectWord(p);
return;
}
Note: Am not sure this breaks something else, I've not tested. At least that works. Test it yourself.
My solution is a bit tweaky, but seems to work at first glance.
You have to make some changes in the Code:
Add mouseIsWholeWordSelection flag and a Range variable which can store the initial selected range after double click (best after line 100, I guess):
private bool mouseIsWholeWordSelection;
private Range mouseIsWholeWordSelectionBaseRange;
Change the selection code for double click event as stated above and extend it a bit (line 5222):
if (e.Clicks == 2)
{
//mouseIsDrag = false;
mouseIsDragDrop = false;
mouseIsWholeWordSelection = true;
//draggedRange = null;
SelectWord(p);
mouseIsWholeWordSelectionBaseRange = Selection.Clone();
return;
}
Add evaluation of dragging event for recreating selection (line 5566):
else if (place != Selection.Start)
{
if (mouseIsWholeWordSelection)
{
Selection.BeginUpdate();
var oldSelection = Selection.Clone();
SelectWord(place);
if (Selection.End >= mouseIsWholeWordSelectionBaseRange.End)
{
Selection.Start = (mouseIsWholeWordSelectionBaseRange.Start > Selection.Start) ? mouseIsWholeWordSelectionBaseRange.Start : Selection.Start;
Selection.End = mouseIsWholeWordSelectionBaseRange.End;
}
else if (Selection.Start < mouseIsWholeWordSelectionBaseRange.End)
{
Selection.Start = new Place(Selection.End.iChar, Selection.End.iLine);
Selection.End = mouseIsWholeWordSelectionBaseRange.Start;
}
Selection.EndUpdate();
DoCaretVisible();
Invalidate();
}
else
{
Place oldEnd = Selection.End;
Selection.BeginUpdate();
if (Selection.ColumnSelectionMode)
{
Selection.Start = place;
Selection.ColumnSelectionMode = true;
}
else
Selection.Start = place;
Selection.End = oldEnd;
Selection.EndUpdate();
DoCaretVisible();
Invalidate();
}
return;
}
Add at every place where isMouseDrag is being set to false:
isMouseWholeWordSelection = false;
And there you go.

Why does MonoTouch.Dialog use public fields for some Element options, and public properties for others

I am trying to get a StringElement's 'Value' to update in the UI when I set it after already setting up the DVC.
e.g:
public partial class TestDialog : DialogViewController
{
public TestDialog() : base (UITableViewStyle.Grouped, null)
{
var stringElement = new StringElement("Hola");
stringElement.Value = "0 Taps";
int tapCount = 0;
stringElement.Tapped += () => stringElement.Value = ++tapCount + " Taps";
Root = new RootElement("TestDialog")
{
new Section("First Section")
{
stringElement,
},
};
}
}
However the StringElement.Value is just a public field, and is only written to the UICell during initialization when Element.GetCell is called.
Why isn't it a property, with logic in the setter to update the UICell (like the majority of Elements, e.g. EntryElement.Value):
public string Value
{
get { return val; }
set
{
val = value;
if (entry != null)
entry.Text = value;
}
}
EDIT :
I made my own version of StringElement, derived from Element (basically just copied the source code from here verbatim)
I then changed it to take a class scoped reference to the cell created in GetCell, rather than function scoped. Then changed the Value field to a property:
public string Value
{
get { return val; }
set
{
val = value;
if (cell != null)
{
// (The below is copied direct from GetCell)
// The check is needed because the cell might have been recycled.
if (cell.DetailTextLabel != null)
cell.DetailTextLabel.Text = Value == null ? "" : Value;
}
}
}
It works in initial testing. However I am not sure on whether taking a reference to the cell is allowed, none of the other elements seem to do it (they only take references to control's placed within the cells). Is it possible that multiple 'live'* cell's are created based on the one MonoTouch.Dialog.Element instance?
*I say live to indicate cells currently part of the active UI. I did notice when navigating back to the dialog from a child dialog the GetCell method is invoked again and a new cell created based on the Element, but this is still a 1-1 between the element and the live cell.
For the main question:
Why does MonoTouch.Dialog use public fields for some Element options, and public properties for others?
I've been through the code, and I don't think there's a consistent reason for use of either.
The Dialog project was not part of the MonoTouch project initially - I don't think Miguel knew how useful it was going to turn out when he started wrote and grew it - I think he was more focussed on writing other apps like TweetStation at the time.
I know of several people (including me!) who have branched the code and adapted it for their purposes. I would guess at some future point Xamarin might write a 2.0 version with stricter coding standards.
Taking references to live cells
For limited use you can do this... but in general don't.
The idea of the table view is that cells get reused when the user scrolls up and down - especially in order to save memory and ui resources. Because of this is a long list, multiple elements might get references to the same cell.
If you do want to cache a cell reference then you probably should override GetCell() so that it never tries to reuse existing cells (never calls DequeueReusableCell)
Alternatively, you could try to change some code in the base Element class in order to find out if the Element has a current attached cell - this is what CurrentAttachedCell does in my branch of Dialog https://github.com/slodge/MvvmCross/blob/master/Cirrious/Cirrious.MvvmCross.Dialog/Dialog/Elements/Element.cs (but that branch has other added functions and dependencies so you probably won't want to use it for this current work!)

How to write an add-in to change text color in Visual Studio editor?

After searching for a long time for a simple way of changing the text color of a #region directive in Visual Studio, I've concluded there is no easy way of doing so.
I know how to change the #region statement color, and how to change the collapsed region color, but I want to change the color of the text with the region description. So:
#region Some text <--- all this text should be in a different color
public void Test()
{
}
#endregion <--- this too
It seems a lot of people are looking for something like this - see How to change the color of expanded regions' titles in VS2008?.
So I've been looking at writing a simple Visual Studio add-in to change the color.
However, it's more complicated than I thought it would be, with classes like Snapshot, Tagger, Classifier, WpfTextViewCreationListener, AdornmentLayer etc.
Simply put, I don't know where to start! I followed a couple of tutorials at the MSDN site, but they seem too complicated for what I'm trying to do.
Can someone point me to the most simple way of doing this? Ie. which classes/methods/events within the VS SDK I should use. I don't mind if the color is not customisable via the UI etc either. I'm using VS2010.
Edit: Just had the mztools website recommended to me; I'll take a look there too. Also noticed that StackOverflow's syntax highlighting of regions is pretty much exactly what I want!
I eventually came up with a solution, at least for VS2010.
Whilst I have used this for coloring '#region' and '#endregion' tags, a similar solution ought to be applicable for any text content in a Visual Studio window.
It seems that this sort of problem can be resolved by creating a IViewTaggerProvider which will 'tag' parts of the source code with a 'classification'.
Visual Studio will provide a style for text tagged with that classification which can then be changed by the user to the desired style via Tools > Options... > Environment > Fonts and Colors.
The Tagger provider looks like:
[Export(typeof(IViewTaggerProvider))]
[ContentType("any")]
[TagType(typeof(ClassificationTag))]
public sealed class RegionTaggerProvider : IViewTaggerProvider
{
[Import]
public IClassificationTypeRegistryService Registry;
[Import]
internal ITextSearchService TextSearchService { get; set; }
public ITagger<T> CreateTagger<T>(ITextView textView, ITextBuffer buffer) where T : ITag
{
if (buffer != textView.TextBuffer)
return null;
var classType = Registry.GetClassificationType("region-foreground");
return new RegionTagger(textView, TextSearchService, classType) as ITagger<T>;
}
}
This creates an ITagger object, which, given a Visual Studio text view, will tag parts of the text with the given classification type. Note that this will work for all text views (i.e. source code editor, 'Find Results' windows etc.). It may be possible to change this by editing the ContentType attribute (to just C#?).
The classification type (in this case "region-foreground") is defined as:
public static class TypeExports
{
[Export(typeof(ClassificationTypeDefinition))]
[Name("region-foreground")]
public static ClassificationTypeDefinition OrdinaryClassificationType;
}
[Export(typeof(EditorFormatDefinition))]
[ClassificationType(ClassificationTypeNames = "region-foreground")]
[Name("region-foreground")]
[UserVisible(true)]
[Order(After = Priority.High)]
public sealed class RegionForeground : ClassificationFormatDefinition
{
public RegionForeground()
{
DisplayName = "Region Foreground";
ForegroundColor = Colors.Gray;
}
}
The Order attribute determines when the classification will be applied compared to other classifications which may also apply to a span of text.
The DisplayName will be used in the Tools > Options... dialog.
Once the classification is defined, an ITagger class can search a view's text and provide classifications for applicable sections of the text it finds.
Simply put, its job is to listen for the ViewLayoutChanged event of the text view, which is fired when the provided text view's content changes (e.g. because the the user has typed something).
It must then search the text for the area of text it is interested in (called a 'span'). Here, it returns spans of lines containing either #region or #endregion. I've kept this simple, but the TextSearchService used to find matches can also search using regular expressions.
Finally, a method is provided for Visual Studio to retrieve the tags of the text it has found, called GetTags(). For a given span collection, this will return text spans with classification tags, i.e. areas of those spans which should be classified in a certain way.
Its code is:
public sealed class RegionTagger : ITagger<ClassificationTag>
{
private readonly ITextView m_View;
private readonly ITextSearchService m_SearchService;
private readonly IClassificationType m_Type;
private NormalizedSnapshotSpanCollection m_CurrentSpans;
public event EventHandler<SnapshotSpanEventArgs> TagsChanged = delegate { };
public RegionTagger(ITextView view, ITextSearchService searchService, IClassificationType type)
{
m_View = view;
m_SearchService = searchService;
m_Type = type;
m_CurrentSpans = GetWordSpans(m_View.TextSnapshot);
m_View.GotAggregateFocus += SetupSelectionChangedListener;
}
private void SetupSelectionChangedListener(object sender, EventArgs e)
{
if (m_View != null)
{
m_View.LayoutChanged += ViewLayoutChanged;
m_View.GotAggregateFocus -= SetupSelectionChangedListener;
}
}
private void ViewLayoutChanged(object sender, TextViewLayoutChangedEventArgs e)
{
if (e.OldSnapshot != e.NewSnapshot)
{
m_CurrentSpans = GetWordSpans(e.NewSnapshot);
TagsChanged(this, new SnapshotSpanEventArgs(new SnapshotSpan(e.NewSnapshot, 0, e.NewSnapshot.Length)));
}
}
private NormalizedSnapshotSpanCollection GetWordSpans(ITextSnapshot snapshot)
{
var wordSpans = new List<SnapshotSpan>();
wordSpans.AddRange(FindAll(#"#region", snapshot).Select(regionLine => regionLine.Start.GetContainingLine().Extent));
wordSpans.AddRange(FindAll(#"#endregion", snapshot).Select(regionLine => regionLine.Start.GetContainingLine().Extent));
return new NormalizedSnapshotSpanCollection(wordSpans);
}
private IEnumerable<SnapshotSpan> FindAll(String searchPattern, ITextSnapshot textSnapshot)
{
if (textSnapshot == null)
return null;
return m_SearchService.FindAll(
new FindData(searchPattern, textSnapshot) {
FindOptions = FindOptions.WholeWord | FindOptions.MatchCase
});
}
public IEnumerable<ITagSpan<ClassificationTag>> GetTags(NormalizedSnapshotSpanCollection spans)
{
if (spans == null || spans.Count == 0 || m_CurrentSpans.Count == 0)
yield break;
ITextSnapshot snapshot = m_CurrentSpans[0].Snapshot;
spans = new NormalizedSnapshotSpanCollection(spans.Select(s => s.TranslateTo(snapshot, SpanTrackingMode.EdgeExclusive)));
foreach (var span in NormalizedSnapshotSpanCollection.Intersection(m_CurrentSpans, spans))
{
yield return new TagSpan<ClassificationTag>(span, new ClassificationTag(m_Type));
}
}
}
For brevity I have omitted namespaces and the using statements, which are typically of the form Microsoft.VisualStudio.Text.*. For these to be available, the Visual Studio 2010 SDK must first be downloaded.
I've been using this solution for the past few months without issue.
One limitation I noticed is colours are not 'blended', so a color with less than 100% opacity will not 'fade out' the existing colours in a span - which might be useful to preserve syntax highlighting.
I also have little idea of its efficiency, as it looks like it will repeatedly search a document on each keypress. I have not done the research to see if Visual Studio optimises this somehow. I do notice a slowdown of Visual Studio on large files (> ~1000 lines), but I also use Resharper, so I cannot attribute this to this plugin alone.
As this was coded mostly using guesswork, I welcome any comments or code changes which could clarify or simplify things or improve upon the code's performance.
I guess you could start with Visual Studio Add-in project, it will used EnvDTE, which was considered as Visual Studio Object Model, and please find MSDN document here:
http://msdn.microsoft.com/en-us/vstudio/bb968855
You could control your visual studio behavior, like debugger, code editor and so on by EnvDTE.

Categories

Resources