Search code examples
sqlwpfwindowsdata-bindingdatagrid

WPF DataGrid Question - Referencing TextBox inside RowDetailsTemplate


After hours of researching how to do this, I am stumped and need help from the experts of stack overflow. I am not a WPF developer and only started teaching myself Monday, so please be patient - but I need to create a user interface to allow an end user to update a few fields in an MS SQL table. My goal with this application is that it displays the results of a stored procedure into a data grid, allowing the end user to select a row giving them full detail on that record using RowDetailsTemplate. Within that inset I would like to give them the ability to execute a couple actions 1) Update a single column in the table, probably by calling a stored procedure and 2) have a button that executes another action probably by also calling a stored procedure, passing in the row's unique ID.

The problem I'm having is I'm not sure how to 1) reference the TextBox from the selected line. The name of the textbox is not found. and 2) I'm not sure how to reference the selected row's bound unique identifier ("invoice number") in the code behind.

Here's basically what my app looks like. You can see I have two small forms within the row detail, Update Status and Email Client. enter image description here

Here is the XAML code that produces it:

<Page x:Class="WpfApp1.Page3"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
  xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
  xmlns:controls="http://schemas.microsoft.com/netfx/2009/xaml/presentation"
  xmlns:local="clr-namespace:WpfApp1" 
  mc:Ignorable="d" 
  d:DesignHeight="450" d:DesignWidth="800"
  Title="Page3">

