所以我使用的是WPF和MVVM模式。我将datagrid的ItemSource属性绑定到ViewModel中的域对象集合。 datagrid还定义了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
中的属性这是我的问题:在我的数据网格下面,我有一个“添加新记录”#39;按钮
<Button Name="newRecordBtn" DockPanel.Dock="Right" Margin="0,0,10,0" Click="newRecordBtn_Click" >New Record</Button>
(在点击事件内)执行我的&#39; AddDetailCommand&#39;在我的ViewModel中。 &#39; AddDetailCommand&#39;只需创建一个新的域对象,将其添加到ViewModel的域对象集合中,然后设置ViewModel&#39; SelectedDetail&#39;属性到新创建的对象。然后我调用RaisePropertyChanged(&#34; FileMoverDetails&#34;),然后调用RaisePropertyChanged(&#34; SelectedDetail&#34;)。
这使我的datagrid选择新创建的项目并展开datagrid的行详细信息。我的datagrid行详细信息包含很多字段,包括ComboBoxes和TextBoxes。这让我想到了我的真正问题:当我在我的一个TextBox上点击 一次 以便我可以开始输入它时,不会将焦点放在文本框中在开始输入之前,我必须先点击 第二 时间。虽然我的行详细信息中的任何控件都会发生这种情况:我必须在任何控件上单击一次,然后在发生任何事情之前再次单击。只有在我点击“添加新记录”时才会遇到此问题。按钮。这意味着如果我在datagrid中选择一个已存在的行,那么焦点是正确的,我可以在任何行详细信息控件上单击一次,它将按预期工作。
关于我遇到的问题的另一个例子,我有一个&#39;删除记录&#39;数据网格行详细信息中的按钮,在激活时会从数据网格的项目集合中删除当前选定的域对象。点击“添加新记录”后#39;按钮我立即点击<删除记录&#39;上的 一次 。按钮,没有任何反应。但是当我第二次点击时,按钮就会起作用。
那我在问什么?点击我的“添加新记录”后#39;按钮我希望能够在我的“删除记录”中点击 一次 。按钮并激活其功能,而无需单击其他时间。
额外信息:
我认为这纯粹是一个焦点问题,所以我赞美我的新纪录&#39;按钮的Click事件在添加新记录后将焦点设置为我的datagrid行详细信息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)。但是当我在另一个TextBox上点击 一次 或我的删除记录&#39;按钮或不同的控件(所有这些都在datagrid的行详细信息中)没有任何反应!然后,我必须单击 第二 时间,以便控件执行我预期的单击操作。
请帮助,我已经努力让这个工作起来,这非常令人沮丧。即使你无法帮助我也要感谢你在这个描述中达到这一点,我知道它很长。
这是我的View的XAML(至少是相关部分,我删除了东西并放置了#34; ......&#34;在删除了东西的地方):
<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>
答案 0 :(得分:2)
经过许多谷歌浏览器标签和耐心,我终于能够解决我的问题了。
我不知道为什么我之前的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&lt;&gt;()方法是什么,请注意我不是该方法的原作者,我是从http://msdn.microsoft.com/en-us/library/bb613579.aspx得到的。