簡體   English   中英

如何將焦點從數據網格中的編輯單元格轉移到 WPF 中的文本框

[英]How to shift focus from an edited cell in a datagrid to a text box in WPF

我正在嘗試讓用戶輸入文本框獲取 ID 字符串並獲取數據以填充數據網格中的行,然后根據用戶輸入的值觸發編輯數據網格中的特定行和單元格,然后將焦點放回用戶在數據網格單元格中輸入值后按下回車/回車鍵時的文本框。

除了最后一步,我的大部分工作正常,必須按兩次回車鍵才能返回文本框,我希望它在第一次按下時返回。

如果用戶直接使用鼠標選擇編輯的單元格,那么它可以正常工作,按一次回車鍵會將焦點發送回文本框。

自動單元格編輯是通過附加屬性和幫助器 class 觸發的,該幫助器偵聽 OnCurrentCellChanged 事件並觸發數據網格 BeginEdit 方法。 我想知道在焦點可以正確改變之前是否需要關閉/結束? (助手 class 根據這里的答案實現,非常感謝@Orace

我怎樣才能做到這一點? 我嘗試提交編輯,取消 PreviewKeyDown 方法后面的 xaml 代碼中的編輯,但沒有成功。 所以可能我必須以某種方式更改 OnCurrentCellChanged 事件方法?

我的 xaml:

<Window x:Class="TestChangingEditedCellFocus.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:TestChangingEditedCellFocus"  d:DataContext="{d:DesignInstance Type=local:ViewModel}"
        mc:Ignorable="d"
        FocusManager.FocusedElement="{Binding ElementName=idfield}"
        Title="MainWindow" Height="450" Width="800">
    <Window.Resources>
        <ResourceDictionary>
            <CollectionViewSource x:Key="srModels" Source="{Binding Models}"/>
        </ResourceDictionary>
    </Window.Resources>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="40"/>
            <RowDefinition/>
        </Grid.RowDefinitions>
        <StackPanel Orientation="Horizontal">
            <TextBox x:Name="idfield" Width="100" Height="30" Text="{Binding Path=ModelId, UpdateSourceTrigger=PropertyChanged}"/>
            <Button IsDefault="True" Command="{Binding Path=Command}" Content="Get Row" Height="30"/>
        </StackPanel>
        <DataGrid Grid.Row="1"
                  x:Name="modelgrid"                  
                  local:DataGridAutoEdit.AutoEditColumn="2"
                  ItemsSource="{Binding Source={StaticResource srModels}}"
                  SelectedItem="{Binding CurrentModel, UpdateSourceTrigger=PropertyChanged,Mode=TwoWay}"
                  AutoGenerateColumns="False" 
                  SelectionMode="Single"
                  SelectionUnit="FullRow"
                  PreviewKeyDown="modelgrid_PreviewKeyDown"
                  >
            <DataGrid.Columns>
                <DataGridTemplateColumn Header="ID" Width="Auto" IsReadOnly="True">
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <Label Content="{Binding Id}" HorizontalContentAlignment="Right"/>
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>
                <DataGridTemplateColumn Header="Name" Width="Auto" IsReadOnly="True">
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <Label Content="{Binding Name}" HorizontalContentAlignment="Right"/>
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>
                <DataGridTemplateColumn Header="Value" Width="Auto">
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <TextBox Text="{Binding Value, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" BorderThickness="0" 
                                     VerticalContentAlignment="Center" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" HorizontalContentAlignment="Right"
                                     FocusManager.FocusedElement="{Binding RelativeSource={RelativeSource Self}}"/>
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>
            </DataGrid.Columns>
        </DataGrid>
    </Grid>
</Window>

Xaml 后面的代碼:

public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            this.DataContext = new ViewModel();
        }

        private void modelgrid_PreviewKeyDown(object sender, KeyEventArgs e)
        {
            if ((e.Key == Key.Enter) || (e.Key == Key.Return))
            {
                //DataGrid grid = sender as DataGrid;
                //grid.CancelEdit();
                idfield.Focus();
                idfield.SelectAll();                
            }
        }
    }

視圖模型:

public class ViewModel : ObservableObject
    {
        private ObservableCollection<model> _models;
        private model _currentModel;
        private string _modelId;
        private ICommand _command;
        
        public ObservableCollection<model> Models
        {
            get
            {
                if (_models is null)
                {
                    _models = new ObservableCollection<model>();
                    OnPropertyChanged("Models");
                }
                return _models;
            }
        }
        public model CurrentModel
        {
            get
            {
                return _currentModel;
            }
            set
            {
                _currentModel = value;
                OnPropertyChanged("CurrentModel");
            }
        }
        public string ModelId
        {
            get
            {
                return _modelId;
            }
            set
            {
                _modelId = value;
                OnPropertyChanged("ModelId");
            }
        }
        public ICommand Command
        {
            get
            {
                if (_command == null)
                {
                    _command = new RelayCommand(param=>ExcecuteCommand(), pred=> ModelId is not null);
                }
                return _command;
            }
        }

        public void ExcecuteCommand()
        {
            Models.Clear();
            if (Models.Count == 0)
            {
                Models.Add(new model() { Id = 1, Name = "name1" });
                Models.Add(new model() { Id = 2, Name = "name2" });
                Models.Add(new model() { Id = 3, Name = "name3" });
                Models.Add(new model() { Id = 4, Name = "name4" });
            }

            foreach (model model in Models)
            {
                System.Diagnostics.Debug.WriteLine("Model: " + model.Name);
                int id = int.Parse(ModelId);
                if(id == model.Id)
                {
                    CurrentModel = model;
                }
            }
            ModelId = null;
        }
    }

DataGridAutoEdit 助手 class 觸發編輯模式:

class DataGridAutoEdit
    {
        public static readonly DependencyProperty AutoEditColumnProperty = DependencyProperty.RegisterAttached("AutoEditColumn", typeof(int), typeof(DataGridAutoEdit), new PropertyMetadata(default(int), AutoEditColumnChangedCallback));

        public static void SetAutoEditColumn(DependencyObject element, int value)
        {
            element.SetValue(AutoEditColumnProperty, value);
        }

        public static int GetAutoEditColumn(DependencyObject element)
        {
            return (int)element.GetValue(AutoEditColumnProperty);
        }

        private static void AutoEditColumnChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (d is not DataGrid dataGrid)
                return;

            GetAutoEditColumnHelper(dataGrid)?.Dispose();

            if (e.NewValue is int columnIndex)
            {
                SetAutoEditColumnHelper(d, new AutoEditColumnHelper(dataGrid, columnIndex));
            }
            else
            {
                d.ClearValue(AutoEditColumnHelperProperty);
            }
        }


        private static readonly DependencyProperty AutoEditColumnHelperProperty = DependencyProperty.RegisterAttached("AutoEditColumnHelper", typeof(AutoEditColumnHelper), typeof(DataGridAutoEdit), new PropertyMetadata(default(AutoEditColumnHelper)));

        private static void SetAutoEditColumnHelper(DependencyObject element, AutoEditColumnHelper value)
        {
            element.SetValue(AutoEditColumnHelperProperty, value);
        }

        private static AutoEditColumnHelper? GetAutoEditColumnHelper(DependencyObject element)
        {
            return element.GetValue(AutoEditColumnHelperProperty) as AutoEditColumnHelper;
        }



        //add private class to get datagrid auto cel edit function working, move to helpers if this works
        private class AutoEditColumnHelper : IDisposable
        {
            private readonly DataGrid _dataGrid;
            private readonly int _columnIndex;

            private object? _lastItem;

            public AutoEditColumnHelper(DataGrid dataGrid, int columnIndex)
            {
                _dataGrid = dataGrid;
                _columnIndex = columnIndex;

                _dataGrid.CurrentCellChanged += OnCurrentCellChanged;
            }

            public void Dispose()
            {
                _dataGrid.CurrentCellChanged -= OnCurrentCellChanged;
            }

            private void OnCurrentCellChanged(object? sender, EventArgs e)
            {
                DataGridCellInfo currentCell = _dataGrid.CurrentCell;
                
                if (!currentCell.IsValid || Equals(currentCell.Item, _lastItem))
                {
                    return;
                }

                DataGridColumn autoEditColumn = GetAutoEditColumn();
                if (autoEditColumn is null)
                {
                    return;
                }

                _dataGrid.Dispatcher.BeginInvoke(() =>
                {
                    _lastItem = _dataGrid.SelectedItem;
                    _dataGrid.CurrentCell = new DataGridCellInfo(_lastItem, autoEditColumn);
                    _dataGrid.BeginEdit();
                });
            }

            private DataGridColumn? GetAutoEditColumn()
            {
                return _columnIndex < 0 || _columnIndex > _dataGrid.Columns.Count ? null : _dataGrid.Columns[_columnIndex];
            }
        }
    }

這里有一個工作簡化的項目。

經過一些嘗試和錯誤后,我發現在代碼后面編輯我的 PreviewKeyDowan 方法以提交和取消編輯並再次使用 SendKeys 發送 Enter 命令可以將 cursor 發送回我想要的文本框字段:

private void modelgrid_PreviewKeyDown(object sender, KeyEventArgs e)
{
    if ((e.Key == Key.Enter) || (e.Key == Key.Return))
    {
        DataGrid grid = sender as DataGrid;
        grid.CommitEdit();
        grid.CancelEdit();
        SendKeys.SendWait("{ENTER}");
        idfield.Focus();              
    }
}

但這看起來很老套,我很樂意提出任何其他建議。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM