You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WPF发票应用:如何在UserControl动态添加TextBox并实现自动聚焦

Hey there! Let's work through the dynamic TextBox row addition and auto-focus functionality for your WPF invoice app. Based on what you described, here's a practical, step-by-step solution:

Core Approach

We'll use an ItemsControl bound to an ObservableCollection to manage dynamic rows—this makes adding/removing rows seamless since the UI automatically updates when the collection changes. Each row will be a DataTemplate containing your two TextBoxes, and we'll handle events to trigger adding a new row and focusing the next TextBox once the current row is filled.

Step 1: Define the Row ViewModel

First, create a simple class to represent each invoice line. This implements INotifyPropertyChanged so the UI updates when data changes, and includes logic to fetch the item name from your database when the code is entered:

public class InvoiceLine : INotifyPropertyChanged
{
    private string _itemCode;
    public string ItemCode
    {
        get => _itemCode;
        set
        {
            _itemCode = value;
            OnPropertyChanged(nameof(ItemCode));
            // Fetch item name from database once code is entered
            if (!string.IsNullOrEmpty(value))
            {
                ItemName = FetchItemNameFromDatabase(value); // Replace with your actual DB call
            }
        }
    }

    private string _itemName;
    public string ItemName
    {
        get => _itemName;
        set
        {
            _itemName = value;
            OnPropertyChanged(nameof(ItemName));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    // Replace this with your real database query logic
    private string FetchItemNameFromDatabase(string itemCode)
    {
        // Example: Query your DB and return the matching item name
        return "Sample Product Name";
    }
}
Step 2: Update the UserControl XAML

Modify your UserControl to use an ItemsControl for dynamic rows. The DataTemplate defines the layout of each row's TextBoxes:

<UserControl x:Class="YourAppNamespace.InvoiceRowControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:YourAppNamespace">
    <Grid Margin="10">
        <ItemsControl x:Name="InvoiceLinesControl" ItemsSource="{Binding InvoiceLines}">
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal" Margin="0,5,0,0">
                        <TextBox x:Name="ItemCodeInput"
                                 Width="180"
                                 Text="{Binding ItemCode, UpdateSourceTrigger=PropertyChanged}"
                                 Margin="0,0,15,0"
                                 LostFocus="ItemCodeInput_LostFocus"/>
                        <TextBox x:Name="ItemNameDisplay"
                                 Width="280"
                                 Text="{Binding ItemName}"
                                 IsReadOnly="True"/> <!-- Make this read-only if users shouldn't edit it -->
                    </StackPanel>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
    </Grid>
</UserControl>
Step 3: Add Logic to the UserControl Code-Behind

In the code-behind, we'll initialize the collection, handle the LostFocus event to add new rows, and auto-focus the next TextBox:

public partial class InvoiceRowControl : UserControl
{
    public ObservableCollection<InvoiceLine> InvoiceLines { get; set; }

    public InvoiceRowControl()
    {
        InitializeComponent();
        InvoiceLines = new ObservableCollection<InvoiceLine>();
        // Add the first row by default
        InvoiceLines.Add(new InvoiceLine());
        DataContext = this;
    }

    private void ItemCodeInput_LostFocus(object sender, RoutedEventArgs e)
    {
        var currentTextBox = sender as TextBox;
        var currentLine = currentTextBox.DataContext as InvoiceLine;

        // Check if the current row is fully filled (adjust this condition to your needs)
        if (!string.IsNullOrWhiteSpace(currentLine.ItemCode) && !string.IsNullOrWhiteSpace(currentLine.ItemName))
        {
            int currentIndex = InvoiceLines.IndexOf(currentLine);
            // Only add a new row if we're on the last row
            if (currentIndex == InvoiceLines.Count - 1)
            {
                InvoiceLines.Add(new InvoiceLine());
                // Wait for the UI to render the new row, then focus its ItemCode TextBox
                Dispatcher.BeginInvoke(new Action(() =>
                {
                    var newRowContainer = InvoiceLinesControl.ItemContainerGenerator.ContainerFromIndex(InvoiceLines.Count - 1) as FrameworkElement;
                    var newCodeTextBox = newRowContainer.FindName("ItemCodeInput") as TextBox;
                    newCodeTextBox.Focus();
                }));
            }
        }
    }
}
Key Notes to Adjust for Your App
  • Condition for Adding Rows: The example checks if both ItemCode and ItemName are filled. You can tweak this—for example, trigger adding a new row as soon as ItemCode is entered and ItemName is fetched.
  • Dispatcher Usage: We use Dispatcher.BeginInvoke to wait for the UI to finish rendering the new row before trying to focus the TextBox. Skipping this might result in a null reference since the new control hasn't been created yet.
  • ReadOnly TextBox: If users shouldn't edit the ItemName TextBox, set IsReadOnly="True" like in the XAML example.

内容的提问来源于stack exchange,提问作者lavanyaabu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 09:28:55