如何将鼠标焦点设置为 WPF 数据网格的行详细信息?



所以我使用WPF和MVVM模式。我将数据网格的ItemSource属性绑定到ViewModel中的域对象集合。数据网格还定义了一个RowDetailsTemplate,它可以帮助我修改域对象上的所有属性。我已经设置了数据网格,因此它一次只允许选择一行。

如果你想知道我的datagrid声明是什么样子的:

<DataGrid Name="detailsDataGrid" Grid.Row="1" 
          AutoGenerateColumns="False"
          ItemsSource="{Binding Source={StaticResource Locator}, Path=Details.FileMoverDetails, Mode=OneWay, ValidatesOnDataErrors=True}" 
          SelectedItem="{Binding Source={StaticResource Locator}, Path=Details.SelectedDetail, Mode=TwoWay, ValidatesOnDataErrors=True}" 
          AreRowDetailsFrozen="True" CanUserAddRows="False" CanUserDeleteRows="False" IsReadOnly="True" SelectionMode="Single">

所以你会注意到SelectedItem也被绑定到ViewModel

中的一个属性上

这是我的问题:在我的数据网格下面,我有一个"添加新记录"按钮

<Button Name="newRecordBtn" DockPanel.Dock="Right" Margin="0,0,10,0" Click="newRecordBtn_Click" >New Record</Button>

它(在click事件中)在我的ViewModel中执行我的'AddDetailCommand'。'AddDetailCommand'只是创建一个新的域对象,将其添加到ViewModel的域对象集合中,然后将ViewModel的'SelectedDetail'属性设置为新创建的对象。然后我调用RaisePropertyChanged("FileMoverDetails"),接着调用RaisePropertyChanged("SelectedDetail")。

这将使我的数据网格选择新创建的项并展开数据网格的行详细信息。我的数据网格的行细节有很多字段,包括组合框和文本框。这给我带来了我真正的问题:当我左键点击一次在我的一个文本框上,这样我就可以开始打字了,它不把焦点放在文本框里,我必须点击时间,然后我才能开始打字。这种情况发生在行详细信息中的任何控件上:我必须在任何控件上单击一次,然后在发生任何事情之前单击第二次。这个问题只有在我点击"添加新记录"按钮时才会遇到。这意味着如果我选择数据网格中已经存在的行,那么焦点是正确的,我可以在任何行细节控件上单击一次,它将按预期工作。

对于我遇到的问题的另一个例子,我在数据网格的行详细信息中有一个"删除记录"按钮,当激活时从数据网格的项集合中删除当前选择的域对象。点击"添加新记录"按钮后,我去并立即点击一次上的"删除记录"按钮,什么也没发生。但当我点击第二次按钮,然后按钮工作。

我问的是什么?在我单击我的"添加新记录"按钮后,我希望能够单击一次在我的"删除记录"按钮上并激活其功能,而无需单击另一个时间。

.

.

额外信息:

我认为这纯粹是一个焦点问题,所以我修改了我的"添加新记录"按钮的点击事件,将焦点设置为添加新记录后我的数据网格的行详细信息TextBox控件之一:

    private void newRecordBtn_Click(object sender, RoutedEventArgs e)
    {
        if (e.Source == newRecordBtn)
        {
            ExecuteNewRecordButtonCommand();
            e.Handled = true;
        }
    }
    private void ExecuteNewRecordButtonCommand()
    {
        if (newRecordBtn.Command.CanExecute(null))
        {
            newRecordBtn.Command.Execute(null);
            if (detailsDataGrid.Items.Count > 0)
            {
                detailsDataGrid.UpdateLayout();
                DataGridRow selectedRow = (DataGridRow)(detailsDataGrid.ItemContainerGenerator.ContainerFromItem(detailsDataGrid.SelectedItem));

                //Get the ContentPresenter of the row details.
                DataGridDetailsPresenter presenter = FindVisualChild<DataGridDetailsPresenter>(selectedRow);
                // Find the localDirectoryText TextBox
                DataTemplate template = presenter.ContentTemplate;
                System.Windows.Controls.TextBox localDirTxt = (System.Windows.Controls.TextBox)template.FindName("localDirectoryText", presenter);
                localDirTxt.Focus();
            }
        }
    }

