У меня есть тестовое приложение, в котором я пытаюсь вставить редактируемый абзац, чтобы пользователь мог писать туда информацию (возможно, это можно реализовать только с помощью «Выполнить», я взял абзац только для примера, если вы знаете, как добавить «Выполнить» в «Выполнить», это будет будь великолепен). Я НЕ ХОЧУ использую для этого richtextbox по двум основным причинам:
Пользователь не может редактировать никакие другие части документа
Flowdocument имеет пагинацию Для того, что я сделал сейчас, у меня есть это: текстовое поле и потоковый документ с одним абзацем (aaaaa bbb cccc), созданным xaml, и одним, созданным кодом
Мой редактируемый абзац идет в конец документа. Я хочу поставить его вместо «bbb», например. Так что он должен как-то найти "bbb" из всего документа, заменить его и поставить на это место мой абзац
Я пытался:
Вот мой xaml
<Grid x:Name = "grid">
<Grid.RowDefinitions>
<RowDefinition Height = "Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<FlowDocumentReader Grid.Row = "1">
<FlowDocument x:Name = "DocumentReader"/>
</FlowDocumentReader>
</Grid>
И вот мой xaml.cs без какого-либо плохого кода с моими попытками установить paragrahp внутри абзаца - просто текстовое поле и редактируемый абзац
Dictionary<string, Paragraph> paragraphs = new Dictionary<string, Paragraph>();
private string text = $"{{\\rtf1\\ansi\\ansicpg1252\\uc1\\htmautsp\\deff2{{\\fonttbl{{\\f0\\fcharset0 Times New Roman;}}{{\\f2\\fcharset0 Palatino Linotype;}}}}{{\\colortbl\\red0\\green0\\blue0;\\red255\\green255\\blue255;}}\\loch\\hich\\dbch\\pard\\plain\\ltrpar\\itap0{{\\lang1033\\fs21\\f2\\cf0 \\cf0\\ql{{\\f2 {{\\ltrch aaaaa bbb ccc}}\\li0\\ri0\\sa0\\sb0\\fi0\\ql\\par}}\r\n}}\r\n}}";
public MainWindow()
{
InitializeComponent();
//this is how data loads in flowdocument in my actual programm
TextRange textRange = new TextRange(DocumentReader.ContentStart, DocumentReader.ContentEnd);
using (MemoryStream stream = new MemoryStream(ASCIIEncoding.Default.GetBytes(text)))
{
textRange.Load(stream, DataFormats.Rtf);
}
//this is what i was testing
var parag = new Paragraph { Name = "paragName" };
parag.Inlines.Add(new Run("as"));
paragraphs.Add("paragName", parag);
DocumentReader.Blocks.Add(parag);
var txt = new TextBox{Tag = "paragName" };
txt.TextChanged += (sender, args) =>
{
paragraphs.First(x => (string)x.Key == txt.Tag).Value.Inlines.Clear();
paragraphs.First(x => (string)x.Key == txt.Tag).Value.Inlines.Add(new Run((sender as TextBox).Text));
};
grid.Children.Add(txt);
}
Это очень сырой, я просто тестировал его, но я не могу решить, как это сделать, пожалуйста, помогите