<Grid Background="#FFDEDEDE">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="20" />     <!--0 Left Margin-->
        <ColumnDefinition Width="*" />
        <ColumnDefinition Width="*" />
        <ColumnDefinition Width="*" />
        <ColumnDefinition Width="20" />
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
        <RowDefinition Height="50"  />      <!--0 Green Button Bar-->
        <RowDefinition Height="auto" />     <!--1 Title Row-->
        <RowDefinition Height="auto" />     <!--2 Report Buttons-->
        <RowDefinition Height="10" />       <!--3 Buffer-->
        <RowDefinition Height="*" />        <!--4 Table-->
        <RowDefinition Height="20" />       <!--5 Bottom Margin-->
    </Grid.RowDefinitions>

    <Border Background="#4a9463" Grid.Row="0" Grid.ColumnSpan="6"/>
    <Button x:Name="BackButton" Grid.Column="3" Grid.Row="0" HorizontalAlignment="Right" VerticalAlignment="Center" Content="Back to Home" Click="BackButton_Click" />

    <TextBlock Grid.Column="1" Grid.Row="1" Grid.ColumnSpan="2" FontSize="20" Text="DH Approvals Automation (TEST)" Margin="0,0,0,10"/>
    
    <WrapPanel Grid.Column="1" Grid.Row="2">
        <TextBlock Text="Date Loaded: " VerticalAlignment="Bottom" />
        <DatePicker x:Name="DateLoaded" />
    </WrapPanel>
    

    <Button x:Name="DispData" Grid.Column="3" Grid.Row="2" HorizontalAlignment="Right" Width="100" Content="Display" Click="DispData_Click"/>



    <controls:DataGrid Grid.Column="1" Grid.Row="4" Grid.ColumnSpan="3" 
                       x:Name="PermInvoices" 
                       AutoGenerateColumns="False"
                       AlternatingRowBackground="#FFE2F9D8"
                       ItemsSource="{Binding}"
                       CanUserResizeRows="False"
                       CanUserAddRows="False"
                       FrozenColumnCount="2" 
                       AreRowDetailsFrozen="True"
                        >
        <controls:DataGrid.RowDetailsTemplate>
            <DataTemplate>
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="10" />
                        <ColumnDefinition Width="300" />
                        <ColumnDefinition Width="300" />
                        <ColumnDefinition Width="300" />
                        <ColumnDefinition Width="300" />
                        <ColumnDefinition Width="10" />
                    </Grid.ColumnDefinitions>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="10"  />
                        <RowDefinition Height="auto" />
                        <RowDefinition Height="auto" />
                        <RowDefinition Height="auto" />
                        <RowDefinition Height="*" />
                        <RowDefinition Height="10" />
                    </Grid.RowDefinitions>

                    <TextBlock Grid.Column="1" Grid.Row="1" Grid.ColumnSpan="3" FontWeight="Bold" Text="Invoice Details:" />

                    <StackPanel Grid.Column="1" Grid.Row="2">
                        <WrapPanel>
                            <TextBlock Text="Invoice #: " FontWeight="Bold"/>
                            <TextBlock Text="{Binding Path=[Invoice Number]}" />
                        </WrapPanel>
                        <WrapPanel>
                            <TextBlock Text="Job #: " FontWeight="Bold"/>
                            <TextBlock Text="{Binding Path=[Job Number]}" />
                        </WrapPanel>
                        <WrapPanel>
                            <TextBlock Text="Candidate Name: " FontWeight="Bold"/>
                            <TextBlock Text="{Binding Path=[First Name]}" />
                            <TextBlock Text=" "/>
                            <TextBlock Text="{Binding Path=[Last Name]}" />
                        </WrapPanel>
                        
                    </StackPanel>
                    
                    <StackPanel Grid.Column="2" Grid.Row="2">
                        <TextBlock Text="Approver Emails: " FontWeight="Bold"/>
                        <TextBlock Text="{Binding Path=[Sales Person Emails]}" />
                    </StackPanel>

                    <StackPanel Grid.Column="3" Grid.Row="2">
                        <TextBlock Text="Billing Contact Emails: " FontWeight="Bold"/>
                        <TextBlock Text="{Binding Path=[Billing Emails]}" />
                    </StackPanel>

                    <StackPanel Grid.Column="1" Grid.Row="3" Grid.ColumnSpan="3">
                        <TextBlock Text="Bullhorn Comments: " FontWeight="Bold"/>
                        <TextBlock Text="{Binding Path=[BHcomments]}" />
                    </StackPanel>

                    <StackPanel Grid.Column="4" Grid.Row="2" Background="#FF7C7C7C" Margin="0,0,0,20">
                        <TextBlock Text="Invoice Status: " FontWeight="Bold" Margin="10"/>
                        <TextBox Name="StatusTxt" Text="{Binding Path=[status]}"  Margin="10"/>
                        <Button Content="Update Status" x:Name="UpdateStatusbtn" Click="UpdateStatusbtn_Click" Margin="10"/>
                    </StackPanel>

                    
                    <Button Grid.Column="4" Grid.Row="3" Margin="10" Height="30" VerticalAlignment="Top" Content="Email Client" x:Name="EmailClientbtn" Click="EmailClientbtn_Click"/>
                    


                </Grid>
            </DataTemplate>
        </controls:DataGrid.RowDetailsTemplate>

        <controls:DataGrid.Columns>
            <controls:DataGridTextColumn Header="Job Number" Binding="{Binding Path=[Job Number]}" IsReadOnly="True"/>
            <controls:DataGridTextColumn Header="Address Code" Binding="{Binding Path=[Address Code]}" IsReadOnly="True"/>
            <controls:DataGridTextColumn Header="First Name" Binding="{Binding Path=[First Name]}" IsReadOnly="True"/>
            <controls:DataGridTextColumn Header="Last Name" Binding="{Binding Path=[Last Name]}" IsReadOnly="True"/>
            <controls:DataGridTextColumn Header="Start Date" Binding="{Binding Path=[Start Date], StringFormat=d}" IsReadOnly="True"/>
            <controls:DataGridTextColumn Header="SBU" Binding="{Binding Path=SBU}" IsReadOnly="True"/>
            <controls:DataGridTextColumn Header="Office" Binding="{Binding Path=Office}" IsReadOnly="True"/>
            <controls:DataGridTextColumn Header="Document Date" Binding="{Binding Path=[Document Date], StringFormat=d}" IsReadOnly="True"/>
            <controls:DataGridTextColumn Header="Invoiced Date" Binding="{Binding Path=[Invoiced Date], StringFormat=d}" IsReadOnly="True"/>
            <controls:DataGridTextColumn Header="Position" Binding="{Binding Path=Position}" IsReadOnly="True"/>
            <controls:DataGridTextColumn Header="Invoice Number" Binding="{Binding Path=[Invoice Number]}" IsReadOnly="True"/>
            <controls:DataGridTextColumn Header="Orig TRX Amount" Binding="{Binding Path=[Original TRX Amount], StringFormat=C}" IsReadOnly="True"/>
            <controls:DataGridTextColumn Header="Current TRX Amount" Binding="{Binding Path=[Current TRX Amount], StringFormat=C}" IsReadOnly="True"/>
            <controls:DataGridTextColumn Header="Status" Binding="{Binding Path=status}"/>
            <controls:DataGridTextColumn Header="BH Comments" Binding="{Binding Path=BHcomments}" IsReadOnly="True"/>
            <controls:DataGridTextColumn Header="Billing Emails" Binding="{Binding Path=[Billing Emails]}" IsReadOnly="True"/>
            <controls:DataGridTextColumn Header="Customer Number" Binding="{Binding Path=[Customer Number]}" IsReadOnly="True"/>
            <controls:DataGridTextColumn Header="Customer Name" Binding="{Binding Path=[Customer Name]}" IsReadOnly="True"/>
            <controls:DataGridTextColumn Header="Tax Schedule ID" Binding="{Binding Path=[Tax Schedule ID]}" IsReadOnly="True"/>
            <controls:DataGridTextColumn Header="Sales Person Emails" Binding="{Binding Path=[Sales Person Emails]}" IsReadOnly="True"/>
        </controls:DataGrid.Columns>
    </controls:DataGrid>




