-
Notifications
You must be signed in to change notification settings - Fork 26
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Adding clickable links to RichTextBoxTarget #13
Comments
I really like the idea! I think it could be very useful to first hide some details! I'm curious about the following things:
Further I would recommend to send as much possible info to the link clicked handler (at least the full logeventinfo) Maybe it's also an idea to let the details be layoutable? So it can be full set from the config and you have full control to show. |
I'm very pleased that you liked my idea! Hope we could get it to release state!
The original solution — yes, because it was what I designed it for. But I really feel this should be generalized. But I'm not sure — how exactly?
Yes, I also thought so, but what would it be? A separate specific layout renderer? If yes, then would it be possible to show the link only for some specific messages? Probably with Another problem is how to attach logEventInfo to the link? For now I'm just parsing the clicked link text, getting integer exception id which I have to place there. It looks like "details N1", "details N2", which is not too great. It should be possible to have hidden urls for the links, but I was not able to make it work yet. Any help on this would be appreciated. So I assume that it's possible to attach some kind of marker to the link, which leads us to following question:
Yes, for now I don't clear the dictionary, and it should be changed. I suppose it should be possible to detect what markers are being deleted when removing first lines on
Surely no, I'm showing this as a simplest example. For our purposes I designed a special form pretty-printing stacks and showing Exception.Data contents. Still I did not mean to bind any specific behaviour to link click event, therefore I'm exposing
Besides my thoughts on layouts above, do you mean here that we could also bind a specific OnLinkClicked behaviour in the (xml) config as well? I'm not sure on this, do you have any proposals on how it could work? |
I also remembered another use-case for this feature. In one of our applications we had to switch to manual writing texts to RTB control instead of logging to be able to add links for some messages. The application itself is about doing some transformations with an Excel file. We display some part of the file in a tabular listview. So when a processing routine finds some problematic cells it prints a message to a RTB including a link, click on which causes preview to focus on a problematic cell. So this could work as a real-world use-case for using links without exceptions. I would really like to use NLog for logging there without missing link functionality. |
Ok, here's what I have been thinking on generalization and configuration of links. We surely don't want to add links to all messages being logged, we want to have ability to specify which messages have links and how links look like. So I think we can achieve these goals by creating specific layout renderer, let's say Now the problem is in how the renderer would actually work. First of all it would need to create some kind of token so the logEventInfo could be found when the link is clicked. [LayoutRenderer("hello-world")]
public class RtbLinkLayoutRenderer : LayoutRenderer
{
internal const string PropertyName = "RTB-link-id";
private static int s_id = 0;
public Layout LinkText {get;set}
protected override void Append(StringBuilder builder, LogEventInfo logEvent)
{
builder.Append(LinkText.Render(logEvent));
int id = Interlocked.Increment(ref s_id);
logEvent.Properties[PropertyName] = id;
}
}
public sealed class RichTextBoxTarget
{
private readonly ConcurrentDictionary<object, LogEventInfo > linkedEvents;
protected override void Write(LogEventInfo logEvent)
{
...
object linkId;
if (logEvent.Properties.TryGetValue(RtbLinkLayoutRenderer.PropertyName, out linkId))
{
linkedEvents[linkId] = logEvent;
}
}
} So this looks a bit ugly (we are adding our custom property to user-space Properties) but it should work. What worries me is how to make the needed part of the text in the RTB control to be a link. The code I use now looks like this: public static void InsertLink(this RichTextBox textBox, string text, string hyperlink, int position)
{
textBox.SelectionStart = position;
//Does not actually work ((
//See: http://stackoverflow.com/questions/2850575/what-is-the-rtf-syntax-for-a-hyperlink
//textBox.SelectedRtf = @"{\rtf1{\field{\*\fldinst{HYPERLINK ""mysuperlink_val""}}{\fldrslt{mysuperlink}}}}";
textBox.SelectedRtf = @"{\rtf1\ansi " + text + @"\v #" + hyperlink + @"\v0}";
textBox.Select(position, text.Length + hyperlink.Length + 1);
textBox.SetSelectionLink(true);
textBox.Select(position + text.Length + hyperlink.Length + 1, 0);
}
private static void SetSelectionStyle(this RichTextBox textBox, UInt32 mask, UInt32 effect)
{
CHARFORMAT2_STRUCT cf = new CHARFORMAT2_STRUCT();
cf.cbSize = (UInt32)Marshal.SizeOf(cf);
cf.dwMask = mask;
cf.dwEffects = effect;
IntPtr wpar = new IntPtr(SCF_SELECTION);
IntPtr lpar = Marshal.AllocCoTaskMem(Marshal.SizeOf(cf));
Marshal.StructureToPtr(cf, lpar, false);
IntPtr res = SendMessage(textBox.Handle, EM_SETCHARFORMAT, wpar, lpar);
Marshal.FreeCoTaskMem(lpar);
} So it seems that I cant just put some markup into the message text via layout renderer. I would need to call some methods on the RTB control to make the link magic work. And I don't see a way to do it with the layout renderer approach. Except to pass a lot of data via logEventInfo.Properties (actually a single object with all the needed data) and do the necessary processing in What do you think? |
Sounds great!
You can also use
I don't see that. You can generate a link text with a Or is it the problem that we need the whole |
Thanks for a SequenceID idea, I've used it in a latest rewrite! Also used GUIDs in another part. |
closed by #14 |
As you may have noticed, we are using RichTextBoxTarget quite extensively. It's very handy to keep track of all the actions visually. We also using the target to store information on exceptions happening during program execution. Still printing whole stacktrace (including inner exceptions) floods the control area, and is not that useful.
But not printing stacktrace is bad as well — it's often needed to identify the problem source. So I was thinking that it would be nice to display only short description (exception message) given that it's possible to show all the exception details on demand. I came with the following solution.
It's possible to create in-place links in the RichTextBox control, so I've patched RTB-target to create links after logging events that contain exceptions. Exception objects are stored in a dictionary inside the target.
Target also adds an LinkClicked handler to the control, and exposes it's own ExceptionLinkClicked event, having Exception object as parameter.
I'm actually very pleased with the results and I think that such functionality could be of use for others (it actually puzzles me that I haven't thought of this solution earlier). Still to be published it might need a complete re-vamp, like passing logEvent parameters to ExceptionLinkClicked handler. Or generalizing the ability to add links by allowing it for log events without exceptions somehow.
So I'm posting it here to get your opinion first. What do you think?
The text was updated successfully, but these errors were encountered: