简体   繁体   中英

Binding additional column with any control in datagrid WPF

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 (ie view-model) for your Order (containing properties such as Id, Name, Quantity, etc ...). This class should also has a property for your Aditionals (eg 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));
    }

With the help of Bahman_Aries post and little bit stuff I am posting exact solution. This is exact solution which I was desired.

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(SP_Get_ItemListResult itemResult)
    {
        DBDataContext dbContext = new DBDataContext();                


         orderItemID = itemResult.orderItemID;
        itemName = itemResult.ItemName;
        quantity = itemResult.quantity;           
        // other properties ...

      var additionals = dbContext.SP_Get_AdditionalsList(orderItemID);

        Additionals = new ObservableCollection<string>();
        foreach (var item in additionals)
        {
            Additionals.Add(item.Additionals);
        }
    }

    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;}
}

No Changes in XAML code.

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;
      List<SP_Get_ItemListResult> itemsSource = dbContext.SP_Get_ItemList(orderRow.orderid).ToList();
            for (int i = 0; i < itemsSource.Count(); i++)
            {
                Orders.Add(new Order(itemsSource[i]));
            }         
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM