Reputation: 145
I would like to display data (Properties) from base class followed by data from a derived class in the columns of a DataGrid. Data displays OK, but data from Derived class is displayed first followed by the data from the derived class. How can I change the order of data so that Data first comes from base class and then from derived class.
abstract class BaseNumbers
{
public int Count { get; set; }
public double B1 { get; set; }
public double B2 { get; set; }
public double B3 { get; set; }
protected BaseNumbers (int c, double b1, double b2, double b3)
{
Count = c;
B1 = b1;
B2 = b2;
B3 = b3;
} // end ctor
public override string ToString() => $"{B1:F},{B2:F},{B3:F}";
class DerivedNumbers : BaseNumbers
{
public double D1 { get; set; }
public double D2 { get; set; }
public double D3 { get; set; }
public DerivedNumbers(int cnt, double b1, double b2, double b3,
double d1, double d2, double d3)
: base(cnt, b1, b2, b3)
{
D1 = d1;
D2 = d2;
D3 = d3;
} // end ctor
public override string ToString() => base.ToString() + $",{D1:F},{D2:F},{D3:F}";
}
<Window x:Class="OOPGridIssue.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"
xmlns:local="clr-namespace:OOPGridIssue"
mc:Ignorable="d"
Title="MainWindow" Height="200" Width="275">
<Window.Resources>
<!-- Data Grid Style -->
<Style x:Key="DataGridStyle" TargetType="DataGrid">
<Setter Property="HorizontalAlignment" Value="Left" />
<Setter Property="VerticalAlignment" Value="Top" />
<Setter Property="AlternatingRowBackground" Value="LightGray" />
<Setter Property="MinWidth" Value="200" />
<Setter Property="MaxHeight" Value="440" />
<Setter Property="Margin" Value="10,5,0,0" />
<Setter Property="ScrollViewer.CanContentScroll" Value="True" />
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto" />
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto" />
</Style>
</Window.Resources>
<Grid>
<!-- Root level grid 6 rows x 4 col -->
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<DataGrid x:Name="dg_Numbers" Grid.Row="0" Grid.Column="0"
Style="{StaticResource DataGridStyle}" />
<DockPanel x:Name="dockPanelButtons" Grid.Row="1" VerticalAlignment="Top" Margin="0,10,10,10" Height="50">
<Button x:Name="btnLoadNumbers" Content ="Load Numbers" FontWeight="Bold"
Margin="10" MaxHeight="25" Click="ButtonLoadNumbers_Click" />
<Button x:Name="ButtonClearNumbers" Content ="Clear Numbers"
Margin="0,10,10,10" MaxHeight="25" Click="ButtonClearNumbers_Click" />
</DockPanel>
</Grid>
</Window>
using System.Collections.Generic;
using System.Windows;
namespace OOPGridIssue
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
readonly List<DerivedNumbers> numbers = new List<DerivedNumbers>()
{
new DerivedNumbers(1,1.0, 2.22, 3, 4.0, 5.555, 6),
new DerivedNumbers(2,1.0, 2.22, 3, 4.0, 5.555, 6),
new DerivedNumbers(3,1.0, 2.22, 3, 4.0, 5.555, 6)
};
public MainWindow()
{
InitializeComponent();
}
private void ButtonClearNumbers_Click(object sender, RoutedEventArgs e)
{
dg_Numbers.ItemsSource = null;
dg_Numbers.Items.Refresh();
}
private void ButtonLoadNumbers_Click(object sender, RoutedEventArgs e)
{
dg_Numbers.ItemsSource = numbers;
dg_Numbers.Items.Refresh();
}
}
}
Upvotes: 1
Views: 428
Reputation: 660
I can propose you two solutions:
Solution #1 Make use of the DataGrid's AutoGenerated event
Firstly, we create an interface
public interface IDerivedNumbers
{
int Count { get; set; }
double B1 { get; set; }
double B2 { get; set; }
double B3 { get; set; }
double D1 { get; set; }
double D2 { get; set; }
double D3 { get; set; }
string ToString();
}
Which we will apply it to the DerivedNumbers class
class DerivedNumbers : BaseNumbers, IDerivedNumbers
{
...
}
Now, lets add our event to the DataGrid
<DataGrid x:Name="dg_Numbers"
Grid.Row="0"
Grid.Column="0"
Style="{StaticResource DataGridStyle}"
AutoGeneratedColumns="dg_Numbers_AutoGeneratedColumns" />
..and code behind
private void dg_Numbers_AutoGeneratedColumns(object sender, EventArgs e)
{
var names = typeof(IDerivedNumbers).GetProperties()
.Select ( (x, index) => new { name = x.Name, index } );
if ( sender is DataGrid datagrid )
{
foreach ( var column in datagrid.Columns )
{
column.DisplayIndex = names.Single ( x => x.name == (string)column.Header ).index;
}
}
}
Solution #2 Hide your numbers classes behind an interface. The trick is your concrete classes must not be visible by the Datagrid, which means you'll need to move them in a separate assembly.
Firstly, lets move BaseNumbers & DerivedNumbers in another assembly and keep them internal.
Then Create a new public interface in that assembly
public interface IDerivedNumbers
{
int Count { get; set; }
double B1 { get; set; }
double B2 { get; set; }
double B3 { get; set; }
double D1 { get; set; }
double D2 { get; set; }
double D3 { get; set; }
string ToString();
}
Apply it to the DerivedNumbers class
class DerivedNumbers : BaseNumbers, IDerivedNumbers
{
...
}
Next, we need to create a public factory
public static class NumbersFactory
{
public static IDerivedNumbers Create ( int cnt,
double b1,
double b2,
double b3,
double d1,
double d2,
double d3 )
{
new DerivedNumbers(cnt, b1, b2, b3, d1, d2, d3);
}
}
Finally link your new assembly and use the factory to create the numbers
public partial class MainWindow : Window
{
readonly List<IDerivedNumbers> numbers = new List<IDerivedNumbers>()
{
NumbersFactory.Create(1,1.0, 2.22, 3, 4.0, 5.555, 6),
NumbersFactory.Create(2,1.0, 2.22, 3, 4.0, 5.555, 6),
NumbersFactory.Create(3,1.0, 2.22, 3, 4.0, 5.555, 6)
};
....
}
Now the Datagrid will only pick up the interface and auto generate the columns in the right order.
Both methods will generate this result:
PS. Thank you for providing a working example by the way !
Upvotes: 1