</Grid>
</Page>

And finally, here is the C# code behind. I have classes for the two buttons from the RowDetailsTemplate but just simply do not know how to reference the selected row's TextBox or bound data for "Invoice Number."

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Configuration;
using System.Data;
using System.Data.SqlClient;
using System.Data.OleDb;

namespace WpfApp1
{
/// <summary>
/// Interaction logic for Page3.xaml
/// </summary>
public partial class Page3 : Page
{
    

    public Page3()
    {
        InitializeComponent();
        DateLoaded.SelectedDate = DateTime.Today;
        
    }
    private void BackButton_Click(object sender, RoutedEventArgs e)
    {
        this.NavigationService.Navigate(new Uri("Page1.xaml", UriKind.Relative));
    }


    private void DispData_Click(object sender, RoutedEventArgs e)
    {
        DateTime? SelectedDate = DateLoaded.SelectedDate;
        string sDateLoaded = SelectedDate.Value.ToString("MM/dd/yyyy", System.Globalization.CultureInfo.InvariantCulture);

        string ConString = ConfigurationManager.ConnectionStrings["PlacementInvoices"].ConnectionString;
        string CmdString = string.Empty;


        CmdString = "exec ssrs_DHAutomationLog @dateloaded"; 
        
        try
        {
            using (SqlConnection con = new SqlConnection(ConString))
            {
                SqlCommand cmd = new SqlCommand(CmdString, con);
                cmd.Parameters.Add("@dateloaded", sDateLoaded);
                SqlDataAdapter sda = new SqlDataAdapter(cmd);
                DataTable dt = new DataTable("Invoices");
                sda.Fill(dt);
                PermInvoices.ItemsSource = dt.DefaultView;
            }

        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.ToString());
        }
    }

    private void UpdateStatusbtn_Click(object sender, RoutedEventArgs e)
    {
        //This is where I just can't figure out how to select the TextBox from the selected row.  And how to pull the "Invoice Number" value from that row.
        string newStatus = StatusTxt.value;
    }

    private void EmailClientbtn_Click(object sender, RoutedEventArgs e)
    {
        //This is where I can't figure out how to pull the "Invoice Number" from the selected row.
        MessageBox.Show("Doesn't Work Yet.");
    }

}

Thanks in advance for your help. I have researched intensively but must just not know the right keywords.


Solution

  • You can use the following code. I retrieved both newStatus and InvoiceNumber in the UpdateStatusbtn_Click event

    And in EmailClientbtn_Click event I just got the value InvoiceNumber.

    UpdateStatusbtn_Click event

    private void UpdateStatusbtn_Click(object sender, RoutedEventArgs e)
    {
        DataGridRow PermInvoicesRow = (DataGridRow)(PermInvoices.ItemContainerGenerator.ContainerFromItem(PermInvoices.SelectedItem));
        if (PermInvoicesRow == null) return;
    
        DataRowView rowSelected = (DataRowView)PermInvoicesRow.Item;
        DataGridDetailsPresenter PermInvoicesPresenter = FindVisualChild<DataGridDetailsPresenter>(PermInvoicesRow);
    
       DataTemplate template = PermInvoicesPresenter.ContentTemplate;
       TextBox textBox = (TextBox)template.FindName("StatusTxt", PermInvoicesPresenter);
    
       //get newStatus form StatusTxt
       string newStatus = textBox.Text;
                
    
             
       //get InvoiceNumber 
       int InvoiceNumber = 0;
       if (rowSelected != null)
       {
           InvoiceNumber = (int)rowSelected.Row["InvoiceNumber"];
       }
    }
    

    EmailClientbtn_Click event

    private void EmailClientbtn_Click(object sender, RoutedEventArgs e)
    {
        DataGridRow PermInvoicesRow = (DataGridRow)(PermInvoices.ItemContainerGenerator.ContainerFromItem(PermInvoices.SelectedItem));
        if (PermInvoicesRow == null) return;
    
        DataRowView rowSelected = (DataRowView)PermInvoicesRow.Item;
               
        int InvoiceNumber = 0;
        if (rowSelected != null)
        {
           InvoiceNumber = (int)rowSelected.Row["InvoiceNumber"];
        }
    }
    

    FindVisualChild method

    public static T FindVisualChild<T>(DependencyObject obj) where T : DependencyObject
    {
       for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
       {
           DependencyObject child = VisualTreeHelper.GetChild(obj, i);
           if (child != null && child is T)
              return (T)child;
           else
           {
               T childOfChild = FindVisualChild<T>(child);
               if (childOfChild != null)
                  return childOfChild;
           }
       }
       return null;
     }