locked
Graphic representation of BigInteger array. RRS feed

  • Question

  • Hi, I'm beginner programmer and I'm trying to graphically represent an array.
    I have class named Macierz witch I use to make some calculation from values in array. Now I want to show user graphical representation of part or all of my array and allow him to make changes. Important notes:

    • Values inside won't ever change from program, only user can change values.
    • I want to keep using static array for calculations.
    • I want Macierz to be usable in Console Application.
    • I want to show rows and columns numbers on the edge and last column "Suma" to automatically reflect changes made by user (this column don't exist in original array).  

    There is some code:

    • From Macierz.cs:
        public class Macierz
        {
            public Macierz(ulong A, ulong B)
            {
                this.a = A;
                this.b = B;
                this.M = new BigInteger[A, B];
            }
    
            private BigInteger[,] M;
            public BigInteger this[ulong A, ulong B]
            {
                get
                {
                    return M[A, B];
                }
                set
                {
                    M[A, B] = value;
                }
            }
    
           
            public DataTable ToDataTable(ulong AStart, ulong ACount, ulong BStart, ulong BCount)
            {
                if (AStart >= a)
                    AStart = a - 1;
                if (BStart >= b)
                    BStart = b - 1;
    
                ulong AStop = AStart + ACount;
                ulong BStop = BStart + BCount;
    
                if (AStop > a)
                    AStop = a;
                if (BStop > b)
                    BStop = b;
    
                DataTable table = new DataTable();
                DataRow tmp;
                BigInteger BItmp;
    
                for (ulong i = BStart; i < BStop; i++)
                    table.Columns.Add(i.ToString(), typeof(BigInteger));
                table.Columns.Add("Suma", typeof(BigInteger));
    
                for (ulong i = AStart; i < AStop; i++)
                {
                    tmp = table.NewRow();
                    for (ulong j = BStart; j < BStop; j++)
                        tmp[j.ToString()] = M[i, j];
    
                    BItmp = 0;
                    for (ulong j = 0; j < B; j++)
                        BItmp += M[i, j];
                    tmp["Suma"] = BItmp; 
    
                    table.Rows.Add(tmp);
                }
                return table;
            }
        }
    }
    • From MainWindow.xaml:
    <DataGrid x:Name="TabelaDataGrid" AutoGenerateColumns="True" CellEditEnding="TabelaDataGrid_CellEditEnding" />
    • From MainWindow.xaml.cs:
    private void PokazButton_Click(object sender, RoutedEventArgs e)
            {
                this.Cursor = Cursors.Wait;
    
                if (czyCala.IsChecked == true)
                    GridTabela = M.ToDataTable(0, M.A, 0, M.B);
                else
                    GridTabela = M.ToDataTable(VA, VACount, VB, VBCount);
    
                TabelaDataGrid.DataContext = this;
                TabelaDataGrid.ItemsSource = GridTabela.DefaultView;
    
                this.Cursor = null;
            }
    
     private void TabelaDataGrid_CellEditEnding(object sender, DataGridCellEditEndingEventArgs e)
            {
                //Here I tried  to insert some code so values changed by user in DataTable object will change values in original array but I don't know how to do this.
            }

    I'm not sure it is good way to this. I can change my approach if there are better solutions. Sorry for all grammatical/ language mistakes (English isn't my main) and thank you in advance. 


    • Edited by Mikiwaw Wednesday, January 15, 2014 1:55 PM
    Wednesday, January 15, 2014 1:50 PM

Answers

  • The big question here is how many column do you expect that matrix to have. For more than couple of tens of column the ItemsControl is probably out of the question since it doesn't do column virtualization. DataGrid at least can do column virtualization, even if that means that horizontal scrolling performance tends to suck.

    Anyway, best thing would be to get the DataGrid editing to work and see for yourself if it's fast enough or not.

    Forget about using that DataTable, it complicates things and provides little in return. What you need is to create a class to wrap the matrix rows in a way that allows the DataGrid control to access them.

    class MatrixRowView {
        private Matrix matrix;
        private int rowIndex;
    
        public MatrixRowView(Matrix matrix, int rowIndex) {
            this.matrix = matrix;
            this.rowIndex = rowIndex;
        }
    
        public int RowIndex { get { return rowIndex; } }
    
        public string this[int columnIndex] {
            get { return matrix[rowIndex, columnIndex].ToString(); }
            set { matrix[rowIndex, columnIndex] = BigInteger.Parse(value); }
        }
    }
    

    Next you need to generate the grid columns manually so you can set proper bindings:

    dataGrid.AutoGenerateColumns = false;
    
    for (int i = 0; i < m.B; i++) {
        dataGrid.Columns.Add(new DataGridTextColumn {
            Binding = new Binding(String.Format("[{0}]", i)),
            Header = i,
        });
    }
    
    

    And create an array of such rows that is used as items source:

    var rows = new MatrixRowView[m.A];
    
    for (int i = 0; i < m.A; i++)
        rows[i] = new MatrixRowView(m, i);
    
    dataGrid.ItemsSource = rows;
    

    And set a row header style so the row number is displayed in the data grid:

    <DataGrid.RowHeaderStyle>
        <Style TargetType="{x:Type DataGridRowHeader}">
            <Setter Property="Content"
                    Value="{Binding RowIndex}" />
        </Style>
    </DataGrid.RowHeaderStyle>
    

    This should get things working, at least in my tests it does.

    If the matrix has many columns make sure to enable column virtualization in the data grid control.

    • Marked as answer by Mikiwaw Monday, January 20, 2014 9:44 PM
    Friday, January 17, 2014 1:15 PM

All replies

  • Hi,

    Using DataGrid is a feasible solution, but in my test, the performance is not so good.

    We can also use ItemsControl to implement this:
    http://stackoverflow.com/questions/16659265/how-to-improve-performance-of-wpf-grid-control-net-4-0-4-5

    <ItemsControl ItemsSource="{Binding Items}"
                          ScrollViewer.HorizontalScrollBarVisibility="Auto"
                          ScrollViewer.VerticalScrollBarVisibility="Auto"
                          ScrollViewer.CanContentScroll="true"
                          ScrollViewer.PanningMode="Both">
                <ItemsControl.Template>
                    <ControlTemplate>
                        <ScrollViewer>
                            <ItemsPresenter/>
                        </ScrollViewer>
                    </ControlTemplate>
                </ItemsControl.Template>
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <ItemsControl ItemsSource="{Binding Items}">
                            <ItemsControl.ItemTemplate>
                                <DataTemplate>
                                    <Label Content="{Binding}"/>
                                </DataTemplate>
                            </ItemsControl.ItemTemplate>
                            <ItemsControl.ItemsPanel>
                                <ItemsPanelTemplate>
                                    <UniformGrid Rows="1"/>
                                </ItemsPanelTemplate>
                            </ItemsControl.ItemsPanel>
                        </ItemsControl>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
                <ItemsControl.ItemsPanel>
                    <ItemsPanelTemplate>
                        <VirtualizingStackPanel VirtualizationMode="Recycling" IsVirtualizing="True"/>
                    </ItemsPanelTemplate>
                </ItemsControl.ItemsPanel>
            </ItemsControl>

    The performance is awesome(1600*20), the Filling of the grid is immediate when I click the button:


    We are trying to better understand customer views on social support experience, so your participation in this interview project would be greatly appreciated if you have time. Thanks for helping make community forums a great place.
    Click HERE to participate the survey.

    Thursday, January 16, 2014 10:15 AM
  • Hi, thanks for answer,

    I made it using ItemsControl as you suggested (by adding ToStringArray() to Macierz.cs) but this don't allow user to change values. I have two ideas how to fix it:

    1. This one is worse than second but probably easier and with better performance:
      Use Labels MouseDoubleClick event to open window with TextBox to allow user input new value, but I don't know how to check inside event witch Label was clicked (how get coordinations), so I can change proper value in original array.
    2. The better one:
      Use TextBoxes instead of labels but this require two way binding and  I don't know how to implement that, when I simply change Label to TextBox I get an exception. Maybe better use TextBoxes with one way binding and use LostFocus event but still I need to know witch one raised the event.

    One more question (less important for me) how can I display rows and columns numbers on left and top edge? They have to be visible all time and scroll with array.

    Thanks.

    Thursday, January 16, 2014 7:14 PM
  • >>I made it using ItemsControl as you suggested (by adding ToStringArray() to Macierz.cs) but this don't allow user to change values.

    I think the second idea is the best way, using two-way binding is a conventional solution, you need to implement property change notification for your class, the following references can help you:

    #How to: Implement Property Change Notification
    http://msdn.microsoft.com/en-us/library/ms743695(v=vs.110).aspx

    #Implementing INotifyPropertyChanged
    http://www.codeproject.com/Articles/41817/Implementing-INotifyPropertyChanged

    >>how can I display rows and columns numbers on left and top edge

    It's hard to implement this requirement, maybe DataGrid is a more convenient way for this scenario.


    We are trying to better understand customer views on social support experience, so your participation in this interview project would be greatly appreciated if you have time. Thanks for helping make community forums a great place.
    Click HERE to participate the survey.

    Friday, January 17, 2014 11:59 AM
  • The big question here is how many column do you expect that matrix to have. For more than couple of tens of column the ItemsControl is probably out of the question since it doesn't do column virtualization. DataGrid at least can do column virtualization, even if that means that horizontal scrolling performance tends to suck.

    Anyway, best thing would be to get the DataGrid editing to work and see for yourself if it's fast enough or not.

    Forget about using that DataTable, it complicates things and provides little in return. What you need is to create a class to wrap the matrix rows in a way that allows the DataGrid control to access them.

    class MatrixRowView {
        private Matrix matrix;
        private int rowIndex;
    
        public MatrixRowView(Matrix matrix, int rowIndex) {
            this.matrix = matrix;
            this.rowIndex = rowIndex;
        }
    
        public int RowIndex { get { return rowIndex; } }
    
        public string this[int columnIndex] {
            get { return matrix[rowIndex, columnIndex].ToString(); }
            set { matrix[rowIndex, columnIndex] = BigInteger.Parse(value); }
        }
    }
    

    Next you need to generate the grid columns manually so you can set proper bindings:

    dataGrid.AutoGenerateColumns = false;
    
    for (int i = 0; i < m.B; i++) {
        dataGrid.Columns.Add(new DataGridTextColumn {
            Binding = new Binding(String.Format("[{0}]", i)),
            Header = i,
        });
    }
    
    

    And create an array of such rows that is used as items source:

    var rows = new MatrixRowView[m.A];
    
    for (int i = 0; i < m.A; i++)
        rows[i] = new MatrixRowView(m, i);
    
    dataGrid.ItemsSource = rows;
    

    And set a row header style so the row number is displayed in the data grid:

    <DataGrid.RowHeaderStyle>
        <Style TargetType="{x:Type DataGridRowHeader}">
            <Setter Property="Content"
                    Value="{Binding RowIndex}" />
        </Style>
    </DataGrid.RowHeaderStyle>
    

    This should get things working, at least in my tests it does.

    If the matrix has many columns make sure to enable column virtualization in the data grid control.

    • Marked as answer by Mikiwaw Monday, January 20, 2014 9:44 PM
    Friday, January 17, 2014 1:15 PM
  • Thank you, this is very helpful answer for me.

    I made it working in the way you showed me and now I am trying to add one more column witch will contain sum of all elements in row. I wrote this:
    Adding extra column:

    dataGrid.Columns.Add(new DataGridTextColumn
                    {
                        Binding = new Binding(String.Format("[{0}]", m.B)),
                        Header = "sum",
                        IsReadOnly = true,                   
                    });

    Inside MatrixRowView.cs:

    public string this[ulong columnIndex]
            {
                get
                {
                    if (columnIndex < matrix.B)
                        return matrix[rowIndex, columnIndex].ToString("R");
                    else
                    {
                        BigInteger tmp = 0;
                        for (ulong i = 0; i < matrix.B; i++)
                            tmp += matrix[RowIndex, i];
                        return tmp.ToString("R");
                    }
                }
                set { matrix[rowIndex, columnIndex] = BigInteger.Parse(value); }
            }

    Now I have questions, how can I make this last column to auto update its values after user change something in array? Maybe there is a way to update only specific cell?

    • Edited by Mikiwaw Wednesday, January 22, 2014 11:58 AM
    Monday, January 20, 2014 10:06 PM