Довольно простым решением было бы использовать TextBlock, а затем вставить TextBox в позицию, которую вы хотите отредактировать.
Следующий пример позволяет EditableTextBlock расширить TextBlock, чтобы расширить TextBlock поведение.
Установка свойства EditableTextBlock.EditableTextRange определяет положение и диапазон в тексте, которые должны быть доступны для редактирования. Полный отображаемый текст можно получить, как обычно, обратившись к унаследованному свойству EditableTextBlock.Text.
Установка свойства EditableTextBlock.EditableTextRange вызовет появление TextBox в указанной позиции. Затем отредактированное значение фиксируется нажатием клавиши Войти или щелчком Button рядом с TextBox.
Затем значок TextBox исчезнет, а отредактированный текст снова станет доступен только для чтения.
Чтобы упростить обработку контента, EditableTextBlock поддерживает один запуск для отображения контента.
Реализация очень проста и должна служить вам полезной отправной точкой.
TextRange.cs
public readonly struct TextRange : IEquatable<TextRange>
{
public TextRange(int index, int length)
{
// TODO::Throw ArgumentException if values are out of range (e.g. < 0)
this.Index = index;
this.Length = length;
}
public bool Equals(TextRange other) => this.Index.Equals(other.Index) && this.Length.Equals(other.Length);
public int Index { get; }
public int Length { get; }
}
EditableTextBlock.cs
public class EditableTextBlock : TextBlock
{
public TextRange EditableTextRange
{
get => (TextRange)GetValue(EditableTextRangeProperty);
set => SetValue(EditableTextRangeProperty, value);
}
public static readonly DependencyProperty EditableTextRangeProperty = DependencyProperty.Register(
"EditableTextRange",
typeof(TextRange),
typeof(EditableTextBlock),
new PropertyMetadata(default(TextRange), OnTextRangeChanged));
public static void SetText(UIElement attachedElement, string value)
=> attachedElement.SetValue(TextProperty, value);
public static string GetText(UIElement attachedElement)
=> attachedElement.GetValue(TextProperty) as string;
public static readonly DependencyProperty TextProperty = DependencyProperty.RegisterAttached(
"Text",
typeof(string),
typeof(EditableTextBlock),
new FrameworkPropertyMetadata(default(string), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
public static RoutedUICommand CommitChangesCommand { get; }
static EditableTextBlock()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(EditableTextBlock), new FrameworkPropertyMetadata(typeof(EditableTextBlock)));
CommitChangesCommand = new RoutedUICommand(
"Commit edit changes",
nameof(CommitChangesCommand),
typeof(EditableTextBlock),
new InputGestureCollection()
{
new KeyGesture(Key.Enter)
});
}
public EditableTextBlock()
{
var editableElementContentTemplate = Application.Current.Resources["EditableElementTemplate"] as DataTemplate;
if (editableElementContentTemplate == null)
{
throw new InvalidOperationException("Define a DataTemplate named "EditableElementTemplate" in App.xaml");
}
var editableContent = new ContentPresenter() { ContentTemplate = editableElementContentTemplate };
this.EditableElement = new InlineUIContainer(editableContent);
this.CommandBindings.Add(new CommandBinding(CommitChangesCommand, ExecuteCommitChangesCommand));
}
private static void OnTextRangeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
=> (d as EditableTextBlock).OnTextRangeChanged((TextRange)e.OldValue, (TextRange)e.NewValue);
private void ExecuteCommitChangesCommand(object sender, ExecutedRoutedEventArgs e)
{
var documentTextBuilder = new StringBuilder();
foreach (Inline documentElement in this.Inlines)
{
documentTextBuilder.Append(documentElement is Run run ? run.Text : GetText((documentElement as InlineUIContainer).Child));
}
var readOnlyDocument = new Run(documentTextBuilder.ToString());
this.Inlines.Clear();
this.Inlines.Add(readOnlyDocument);
}
protected virtual void OnTextRangeChanged(TextRange oldTextRange, TextRange newTextRange)
{
Inline documentContent = this.Inlines.FirstInline;
if (documentContent is Run run && newTextRange.Index < run.Text.Length)
{
string newPreceedingReadOnlyRangeText = run.Text.Substring(0, newTextRange.Index);
var newReadOnlyElement = new Run(newPreceedingReadOnlyRangeText);
this.Inlines.InsertBefore(documentContent, newReadOnlyElement);
string newEditableRangeText = run.Text.Substring(newTextRange.Index, newTextRange.Length);
SetText(this.EditableElement.Child, newEditableRangeText);
this.Inlines.InsertAfter(documentContent, this.EditableElement);
this.Inlines.Remove(documentContent);
string remainingReadOnlyRangeText = run.Text.Substring(newTextRange.Index + newTextRange.Length);
var remainingReadOnlyElement = new Run(remainingReadOnlyRangeText);
this.Inlines.InsertAfter(this.EditableElement, remainingReadOnlyElement);
}
else // Append
{
string newEditableRangeText = String.Empty;
SetText(this.EditableElement.Child, newEditableRangeText);
this.Inlines.Add(this.EditableElement);
}
}
private InlineUIContainer EditableElement { get; }
}
App.xaml
<Application xmlns:system = "clr-namespace:System;assembly=netstandard">
<Application.Resources>
<DataTemplate x:Key = "EditableElementTemplate"
DataType = "{x:Type system:String}">
<StackPanel Orientation = "Horizontal">
<TextBox Text = "{Binding RelativeSource = {RelativeSource AncestorType=ContentPresenter}, Path=(local:EditableTextBlock.Text), UpdateSourceTrigger=PropertyChanged}" />
<Button Command = "{x:Static local:EditableTextBlock.CommitChangesCommand}"
Content = "Ok" />
</StackPanel>
</DataTemplate>
</Application.Resources>
</Application>
MainWindow.xaml
<Window>
<local:EditableTextBlock x:Name = "Document" />
</Window>
MainWindow.xaml.cs
partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.Loaded #= OnLoaded;
}
private void OnLoaded(object sender, EventArgs e)
{
var documentText = "This is some random text.";
this.Document.Text = documentText;
int editableTextIndex = this.Document.Text.IndexOf("random");
int editableTextLength = "random".Length;
this.Document.EditableTextRange = new TextRange(editableTextIndex, editableTextLength);
}
}
у меня получилось, благодаря вам!