Does anyone know how to go about creating mouse over effects for custom annotations?
For example, if I "mouse over" between a custom annotation range I may want the font colour, or font size to change or to even highlight the annotation. Going even further, maybe have some way to activate a little popup window which in turn may display more detailed information about the annotation properties (I guess that's similar to the Comments Annotations feature).
Any information would be really handy.
Thanks for your time,
Rob
10 Answers, 1 is accepted
The suggested approach in scenarios like these is to create custom UILayer. You can read more about how to do that in this help article or check out out this online demo. However, there are some limitations when using UILayers, as they are updated after the layout of the document is already calculated. So you can add additional UI within the RadRichTextBox control, but you cannot affect the current layout of the document inside it. For example, you can add highlight over the elements in your custom annotation, but changing the font size means that document's layout should be invalidated and it is not recommended to do that, inside the UILayer.
The other part of the API that you will probably need are the GetDocumentPositionFromViewPoint and GetViewPointFromDocumentPosition methods of the current presenter that is showing the document (accessible trough the ActiveEditorPresenter property of the RadRichTextBox). These will help you to translate the coordinates that you get form the MouseMove event to an actual position inside the document you are editing and vice versa.
Please don't hesitate to contact us again if you need more help.
Alex
the Telerik team
Explore the entire Telerik portfolio by downloading the Ultimate Collection trial package. Get it now >>
I have tried the following but I don't ge the correct result.
Any suggestions?
private
void
radRichTextBox_MouseMove(
object
sender, MouseEventArgs e)
{
Point mousePosition = PointToScreen(Mouse.GetPosition(
this
));
DocumentPosition mouseDocumentPosition =
this
.radRichTextBox.ActiveEditorPresenter.GetDocumentPositionFromViewPoint(mousePosition);
AnnotationRangeMarkerBase annotation = RadDocumentExtensions.GetPreviousAnnotationMarkerEndAndStartRanges(mouseDocumentPosition);
MessageBox.Show(annotation.ToString());
}
Here's the code I use to get the annotation marker from the mouse position:
public
static
AnnotationRangeMarkerBase GetPreviousAnnotationMarkerEndAndStartRanges(DocumentPosition position)
{
InlineLayoutBox inlineLayoutBox = position.GetCurrentInlineBox();
do
{
inlineLayoutBox = (InlineLayoutBox)DocumentStructureCollection.GetPreviousElementOfType(inlineLayoutBox,
typeof
(AnnotationMarkerLayoutBox));
}
while
(inlineLayoutBox !=
null
&& (!(inlineLayoutBox.AssociatedInline
is
AnnotationRangeStart) && !(inlineLayoutBox.AssociatedInline
is
AnnotationRangeEnd)));
if
(inlineLayoutBox ==
null
)
{
return
null
;
}
return
inlineLayoutBox.AssociatedInline
as
AnnotationRangeMarkerBase;
}
thanks,
Rob
private
void
radRichTextBox_MouseMove(
object
sender, MouseEventArgs e)
{
Point mousePoint = PointToScreen(Mouse.GetPosition(
this
));
Point docPoint =
this
.radRichTextBox.ActiveEditorPresenter.GetDocumentPointFromViewPoint(mousePoint);
DocumentPosition mouseDocumentPosition =
this
.radRichTextBox.ActiveEditorPresenter.GetDocumentPositionFromViewPoint(docPoint);
AnnotationRangeMarkerBase annotation = RadDocumentExtensions.GetPreviousAnnotationMarkerEndAndStartRanges(mouseDocumentPosition);
if
(annotation !=
null
)
{
//MessageBox.Show(annotation.ToString());
if
(annotation
is
SemanticRangeStart || annotation
is
SemanticRangeEnd)
{
SemanticRangeStart product = RadDocumentExtensions.GetPreviousAnnotationRangeStart<SemanticRangeStart>(mouseDocumentPosition);
MessageBox.Show(product.Product.Name +
" "
+ product.Product.ID);
}
}
}
http://www.telerik.com/community/forums/silverlight/richtextbox/moving-image.aspx
This now works for me (although I do have to offset the Y value slightly).
private
void
radRichTextBox_MouseMove(
object
sender, MouseEventArgs e)
{
Point mousePoint = e.GetPosition(
this
);
mousePoint.Y -= 20;
DocumentPosition mouseDocumentPosition =
this
.radRichTextBox.ActiveEditorPresenter.GetDocumentPositionFromViewPoint(mousePoint);
AnnotationRangeMarkerBase annotation = RadDocumentExtensions.GetPreviousAnnotationMarkerEndAndStartRanges(mouseDocumentPosition);
if
(annotation !=
null
)
{
//MessageBox.Show(annotation.ToString());
if
(annotation
is
SemanticRangeStart || annotation
is
SemanticRangeEnd)
{
SemanticRangeStart product = RadDocumentExtensions.GetPreviousAnnotationRangeStart<SemanticRangeStart>(mouseDocumentPosition);
MessageBox.Show(product.Product.Name +
" "
+ product.Product.ID);
}
}
}
I can see from the demo example, HightlightCurrentLine & HighlightCurrentWord, you override the OnDocumentChanged & Changing methods and assign CaretPositionChanged events.
protected
override
void
OnDocumentChanged()
{
base
.OnDocumentChanged();
if
(
this
.Document !=
null
)
{
this
.Document.CaretPosition.PositionChanged += CaretPosition_PositionChanged;
}
}
protected
override
void
OnDocumentChanging()
{
base
.OnDocumentChanging();
if
(
this
.Document !=
null
)
{
this
.Document.CaretPosition.PositionChanged -= CaretPosition_PositionChanged;
}
}
private
void
CaretPosition_PositionChanged(
object
sender, EventArgs e)
{
this
.UpdateViewPort(
this
.LastUpdateContext);
}
I can't find a way to assign RadRichTextBox_MouseMove event. ** if that is the correct way to achieve what I'm looking for **
You can get to the RadRichTextBox control from the UILayerUpdateContext like this: (RadRichTextBox)context.Presenter.Owner. However I can suggest you a slightly different approach. You can create elements needed for the highlight and add them to the layer but with a low opacity. This way you can attach the mouse events directly to them and only change their opacity when the mouse is over them. You can also add a tool tip with the additional information you need to show about the elements.
I've attached a simple project that demonstrates this approach. There is a layer that will highlight only the words with text "test" in the document when the mouse is over them and also show a tooltip .
I hope this will help you to move on.
Alex
the Telerik team
Explore the entire Telerik portfolio by downloading the Ultimate Collection trial package. Get it now >>
Thank you very much for your demo. It was a huge help. I now have a much better idea of how the custom layers work.
I have modified your code so that the annotations are applied to my semantic ranges (please let me know if you think there is a better way of achieving this).
public
override
void
UpdateUIViewPortOverride(UILayerUpdateContext context)
{
foreach
(AnnotationMarkerLayoutBox annotationMarkerLayoutBox
in
context.GetVisibleLayoutBoxes().Where(s => s.AssociatedDocumentElement
is
SemanticRangeStart))
{
AnnotationMarkerLayoutBox endAnnotationLayoutBox = annotationMarkerLayoutBox;
while
(!(endAnnotationLayoutBox.AssociatedDocumentElement
is
SemanticRangeEnd))
{
endAnnotationLayoutBox = (AnnotationMarkerLayoutBox)DocumentStructureCollection.GetNextElementOfType(endAnnotationLayoutBox,
typeof
(AnnotationMarkerLayoutBox));
}
this
.AddRectangle(annotationMarkerLayoutBox, endAnnotationLayoutBox);
}
}
private
void
AddRectangle(InlineLayoutBox startRect, InlineLayoutBox endRect)
{
Rectangle rectangle =
new
Rectangle();
rectangle.Width = Math.Max(0, (endRect.BoundingRectangle.Location.X - startRect.BoundingRectangle.Location.X));
rectangle.Height = startRect.LineInfo.Height;
Canvas.SetTop(rectangle, startRect.ControlBoundingRectangle.Top);
Canvas.SetLeft(rectangle, startRect.BoundingRectangle.Left);
rectangle.Fill = FillBrush;
rectangle.Opacity = MouseOverHighlightLayer.MouseOutOpacity;
rectangle.MouseEnter += (s, e) => { ((Rectangle)s).Opacity = MouseOverHighlightLayer.MouseInOpacity; };
rectangle.MouseLeave += (s, e) => { ((Rectangle)s).Opacity = MouseOverHighlightLayer.MouseOutOpacity; };
ToolTipService.SetToolTip(rectangle,
"This is a sample tooltip"
);
base
.AddDecorationElement(rectangle);
}
The only problem I have is that the Top and bottom positions are not reporting the correct locations. It is as if the bounding rectangle is offset. You can see what I mean by taking a look at the attached image.
It is obvious to see that the top edge of the rectangle layer should be the bottom position. I came across this issue in another situation when trying to record the location of an annotation. I guess I can manually offset the Y position to compensate for this. Do you know of any other solutions?
Thank you again for all of your help.
Rob
Canvas.SetTop(rectangle, startInline.ControlBoundingRectangle.Top - (startInline.LineInfo.Height / 1.5));
It isn't ideal, but it will do for now as fairly good quick fix.
You've made great progress so far. The reason for the strange offset is that the bounding rectangles of the annotations are really small ant they are positioned on baseline of the current line. So using the Top of the bounding rectangle of the AnnotationLayoutBox, for positioning the UI elements you've created, will produce such results.
However there is a solution to your problem - to calculate the Y coordinate of the line that contains the annotation box. Here is how this can be done (similar method is used in the online demo:
private
static
float
GetLineY(InlineLayoutBox box)
{
return
box.ControlBoundingRectangle.Top - (box.LineInfo.BaselineOffset - box.BaselineOffset);
}
Now that you have this method , you can set the Canvas.Top property of the Rectangle element you've created this way:
Canvas.SetTop(rectangle, GetLineY(startRect));
I hope this makes sense.
Kind regards,Alex
the Telerik team
Explore the entire Telerik portfolio by downloading the Ultimate Collection trial package. Get it now >>