I have one requirement to bind additional column either in “listview” or “datagrid” inside datagrid using WPF C#.
Basic datagrid I have as follows
<table border="1" width="100%">
<tr>
<td>Item Id</td>
<td>Item Name</td>
<td>Quantity</td>
<td>Price</td>
</tr>
<tr>
<td>500</td>
<td>Sanwich</td>
<td>2</td>
<td>10</td>
</tr>
</table>
For the above datagrid
my XAML
code as follows.
<DataGrid Grid.Column="1" Margin="8,60,8,16"AutoGenerateColumns="False" Height="350"
HorizontalAlignment="Left" Name="gridOrderDetails" VerticalAlignment="Top" Width="450" >
<DataGrid.Columns>
<DataGridTextColumn Header="Item ID" Binding="{Binding itemId}" />
<DataGridTextColumn Header="Quantity" Binding="{Binding quantity}" />
<DataGridTextColumn Header="Item Name" Binding="{Binding comments}" />
<DataGridTextColumn Header="Item Price" Binding="{Binding ItemPrice}" />
</DataGrid.Columns>
</DataGrid>
My code behind code as follows in button click event:
private void Button_Click(object sender, RoutedEventArgs e)
{
try
{
DBDataContext dbContext = new DBDataContext();
SP_Get_OrderItemResult orderRow = gridOrder.SelectedItem as SP_Get_OrderItemResult;
gridOrderDetails.ItemsSource = dbContext.SP_Get_ItemList(orderRow.orderid);
}
catch (Exception Ex)
{
MessageBox.Show(Ex.Message);
return;
}
}
My desired output is as follows
<table border="1" width="100%">
<tr>
<td>Item Id</td>
<td>Item Name</td>
<td>Additionals</td>
<td>Quantity</td>
<td>Price</td>
</tr>
<tr>
<td>500</td>
<td>Sanwich</td>
<td>Egg<p class="MsoNormal" style="margin-left:0in">Ketchup</p>
<p>
<span style="font-size: 11.0pt; line-height: 115%; font-family: Calibri,sans-serif">
Salad</span></td>
<td>2</td>
<td>10</td>
</tr>
</table>
To achieve this datagrid
I wrote my XAML
code as follows by using DataGridTemplateColumn
.
<DataGrid Grid.Column="1" Margin="8,60,8,16"AutoGenerateColumns="False" Height="350"
HorizontalAlignment="Left" Name="gridOrderDetails" VerticalAlignment="Top" Width="450" LoadingRow="gridOrderDetails_LoadingRow">
<DataGrid.Columns>
<DataGridTextColumn Header="Item ID" Binding="{Binding itemId}" />
<DataGridTextColumn Header="Item Name" Binding="{Binding ItemName}" />
<DataGridTemplateColumn Header="Addtionals">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<DataGrid AutoGenerateColumns="False" Name="gridAdditionals" >
<DataGridTextColumn Header="Additional" Binding="Additionals"></DataGridTextColumn>
</DataGrid>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Header="Quantity" Binding="{Binding quantity}" />
<DataGridTextColumn Header="Comments" Binding="{Binding comments}" />
<DataGridTextColumn Header="Item Price" Binding="{Binding ItemPrice}" />
</DataGrid.Columns>
</DataGrid>
In code behind I tried with LoadingRow
event but unable to pass corresponding ItemID
from cell value.
private void gridOrderDetails_LoadingRow(object sender, DataGridRowEventArgs e)
{
DCAPPDBDataContext dbContext = new DCAPPDBDataContext();
DataGrid grid = FindChild<DataGrid>(gridOrderDetails, "gridAdditionals");
grid.ItemsSource = dbContext.SP_Get_AdditionalsList(500);
}
I guess the logic I am using is wrong. This can be achieved using ItemDataBound
in Asp.net C#.
Can anyone help me out how to do this in WPF C#.
When using WPF
it's appropriate to follow the MVVM
pattern. Either way, you should define a proper class (i.e. view-model) for your Order
(containing properties such as Id, Name, Quantity, etc ...). This class should also has a property for your Aditionals
(e.g. a collection of strings
). Then by using proper DataBindings
you'll achieve your desired functionality.
This is a working example to give you an idea:
Order class:
public class Order : INotifyPropertyChanged
{
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
public Order(int id)
{
DBDataContext dbContext = new DBDataContext();
DCAPPDBDataContext dbContext2 = new DCAPPDBDataContext();
var itemsSource = dbContext.SP_Get_ItemList(id);
var additionals = dbContext2.SP_Get_AdditionalsList(id);
Id = id;
Name = itemsSource.name;
Quantity = itemsSource.quantity;
// other properties ...
Additionals = new ObservableCollection<string>();
foreach (var item in additionals)
{
Additionals.Add(item);
}
}
private int _id;
public int Id
{
get
{
return _id;
}
set
{
_id = value;
OnPropertyChanged("Id");
}
}
// define other properties e.g. Name, Quantity, Price, etc.
// the same way as Id.
public ObservableCollection<string> Additionals {get;set;}
}
Xaml:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<DataGrid Grid.Row="0" AutoGenerateColumns="False"
ItemsSource="{Binding Orders}"
SelectedItem="{Binding SelectedOrder}">
<DataGrid.Columns>
<DataGridTextColumn Header="Item ID" Binding="{Binding Id}" />
<DataGridTextColumn Header="Item Name" Binding="{Binding Name}" />
<DataGridTemplateColumn Header="Addtionals">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ItemsControl ItemsSource="{Binding Additionals}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Header="Quantity" Binding="{Binding Quantity}" />
<DataGridTextColumn Header="Item Price" Binding="{Binding Price}" />
</DataGrid.Columns>
</DataGrid>
<Button Grid.Row="1" Content="New Order" Click="Order_Click"/>
</Grid>
MainWindow.cs:
public ObservableCollection<Order> Orders { get; set; }
public Order SelectedOrder { get; set; }
public MainWindow()
{
InitializeComponent();
DataContext = this;
Orders = new ObservableCollection<Order>();
}
private void Order_Click(object sender, RoutedEventArgs e)
{
SP_Get_OrderItemResult orderRow = gridOrder.SelectedItem as SP_Get_OrderItemResult;
Orders.Add(new Order(orderRow.orderid));
}