Skip to content

Instantly share code, notes, and snippets.

@sappho192
Created June 27, 2024 08:51
Show Gist options
  • Save sappho192/14cfb1e4d0eef73ba56419a5715e6701 to your computer and use it in GitHub Desktop.
Save sappho192/14cfb1e4d0eef73ba56419a5715e6701 to your computer and use it in GitHub Desktop.
WPF FlowDocumentScrollViewer example
<Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<FlowDocumentScrollViewer Name="ChatPanel" Block.LineHeight="2">
<!--<RichTextBox x:Name="rtbChatlog1" IsReadOnly="True" >
<RichTextBox.ContextMenu>
<ContextMenu>
<MenuItem Header="복사" Command="ApplicationCommands.Copy"/>
<MenuItem Header="Custom Item" Click="MenuItem_Click"/>
</ContextMenu>
</RichTextBox.ContextMenu>
</RichTextBox>-->
</FlowDocumentScrollViewer>
</Grid>
</Window>
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Media;
namespace WpfApp1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private FlowDocument _chatDocument;
public MainWindow()
{
InitializeComponent();
// Initialize the FlowDocument that will contain all chat messages
_chatDocument = new FlowDocument();
ChatPanel.Document = _chatDocument;
}
private void AddMessage(string message)
{
var paragraph = new Paragraph(new Run(message))
{
Foreground = Brushes.Blue,
FontWeight = FontWeights.Bold
};
// Create and attach a custom context menu to the paragraph
var contextMenu = new ContextMenu();
var menuItem = new MenuItem { Header = "Custom Action" };
menuItem.Click += MenuItem_Click;
// Store the Paragraph object in the Tag property of the MenuItem
menuItem.Tag = paragraph;
contextMenu.Items.Add(menuItem);
var menuItemReplace = new MenuItem { Header = "Replace" };
menuItemReplace.Click += MenuItemReplace_Click;
menuItemReplace.Tag = paragraph;
contextMenu.Items.Add(menuItemReplace);
paragraph.ContextMenu = contextMenu;
_chatDocument.Blocks.Add(paragraph);
}
private static int count = 1;
private void MenuItemReplace_Click(object sender, RoutedEventArgs e)
{
// Retrieve the Paragraph object from the Tag property of the MenuItem
var paragraph = ((MenuItem)sender).Tag as Paragraph;
if (paragraph != null)
{
string newMessage = $"replaced! ({count++})";
ReplaceTextInParagraph(paragraph, newMessage);
MessageBox.Show($"Custom action triggered for paragraph: {newMessage}");
}
}
private void MenuItem_Click(object sender, RoutedEventArgs e)
{
// Retrieve the Paragraph object from the Tag property of the MenuItem
var paragraph = ((MenuItem)sender).Tag as Paragraph;
if (paragraph != null)
{
string paragraphText = GetTextFromParagraph(paragraph);
MessageBox.Show($"Custom action triggered for paragraph: {paragraphText}");
}
}
private string GetTextFromParagraph(Paragraph paragraph)
{
StringBuilder sb = new StringBuilder();
foreach (Inline inline in paragraph.Inlines)
{
if (inline is Run run)
{
sb.Append(run.Text);
}
else if (inline is Span span)
{
foreach (Inline childInline in span.Inlines)
{
if (childInline is Run childRun)
{
sb.Append(childRun.Text);
}
}
}
// Handle other types of Inline elements if necessary
}
return sb.ToString();
}
private void ReplaceTextInParagraph(Paragraph paragraph, string newText)
{
// Clear existing inlines
paragraph.Inlines.Clear();
// Add new text
paragraph.Inlines.Add(new Run(newText));
}
protected override void OnContentRendered(EventArgs e)
{
base.OnContentRendered(e);
// Example usage: Add multiple messages
for (int i = 0; i < 500; i++)
{
AddMessage($"Lorem ipsum dolor sit amet, consectetur adipiscing elit. ({i + 1})");
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment