如何将鼠标焦点设置为WPF数据网格的行详细信息
本文关键字:数据网 数据 网格 详细信息 WPF 鼠标 焦点 设置 | 更新日期: 2023-09-27 18:17:57
所以我使用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获得了它。