tags:

views:

59

answers:

3

Hi,

I have a byte array that can be very big in size. I want to bind it to a grid with a fixed size of column, lets say 10.

so the first 10 bytes will be on the first row, the 10 next bytes will be on the second row... until the end of the array.

I need to be able to edit any bytes and this need to be reflected into the array. My byte array needs to stay a simple byte array.

All that using WPF C#.

Thank you for your help!

EDIT :

Actually, AS-CII's solution doesn't save updated values into the original array. I modified the example to meet this criteria :

<DataGrid AutoGenerateColumns="False" Name="dataGrid1" ItemsSource="{Binding Bytes}" ColumnWidth="1*">
    <DataGrid.Columns>
        <DataGridTextColumn Header="1" Binding="{Binding [0]}"></DataGridTextColumn>
        <DataGridTextColumn Header="2" Binding="{Binding [1]}"></DataGridTextColumn>
        <DataGridTextColumn Header="3" Binding="{Binding [2]}"></DataGridTextColumn>
        <DataGridTextColumn Header="4" Binding="{Binding [3]}"></DataGridTextColumn>
        <DataGridTextColumn Header="5" Binding="{Binding [4]}"></DataGridTextColumn>
        <DataGridTextColumn Header="6" Binding="{Binding [5]}"></DataGridTextColumn>
        <DataGridTextColumn Header="7" Binding="{Binding [6]}"></DataGridTextColumn>
        <DataGridTextColumn Header="8" Binding="{Binding [7]}"></DataGridTextColumn>
        <DataGridTextColumn Header="9" Binding="{Binding [8]}"></DataGridTextColumn>
        <DataGridTextColumn Header="10" Binding="{Binding [9]}"></DataGridTextColumn>
    </DataGrid.Columns>
</DataGrid>

Notice the only change was Array[0] to [0]

public struct ArrayPiece<T>
    {       
        private T[] m_Data;
        private int m_Offset;
        private int m_Length;

        public T this[int index] { 
            get{                
                return m_Length > index? m_Data[m_Offset + index] : default(T);
            }

            set{
                if(m_Length > index)
                    m_Data[m_Offset + index] = value;
            }
        }

        public ArrayPiece(T[] array, int offset, int length)
            : this()
        {
            m_Data = array;
            m_Offset = offset;
            m_Length = length;         
        }
    }

And this is the new ArrayPiece.

With those change, when, within the UI, a value is changed, it is updated to the original array.

There is one problem with this : If the last ArrayPiece only have 8 elements instead of 10, the 2 left elements will show 0 in the DataGrid unlike when using an array directly. I tried implementing Length and LongLength property without success. If I throw index out of bound, it is not caught.

Thanks!

A: 

I guess you can use an array of ArraySegment's to map your rows (ArraySegment) to the underlying array. You need to do the slicing into segments your self, but that should be a simple loop.

Edit:
I'm don't know WPF good enough to know if you can bind to indexes of ArraySegment or not.

Albin Sunnanbo
A: 

Is there any chance a textbox with a fixed-width font (like Consolas or Courier New) would work? You could write a value converter from byte[] to string, or if you're using MVVM do the translation there.

Domenic
A: 

This is WPF part:

<DataGrid AutoGenerateColumns="False" Name="dataGrid1" ItemsSource="{Binding Bytes}" ColumnWidth="1*">
        <DataGrid.Columns>
            <DataGridTextColumn Header="1" Binding="{Binding Piece[0]}"></DataGridTextColumn>
            <DataGridTextColumn Header="2" Binding="{Binding Piece[1]}"></DataGridTextColumn>
            <DataGridTextColumn Header="3" Binding="{Binding Piece[2]}"></DataGridTextColumn>
            <DataGridTextColumn Header="4" Binding="{Binding Piece[3]}"></DataGridTextColumn>
            <DataGridTextColumn Header="5" Binding="{Binding Piece[4]}"></DataGridTextColumn>
            <DataGridTextColumn Header="6" Binding="{Binding Piece[5]}"></DataGridTextColumn>
            <DataGridTextColumn Header="7" Binding="{Binding Piece[6]}"></DataGridTextColumn>
            <DataGridTextColumn Header="8" Binding="{Binding Piece[7]}"></DataGridTextColumn>
            <DataGridTextColumn Header="9" Binding="{Binding Piece[8]}"></DataGridTextColumn>
            <DataGridTextColumn Header="10" Binding="{Binding Piece[9]}"></DataGridTextColumn>
        </DataGrid.Columns>
    </DataGrid>

And this is the code-behind:

public partial class MainWindow : Window, INotifyPropertyChanged
{
    public MainWindow()
    {
        InitializeComponent();

        // Set the datacontext
        this.DataContext = this;

        // Sample array generation
        byte[] array = new byte[138];

        for (int i = 0; i < array.Length; i++)
        {
            array[i] = (byte)i;
        }

        int length = (int)Math.Ceiling(array.Length / 10m);

        Bytes = (from i in Enumerable.Range(0, length)
                let offset = i * 10
                let count = offset + 10 > array.Length ? array.Length - offset : 10
                select new ArrayPiece<byte>(array, offset, count)).ToArray();
    }

    ArrayPiece<byte>[] _bytes;

    public ArrayPiece<byte>[] Bytes
    {
        get { return _bytes; }
        set { _bytes = value; OnPropertyChanged("Bytes"); }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}

public struct ArrayPiece<T>
{
    public T[] Piece { get; set; }

    public ArrayPiece(T[] array, int offset, int length) : this()
    {
        Piece = new T[length];
        Buffer.BlockCopy(array, offset, Piece, 0, length);
    }
}

We use LINQ to split the array in multiple rows and then, with WPF binding, we show the data into the DataGrid.

EDIT: For the big dimensions of the array I don't think they are a problem 'cause DataGrid in WPF uses UI Virtualization, and so it just allocates the memory he need to display visible data.

AS-CII