I use AvalonEdit:TextEditor. Can i enable quick search dialog (on Ctrl-F for example) for this control? Or maybe someone has code for search words into AvalonEdit:TextEditor text?
There isn't much documentation about it, but AvalonEdit does have a built in SearchPanel class that sounds exactly like what you want. There is even a SearchInputHandler class that makes it trivial to get it hooked up to your editor, responding to keyboard shortcuts, etc. Here is some sample code that attached the standard search logic to an editor:
myEditor.TextArea.DefaultInputHandler.NestedInputHandlers.Add(new SearchInputHandler(myEditor.TextArea));
Here is a screenshot of what it will look like (this is taken from ILSpy which uses AvalonEdit). You can see the search control in the top right, the search options it supports, and the automatic highlighting it does of matching results.
There isn't any support for replace...but if you just need searching, this can be a great solution.
For Avalon Edit Version 5.0.1.0 and up, just do this:
SearchPanel.Install(XTBAvalonEditor);
Where XTBAvalonEditor is the WPF AvalonEdit control name.
Make sure to add this using statement:
using ICSharpCode.AvalonEdit.Search;
Then when the editor has focus, press CTL-F: You'll see the find control pop up in upper right hand corner.
In the TextEditor constructor in the ICSharpCode.AvalonEdit project, add SearchPanel.Install(this.TextArea); and voila, using ctrl+f opens the search window.
(using the line from Stephen McDaniel's post (replace myEditor with this) also works, but the support for SearchInputHandler is being removed)
(works well with AvalonEdit inside AvalonDock with MVVM)
From:
public TextEditor() : this(new TextArea())
{
}
To:
public TextEditor() : this(new TextArea())
{
SearchPanel.Install(this.TextArea);
}
The last time I checked it was a "No". You will have to implement your own search/replace functionality.
http://community.icsharpcode.net/forums/p/11536/31542.aspx#31542
You can quickly add find/replace from here - http://www.codeproject.com/Articles/173509/A-Universal-WPF-Find-Replace-Dialog
ICSharpCode.AvalonEdit 4.3.1.9429
Search and highlight item.
private int lastUsedIndex = 0;
public void Find(string searchQuery)
{
if (string.IsNullOrEmpty(searchQuery))
{
lastUsedIndex = 0;
return;
}
string editorText = this.textEditor.Text;
if (string.IsNullOrEmpty(editorText))
{
lastUsedIndex = 0;
return;
}
if (lastUsedIndex >= searchQuery.Count())
{
lastUsedIndex = 0;
}
int nIndex = editorText.IndexOf(searchQuery, lastUsedIndex);
if (nIndex != -1)
{
var area = this.textEditor.TextArea;
this.textEditor.Select(nIndex, searchQuery.Length);
lastUsedIndex=nIndex+searchQuery.Length;
}
else
{
lastUsedIndex=0;
}
}
Replace operation:
public void Replace(string s, string replacement, bool selectedonly)
{
int nIndex = -1;
if(selectedonly)
{
nIndex = textEditor.Text.IndexOf(s, this.textEditor.SelectionStart, this.textEditor.SelectionLength);
}
else
{
nIndex = textEditor.Text.IndexOf(s);
}
if (nIndex != -1)
{
this.textEditor.Document.Replace(nIndex, s.Length, replacement);
this.textEditor.Select(nIndex, replacement.Length);
}
else
{
lastSearchIndex = 0;
MessageBox.Show(Locale.ReplaceEndReached);
}
}
In my case, I couldn't find the Search.Install(...) method so I used the below code to add the search functionality.
textEditor.TextArea.DefaultInputHandler.NestedInputHandlers.Add(new SearchInputHandler(textEditor.TextArea));
The search box can be activated by pressing Ctrl + F on your keyboard.
Related
This questions has nothing in common with Reorder / move / dragdrop ListViewItems within the same ListView Control in C# Windows Forms because it will only work for large/small icons view mode!
I found some nice little code here:
http://snipplr.com/view/33427/
using System.Drawing;
using System.Windows.Forms;
namespace System.Windows.Forms // May need to set to something else
{
/// <summary>
/// A ListView with DragDrop reordering.
/// <see cref="http://support.microsoft.com/kb/822483/en-us"/>
/// </summary>
public class ListViewWithReordering : ListView
{
protected override void OnItemDrag(ItemDragEventArgs e)
{
base.OnItemDrag(e);
//Begins a drag-and-drop operation in the ListView control.
this.DoDragDrop(this.SelectedItems, DragDropEffects.Move);
}
protected override void OnDragEnter(DragEventArgs drgevent)
{
base.OnDragEnter(drgevent);
int len = drgevent.Data.GetFormats().Length - 1;
int i;
for (i = 0; i <= len; i++)
{
if (drgevent.Data.GetFormats()[i].Equals("System.Windows.Forms.ListView+SelectedListViewItemCollection"))
{
//The data from the drag source is moved to the target.
drgevent.Effect = DragDropEffects.Move;
}
}
}
protected override void OnDragDrop(DragEventArgs drgevent)
{
base.OnDragDrop(drgevent);
//Return if the items are not selected in the ListView control.
if (this.SelectedItems.Count == 0)
{
return;
}
//Returns the location of the mouse pointer in the ListView control.
Point cp = this.PointToClient(new Point(drgevent.X, drgevent.Y));
//Obtain the item that is located at the specified location of the mouse pointer.
ListViewItem dragToItem = this.GetItemAt(cp.X, cp.Y);
if (dragToItem == null)
{
return;
}
//Obtain the index of the item at the mouse pointer.
int dragIndex = dragToItem.Index;
ListViewItem[] sel = new ListViewItem[this.SelectedItems.Count];
for (int i = 0; i <= this.SelectedItems.Count - 1; i++)
{
sel[i] = this.SelectedItems[i];
}
for (int i = 0; i < sel.GetLength(0); i++)
{
//Obtain the ListViewItem to be dragged to the target location.
ListViewItem dragItem = sel[i];
int itemIndex = dragIndex;
if (itemIndex == dragItem.Index)
{
return;
}
if (dragItem.Index < itemIndex)
itemIndex++;
else
itemIndex = dragIndex + i;
//Insert the item at the mouse pointer.
ListViewItem insertItem = (ListViewItem)dragItem.Clone();
this.Items.Insert(itemIndex, insertItem);
//Removes the item from the initial location while
//the item is moved to the new location.
this.Items.Remove(dragItem);
}
}
}
}
which is based on / was adapted from http://www.codeproject.com/Articles/4576/Drag-and-Drop-ListView-row-reordering.
Unfortunately the code download doesn't work.
However - how is this code beeing used? Simply pasting it in my forms-class won't work. I supposed this code simply overrides all drag events from all listviews within a form?!
Sorry this is such a stupid question, but how do I use this code?
Someone has kindly taken the standard .NET ListView control and used something called Inheritance to make their own version with extra functionality. You should read up on it, it's a useful thing to know.
To get it to work in your project you need to add this class as described below.
I assume you're using Visual Studio, you haven't mentioned otherwise.
In Solution Explorer on the right hand side Right Click on your
solution and go to Add > Class.
Name the class whatever you like (ListViewWithReordering.cs would
make the most sense) and then open that file. Past all of this code
into the file.
Where it says namespace at the top you'll want to edit this to
match your own programs namespace.
Build your project.
In the Toolbox on the left where you normally see all of the
standard .NET controls there should be a section at the top that now
contains ListViewWithReordering.
Drag this control onto your project.
Done! Use it like any other ListView control. The rest should work automatically (as long as this class is indeed reliable).
I created a design example of drag and drop, download the project on the following link. Any questions ask again.
https://drive.google.com/file/d/0B21l6Fz0byBMSi1VTm52V2E4VVE/view?usp=sharing&resourcekey=0-jNA3sA85A6lygH3TyISstA
First add this class to your project.
Then, If you already have a listview from the forms designer, go to the InitializeComponent() method of your form constructor. There your listview is declared as:
private System.Windows.Forms.ListView listView1;
Change this declaration to:
private ListViewWithReordering listView1;
I'm writing a Visual Studio Extension to customize my editor. I would like to change the text that is displayed when a block of code is collapsed.
I have tried the following code :
ITagAggregator<IntraTextAdornmentTag> aggregator;
[...]
aggregator.BatchedTagsChanged += OnBatchedTagsChanged;
[...]
public void OnBatchedTagsChanged(object sender, BatchedTagsChangedEventArgs e)
{
string newText;
bool textCreated;
NormalizedSnapshotSpanCollection list = new NormalizedSnapshotSpanCollection(e.Spans.SelectMany(x => x.GetSpans(textView.TextBuffer)));
if (list.Count != 0)
{
IEnumerable<IMappingTagSpan<IntraTextAdornmentTag>> tags = aggregator.GetTags(list);
foreach (IMappingTagSpan<IntraTextAdornmentTag> tag in tags)
{
if (tag.Tag.Adornment is OutliningCollapsedAdornmentControl)
{
NormalizedSnapshotSpanCollection spans = tag.Span.GetSpans(textView.TextSnapshot);
if (spans.Count == 0) continue;
OutliningCollapsedAdornmentControl adornmentControl = (OutliningCollapsedAdornmentControl)tag.Tag.Adornment;
TextBlock textBlock = adornmentControl.GetChild<TextBlock>();
textCreated = TryCreateText(spans[0], out newText);
if (textCreated)
{
adornmentControl.Content = newText;
textBlock.Text = newText;
}
}
}
}
}
I does change the text, but when it's scrolled out of the the screen and back in, the text reverts back to the default value.
Edit :
I also tried MSDN's walkthrough.
I works fine if I collapse blocks by clicking on the "+" sign, but the blocks don't collapse when I use Ctrl+M+O.
I guess the problem comes from the fact that I'm creating regions that already exist.
Could someone please tell me what I could do?
(Tested both in VS2010 and VS2013 with the same result)
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.
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.
I am developing a C# application to communicate with a MUD/MOO server. Basically it takes text and displays it on the screen. At the moment i'm using a RichTextBox to display the text and I have coloured text working fine and I only have to implement URLs, however while doing this I discovered that to add URLs with custom text (e.g. NOT http://, like: Click here) I need to use a Win32 API, I can't do this... at all. This needs to work in mono on linux (and possibly mac). Is there anyway to make this work? or what alternative avenues should i pursue? (I was considering switching to HTML, but is there a good cross-platform HTML control?) (All of this HAS to be free).
Thanks in advanced.
I managed to do it in the end using:
Stack<Link> Links = new Stack<Link>();
internal class Link
{
public int starts = 0;
public int ends = 0;
public string url = "";
}
private string GetLink(RichTextBox rtb, Point point)
{
int index = rtb.GetCharIndexFromPosition(point);
foreach (Link link in Links.ToArray())
if (link.starts <= index && link.ends >= index)
return link.url;
return "";
}
just append all links to the Links stack, and use GetLink inside the MouseDown event :)
as an alternative you can use GtkTextView, it should be cross platform. You can add a hyper link styled text using code below:
TextTag tag = new TextTag("link");
tag.Foreground = "blue";
tag.Underline = Pango.Underline.Single;
textview1.Buffer.TagTable.Add(tag);
Gtk.TextIter iter = textview1.Buffer.GetIterAtOffset(0);
textview1.Buffer.InsertWithTagsByName(ref iter, "link text", "link");
where textview1 is Gtk.TextView.
you should be able to change the cursor and react on mouse clicks using "motion-notify-event" and "event-after" events.
hope this helps, regards