Thanks, Andrew, that did it. The key was that I had to create a separate DataContext for the DockPanel in question, and then bound it to a new instance of the Pledge object created higher up in the template.
<ListBox>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.Resources>
<local:Pledge x:Key="pledge" />
</Grid.Resources>
<DockPanel DataContext="{Binding Source={StaticResource pledge}}" >
<TextBox Name="txtAmount" Text="{Binding Path=Amount}" />
<TextBox Name="txtEnvelopeID" Text="{Binding Path=EnvelopeID}" />
<!-- Bind the Tag property of the Button to the DataContext of the ListBoxItem, i.e. the current Family object -->
<Button Tag="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=DataContext}" Name="addPledge" Click="addPledge_Click" >
Add Pledge -->
</Button>
</DockPanel>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I still needed to know more details to setup the Pledge object appropriately, but I was able to get them by binding the Button's Tag property to the DataContext of its templated parent:
<Button
Tag="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=DataContext}"
Name="addPledge"
Click="addPledge_Click"
/>
And from there, I was able to handle the rest in code-behind:
private void addPledge_Click(object sender, RoutedEventArgs e)
{
try
{
// Set any additional necessary properties.
Button button = (Button)sender;
Pledge pledge = (Pledge)button.DataContext;
pledge.PledgeYear = (PledgeYear)cboYear.SelectedItem;
pledge.Family = (Family)button.Tag;
// Update the database and refresh the Listboxes.
entities.AddToPledge(pledge);
entities.SaveChanges();
LoadUnpledgedListBox();
LoadPledgedListBox();
}
catch (System.Data.UpdateException ex)
{
MessageBox.Show("Error updating database: " + ex.Message);
}
catch (System.Exception ex)
{
Classes.Utils.HandleGenericError(ex);
}
}
Many thanks -- that pointed me in the right direction.