So I am using WPF and the MVVM pattern. I have my datagrid's ItemSource property bound to a collection of domain objects in my ViewModel. The datagrid also has a RowDetailsTemplate defined which helps me modify all of my properties on my domain object. I have the datagrid set up so it only allows a single row to be selected at a time.
In case you are wondering here is what my datagrid declaration looks like:
<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">
So you will also notice that the SelectedItem is also bound to a property in my ViewModel
Here is my problem: below my datagrid I have an 'add new record' button
<Button Name="newRecordBtn" DockPanel.Dock="Right" Margin="0,0,10,0" Click="newRecordBtn_Click" >New Record</Button>
which (inside the click event) executes my 'AddDetailCommand' in my ViewModel. The 'AddDetailCommand' simply creates a new domain object, adds it to the ViewModel's collection of domain objects and then sets the ViewModel's 'SelectedDetail' property to the newly created object. Then I call RaisePropertyChanged("FileMoverDetails") followed by RaisePropertyChanged("SelectedDetail").
This makes my datagrid select the newly created item and expand the datagrid's row details. My datagrid's row details has a lot of fields including ComboBoxes and TextBoxes. Which brings me to my real problem: when I left click once on one of my TextBoxes so that I can start typing it doesn't place focus in the text box and I have to click a second time before I can start typing. This happens with any control inside my row details though: I have to click once on any control and then click a second time before anything happens. This problem is only encountered if I click my 'add new record' button. Meaning if I select an already existing row in the datagrid then the focus is correct and I can click once on any row details control and it will work as expected.
For another example of the issue I am having, I have a 'remove record' button in the datagrid's row details which when activated removes the currently selected domain object from the datagrid's item collection. After clicking the 'Add new record' button I go and immediately click once on the 'remove record' button and nothing happens. But when I click a second time then the button works.
So what am I asking? After I click my 'add new record' button I want to be able to click once on my 'remove record' button and have its functionality activated without having to click another time.
.
.
Extra information:
I thought this was purely a focus issue so I ammended my 'add new record' button's Click event to set the focus to one of my datagrid's row details TextBox control after adding the new record:
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();
}
}
}
Guess what happened? It set the keyboard focus into the TextBox like I expected it would (I can start typing immediately and it will place my typed text into the TextBox). But when I click once on a different TextBox or my 'Remove record' button or a different control (all of which are in the datagrid's row details) nothing happens! I have to then click a second time for the control to do what I expected only a single click to do.
Please help, I have tried so hard to get this to work and it is very frustrating. Even if you can't help I want to thank you for getting to this point in this description, I know it is very long.
Here is my View's XAML (well at least the relevant parts, I remove stuff and placed "..." in the places where stuff was removed):
<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>