Search code examples
c#wpfhexhexdump

How to open a file in HEX regardless of its extension and size?


I'm new in C# and don't know which way I should use to work with big files. I tried ReadAllBytes() method and BitConverter and then show it in TextBlock but it takes a very long time and my program freezes. I used Be.Hex editor and this one opens any file instantly!

I also tried FileStream and Read method and it takes a long time too. I really have no idea how to make it right.

using (FileStream fstream = File.OpenRead(path)) 
{
    byte[] buffer = new byte[_fileStream.Length];
    fstream.Read(buffer, 0, buffer.Length);
    string hexValues = BitConverter.ToString(buffer);
}

Solution

  • You shouldn't read the entire file into memory but rather leverage Stream to lazily load and display the file in chunks on demand, and WPF's virtualization capabilities to ensure the UI is not overwhelmed. Here's an extremely simple example:

    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            int rowLength = 32;
            var str = File.OpenRead("c:\\MyFile.bin");
            int rows = (int)Math.Ceiling(str.Length / (double)rowLength);
    
            var rowVMs = Enumerable.Range(0, rows).Select(row =>
            {
                return new HexRow(str, row * rowLength, rowLength);
            });
            this.HexDump = new ObservableCollection<HexRow>(rowVMs);
    
            InitializeComponent();
        }
    
        public ObservableCollection<HexRow> HexDump
        {
            get; set;
        }
    }
    
    public class HexRow : INotifyPropertyChanged
    {
        Stream _source;
        int _start;
        int _count;
        bool _isLoading;
        string _HexString;
    
        public HexRow(Stream source, int start, int count)
        {
            _source = source;
            _start = start;
            _count = count;
        }
    
        public event PropertyChangedEventHandler? PropertyChanged;
    
        public string HexString
        {
            get
            {
                if (_HexString == null && !_isLoading)
                    LoadHexStringAsync();
                return _HexString;
            }
            set
            {
                this._HexString = value;
                this.PropertyChanged?.Invoke(
                    this, 
                    new PropertyChangedEventArgs(nameof(HexString)));
            }
        }
    
        private async void LoadHexStringAsync()
        {
            _isLoading = true;
            byte[] data = new byte[_count];
            _source.Seek(_start, SeekOrigin.Begin);
            await _source.ReadAsync(data, 0, _count);
            this.HexString = BitConverter.ToString(data);
            _isLoading = false;
        }
    }
    

    And the XAML:

    <Window x:Class="WpfApp1.MainWindow"
            x:Name="_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="MainWindow" Height="450" Width="800">
        <ListBox ItemsSource="{Binding ElementName=_mainWindow, Path=HexDump}">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <TextBlock FontFamily="Courier New"
                               Text="{Binding HexString, IsAsync=True}" />
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
    </Window>
    

    In a real-world scenario you would need to build your own custom viewer that employed VirtualizingStackPanel rather than using a ListBox. For more efficiency you'd probably also load in bigger chunks than just one row at a time. But this should give you a good starting point.