Current filter:
                                You should refresh the page.
                                  • Dear support,

                                    in our XAF web application I want to add an action (not necesseraly a XAF action) to the ASPxRichEdit control, either to the ribbon menu or to the context menu.

                                    The use case is the following: We provide a custom 'create link' action that, when executed, displays a list view for a specific object type. The user can select one object and accept. Afterwards we insert a hyperlink in the document at the current cursor position that points to the detailview of that object (with the new user friendly url).

                                    I checked your demo (https://demos.devexpress.com/ASPxRichEditDemos/BarsAndUI/ContextMenuCustomization.aspx) and the documentation and I found the client side event 'RichEditPopupMenuShowing'. I tried to hook on to the event as decribed here:

                                    https://www.devexpress.com/Support/Center/Question/Details/T355096/how-to-add-a-handler-to-the-aspxgridview-clientsideevents-selectionchanged-event

                                    That code works out of the box for the grid, but for the ASPxRichEdit my handler for the 'RichEditPopupMenuShowing' event was not called (I just used an alert as in your example).

                                    Now I am not sure if I went in the right direction with client side events in the first place!? So what is the best way to

                                    a) add an item / action to the ribbon menu and handle a click event (if that is possible at all - I have only found the option use a complete custom ribbon so far...)?
                                    b) add an item to the context menu and handle a click event?
                                    c) in case of client side events, how can I then display a ListView of a specific type / specific id where the user can select one object?

                                    Thank you and regards
                                    Bernd

                                1 Solution

                                Creation Date Importance Sort by

                                Hello Bernd,

                                The ASPxClientRichEdit.PopupMenuShowing event should be appropriate for adding a custom item to the context menu. Here is an example of how to add a custom item to the ribbon control and the context menu:

                                [C#]
                                using DevExpress.ExpressApp; using DevExpress.ExpressApp.Office.Web; using DevExpress.ExpressApp.Web.Utils; using DevExpress.Web; using DevExpress.Web.ASPxRichEdit; using System; namespace FeatureCenter.Module.Web { public class RichTextEditorMenuController : ViewController<DetailView> { protected override void OnActivated() { base.OnActivated(); foreach(ASPxRichTextPropertyEditor richPropertyEditor in View.GetItems<ASPxRichTextPropertyEditor>()) { richPropertyEditor.ControlCreated += RichPropertyEditor_ControlCreated; } } private void RichPropertyEditor_ControlCreated(object sender, EventArgs e) { ASPxRichEdit richEditor = ((ASPxRichTextPropertyEditor)sender).ASPxRichEditControl; richEditor.RibbonTabs[0].Groups[0].Items.Add(new RibbonButtonItem("CustomItem")); string customCommandClickHandler = "function(s, e) { if (e.commandName == 'CustomItem') { alert('1'); } }"; ClientSideEventsHelper.AssignClientHandlerSafe(richEditor, "CustomCommandExecuted", customCommandClickHandler, "RichTextEditorMenuController"); string popupMenuShowingHandler = "function(s, e) {" + "var customItem = new ASPxClientRichEditPopupMenuItem();" + "customItem.name = 'CustomItem';" + "customItem.text = 'Custom Item';" + "e.menuItems.Add(customItem);}"; ClientSideEventsHelper.AssignClientHandlerSafe(richEditor, "PopupMenuShowing", popupMenuShowingHandler, "RichTextEditorMenuController"); } protected override void OnDeactivated() { base.OnDeactivated(); foreach (ASPxRichTextPropertyEditor richPropertyEditor in View.GetItems<ASPxRichTextPropertyEditor>()) { richPropertyEditor.ControlCreated -= RichPropertyEditor_ControlCreated; } } } }

                                To learn how to show an XAF ListView from a client-side script, refer to the following topic: How to: Raise XAF Callbacks from Client-Side Events and Process these Callbacks on Server.

                                Please let me know if you need further assistance.

                                Show all comments
                                • Bernd F. 07.26.2019

                                  Hello Anatol,

                                  thank you for your explanation. I was able to add the context menu items, trigger an event and handle it on the server side successfuly.
                                  But now (as described in my usecase above) I want to insert a hyperlink at the current cursor position.

                                  Here is the solution that I was able to implement (instead of a hyperlink I just insert some text for testing purposes):

                                  [C#]
                                  using System; using System.IO; using System.Linq; using System.Reflection; using DevExpress.ExpressApp; using DevExpress.ExpressApp.Office.Web; using DevExpress.ExpressApp.Web; using DevExpress.ExpressApp.Web.Templates; using DevExpress.ExpressApp.Web.Utils; using DevExpress.Web.ASPxRichEdit; using DevExpress.XtraRichEdit; using SDPlus.Module.UtilityClasses; namespace SDPlus.Module.Web.Controllers { public partial class AmxCreateReferenceDetailViewController : ViewController<DetailView>, IXafCallbackHandler { private readonly string _handlerId; public AmxCreateReferenceDetailViewController() { InitializeComponent(); _handlerId = "AmxCreateReferenceDetailViewController" + Guid.NewGuid(); } protected XafCallbackManager CallbackManager { get { return ((ICallbackManagerHolder) WebWindow.CurrentRequestPage).CallbackManager; } } public void ProcessAction(string parameter) { string[] parameters = parameter.Split(';'); string propertyName = parameters[1]; PropertyInfo property = View.CurrentObject.GetType().GetProperty(propertyName); byte[] propertyValue = (byte[]) property.GetValue(View.CurrentObject, null); using (RichEditDocumentServer srv = new RichEditDocumentServer()) { using (MemoryStream inStream = new MemoryStream(propertyValue)) { srv.Document.LoadDocument(inStream); srv.Document.AppendText("Here I would insert a hyperlink at the caret position instead"); using (MemoryStream outStream = new MemoryStream()) { srv.Document.SaveDocument(outStream, DocumentFormat.OpenXml); property.SetValue(View.CurrentObject, outStream.ToArray()); } } } // I don't really want to commit ObjectSpace.CommitChanges(); ObjectSpace.Refresh(); } protected override void OnActivated() { base.OnActivated(); foreach (ASPxRichTextPropertyEditor richPropertyEditor in View.GetItems<ASPxRichTextPropertyEditor>()) { richPropertyEditor.ControlCreated += RichPropertyEditor_ControlCreated; } } protected override void OnDeactivated() { base.OnDeactivated(); foreach (ASPxRichTextPropertyEditor richPropertyEditor in View.GetItems<ASPxRichTextPropertyEditor>()) { richPropertyEditor.ControlCreated -= RichPropertyEditor_ControlCreated; } } protected override void OnViewControlsCreated() { base.OnViewControlsCreated(); CallbackManager.RegisterHandler(_handlerId, this); } private void RichPropertyEditor_ControlCreated(object sender, EventArgs e) { ASPxRichEdit richEditor = ((ASPxRichTextPropertyEditor) sender).ASPxRichEditControl; //richEditor.RibbonTabs[0].Groups[0].Items.Add(new RibbonButtonItem("CustomItem")); // Add context menu item in PopupMenuShowing event string popupMenuShowingHandler = "function(s, e) {" + "var customItem = new ASPxClientRichEditPopupMenuItem();" + "customItem.name = 'CustomItem';" + "customItem.text = 'Custom Item';" + "e.menuItems.Add(customItem);}"; ClientSideEventsHelper.AssignClientHandlerSafe(richEditor, "PopupMenuShowing", popupMenuShowingHandler, "AmxCreateReferenceDetailViewController"); // Add handler that executes when the command is clicked string editorProperty = ((ASPxRichTextPropertyEditor) sender).PropertyName; string editorClientInstanceName = $"rtf{editorProperty}"; richEditor.ClientInstanceName = editorClientInstanceName; // Helps us identify the command and property string callbackScriptParameter = $"e.commandName + ';{editorProperty};{editorClientInstanceName};' + {editorClientInstanceName}.document.activeSubDocument.length"; string callbackScript = CallbackManager.GetScript(_handlerId, callbackScriptParameter); string customCommandClickHandler = "function(s, e) { if (e.commandName == 'CustomItem') {" + callbackScript + "} }"; ClientSideEventsHelper.AssignClientHandlerSafe(richEditor, "CustomCommandExecuted", customCommandClickHandler, "AmxCreateReferenceDetailViewController"); } } }

                                  The interesting part is the 'ProcessAction' method. What I really would like to do is to access the client side rich edit API and insert the hyperlink, but I guess that is not possible in the server code, correct?

                                  The problem of my (prototype) solution is that I need to commit and refresh. The best case scenario would be that a link (or text as in my prototype) is inserted at the current cursor position and afterwards the cursor is located after or before the link and no commit is performed.

                                  Do you have a solution for that requirement?

                                • Bernd F. 07.28.2019

                                  To be more precise, after the client event from the popup menu action triggers and goes back to the server, I want to display a popup list view where the user can select the link target (that is why I go back to the server code in the first place).

                                  So I think I need to be able to handle to 'OK' press of the popup list view on the client, evaluate the oid of the selected item and use the client side API to add the hyperlink. What do you think, how could this last part of the use case be implemented?

                                • Gosha (DevExpress Support) 07.29.2019

                                  Hi Bernd,

                                  You can open a popup List View from your controller ProcessAction method using the ShowViewInPopupWindow method. If you pass the okDelegate action to this method, you can invoke the WebWindow.RegisterStartupScript method to execute a JavaScript code after a user clicks the popup OK button. ASPxClientRichEdit provides client-side methods to manage the cursor position and to insert text. The RichEdit - How to get the cursor position and ASPxRichEdit - Insert/Edit text dynamically tickets describe how to do this. The following code illustrates this:

                                  [C#]
                                  public void ProcessAction(string parameter) { ListView view = Application.CreateListView(typeof(Test), false); Action acceptAction = () => { string[] parameters = parameter.Split(';'); string script = $"setTimeout(() => {{ var rich = ASPxClientRichEdit.GetControlCollection().GetByName('{parameters[2]}'); rich.selection.intervals = {parameters[4]}; rich.commands.insertText.execute('Text')}}, 0);"; ((WebWindow)WebApplication.Instance.MainWindow).RegisterStartupScript("Test", script); }; Application.ShowViewStrategy.ShowViewInPopupWindow(view, acceptAction); }

                                  It's also necessary to pass the current cursor position when a user clicks your custom context menu item.

                                  [C#]
                                  string callbackScriptParameter = $"e.commandName + ';{editorProperty};{editorClientInstanceName};' + {editorClientInstanceName}.document.activeSubDocument.length +';' + JSON.stringify(s.selection.intervals)"; string callbackScript = CallbackManager.GetScript(_handlerId, callbackScriptParameter); string customCommandClickHandler = "function(s, e) { if (e.commandName == 'CustomItem') {" + callbackScript + "} }"; ClientSideEventsHelper.AssignClientHandlerSafe(richEditor, "CustomCommandExecuted", customCommandClickHandler, "AmxCreateReferenceDetailViewController");
                                • Bernd F. 07.30.2019

                                  Hi Gosha,

                                  I was able to finish the use case. Thank you for your help!

                                • Gosha (DevExpress Support) 07.31.2019

                                  You are welcome, Bernd!

                                • Bernd F. 08.01.2019

                                  Hello Gosha,

                                  could you please help me again with the following: I refactored your client side code a little bit to not create, but change a reference.
                                  The code looks like this:

                                  [C#]
                                  string editScript = "setTimeout(() => {{ " + $"var rich = ASPxClientRichEdit.GetControlCollection().GetByName('{parameters[1]}'); " + $"var intervals = {parameters[2]}; " + "var field = rich.document.activeSubDocument.findFields(intervals[0].start)[0];" + "if (field && field.isHyperlink) {" + "var hyperlinkSettings = { " + $"url: '{linkUrl}', text: '{reference.Text}', tooltip: '{reference.Comment}' " + "};" + "alert('This alert does appear'); " + "rich.commands.changeHyperlink.execute(field.index, hyperlinkSettings); " + "alert('This alert does not appear'); " + "} " + "}}, 0);"; ((WebWindow) WebApplication.Instance.MainWindow).RegisterStartupScript("Test", isExistingReference ? editScript : insertScript);

                                  This is how the script looks during runtime:

                                  [C#]
                                  setTimeout(() => {{ var rich = ASPxClientRichEdit.GetControlCollection().GetByName('rtfAuditObjective'); var intervals = [{"start":237,"length":0}]; var field = rich.document.activeSubDocument.findFields(intervals[0].start)[0]; if (field && field.isHyperlink) { var hyperlinkSettings = { url: 'http://localhost:2064/Amx_Audit_DetailView/4ef43945-60c0-4c48-bda4-57f3f3f6c512', text: '2019-001 test x', tooltip: 'xxx' }; alert('This alert does appear'); rich.commands.changeHyperlink.execute(field.index, hyperlinkSettings); alert('This alert does not appear'); } }}, 0);

                                  I debugged all lines with alerts, and the result is that the field is found and the field is a hyperlink, but in the end the hyperlink is not changed and the last alert is not executed. So I suppose that the command crashes. Do you have an idea? Why can the command fail even though the parameters seem to fit?

                                • Bernd F. 08.01.2019

                                  I was able to solve it my self. The "optional" subdocumentId parameter needs to be specified:

                                  [C#]
                                  ... "rich.commands.changeHyperlink.execute(field.index, hyperlinkSettings, rich.document.activeSubDocument.id); " + ...

                                  Maybe you could explain in your documentation when this optional parameter is needed:
                                  https://docs.devexpress.com/AspNet/js-ChangeHyperlinkCommand.execute(fieldIndex-hyperlinkSettings)

                                • Gosha (DevExpress Support) 08.02.2019

                                  It's great to hear that the issue has been resolved, Bernd. I also see that a hyperlink is not changed if the subDocumentId parameter is not passed. I've passed this information to our ASP.NET team so that they will check this behavior.

                                • Gosha (DevExpress Support) 08.02.2019

                                  Hello Bernd,

                                  I've created a separate ticket on your behalf (T803552: ASPxRichEdit does not update a hyperlink if the subDocumentId parameter is not passed to the commands.changeHyperlink.execute method) for this issue in our ASPxRichEdit control. Our team will research this behavior.