A powerful and flexible hexadecimal editor control for Avalonia, supporting both desktop and console applications. HexView provides comprehensive binary file viewing, editing, comparison, and analysis capabilities with a modern, performant architecture.
| Category | Features |
|---|---|
| View Modes | Single file view, Side-by-side diff view with synchronized scrolling |
| Display Formats | Hexadecimal (base 16), Decimal (base 10), Octal (base 8), Binary (base 2) |
| Character Encodings | ASCII, UTF-8, UTF-16LE, UTF-16BE |
| Edit Modes | Overwrite mode, Insert mode with full undo/redo support |
| Large File Support | Memory-mapped files, Virtual scrolling, Efficient chunked operations |
| Search & Replace | Pattern search, Wildcard matching, Text search, Find & replace with batch operations |
| Navigation | Address jump, Back/forward history, Bookmarks, Next/previous difference |
| Selection Operations | Copy/paste (hex & ASCII), Fill, Zero-fill, Increment, Randomize |
| Visual Customization | Configurable colors, Font size adjustment, Customizable bytes per line |
| File Operations | Save, Save As, Export patch, Drag & drop support |
| Feature | Description |
|---|---|
| Piece Table Editing | Efficient in-memory edit tracking without modifying original file |
| Byte Overlay System | Virtual overlay for tracking edits with support for insert/delete operations |
| Edit Journal | Unlimited undo/redo with batch operation support |
| Difference Highlighting | Automatic visual highlighting of differences in diff mode |
| Extensible Architecture | Interface-based design for custom formatters and data sources |
| Keyboard Shortcuts | Comprehensive keyboard navigation and operation shortcuts |
| Event System | Caret moved, Selection changed, and edit notifications |
| External Integration | Callbacks for edit tracking and custom diff providers |
Install the HexView NuGet package:
dotnet add package HexViewOr via Package Manager:
Install-Package HexView<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:hexView="clr-namespace:HexView.Avalonia.Controls;assembly=HexView">
<hexView:HexViewControl x:Name="HexView" />
</Window>using HexView.Avalonia.Controls;
using HexView.Avalonia.Services;
// Load a file
var lineReader = new MemoryMappedLineReader("path/to/file.bin");
var formatter = new HexFormatter(lineReader);
HexView.LineReader = lineReader;
HexView.HexFormatter = formatter;public async Task OpenFile(string filePath)
{
// Create line reader for memory-mapped file access
var lineReader = new MemoryMappedLineReader(filePath);
// Create formatter with default settings (base 16, 16 bytes per line)
var formatter = new HexFormatter(lineReader)
{
BytesWidth = 16, // Bytes per line
Base = 16, // Hexadecimal
GroupSize = 8, // Group separator every 8 bytes
ShowGroupSeparator = true
};
// Create byte overlay for edit tracking
var overlay = new ByteOverlay(lineReader);
var overlayReader = new ByteOverlayLineReader(overlay);
// Assign to control
HexView.LineReader = overlayReader;
HexView.HexFormatter = formatter;
}// Switch to binary display
formatter.Base = 2;
// Switch to decimal display
formatter.Base = 10;
// Change bytes per line
formatter.BytesWidth = 8;
// Configure ASCII encoding
formatter.Encoding = Encoding.UTF8;// Create overlay for tracking edits
var baseReader = new MemoryMappedLineReader("file.bin");
var overlay = new ByteOverlay(baseReader);
var overlayReader = new ByteOverlayLineReader(overlay);
// Enable editing
HexView.Editable = true;
HexView.LineReader = overlayReader;
// Programmatically edit bytes
overlay.OverwriteByte(0x100, 0xFF);
overlay.InsertBytes(0x200, new byte[] { 0xDE, 0xAD, 0xBE, 0xEF });
overlay.DeleteRange(0x300, 4);
// Check if file has been modified
bool hasEdits = overlay.GetOverwriteEdits().Any() ||
overlay.GetInserts().Any() ||
overlay.GetDeletions().Any();// Create edit journal
var journal = new EditJournal();
// Configure byte overlay with journal
overlay.Journal = journal;
// Perform operations
overlay.OverwriteByte(0x100, 0xFF);
overlay.InsertBytes(0x200, new byte[] { 0xAA, 0xBB });
// Undo last operation
if (journal.CanUndo)
{
journal.Undo(overlay);
}
// Redo operation
if (journal.CanRedo)
{
journal.Redo(overlay);
}
// Batch operations (single undo unit)
journal.BeginBatch();
overlay.OverwriteByte(0x100, 0x11);
overlay.OverwriteByte(0x101, 0x22);
overlay.OverwriteByte(0x102, 0x33);
journal.EndBatch();var searchService = new HexSearchService();
// Search for hex pattern
var pattern = new byte[] { 0xDE, 0xAD, 0xBE, 0xEF };
var offset = searchService.FindNext(lineReader, pattern, startOffset);
if (offset != -1)
{
// Found at offset
HexView.MoveCaretTo(offset);
}
// Search with wildcards (? = any nibble)
var wildcardPattern = "DE AD ?? EF";
var maskPattern = searchService.ParseHexBytesWithWildcards(wildcardPattern);
offset = searchService.FindNext(lineReader, maskPattern.pattern,
startOffset, maskPattern.mask);
// Text search
var textBytes = Encoding.ASCII.GetBytes("Hello");
offset = searchService.FindNext(lineReader, textBytes, 0);
// Navigate to address
var success = searchService.TryNavigateToAddress(HexView, "0x1234");var searchService = new HexSearchService();
var overlay = (ByteOverlay)((ByteOverlayLineReader)HexView.LineReader).Overlay;
// Replace next occurrence
var findPattern = new byte[] { 0xAA, 0xBB };
var replacePattern = new byte[] { 0xCC, 0xDD };
var offset = searchService.ReplaceNext(
overlay,
findPattern,
replacePattern,
startOffset
);
// Replace all occurrences
var count = searchService.ReplaceAll(
overlay,
findPattern,
replacePattern
);
Console.WriteLine($"Replaced {count} occurrences");var selectionService = new SelectionService();
// Fill selection with specific byte
selectionService.FillSelection(overlay, 0xFF,
HexView.SelectionStart, HexView.SelectionLength);
// Zero-fill selection
selectionService.ZeroFillSelection(overlay,
HexView.SelectionStart, HexView.SelectionLength);
// Increment all bytes in selection
selectionService.IncrementSelection(overlay,
HexView.SelectionStart, HexView.SelectionLength);
// Randomize selection
selectionService.RandomizeSelection(overlay,
HexView.SelectionStart, HexView.SelectionLength, seed: 12345);
// Copy as hex
var hexString = selectionService.CopySelectionAsHex(
lineReader, HexView.SelectionStart, HexView.SelectionLength, spaced: true);
// Copy as ASCII
var asciiString = selectionService.CopySelectionAsAscii(
lineReader, HexView.SelectionStart, HexView.SelectionLength, Encoding.ASCII);
// Paste hex bytes
var hexInput = "DE AD BE EF";
selectionService.PasteHexBytes(overlay, hexInput,
HexView.CaretOffset, insertMode: false);
// Paste ASCII text
var textInput = "Hello World";
selectionService.PasteAsciiBytes(overlay, textInput,
HexView.CaretOffset, Encoding.UTF8, insertMode: false);var navigationService = new NavigationService();
// Add bookmark
navigationService.AddBookmark(0x1000, "Important location");
// List bookmarks
var bookmarks = navigationService.ListBookmarks();
// Remove bookmark
navigationService.RemoveBookmark(0x1000);
// Navigate with history
navigationService.Visit(HexView.CaretOffset);
HexView.MoveCaretTo(0x2000);
// Go back
if (navigationService.CanGoBack)
{
var previousOffset = navigationService.GoBack();
HexView.MoveCaretTo(previousOffset);
}
// Go forward
if (navigationService.CanGoForward)
{
var nextOffset = navigationService.GoForward();
HexView.MoveCaretTo(nextOffset);
}// Create two hex view controls
var leftReader = new MemoryMappedLineReader("file1.bin");
var rightReader = new MemoryMappedLineReader("file2.bin");
var leftFormatter = new HexFormatter(leftReader);
var rightFormatter = new HexFormatter(rightReader);
LeftHexView.LineReader = leftReader;
LeftHexView.HexFormatter = leftFormatter;
RightHexView.LineReader = rightReader;
RightHexView.HexFormatter = rightFormatter;
// Compute differences
var differences = ComputeDifferences(leftReader, rightReader);
// Provide difference highlighting
LeftHexView.DifferencesProvider = () => differences;
RightHexView.DifferencesProvider = () => differences;
// Synchronized scrolling
LeftHexView.PropertyChanged += (s, e) =>
{
if (e.Property.Name == nameof(HexViewControl.CaretOffset))
{
RightHexView.MoveCaretTo(LeftHexView.CaretOffset);
}
};var saveService = new SaveService();
var overlay = (ByteOverlay)((ByteOverlayLineReader)HexView.LineReader).Overlay;
// Save to new file
await saveService.SaveAs(overlay, "output.bin");
// Export patch file
var patchContent = saveService.ExportPatch(overlay);
await File.WriteAllTextAsync("changes.patch", patchContent);
// Patch format example:
// OW 00000100 FF # Overwrite at 0x100 with 0xFF
// IN 00000200 DEADBEEF # Insert DEADBEEF at 0x200
// DL 00000300 00000004 # Delete 4 bytes at 0x300// Customize colors
HexView.CaretBrush = new SolidColorBrush(Colors.Red);
HexView.EditedBrush = new SolidColorBrush(Colors.Orange);
HexView.SelectionBrush = new SolidColorBrush(Color.FromArgb(80, 0, 120, 215));
HexView.MatchBrush = new SolidColorBrush(Colors.Yellow);
HexView.DiffBrush = new SolidColorBrush(Color.FromArgb(80, 144, 238, 144));
// Font customization
HexView.FontFamily = new FontFamily("Consolas");
HexView.FontSize = 14;
// Zoom support
HexView.PointerWheelChanged += (s, e) =>
{
if (e.KeyModifiers.HasFlag(KeyModifiers.Control))
{
HexView.FontSize += e.Delta.Y > 0 ? 1 : -1;
e.Handled = true;
}
};// Caret position changed
HexView.CaretMoved += (offset) =>
{
Console.WriteLine($"Caret moved to: 0x{offset:X}");
// Update status bar
StatusText.Text = $"Offset: 0x{offset:X8} ({offset})";
// Read byte at caret
if (HexView.TryGetByteAt(offset, out var value))
{
StatusText.Text += $" | Value: 0x{value:X2} ({value})";
}
};
// Selection changed
HexView.SelectionChanged += () =>
{
var start = HexView.SelectionStart;
var length = HexView.SelectionLength;
Console.WriteLine($"Selection: 0x{start:X} - 0x{(start + length):X} ({length} bytes)");
};
// Byte written (for external edit tracking)
HexView.ByteWriteAction = (offset, oldValue, newValue) =>
{
Console.WriteLine($"Byte at 0x{offset:X}: 0x{oldValue:X2} -> 0x{newValue:X2}");
};// Implement ILineReader for custom data sources
public class CustomLineReader : ILineReader
{
private readonly byte[] _data;
public long Length => _data.Length;
public CustomLineReader(byte[] data)
{
_data = data;
}
public void GetLine(long offset, Span<byte> buffer)
{
var length = Math.Min(buffer.Length, _data.Length - offset);
_data.AsSpan((int)offset, (int)length).CopyTo(buffer);
}
public int Read(long offset)
{
return offset < _data.Length ? _data[offset] : -1;
}
}
// Use custom reader
var customReader = new CustomLineReader(myData);
var formatter = new HexFormatter(customReader);
HexView.LineReader = customReader;
HexView.HexFormatter = formatter;Ctrl+O- Open fileCtrl+S- Save fileCtrl+Shift+S- Save As
Ctrl+Z- UndoCtrl+Y/Ctrl+Shift+Z- RedoDelete- Delete selection (insert mode)Insert- Toggle insert/overwrite mode
Ctrl+C- Copy selection as hexCtrl+Shift+C- Copy selection as ASCIICtrl+V- Paste hex bytesCtrl+Shift+V- Paste ASCII text
Ctrl+F- Focus searchCtrl+H- Focus replaceCtrl+G- Go to addressF3- Find nextShift+F3- Find previousAlt+Left- Navigate backAlt+Right- Navigate forwardCtrl+B- Add bookmark
Ctrl+A- Select allCtrl++/Ctrl+=- Zoom inCtrl+-- Zoom outCtrl+0- Reset zoom
Arrow Keys- Move caretHome- Move to line startEnd- Move to line endPage Up/Page Down- Scroll pageCtrl+Home- Go to file startCtrl+End- Go to file end
HexView uses a layered architecture for flexibility and performance:
┌─────────────────────────────────────┐
│ HexViewControl (UI) │
│ - Rendering & User Interaction │
└─────────────────┬───────────────────┘
│
┌─────────────────┴───────────────────┐
│ Services Layer │
│ - HexFormatter │
│ - HexSearchService │
│ - SelectionService │
│ - NavigationService │
│ - EditJournal │
│ - SaveService │
└─────────────────┬───────────────────┘
│
┌─────────────────┴───────────────────┐
│ Data Layer │
│ - ByteOverlay (Edit Tracking) │
│ - ByteOverlayLineReader │
│ - MemoryMappedLineReader │
│ - ILineReader (Interface) │
└─────────────────────────────────────┘
- Interface-based design: Easy to extend with custom implementations
- Piece table editing: Efficient edit tracking without modifying original file
- Memory-mapped files: Large file support without loading into RAM
- Virtual scrolling: Only render visible lines for performance
- Event-driven updates: Responsive UI with minimal overhead
- Memory-mapped files enable editing multi-gigabyte files
- Chunked operations (64KB default) for optimal I/O
- Virtual scrolling renders only visible content
- Piece table keeps edits separate from original data
- Efficient pattern search with overlap detection
- Lazy computation of differences in diff mode
- .NET 9.0 or later
- Avalonia 11.2 or later
git clone https://github.com/wieslawsoltes/HexView.git
cd HexView
dotnet build# Desktop sample
dotnet run --project samples/HexView.Desktop
# Console sample (requires compatible terminal)
dotnet run --project samples/HexView.ConsoleContributions are welcome! Please feel free to submit a Pull Request.
This project is licensed under the MIT License - see the LICENSE.md file for details.
- Built with Avalonia UI
- Inspired by classic hex editors like HxD and 010 Editor
- Report issues: GitHub Issues
- Discussions: GitHub Discussions