AmbientHighlightingStyleRegistry

SyntaxEditor for WPF Forum

Posted 10 years ago by Matt Kerchmar
Avatar
Hello,

I am working on a fonts and colors dialog at the moment. The Quickstart that shows how to set something like this up is using the AmbientHighlightingStyleRegistry. I was able to get this all set up, and the dialog is functional. But the changes seem to be immediate in the application, so clicking cancel on the dialog doesn't undo the changes.

What would be a good approach to this problem? My initial thought was that I should create a new style registry in the window with the syntax editor. I could then modify the syntax language constructor to take a style registry as an argument, then pass that style registry into the classification type provider constructor from within the syntax language constructor.

What I expected to happen then was that there would be no syntax highlighting at all in the syntax editor (since I had essentially disconnected the ambient registry). This did happen as expected. But when I went into my fonts and colors dialog and made changes (which affect the ambient registry), the changes were reflected in the syntax editor.

I'm curious what remains connected between the ambient registry and my syntax editor control. Further, I'm wondering if there is a more correct way to do this.

One last question, I have multiple types of windows that display syntax highlighted code (an output window and document style input windows). They all use the same syntax language, but I want the user to be able to override their highlighting settings individually. I thought that this also could be solved with multiple registries. Does this sound like a reasonable approach?

Thanks,
Matt Kerchmar

[Modified at 02/03/2010 01:36 PM]

Comments (10)

Posted 10 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar
Hi Matt,

The ambient registry is a fallback mechanism. So say you define "Keyword" in your custom registry. If your IClassificationTag implements IHighlightingStyleRegistryProvider, it will use the registry you specify there first. Then if that is null (or your tag doesn't implement it), it will use the ambient registry.

The StyleRegistryClassificationTag is a special IClassificationTag implementation that also implements IHighlightingStyleRegistryProvider, so it would be useful for you here.

The only thing is that you'd need your token tagger to return tags of that type for its ITagger<IClassificationTag>.GetTags method results. Perhaps you can override TokenTagger and override that method's results.

Then you could have different TokenTagger-instances use different registries. And you can have your style selection window hit different registry instances.

Hope that helps.


Actipro Software Support

Posted 10 years ago by Matt Kerchmar
Avatar
Hello,

The language I'm using was generated using the WPF Language Designer tool. I generated C# source files with the tool and included them in my project instead of using a language definition file. There is already a TokenTagger derivative that was generated by the tool.

In this TokenTagger derivative class, I created an override of the GetTags function. The code that I currently have is as follows:

        public override IEnumerable<TagSnapshotRange<ITokenTag>> GetTags(NormalizedTextSnapshotRangeCollection snapshotRanges, object parameter)
        {
            IEnumerable<TagSnapshotRange<ITokenTag>> originalTagSnapshotRanges = base.GetTags(snapshotRanges, parameter);
            Collection<TagSnapshotRange<ITokenTag>> newTagSnapshotRanges = new Collection<TagSnapshotRange<ITokenTag>>();
            foreach (TagSnapshotRange<ITokenTag> originalRange in originalTagSnapshotRanges)
            {
                IClassificationTag originalTag = originalRange.Tag as IClassificationTag;
                if (originalTag == null) throw new NotSupportedException();
                ITokenTag newTag = new StyleRegistryClassificationTag(
                    originalTag.ClassificationType, ClassificationTypeProvider.Registry);
                TagSnapshotRange<ITokenTag> newRange = 
                    new TagSnapshotRange<ITokenTag>(originalRange.SnapshotRange, newTag);
                newTagSnapshotRanges.Add(newRange);
            }
            return newTagSnapshotRanges;
        }
Maybe I'm interpreting your instructions wrong. But I thought this was the basic idea you were pointing me to.

But the StyleRegistryClassificationTag class doesn't implement the ITokenTag interface, so I can't actually assign a new StyleRegistryClassificationTag to an ITokenTag variable like I'm doing in that code above (it doesn't compile).

Could you clarify what it is that I need to do here?

Thanks.
-Matt Kerchmar
Posted 10 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar
Hi Matt,

The TokenTagger class implements two tagger interfaces, one for ITokenTag and one for IClassification tag. You'd want to reimplement the one for ITagger<IClassificationTag>, possibly like this (untested):
IEnumerable<TagSnapshotRange<IClassificationTag>> ITagger<IClassificationTag>.GetTags(NormalizedTextSnapshotRangeCollection snapshotRanges, object parameter) {
    IEnumerable<TagSnapshotRange<ITokenTag>> tagRanges = this.GetTags(snapshotRanges, parameter);
    if (tagRanges != null) {
        foreach (TagSnapshotRange<ITokenTag> tagRange in tagRanges) {
            IClassificationTag tag = tagRange.Tag as IClassificationTag;
            if ((tag != null) && (tag.ClassificationType != null))
                yield return new TagSnapshotRange<IClassificationTag>(tagRange.SnapshotRange,
                    new StyleRegistryClassificationTag(tag.ClassificationType, ClassificationTypeProvider.Registry));
        }
    }
}


