WPF DataGrid 问题 - 在 RowDetailsTemplate 中引用 TextBox

WPF DataGrid Question - Referencing TextBox inside RowDetailsTemplate

经过几个小时的研究如何做到这一点,我感到很困惑,需要堆栈溢出专家的帮助。我不是 WPF 开发人员,周一才开始自学,所以请耐心等待 - 但我需要创建一个用户界面以允许最终用户更新 MS SQL table 中的一些字段。我对该应用程序的目标是将存储过程的结果显示到数据网格中,允许最终用户使用 RowDetailsTemplate select 一行,为他们提供该记录的完整详细信息。在该插图中,我想让他们能够执行几个操作 1) 更新 table 中的单个列,可能通过调用存储过程,以及 2) 有一个按钮可以执行另一个操作,也可能通过调用一个存储过程,传入行的唯一 ID。

我遇到的问题是我不确定如何 1) 从 selected 行引用 TextBox。找不到文本框的名称。和 2) 我不确定如何在后面的代码中引用 selected 行的绑定唯一标识符(“发票编号”)。

我的应用大致如下所示。您可以看到我在行详细信息中有两个小表格,更新状态和电子邮件客户端。

这是生成它的 XAML 代码:

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

最后,这是后面的 C# 代码。我有 类 用于 RowDetailsTemplate 中的两个按钮,但只是不知道如何引用 selected 行的文本框或“发票编号”的绑定数据。

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.");
    }

}

在此先感谢您的帮助。我已经深入研究了,但肯定不知道正确的关键字。

您可以使用以下代码。我在 UpdateStatusbtn_Click 事件

中检索了 newStatusInvoiceNumber

并且在 EmailClientbtn_Click 事件中我刚得到值 InvoiceNumber

UpdateStatusbtn_Click 事件

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 事件

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 方法

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