WPF C#/VB
Notepad App with Spellchecker
C#
// AZUL CODING ---------------------------------------
// WPF C#/VB - Notepad App with Spellchecker
// https://youtu.be/7lxpGNWRvWs
using System;
using System.Text;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using Microsoft.Win32;
using System.Windows.Media;
using System.Text.RegularExpressions;
using System.Drawing.Text;
namespace AzulNotepad;
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private int lastSearchIndex = 0;
private static readonly OpenFileDialog OpenDialog = new()
{
Title = "Select a text file",
Filter = "Text files (.txt)|*.txt"
};
private static readonly SaveFileDialog SaveDialog = new()
{
Title = "Save to a text file",
Filter = "Text files (.txt)|*.txt"
};
public MainWindow()
{
InitializeComponent();
FontComboBox.ItemsSource = new InstalledFontCollection().Families.Select(x => x.Name);
FontComboBox.SelectedItem = MainTextBox.FontFamily.ToString();
FontSizeComboBox.ItemsSource = Enumerable.Range(0, 9).Select(i => 8 + (i * 2)).ToList();
FontSizeComboBox.SelectedItem = (int)MainTextBox.FontSize;
}
private void MainTextBox_TextChanged(object sender, TextChangedEventArgs e)
{
UndoBtn.IsEnabled = MainTextBox.CanUndo;
RedoBtn.IsEnabled = MainTextBox.CanRedo;
UpdateStatusBar();
}
private void MainTextBox_SelectionChanged(object sender, RoutedEventArgs e)
{
UpdateStatusBar();
}
#region Open and save
private void OpenBtn_Click(object sender, RoutedEventArgs e)
{
if (OpenDialog.ShowDialog() == true)
{
try
{
MainTextBox.Text = File.ReadAllText(OpenDialog.FileName);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
}
private void SaveBtn_Click(object sender, RoutedEventArgs e)
{
if (SaveDialog.ShowDialog() == true)
{
try
{
File.WriteAllText(SaveDialog.FileName, MainTextBox.Text, Encoding.UTF8);
MessageBox.Show("File saved successfully", "Success", MessageBoxButton.OK, MessageBoxImage.Information);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
}
#endregion
#region Undo/redo
private void UndoBtn_Click(object sender, RoutedEventArgs e)
{
MainTextBox.Undo();
if (!MainTextBox.CanUndo)
UndoBtn.IsEnabled = false;
}
private void RedoBtn_Click(object sender, RoutedEventArgs e)
{
MainTextBox.Redo();
if (!MainTextBox.CanRedo)
RedoBtn.IsEnabled = false;
}
#endregion
#region Clipboard
private void CutBtn_Click(object sender, RoutedEventArgs e)
{
MainTextBox.Cut();
}
private void CopyBtn_Click(object sender, RoutedEventArgs e)
{
MainTextBox.Copy();
}
private void PasteBtn_Click(object sender, RoutedEventArgs e)
{
MainTextBox.Paste();
}
#endregion
#region Find and replace
private void FindReplaceBtn_Click(object sender, RoutedEventArgs e)
{
FindReplaceMenu.Visibility = FindReplaceMenu.Visibility == Visibility.Visible
? Visibility.Collapsed : Visibility.Visible;
}
private (RegexOptions regexOptions, string pattern) GetSearchOptions(string searchText)
{
RegexOptions regexOptions = MatchCaseCheckBox.IsChecked ?? false
? RegexOptions.None : RegexOptions.IgnoreCase;
string pattern = MatchWordCheckBox.IsChecked ?? false
? $@"\b{Regex.Escape(searchText)}\b"
: Regex.Escape(searchText);
return (regexOptions, pattern);
}
private void FindOrReplace(bool replace = false)
{
string searchText = FindTextBox.Text;
string replaceText = ReplaceTextBox.Text;
string mainText = MainTextBox.Text;
if (string.IsNullOrEmpty(searchText))
{
MessageBox.Show("Please enter text to find.", "Find and replace",
MessageBoxButton.OK, MessageBoxImage.Information);
return;
}
lastSearchIndex = Math.Max(0, Math.Min(lastSearchIndex, mainText.Length));
string remainingText = mainText[lastSearchIndex..];
(RegexOptions regexOptions, string pattern) = GetSearchOptions(searchText);
Match match = Regex.Match(remainingText, pattern, regexOptions);
if (match.Success)
{
int absoluteIndex = lastSearchIndex + match.Index;
if (replace)
{
mainText = mainText.Remove(absoluteIndex, match.Length).Insert(absoluteIndex, replaceText);
MainTextBox.Text = mainText;
}
MainTextBox.Select(absoluteIndex, replace ? replaceText.Length : match.Length);
MainTextBox.Focus();
lastSearchIndex = absoluteIndex + (replace ? replaceText.Length : match.Length);
}
else
{
match = Regex.Match(mainText, pattern, regexOptions);
if (match.Success)
{
if (replace)
{
mainText = mainText.Remove(match.Index, match.Length).Insert(match.Index, replaceText);
MainTextBox.Text = mainText;
}
MainTextBox.Select(match.Index, replace ? replaceText.Length : match.Length);
MainTextBox.Focus();
lastSearchIndex = match.Index + (replace ? replaceText.Length : match.Length);
}
else
{
MessageBox.Show($"Cannot find \"{searchText}\"", "Find and replace",
MessageBoxButton.OK, MessageBoxImage.Information);
lastSearchIndex = 0;
}
}
}
private void FindNextBtn_Click(object sender, RoutedEventArgs e)
{
FindOrReplace();
}
private void ReplaceBtn_Click(object sender, RoutedEventArgs e)
{
FindOrReplace(true);
}
private void ReplaceAllBtn_Click(object sender, RoutedEventArgs e)
{
string searchText = FindTextBox.Text;
string replaceText = ReplaceTextBox.Text;
string mainText = MainTextBox.Text;
if (string.IsNullOrEmpty(searchText))
{
MessageBox.Show("Please enter text to find.", "Find and replace",
MessageBoxButton.OK, MessageBoxImage.Information);
return;
}
(RegexOptions regexOptions, string pattern) = GetSearchOptions(searchText);
string replacedText = Regex.Replace(mainText, pattern, replaceText, regexOptions);
int replacementCount = Regex.Matches(mainText, pattern, regexOptions).Count;
MainTextBox.Text = replacedText;
MessageBox.Show($"Replaced {replacementCount} occurrence(s).", "Find and replace",
MessageBoxButton.OK, MessageBoxImage.Information);
lastSearchIndex = 0;
}
#endregion
#region Spellchecker and other features
private void SpellcheckerBtn_Click(object sender, RoutedEventArgs e)
{
MainTextBox.SpellCheck.IsEnabled = !MainTextBox.SpellCheck.IsEnabled;
}
private void DateTimeBtn_Click(object sender, RoutedEventArgs e)
{
string currentDateTime = DateTime.Now.ToString("dd/MM/yyyy HH:mm");
MainTextBox.SelectedText = currentDateTime;
MainTextBox.Focus();
}
private void FontBtn_Click(object sender, RoutedEventArgs e)
{
FontMenu.Visibility = FontMenu.Visibility == Visibility.Visible
? Visibility.Collapsed : Visibility.Visible;
}
private void UpdateFontBtn_Click(object sender, RoutedEventArgs e)
{
try
{
MainTextBox.FontFamily = new FontFamily(FontComboBox.Text);
double fontSize = Convert.ToDouble(FontSizeComboBox.Text);
if (fontSize <= 1)
{
MessageBox.Show("Please enter a font size greater than 1.", "Font size",
MessageBoxButton.OK, MessageBoxImage.Exclamation);
return;
}
MainTextBox.FontSize = fontSize;
}
catch
{
MessageBox.Show("Please enter a valid font name and size.", "Font",
MessageBoxButton.OK, MessageBoxImage.Exclamation);
}
}
private void UpdateStatusBar()
{
int caretIndex = MainTextBox.CaretIndex;
int chars = MainTextBox.Text.Length;
string textBeforeCaret = MainTextBox.Text[..caretIndex];
int lines = textBeforeCaret.Split('\n').Length;
int lastNewLineIndex = textBeforeCaret.LastIndexOf('\n');
int columns = lastNewLineIndex == -1
? caretIndex + 1
: caretIndex - lastNewLineIndex;
string trimmedText = MainTextBox.Text.Trim();
int words = string.IsNullOrEmpty(trimmedText)
? 0 : Regex.Matches(trimmedText, @"\S+").Count;
StatusText.Text = $"Ln {lines} • Col {columns} • {chars} characters • {words} words";
}
#endregion
}
VB.NET
' AZUL CODING ---------------------------------------
' WPF C#/VB - Notepad App with Spellchecker
' https://youtu.be/7lxpGNWRvWs
Imports System
Imports System.Text
Imports System.IO
Imports Microsoft.Win32
Imports System.Text.RegularExpressions
Imports System.Drawing.Text
Class MainWindow
Private lastSearchIndex As Integer = 0
Private Shared ReadOnly OpenDialog As New OpenFileDialog() With {
.Title = "Select a text file",
.Filter = "Text files (.txt)|*.txt"
}
Private Shared ReadOnly SaveDialog As New SaveFileDialog() With {
.Title = "Save to a text file",
.Filter = "Text files (.txt)|*.txt"
}
Public Sub New()
InitializeComponent()
FontComboBox.ItemsSource = New InstalledFontCollection().Families.Select(Function(x) x.Name)
FontComboBox.SelectedItem = MainTextBox.FontFamily.ToString()
FontSizeComboBox.ItemsSource = Enumerable.Range(0, 9).Select(Function(i) 8 + (i * 2)).ToList()
FontSizeComboBox.SelectedItem = CInt(MainTextBox.FontSize)
End Sub
Private Sub MainTextBox_TextChanged(sender As Object, e As TextChangedEventArgs)
UndoBtn.IsEnabled = MainTextBox.CanUndo
RedoBtn.IsEnabled = MainTextBox.CanRedo
UpdateStatusBar()
End Sub
Private Sub MainTextBox_SelectionChanged(sender As Object, e As RoutedEventArgs)
UpdateStatusBar()
End Sub
#Region "Open and save"
Private Sub OpenBtn_Click(sender As Object, e As RoutedEventArgs)
If OpenDialog.ShowDialog() = True Then
Try
MainTextBox.Text = File.ReadAllText(OpenDialog.FileName)
Catch ex As Exception
MessageBox.Show(ex.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error)
End Try
End If
End Sub
Private Sub SaveBtn_Click(sender As Object, e As RoutedEventArgs)
If SaveDialog.ShowDialog() = True Then
Try
File.WriteAllText(SaveDialog.FileName, MainTextBox.Text, Encoding.UTF8)
MessageBox.Show("File saved successfully", "Success", MessageBoxButton.OK, MessageBoxImage.Information)
Catch ex As Exception
MessageBox.Show(ex.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error)
End Try
End If
End Sub
#End Region
#Region "Undo/redo"
Private Sub UndoBtn_Click(sender As Object, e As RoutedEventArgs)
MainTextBox.Undo()
If Not MainTextBox.CanUndo Then
UndoBtn.IsEnabled = False
End If
End Sub
Private Sub RedoBtn_Click(sender As Object, e As RoutedEventArgs)
MainTextBox.Redo()
If Not MainTextBox.CanRedo Then
RedoBtn.IsEnabled = False
End If
End Sub
#End Region
#Region "Clipboard"
Private Sub CutBtn_Click(sender As Object, e As RoutedEventArgs)
MainTextBox.Cut()
End Sub
Private Sub CopyBtn_Click(sender As Object, e As RoutedEventArgs)
MainTextBox.Copy()
End Sub
Private Sub PasteBtn_Click(sender As Object, e As RoutedEventArgs)
MainTextBox.Paste()
End Sub
#End Region
#Region "Find and replace"
Private Sub FindReplaceBtn_Click(sender As Object, e As RoutedEventArgs)
FindReplaceMenu.Visibility = If(FindReplaceMenu.Visibility = Visibility.Visible,
Visibility.Collapsed, Visibility.Visible)
End Sub
Private Function GetSearchOptions(searchText As String) As Tuple(Of RegexOptions, String)
Dim regexOptions As RegexOptions = If(MatchCaseCheckBox.IsChecked = True,
regexOptions.None, regexOptions.IgnoreCase)
Dim pattern As String = If(MatchWordCheckBox.IsChecked = True,
$"\b{Regex.Escape(searchText)}\b",
Regex.Escape(searchText))
Return New Tuple(Of RegexOptions, String)(regexOptions, pattern)
End Function
Private Sub FindOrReplace(Optional replace As Boolean = False)
Dim searchText As String = FindTextBox.Text
Dim replaceText As String = ReplaceTextBox.Text
Dim mainText As String = MainTextBox.Text
If String.IsNullOrEmpty(searchText) Then
MessageBox.Show("Please enter text to find.", "Find and replace",
MessageBoxButton.OK, MessageBoxImage.Information)
Return
End If
lastSearchIndex = Math.Max(0, Math.Min(lastSearchIndex, mainText.Length))
Dim remainingText As String = mainText.Substring(lastSearchIndex)
Dim searchOptionsResult = GetSearchOptions(searchText)
Dim regexOptions As RegexOptions = searchOptionsResult.Item1
Dim pattern As String = searchOptionsResult.Item2
Dim match As Match = Regex.Match(remainingText, pattern, regexOptions)
If match.Success Then
Dim absoluteIndex As Integer = lastSearchIndex + match.Index
If replace Then
mainText = mainText.Remove(absoluteIndex, match.Length).Insert(absoluteIndex, replaceText)
MainTextBox.Text = mainText
End If
MainTextBox.Select(absoluteIndex, If(replace, replaceText.Length, match.Length))
MainTextBox.Focus()
lastSearchIndex = absoluteIndex + If(replace, replaceText.Length, match.Length)
Else
match = Regex.Match(mainText, pattern, regexOptions)
If match.Success Then
If replace Then
mainText = mainText.Remove(match.Index, match.Length).Insert(match.Index, replaceText)
MainTextBox.Text = mainText
End If
MainTextBox.Select(match.Index, If(replace, replaceText.Length, match.Length))
MainTextBox.Focus()
lastSearchIndex = match.Index + If(replace, replaceText.Length, match.Length)
Else
MessageBox.Show($"Cannot find ""{searchText}""", "Find and replace",
MessageBoxButton.OK, MessageBoxImage.Information)
lastSearchIndex = 0
End If
End If
End Sub
Private Sub FindNextBtn_Click(sender As Object, e As RoutedEventArgs)
FindOrReplace()
End Sub
Private Sub ReplaceBtn_Click(sender As Object, e As RoutedEventArgs)
FindOrReplace(True)
End Sub
Private Sub ReplaceAllBtn_Click(sender As Object, e As RoutedEventArgs)
Dim searchText As String = FindTextBox.Text
Dim replaceText As String = ReplaceTextBox.Text
Dim mainText As String = MainTextBox.Text
If String.IsNullOrEmpty(searchText) Then
MessageBox.Show("Please enter text to find.", "Find and replace",
MessageBoxButton.OK, MessageBoxImage.Information)
Return
End If
Dim searchOptionsResult = GetSearchOptions(searchText)
Dim regexOptions As RegexOptions = searchOptionsResult.Item1
Dim pattern As String = searchOptionsResult.Item2
Dim replacedText As String = Regex.Replace(mainText, pattern, replaceText, regexOptions)
Dim replacementCount As Integer = Regex.Matches(mainText, pattern, regexOptions).Count
MainTextBox.Text = replacedText
MessageBox.Show($"Replaced {replacementCount} occurrence(s).", "Find and replace",
MessageBoxButton.OK, MessageBoxImage.Information)
lastSearchIndex = 0
End Sub
#End Region
#Region "Spellchecker and other features"
Private Sub SpellcheckerBtn_Click(sender As Object, e As RoutedEventArgs)
MainTextBox.SpellCheck.IsEnabled = Not MainTextBox.SpellCheck.IsEnabled
End Sub
Private Sub DateTimeBtn_Click(sender As Object, e As RoutedEventArgs)
Dim currentDateTime As String = Now.ToString("dd/MM/yyyy HH:mm")
MainTextBox.SelectedText = currentDateTime
MainTextBox.Focus()
End Sub
Private Sub FontBtn_Click(sender As Object, e As RoutedEventArgs)
FontMenu.Visibility = If(FontMenu.Visibility = Visibility.Visible,
Visibility.Collapsed, Visibility.Visible)
End Sub
Private Sub UpdateFontBtn_Click(sender As Object, e As RoutedEventArgs)
Try
MainTextBox.FontFamily = New FontFamily(FontComboBox.Text)
Dim fontSize As Double = Convert.ToDouble(FontSizeComboBox.Text)
If fontSize <= 1 Then
MessageBox.Show("Please enter a font size greater than 1.", "Font size",
MessageBoxButton.OK, MessageBoxImage.Exclamation)
Return
End If
MainTextBox.FontSize = fontSize
Catch
MessageBox.Show("Please enter a valid font name and size.", "Font",
MessageBoxButton.OK, MessageBoxImage.Exclamation)
End Try
End Sub
Private Sub UpdateStatusBar()
Dim caretIndex As Integer = MainTextBox.CaretIndex
Dim chars As Integer = MainTextBox.Text.Length
Dim textBeforeCaret As String = MainTextBox.Text.Substring(0, caretIndex)
Dim lines As Integer = textBeforeCaret.Split(CChar(vbLf)).Length
Dim lastNewLineIndex As Integer = textBeforeCaret.LastIndexOf(vbLf)
Dim columns As Integer = If(lastNewLineIndex = -1,
caretIndex + 1,
caretIndex - lastNewLineIndex)
Dim trimmedText As String = MainTextBox.Text.Trim()
Dim words As Integer = If(String.IsNullOrEmpty(trimmedText),
0, Regex.Matches(trimmedText, "\S+").Count)
StatusText.Text = $"Ln {lines} • Col {columns} • {chars} characters • {words} words"
End Sub
#End Region
End Class
XAML
<!-- AZUL CODING --------------------------------------- -->
<!-- WPF C#/VB - Notepad App with Spellchecker -->
<!-- https://youtu.be/7lxpGNWRvWs -->
<Window x:Class="AzulNotepad.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"
mc:Ignorable="d"
Title="Notepad - Azul Coding" Height="400" SizeToContent="Width" ResizeMode="CanMinimize">
<DockPanel Background="White">
<StackPanel DockPanel.Dock="Top" Margin="20,20,20,0" Orientation="Horizontal">
<Button Name="OpenBtn" Click="OpenBtn_Click" ToolTip="Open a file" Padding="8,5" Margin="0,0,5,0" Background="#f0f0f0">
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
<Image Height="24" Width="24" Source="https://img.icons8.com/fluency/48/opened-folder.png"/>
</StackPanel>
</Button>
<Button Name="SaveBtn" Click="SaveBtn_Click" ToolTip="Save a file" Padding="8,5" Margin="0,0,5,0" Background="#f0f0f0">
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
<Image Height="24" Width="24" Source="https://img.icons8.com/fluency/48/save.png"/>
</StackPanel>
</Button>
<Rectangle Width="2" Margin="0,0,5,0" Fill="DodgerBlue"/>
<Button Name="UndoBtn" Click="UndoBtn_Click" IsEnabled="False" ToolTip="Undo" Padding="8,5" Margin="0,0,5,0" Background="#f0f0f0">
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
<Image Height="24" Width="24" Source="https://img.icons8.com/fluency/48/undo.png"/>
</StackPanel>
</Button>
<Button Name="RedoBtn" Click="RedoBtn_Click" IsEnabled="False" ToolTip="Redo" Padding="8,5" Margin="0,0,5,0" Background="#f0f0f0">
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
<Image Height="24" Width="24" Source="https://img.icons8.com/fluency/48/redo.png"/>
</StackPanel>
</Button>
<Rectangle Width="2" Margin="0,0,5,0" Fill="DodgerBlue"/>
<Button Name="CutBtn" Click="CutBtn_Click" ToolTip="Cut" Padding="8,5" Margin="0,0,5,0" Background="#f0f0f0">
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
<Image Height="24" Width="24" Source="https://img.icons8.com/fluency/48/cut.png"/>
</StackPanel>
</Button>
<Button Name="CopyBtn" Click="CopyBtn_Click" ToolTip="Copy" Padding="8,5" Margin="0,0,5,0" Background="#f0f0f0">
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
<Image Height="24" Width="24" Source="https://img.icons8.com/fluency/48/copy.png"/>
</StackPanel>
</Button>
<Button Name="PasteBtn" Click="PasteBtn_Click" ToolTip="Paste" Padding="8,5" Margin="0,0,5,0" Background="#f0f0f0">
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
<Image Height="24" Width="24" Source="https://img.icons8.com/fluency/48/paste.png"/>
</StackPanel>
</Button>
<Rectangle Width="2" Margin="0,0,5,0" Fill="DodgerBlue"/>
<ToggleButton Name="FindReplaceBtn" Click="FindReplaceBtn_Click" ToolTip="Toggle find and replace menu" Padding="8,5" Margin="0,0,5,0" Background="#f0f0f0">
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
<Image Height="24" Width="24" Source="https://img.icons8.com/fluency/48/find-and-replace.png"/>
</StackPanel>
</ToggleButton>
<ToggleButton Name="SpellcheckerBtn" Click="SpellcheckerBtn_Click" ToolTip="Toggle spellchecker" Padding="8,5" Margin="0,0,5,0" Background="#f0f0f0">
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
<Image Height="24" Width="24" Source="https://img.icons8.com/fluency/48/spell-check.png"/>
</StackPanel>
</ToggleButton>
<ToggleButton Name="FontBtn" Click="FontBtn_Click" ToolTip="Toggle font menu" Padding="8,5" Margin="0,0,5,0" Background="#f0f0f0">
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
<Image Height="24" Width="24" Source="https://img.icons8.com/fluency/48/choose-font.png"/>
</StackPanel>
</ToggleButton>
<Button Name="DateTimeBtn" Click="DateTimeBtn_Click" ToolTip="Insert date and time" Padding="8,5" Margin="0" Background="#f0f0f0">
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
<Image Height="24" Width="24" Source="https://img.icons8.com/fluency/48/overtime--v1.png"/>
</StackPanel>
</Button>
</StackPanel>
<StackPanel x:Name="FontMenu" Visibility="Collapsed" d:Visibility="Visible" DockPanel.Dock="Top" Margin="20,20,20,-10" Orientation="Horizontal">
<ComboBox x:Name="FontComboBox" ToolTip="Font name" Margin="0,0,5,0" Padding="5,0" Width="175" FontSize="16" IsEditable="True" VerticalContentAlignment="Center"></ComboBox>
<ComboBox x:Name="FontSizeComboBox" ToolTip="Font size" Margin="0,0,5,0" Padding="5,0" Width="75" FontSize="16" IsEditable="True" VerticalContentAlignment="Center"></ComboBox>
<Button Name="UpdateFontBtn" Click="UpdateFontBtn_Click" ToolTip="Update font" Padding="8,5" Margin="0" Background="#f0f0f0">
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
<Image Height="18" Width="18" Source="https://img.icons8.com/fluency/36/checkmark--v1.png"/>
</StackPanel>
</Button>
</StackPanel>
<DockPanel DockPanel.Dock="Bottom" Margin="20,0,20,20">
<Rectangle DockPanel.Dock="Top" Height="2" Fill="DodgerBlue"/>
<TextBlock x:Name="StatusText" Text="Ln 1 • Col 1 • 0 characters • 0 words" DockPanel.Dock="Bottom" Margin="0,5,0,0" FontSize="14"></TextBlock>
</DockPanel>
<StackPanel x:Name="FindReplaceMenu" Visibility="Collapsed" d:Visibility="Visible" DockPanel.Dock="Bottom" Margin="20,-10,20,20">
<StackPanel Orientation="Horizontal" Margin="0,0,0,5">
<TextBlock Text="Find:" Margin="0,0,10,0" FontSize="14" VerticalAlignment="Center" MinWidth="53"></TextBlock>
<TextBox x:Name="FindTextBox" Margin="0,0,5,0" Padding="5,0" Width="200" FontSize="16" VerticalContentAlignment="Center"></TextBox>
<Button x:Name="FindNextBtn" Click="FindNextBtn_Click" ToolTip="Find next" Padding="8,5" Margin="0,0,10,0" Background="#f0f0f0">
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
<Image Height="18" Width="18" Source="https://img.icons8.com/fluency/36/search.png"/>
</StackPanel>
</Button>
<CheckBox x:Name="MatchCaseCheckBox" Content="Match case" VerticalAlignment="Center" FontSize="14" Margin="5,0,15,0" VerticalContentAlignment="Center"/>
<CheckBox x:Name="MatchWordCheckBox" Content="Whole word" VerticalAlignment="Center" FontSize="14" VerticalContentAlignment="Center"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Replace:" Margin="0,0,10,0" FontSize="14" VerticalAlignment="Center" MinWidth="53"></TextBlock>
<TextBox x:Name="ReplaceTextBox" Margin="0,0,5,0" Padding="5,0" Width="200" FontSize="16" VerticalContentAlignment="Center"></TextBox>
<Button x:Name="ReplaceBtn" Click="ReplaceBtn_Click" Padding="10,5" Margin="0,0,5,0" Background="#f0f0f0" FontSize="14" Content="Replace next occurrence"/>
<Button x:Name="ReplaceAllBtn" Click="ReplaceAllBtn_Click" Padding="10,5" Margin="0" Background="#f0f0f0" FontSize="14" Content="Replace all"/>
</StackPanel>
</StackPanel>
<TextBox x:Name="MainTextBox" TextChanged="MainTextBox_TextChanged" SelectionChanged="MainTextBox_SelectionChanged" AcceptsReturn="True" AcceptsTab="True" Margin="20" FontFamily="Segoe UI" FontSize="16" Padding="5"></TextBox>
</DockPanel>
</Window>