Actipro Software Support

Posted 10 years ago by Matt Kerchmar
Avatar
Hello,

This was very helpful. I now have a functioning options dialog that can change the style of my output window and my script window(s) independently.

It isn't working perfectly though.

Changes do not show up in existing documents properly. After opening a new document (where the changes are reflected correctly), the existing documents become able to reflect the changes, but they only update in places where you make text changes to the document. For example:
  1. I create a document. I write a comment in the document in the language that the document is using. The text is classified as a comment and colored green.
  2. I change the color of Script Window comments to blue using my options dialog.
  3. The existing comment in the existing window does not change. If I type a new comment in the existing script window, it is also colored green still.
  4. I open a new document. New comments in the new document are colored blue.
  5. If I go back to the original comments in the original document, they become colored blue if I make any change to the comment. This only affects the comment being changed, not the entire document.
All my document windows are sharing a static HighlightingStyleRegistry. Because of this, I think any change to this registry should affect all documents. Each script window is using its own Syntax Editor control. Is this a good way of doing this?

I'm thinking this issue might have something to do with the document needing to be re-parsed fully after making a style change so that everything is re-tagged. Does this sound likely?

Thanks,
Matt Kerchmar
Posted 10 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar
Hi Matt,

Right now it looks like we are attaching to the AmbientHighlightingStyleRegistry's Changed event and refresh everything when that occurs. But since you are using alternate registries, we don't know when they are changed.

Could you call SyntaxEditor.InvalidateViews() on your editor instances to refresh things?


Actipro Software Support

Posted 10 years ago by Matt Kerchmar
Avatar
Hello,

I updated my application so that when you close the options dialog, InvalidateViews() is called on all open script windows.

I verified that this line of code is properly getting hit for each script window, but it did not resolve the issue. The behavior is the same as I described in my prior reply.

Thanks,
Matt Kerchmar
Posted 10 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar
Hi Matt,

Could you make a simple sample project that shows this happening and email that over so we can debug with it? Thanks!

Another workaround idea is that perhaps you could make a change on the AmbientHighlightingStyleRegisty after you change your other registry, just to kick off the event that SyntaxEditor is listening to.


Actipro Software Support

Posted 10 years ago by Matt Kerchmar
Avatar
Hello,

I have emailed a sample project to the support address. It should appear from my name and the email address I have on my forum account.

The sample demonstrates, in the simplest possible scenario that I could come up with, two issues that I'm facing. When the sample starts up, you should see a window with a Syntax Editor instance with some example text already filled in. There are two BrushEditBox instances in the window, each data bound to the IHighlightingStyle the corresponding label indicates. The IHighlightingStyle objects are in a registry instanced in the window class, not the AmbientHighlightingStyleRegistry. I have made changes to the generated SyntaxLanguage and TokenTagger files to support using a registry other than the AmbientHighlightingStyleRegistry, as instructed in the thread I linked at the beginning of this email.
  1. The first issue is that when you make a change to a style, it doesn't update in the editor window. The easiest way to see this issue is to change the comment coloring to another distinct color, such as purple. Note that nothing changes in the editor window when you make this change. However, if you type a new comment in the window, or then modify the existing comment, the highlighting does update. Note that there is a callback that is invoked when the foreground is changed which calls InvalidateViews on the editor instance, but it caused no change in the behavior.
  2. The second issue is with the PlainText style. Changes to this style do not seem to effect anything. I thought this is the default base style which all other styles deviate from. But now I'm doubting. In my main application, not this sample, I'm trying to change the font for the entire document, so I thought that changing the font settings in the PlainText style would accomplish that, but it wasn't working. So with a little more poking around, I discovered that no changes to the PlainText style seem to affect anything. What am I missing here?
Thanks for your time. Please let me know if I have been unclear about anything.

-Matt Kerchmar

[Modified at 03/01/2010 12:15 PM]

[Modified at 03/01/2010 02:02 PM]
Posted 10 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar
Thanks Matt, we'll look at what you sent and will reply to your ticket.


Actipro Software Support

Posted 10 years ago by Matt Kerchmar
Avatar
Thanks for the reply in my support ticket!

For others who might run across this posting and wonder what the outcome was, they made a change to SyntaxEditor.InvalidateViews that will cause the cached brushes to be cleared. The change should be available in a future version of Syntax Editor, from what I have been told.
The latest build of this product (v2019.1 build 0683) was released 1 month ago, which was after the last post in this thread.

Add Comment

Please log in to a validated account to post comments.