Question

In a WPF page that I'm designing for my church, I have two ListBox controls bound to the results of two Linq-to-Entities queries: the first listbox/query contains all the folks who haven't yet pledged for the year, the second listbox/query contains all the folks who have pledged (and their associated pledges). Specifically:

var familiesWithNoPledges = from family in entities.Family.Include("Pledge")
    where !family.Pledge.Where(p => p.PledgeYearID == pledgeYear).Any()
    select family;

var familiesWithPledges = from family in entities.Family
    join pledge in entities.Pledge.Include("PledgeFrequency").Where(p => p.PledgeYearID == pledgeYear) on family equals pledge.Family
    select new { family, pledge };

When the user of the application selects a family from the first ListBox, the ListBox item expands slightly to show the fields for a pledge, so that it's easy to add the pledge details to the second ListBox. It looks something like this:

http://wouldbetheologian.com/images/PledgeManager.jpg (Offsite Image)

The underlying database (among other things) has a "Family" table with a 1:many relationship to a "Pledge" table. And when the user clicks the "Add Pledge" button, I'd like to create the new Pledge instance, save it to the database, and then refresh the two controls.

But I can't figure out how to do that. If I try to do it in the code-behind, it doesn't seem like the event handler for the (n-instances of the) "Add Pledge" button can reference the controls in question; and in XAML, if I try to bind the ListBoxItem DataTemplate controls to the Family.Pledge (or Family.Pledge.FirstOrDefault()) fields, those fields are still empty when I look at the current Family object in the event handler for the Add Pledge button.

Any thoughts on how to solve this? Or is there a better UI model I should look at altogether?

Thanks in advance.

Was it helpful?

Solution 2

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.

OTHER TIPS

The easiest way to do it is to have the class that is representing items in your ListBox ItemsSource to have all the fields that you are referencing in your DataTemplate. Then in your button handler you can access the new pledge info by using the data context of the button.

    private void AddPledge_Click(object sender, RoutedEventArgs e)
    {
        Button b = sender as Button;
        PotentialPledge p = b.DataContext as PotentialPledge;

        //do stuff with pledge
    }

There might be a way to do this without having the templated fields be part of the class that makes up the ItemsSource for the ListBox (just learning WPF myself), but this works. If that doesn't help, I'll look back later (off to work now).

Anyway, awesome job making an app for your church, it's looking pretty good.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top