microsoft-ui-xaml
microsoft-ui-xaml copied to clipboard
Proposal: expose native richedit document control
The existing TOM that's exposed in xaml for RE is fairly basic and doesn't allow accessing the underlying RE OM. XAML keeps track of some properties so it makes sense (we dont't want certain props to be updated without XAML's involvement, lest XAML won't know about them and things will get wonky™️ ). However, this is severely limiting as the WinRT TOM is pretty limited.
One example that doesn't seem to work today, is being able to load a RTF document file into a RTB/REB. Another set of examples is regarding doing hit-testing on specific sections of a document (we had to work around this in RNW, but since the native OM isn't exposed, our solution ends up being very inefficient and does a lot more calculations than should be necessary).
Open Questions
Do we expose the full TOM (down to the RE hwnd?) and have a disclaimer of "use at your own risk"? or do we expose more properties piecemeal?
@asklar what part of not loading an RTF file isn't working for you? The example in the docs showing loading in an RTF file stream into a RichEditBox which is also in the controls gallery?
https://docs.microsoft.com/en-us/windows/apps/design/controls/rich-edit-box
@michael-hawker yes, loading from RTF works - what isn't possible today is to access the underlying RichEdit control to be able to call the win32 APIs. The WinRT TOM only expose a small set of features, so the proposal is to expose the HWND directly so that we aren't getting in the way of replicating WM_* based APIs with WinRT for RE.
Exposing RTFText as a property to allow binding would be useful. I'm currently doing so as a DependencyProperty, in order to use the ITextDocument interface.
@asklar I'm at a loss trying to triage this issue, could you assign an appropriate team and area as well as suggest a PM to drive the feature proposal? I suspect @marb2000
I think this would end up going to either @ranjeshj or @codendone 's teams for work in Text; I'm not sure who would be the PM but @marb2000 seems right :)
What will be the advantage if xthe richedit document cojntrol will be exposed? Will be able to define the headings, tables etc?
In our case, we use multiple RichEditBox controls in our app, and both create reports and export them to another app by combining them into composite documents. This requires removing duplicate and unnecessary document properties from the RTF documents beneath each RichEditbox.
I also need UI support (WYSIWYG) for editing tables in a RichEditBox.
For now, I'll have to rely on some very bad code (Hope it doesn't break 🙈)
using Microsoft.UI.Xaml.Controls;
using System.Runtime.InteropServices;
using Windows.Win32.Foundation;
using Windows.Win32.UI.Controls.RichEdit;
using WinRT;
using static Windows.Win32.PInvoke;
using ITextDocument2_Win32 = Windows.Win32.UI.Controls.RichEdit.ITextDocument2;
public sealed class Win32RichEditBox : RichEditBox
{
readonly ITextServices2_WinUIEdit _txtServices;
readonly ITextDocument2_Win32 _win32Doc;
public unsafe Win32RichEditBox()
{
nint pCWinRTextDocument = ((IWinRTObject)Document).NativeObject.ThisPtr;
nint pCTxtEdit = *(nint*)(pCWinRTextDocument + 64);
var cTxtEdit = Marshal.GetObjectForIUnknown(pCTxtEdit);
_txtServices = (ITextServices2_WinUIEdit)cTxtEdit;
_win32Doc = (ITextDocument2_Win32)cTxtEdit;
}
public unsafe void InsertTable() { ... }
public void InsertTableRowAbove(int rowCount = 1)
{
var range = SelectionRange;
range.GetDuplicate2(out range).ThrowOnFailure();
range.Expand((int)tomConstants.tomRow, out _).ThrowOnFailure();
// Insert above
range.GetStart(out var position).ThrowOnFailure();
range.SetStart(position).ThrowOnFailure();
range.SetEnd(position).ThrowOnFailure();
range.GetRow(out var row).ThrowOnFailure();
row.Insert(rowCount).ThrowOnFailure();
}
}
@ShortDevelopment It probably isn't too likely that there will be changes in the underlying RichEdit implementation to break your code, but it would still be safer to switch to RTF to add the table, such as this example:
var doc1 = richEditBox.TextDocument;
var range = doc.GetRange(0, 0);
range.SetText(TextSetOptions.FormatRtf,
@"{\rtf1\ansi\deff0" +
@"\trowd" +
@"\cellx1000" +
@"\cellx2000" +
@"\cellx3000" +
@"\intbl r1, c1\cell" +
@"\intbl r1, c2\cell" +
@"\intbl r1, c3\cell" +
@"\row" +
@"\trowd" +
@"\cellx1000" +
@"\cellx2000" +
@"\cellx3000" +
@"\intbl longer r2, c1\cell" +
@"\intbl r2, c2\cell" +
@"\intbl r2, c3\cell" +
@"\row" +
@"}");
That produces two rows of 3 columns each. The syntax isn't pretty, but it can be generated. and doesn't require any unsafe code.
@codendone That would certainly be a lot safer, but I actually want to be able to dynamically insert table rows into existing tables.