猜猜发生了什么?它像我期望的那样将键盘焦点设置到TextBox中(我可以立即开始输入,它会将我输入的文本放入TextBox中)。但是,当我点击一旦在不同的文本框或我的"删除记录"按钮或不同的控件(所有这些都在数据网格的行细节)什么都没有发生!然后,我必须单击时间,以便控件执行我期望的只需单击一次即可完成的操作。

请帮助,我已经努力让这个工作,这是非常令人沮丧的。即使你不能帮我,我也想感谢你在这个描述中谈到这一点,我知道这很长。

这是我的视图的XAML(好吧,至少是相关的部分,我删除了东西,并在东西被删除的地方放置了"…"):

<Grid>
    <Grid.RowDefinitions>
    ...
    </Grid.RowDefinitions>
    <DataGrid Name="detailsDataGrid" Grid.Row="1" AutoGenerateColumns="False" ItemsSource="{Binding Source={StaticResource Locator}, Path=Details.FileMoverDetails, Mode=OneWay, ValidatesOnDataErrors=True}" SelectedItem="{Binding Source={StaticResource Locator}, Path=Details.SelectedDetail, Mode=TwoWay, ValidatesOnDataErrors=True}" AreRowDetailsFrozen="True" CanUserAddRows="False" CanUserDeleteRows="False" IsReadOnly="True" SelectionMode="Single">
        <DataGrid.Columns>
            ...
            <DataGridTextColumn Header="Local Directory" Binding="{Binding Path=LocalDirectory, ValidatesOnDataErrors=True, Mode=TwoWay, UpdateSourceTrigger=LostFocus}" Width="175"/>
            <DataGridTextColumn Header="Remote Directory" Binding="{Binding Path=RemoteDirectory, ValidatesOnDataErrors=True, Mode=TwoWay, UpdateSourceTrigger=LostFocus}" Width="175"/>
            ...
        </DataGrid.Columns>
        <DataGrid.RowDetailsTemplate>
            <DataTemplate>
                <Grid Name="rowDetailsGrid" Margin="5,10,0,10" Width="{Binding ElementName=detailsView, Path=ActualWidth}"  HorizontalAlignment="Left" VerticalAlignment="Top" DataContext="{Binding Source={StaticResource Locator}, Path=Details.SelectedDetail}" IsEnabled="{Binding Source={StaticResource Locator}, Path=Details.CanEditDetails, Mode=OneWay}">
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="1*"/>
                        <ColumnDefinition Width="20"/>
                        <ColumnDefinition Width="190"/>
                        <ColumnDefinition Width="20"/>
                        <ColumnDefinition Width="250"/>
                        <ColumnDefinition Width="40"/>
                    </Grid.ColumnDefinitions>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="Auto"/>
                        <RowDefinition Height="Auto"/>
                        <RowDefinition Height="Auto"/>
                        <RowDefinition Height="Auto"/>
                        <RowDefinition Height="Auto"/>
                        <RowDefinition Height="Auto"/>
                        <RowDefinition Height="Auto"/>
                        <RowDefinition Height="30"/>
                        <RowDefinition Height="Auto"/>
                        <RowDefinition Height="Auto"/>
                        <RowDefinition Height="Auto"/>
                    </Grid.RowDefinitions>
                    <StackPanel Grid.Column="0" Grid.Row="8" Grid.ColumnSpan="3" Orientation="Vertical">
                        ...
                        <Grid>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="1*"/>
                                <ColumnDefinition Width="Auto"/>
                            </Grid.ColumnDefinitions>
                            <TextBox Grid.Column="0" Name="localDirectoryText" Text="{Binding Path=LocalDirectory, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" PreviewKeyDown="localDirectoryText_PreviewKeyDown"/>
                            <Button Grid.Column="1" Width="20" Margin="3,3,0,3" Name="localDirectoryButton" Click="localDirectoryButton_Click">...</Button>
                        </Grid>
                    </StackPanel>
                    <StackPanel Grid.Column="0" Grid.Row="9" Grid.ColumnSpan="3" Orientation="Vertical">
                        ...
                        <Grid>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="1*"/>
                                <ColumnDefinition Width="Auto"/>
                            </Grid.ColumnDefinitions>
                            <TextBox Grid.Column="0" Name="remoteDirectoryText" Text="{Binding Path=RemoteDirectory, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" PreviewKeyDown="remoteDirectoryText_PreviewKeyDown"/>
                            <Button Grid.Column="1" Width="20" Margin="3,3,0,3" Name="remoteDirectoryButton" Click="remoteDirectoryButton_Click">...</Button>
                        </Grid>
                    </StackPanel>
                    ...
                    <DockPanel Grid.Column="4" Grid.Row="10" LastChildFill="False">
                        <Button Name="removeRecordBtn" DockPanel.Dock="Right" Margin="0,15,0,0" Command="{Binding Path=RemoveSelectedDetailCommand}">Remove Record</Button>
                    </DockPanel>
                </Grid>
            </DataTemplate>
        </DataGrid.RowDetailsTemplate>
    </DataGrid>
    <Grid Grid.Row="2" DataContext="{Binding Source={StaticResource Locator}, Path=Details}" IsEnabled="{Binding Path=HasInstance, Mode=OneWay}" Width="{Binding ElementName=detailsView, Path=ActualWidth}">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="1*"/>
            <ColumnDefinition Width="Auto" MinWidth="125"/>
        </Grid.ColumnDefinitions>
        <DockPanel Grid.Column="1" LastChildFill="False" Margin="0,10,0,7">
            <Button Name="newRecordBtn" DockPanel.Dock="Right" Margin="0,0,10,0" Command="{Binding Path=AddDetailCommand}" Click="newRecordBtn_Click" >New Record</Button>
        </DockPanel>
    </Grid>
</Grid>

经过许多google chrome标签和许多耐心,我终于能够解决我的问题。

我不知道为什么我以前的ExecuteNewRecord()方法没有实现适当的焦点,但我通过以下方式修复了它:首先将焦点放在当前选定行的第一个单元格上,然后将焦点移动到当前选定行的第二个单元格上。这样做在某种程度上纠正了焦点问题。

这是我修改的newRecordBtn_Click()和ExecuteNewRecordButtonCommand():

private void newRecordBtn_Click(object sender, RoutedEventArgs e)
{
    if (e.Source == newRecordBtn)
    {
        ExecuteNewRecordButtonCommand();
        e.Handled = true;
    }
}
private void ExecuteNewRecordButtonCommand()
{
    if (newRecordBtn.Command.CanExecute(null))
    {
        newRecordBtn.Command.Execute(null);
        if (detailsDataGrid.Items.Count > 0)
        {
            detailsDataGrid.UpdateLayout();
            DataGridRow selectedRow = 
                (DataGridRow)(detailsDataGrid.ItemContainerGenerator
                                                .ContainerFromItem(detailsDataGrid.SelectedItem));
            DataGridCellsPresenter cellPresenter = FindVisualChild<DataGridCellsPresenter>(selectedRow);
            System.Windows.Controls.DataGridCell firstCell =
                (System.Windows.Controls.DataGridCell)(cellPresenter.ItemContainerGenerator
                                                                    .ContainerFromIndex(0));
            firstCell.Focus();
            firstCell.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
        }
    }
}

另外,如果有人想知道FindVindVisualChild<>()方法是什么,那么请注意,我不是该方法的原作者,我从http://msdn.microsoft.com/en-us/library/bb613579.aspx获得了它。

最新更新