Francis Rodgers
Francis Rodgers

Reputation: 4685

XAML Binding to Sql Server

I have a SQL Server DB with the following tables and relationships:

Jobs which contains many sessions. Sessions which contains many breaks.

First of all, dont be frightened by the large amount of code posted here. The relevant parts are the highlighted parts dealing with the list, the rest is just for comparison to see where I am going wrong.

In XAML I am trying and succeeding when binding jobs. I get it to filter out the correct sessions. No problem there.

My problem is that when I try to get it to filter the breaks that belong to each session it wont work but I am using all the same principles.

I am not using (nor interested in using) Linq2Sql as it creates too many additional classes that bloat my code. I am just using direct databinding.

I have asked this question before and posted code, but I never got any reply because the code was simply too long to read in a reasonable timeframe.

My question here is, what am I doing wrong. I was with the impression that since I can successfully bind and filter sessions, then I should be able to do likewise with sessions and filter breaks. But it doesnt work.

I am getting somewhat desparate for help and appreicate any answers.

EDIT: Once again I have included code samples. I am not trying to hide the code for secrecy and copyright. It is just an exercise I am doing to learn so I wouldnt mind posting the full code. But it is very long. So I will just post the parts I think are relevant. If you want more just ask.

For those of you interested in skipping to the good part where the problems are, look under the part of the Breaks list box. The rest is just there for comparison to help you debug. There is also C# code below to help further. Again, look at the list part the rest is just for debugging.

Below is the relevant XAML

<!--Jobs List box - Works fine-->
<ListBox Name="lstJobs"  DockPanel.Dock="Top"
        MinWidth="150" MinHeight="200" MaxHeight="250"
        ScrollViewer.VerticalScrollBarVisibility="Visible"
        SelectionChanged="lstJobs_SelectionChanged"

        IsSynchronizedWithCurrentItem="True"
        DataContext="{Binding Tables[JobDetails]}"
        ItemsSource="{Binding}"
        >
    <ListBox.ItemTemplate>
        <DataTemplate>
            <Grid>
                <StackPanel Orientation="Horizontal" Margin="3,0,3,0">
                    <TextBlock Text="{Binding Path=Title}"/>
                    <TextBlock Text=" "/>
                    <TextBlock Text="{Binding Path=ID}"/>
                </StackPanel>
            </Grid>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

<!--How Jobs listbox is bound to relevant fields in jobs table. This works fine-->
<TextBox    Text="{Binding        ElementName=lstJobs, Path=SelectedItem.ID,            UpdateSourceTrigger=PropertyChanged}" Name="txtJobNo"          Grid.Row="1"                 IsEnabled="False"/>
<TextBox    Text="{Binding        ElementName=lstJobs, Path=SelectedItem.Title,         UpdateSourceTrigger=PropertyChanged}" Name="txtJobTitle"       Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="2"/>
<TextBox    Text="{Binding        ElementName=lstJobs, Path=SelectedItem.Description,   UpdateSourceTrigger=PropertyChanged}" Name="txtJobDesc"        Grid.Row="2" Grid.Column="1" Grid.ColumnSpan="3" Grid.RowSpan="2"/>


<!--Sessions List box, Automatically filtered based on relationship (see last binding line). This works fine too-->
<ListBox Name="lstSessions" DockPanel.Dock="Top" MinWidth="150"
        MinHeight="200" MaxHeight="220"
        ScrollViewer.VerticalScrollBarVisibility="Visible"
        SelectionChanged="lstSessions_SelectionChanged"

        IsSynchronizedWithCurrentItem="True"
        DataContext="{Binding Path=Tables[JobDetails]}"
        ItemsSource="{Binding Path=relJobDetailsSessionDetails}"
        >
    <ListBox.ItemTemplate>
        <DataTemplate>
            <Grid>
                <StackPanel Orientation="Horizontal" Margin="3,0,3,0">
                    <TextBlock Text="{Binding Path=Title}" />
                    <TextBlock Text="{Binding Path=ID}" />
                </StackPanel>
            </Grid>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

<!--How Sessions listbox is bound to relevant fields in Sessions table. This works fine-->
<TextBox Name="txtSessionNo"          Text="{Binding ElementName=lstSessions, Path=SelectedItem.ID,            UpdateSourceTrigger=PropertyChanged}" Grid.Row="1" Grid.Column="0"/>
<TextBox Name="txtSessionTitle"       Text="{Binding ElementName=lstSessions, Path=SelectedItem.Title,         UpdateSourceTrigger=PropertyChanged}" Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="3"/>
<TextBox Name="txtSessionDesc"        Text="{Binding ElementName=lstSessions, Path=SelectedItem.Description,   UpdateSourceTrigger=PropertyChanged}" Grid.Row="2" Grid.Column="1" Grid.ColumnSpan="3" Grid.RowSpan="2"/>


<!--Breaks List box, Should be automatically filtered (it is), but it does not change when a job or session is selected. Why?? -->
<ListBox Name="lstBreaks" MinWidth="150" MinHeight="140" MaxHeight="140"
        ScrollViewer.VerticalScrollBarVisibility="Visible"
        SelectionChanged="lstBreaks_SelectionChanged"

        IsSynchronizedWithCurrentItem="True"
        DataContext="{Binding Path=Tables[SessionDetails]}"
        ItemsSource="{Binding Path=relSessionDetailsBreakDetails}"
        >
    <ListBox.ItemTemplate>
        <DataTemplate>
            <Grid>
                <StackPanel Orientation="Horizontal" Margin="3,0,3,0">
                    <TextBlock Text="{Binding Path=Title}" />
                    <TextBlock Text="{Binding Path=ID}" />
                </StackPanel>
            </Grid>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

<!--How Breaks listbox is bound to relevant fields in Breaks table. This works fine as before-->
<TextBox Name="txtBreakNo"          Text="{Binding ElementName=lstBreaks, Path=SelectedItem.ID,            UpdateSourceTrigger=PropertyChanged}" Grid.Row="1" Grid.Column="0"/>
<TextBox Name="txtBreakTitle"       Text="{Binding ElementName=lstBreaks, Path=SelectedItem.Title,         UpdateSourceTrigger=PropertyChanged}" Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="2"/>
<ComboBox Name="cbxBreakType"       Text="{Binding ElementName=lstBreaks, Path=SelectedItem.Description,     UpdateSourceTrigger=PropertyChanged}" Grid.Row="1" Grid.Column="3"/>

Following is the C# Code behind (Once again, the highlighted part is the breaks and the rest is just for comparison, so you can skip directly to that if you like):

//Connection String
string conString = "XYZ Works ok, no prob here";

//Data Adaptors for various tables
SqlDataAdapter daJobDetails = new SqlDataAdapter();
SqlDataAdapter daSessionDetails = new SqlDataAdapter();
SqlDataAdapter daBreakDetails = new SqlDataAdapter();

//The dataset to hold all of the data
DataSet dsDataSet = new DataSet();

//Step 1: Create Connection
SqlConnection conn = new SqlConnection(conString);

//Open Connection
conn.Open();


//Load Job Details Table - works fine.
daJobDetails.SelectCommand = new SqlCommand("Select * From JobDetails", conn);
daJobDetails.Fill(dsDataSet, "JobDetails");

//Load Session Details table - works fine.
daSessionDetails.SelectCommand = new SqlCommand("SELECT * FROM SessionDetails", conn);
daSessionDetails.Fill(dsDataSet, "SessionDetails");

//Relation: JobDetails.ID = SessionDetails.JobID. - Works fine
dsDataSet.Relations.Add("relJobDetailsSessionDetails",
    dsDataSet.Tables["JobDetails"].Columns["ID"],
    dsDataSet.Tables["SessionDetails"].Columns["JobID"]);


//**** Possible problem code *****
//Load Break Details table - could there be something wrong here.
daBreakDetails.SelectCommand = new SqlCommand("SELECT * FROM BreakDetails", conn);
daBreakDetails.Fill(dsDataSet, "BreakDetails");

//**** Possible problem code *****
//Relation: SessionDetails.ID = BreakDetails.SessionID - Could there be something wrong here
dsDataSet.Relations.Add("relSessionDetailsBreakDetails",
    dsDataSet.Tables["SessionDetails"].Columns["ID"],
    dsDataSet.Tables["BreakDetails"].Columns["SessionID"]);



//Set the DataContext to the DataSet
expJobs.DataContext = dsDataSet;


//Close connection
conn.Close();

Upvotes: 1

Views: 4454

Answers (1)

Phil Sandler
Phil Sandler

Reputation: 28046

As requested, here is a full repro case.

Update: updated with the working code from your link. Looks like the answer was binding to SelectedItem => relation. Very logical, actually.

XAML:

<Window x:Class="WpfApplication2.MainWindow" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Title="MainWindow" Height="350" Width="525">

    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width=".5*"/>
            <ColumnDefinition Width=".5*"/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height=".33*"/>
            <RowDefinition Height=".33*"/>
            <RowDefinition Height=".33*"/>
        </Grid.RowDefinitions>
        <!--Jobs List box - Works fine-->
        <ListBox Name="lstJobs" Grid.Column="0" Grid.Row="0" 
             MinWidth="150"  
             MinHeight="200"  
             MaxHeight="250"          
             ScrollViewer.VerticalScrollBarVisibility="Visible"          
             SelectionChanged="lstJobs_SelectionChanged"           
             IsSynchronizedWithCurrentItem="True"          
             ItemsSource="{Binding Path=JobDetails}"                      
             >
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <Grid>
                        <StackPanel Orientation="Horizontal" Margin="3,0,3,0">
                            <TextBlock Text="{Binding Path=ID}"/>
                            <TextBlock Text="{Binding Path=Title}"/>
                        </StackPanel>
                    </Grid>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
        <!--How Jobs listbox is bound to relevant fields in jobs table. This works fine-->
        <StackPanel Orientation="Vertical" Grid.Column="1" Grid.Row="0">
            <TextBox Text="{Binding ElementName=lstJobs, Path=SelectedItem.ID, UpdateSourceTrigger=PropertyChanged}"  
             Name="txtJobNo" Grid.Row="1" IsEnabled="False"/>
            <TextBox Text="{Binding ElementName=lstJobs, Path=SelectedItem.Title, UpdateSourceTrigger=PropertyChanged}"  
             Name="txtJobTitle" Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="2"/>
            <TextBox Text="{Binding ElementName=lstJobs, Path=SelectedItem.Description, UpdateSourceTrigger=PropertyChanged}"  
             Name="txtJobDesc" Grid.Row="2" Grid.Column="1" Grid.ColumnSpan="3" Grid.RowSpan="2"/>
        </StackPanel>

        <!--Sessions List box, Automatically filtered based on relationship (see last binding line). This works fine too -->
        <ListBox Name="lstSessions" Grid.Column="0" Grid.Row="1" 
             DockPanel.Dock="Top"  
             MinWidth="150"          
             MinHeight="200"  
             MaxHeight="220"          
             ScrollViewer.VerticalScrollBarVisibility="Visible"          
             SelectionChanged="lstSessions_SelectionChanged"           
             IsSynchronizedWithCurrentItem="True"          
             ItemsSource="{Binding ElementName=lstJobs, Path=SelectedItem.relJobDetailsSessionDetails}" 
             >
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <Grid>
                        <StackPanel Orientation="Horizontal" Margin="3,0,3,0">
                            <TextBlock Text="{Binding Path=Title}" />
                            <TextBlock Text="{Binding Path=ID}" />
                        </StackPanel>
                    </Grid>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>


        <!--How Sessions listbox is bound to relevant fields in Sessions table. This works fine-->
        <StackPanel Orientation="Vertical" Grid.Column="1" Grid.Row="1">
            <TextBox Name="txtSessionNo" Text="{Binding ElementName=lstSessions, Path=SelectedItem.ID,  UpdateSourceTrigger=PropertyChanged}"  
             Grid.Row="1" Grid.Column="0"/>
            <TextBox Name="txtSessionTitle" Text="{Binding ElementName=lstSessions, Path=SelectedItem.Title, UpdateSourceTrigger=PropertyChanged}"  
             Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="3"/>
            <TextBox Name="txtSessionDesc" Text="{Binding ElementName=lstSessions, Path=SelectedItem.Description, UpdateSourceTrigger=PropertyChanged}"  
             Grid.Row="2" Grid.Column="1" Grid.ColumnSpan="3" Grid.RowSpan="2"/>
        </StackPanel>

        <!--Breaks List box, Should be automatically filtered (it is), but it does not change when a job or session is selected. Why?? -->
        <ListBox Name="lstBreaks" Grid.Column="0" Grid.Row="2" 
             MinWidth="150"  
             MinHeight="140"  
             MaxHeight="140"          
             ScrollViewer.VerticalScrollBarVisibility="Visible"          
             SelectionChanged="lstBreaks_SelectionChanged"           
             IsSynchronizedWithCurrentItem="True"          
             ItemsSource="{Binding ElementName=lstSessions, Path=SelectedItem.relSessionDetailsBreakDetails}"          
             >
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <Grid>
                        <StackPanel Orientation="Horizontal" Margin="3,0,3,0">
                            <TextBlock Text="{Binding Path=Title}" />
                            <TextBlock Text="{Binding Path=ID}" />
                        </StackPanel>
                    </Grid>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
        <!--How Breaks listbox is bound to relevant fields in Breaks table. This works fine as before-->
        <StackPanel Orientation="Vertical" Grid.Column="1" Grid.Row="2">
            <TextBox Name="txtBreakNo" DockPanel.Dock="Bottom"  
             Text="{Binding ElementName=lstBreaks, Path=SelectedItem.ID, UpdateSourceTrigger=PropertyChanged}"  
             Grid.Row="2" Grid.Column="1"/>
            <TextBox Name="txtBreakTitle" DockPanel.Dock="Bottom" 
             Text="{Binding ElementName=lstBreaks, Path=SelectedItem.Title, UpdateSourceTrigger=PropertyChanged}"  
             Grid.Row="2" Grid.Column="2"/>
            <ComboBox Name="cbxBreakType" DockPanel.Dock="Bottom" 
              Text="{Binding ElementName=lstBreaks, Path=SelectedItem.Description, UpdateSourceTrigger=PropertyChanged}"  
              Grid.Row="2" Grid.Column="3"/>
        </StackPanel>

    </Grid>


</Window>

Code behind:

using System;
using System.Data;
using System.Windows;
using System.Windows.Controls;

namespace WpfApplication2
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            CreateData();
            this.DataContext = Data;
        }


    public DataSet Data { get; set; }

    private void CreateData()
    {
        Data = new DataSet();
        Data.Tables.Add(CreateJobTable());
        Data.Tables.Add(CreateSessionsTable());
        Data.Tables.Add(CreateBreaks());

        DataRelation relation = GetJobSessionRelations();
        DataRelation relation2 = GetSessionBreakRelations();

        Data.Relations.AddRange(new[] {relation, relation2});
    }

    private DataTable CreateJobTable()
    {
        var jobs = new DataTable();
        jobs.TableName = "JobDetails";
        var col1 = new DataColumn("ID");
        var col2 = new DataColumn("Title");
        var col3 = new DataColumn("Description");

        col1.DataType = Type.GetType("System.Int32");
        col2.DataType = Type.GetType("System.String");
        col3.DataType = Type.GetType("System.String");

        jobs.Columns.Add(col1);
        jobs.Columns.Add(col2);
        jobs.Columns.Add(col3);

        DataRow row = jobs.NewRow();
        row["ID"] = 1;
        row["Title"] = "Job 1";
        row["Description"] = "Job Desc 1";
        jobs.Rows.Add(row);

        DataRow row2 = jobs.NewRow();
        row2["ID"] = 2;
        row2["Title"] = "Job 2";
        row2["Description"] = "Job Desc 2";
        jobs.Rows.Add(row2);

        return jobs;
    }

    private DataTable CreateSessionsTable()
    {
        var sessions = new DataTable();
        sessions.TableName = "SessionDetails";
        var col1 = new DataColumn("ID");
        var col2 = new DataColumn("Title");
        var col3 = new DataColumn("Description");
        var col4 = new DataColumn("JobID");

        col1.DataType = Type.GetType("System.Int32");
        col2.DataType = Type.GetType("System.String");
        col3.DataType = Type.GetType("System.String");
        col4.DataType = Type.GetType("System.Int32");

        sessions.Columns.Add(col1);
        sessions.Columns.Add(col2);
        sessions.Columns.Add(col3);
        sessions.Columns.Add(col4);

        DataRow row = sessions.NewRow();
        row["ID"] = 1;
        row["Title"] = "Session 1";
        row["Description"] = "Session Desc 1";
        row["JobID"] = 1;
        sessions.Rows.Add(row);

        DataRow row2 = sessions.NewRow();
        row2["ID"] = 2;
        row2["Title"] = "Session 2";
        row2["Description"] = "Session Desc 2";
        row2["JobID"] = 1;
        sessions.Rows.Add(row2);

        DataRow row3 = sessions.NewRow();
        row3["ID"] = 3;
        row3["Title"] = "Session 3";
        row3["Description"] = "Session Desc 3";
        row3["JobID"] = 2;
        sessions.Rows.Add(row3);

        DataRow row4 = sessions.NewRow();
        row4["ID"] = 4;
        row4["Title"] = "Session 4";
        row4["Description"] = "Session Desc 4";
        row4["JobID"] = 2;
        sessions.Rows.Add(row4);

        return sessions;
    }

    private DataTable CreateBreaks()
    {
        var breaks = new DataTable();
        breaks.TableName = "BreakDetails";
        var col1 = new DataColumn("ID");
        var col2 = new DataColumn("Title");
        var col3 = new DataColumn("Description");
        var col4 = new DataColumn("SessionID");

        col1.DataType = Type.GetType("System.Int32");
        col2.DataType = Type.GetType("System.String");
        col3.DataType = Type.GetType("System.String");
        col4.DataType = Type.GetType("System.Int32");

        breaks.Columns.Add(col1);
        breaks.Columns.Add(col2);
        breaks.Columns.Add(col3);
        breaks.Columns.Add(col4);

        DataRow row = breaks.NewRow();
        row["ID"] = 1;
        row["Title"] = "Break 1";
        row["Description"] = "Break Desc 1";
        row["SessionID"] = 1;
        breaks.Rows.Add(row);

        DataRow row2 = breaks.NewRow();
        row2["ID"] = 2;
        row2["Title"] = "Break 2";
        row2["Description"] = "Break Desc 2";
        row2["SessionID"] = 2;
        breaks.Rows.Add(row2);

        DataRow row3 = breaks.NewRow();
        row3["ID"] = 3;
        row3["Title"] = "Break 3";
        row3["Description"] = "Break Desc 3";
        row3["SessionID"] = 3;
        breaks.Rows.Add(row3);


        DataRow row4 = breaks.NewRow();
        row4["ID"] = 4;
        row4["Title"] = "Break 4";
        row4["Description"] = "Break Desc 4";
        row4["SessionID"] = 4;
        breaks.Rows.Add(row4);

        return breaks;
    }

    private DataRelation GetSessionBreakRelations()
    {
        return new DataRelation("relJobDetailsSessionDetails", Data.Tables["JobDetails"].Columns["ID"],
                                Data.Tables["SessionDetails"].Columns["JobID"]);
    }

    private DataRelation GetJobSessionRelations()
    {
        var dataRelation = new DataRelation("relSessionDetailsBreakDetails", Data.Tables["SessionDetails"].Columns["ID"],
                                            Data.Tables["BreakDetails"].Columns["SessionID"]);
        return dataRelation;
    }

    private void lstJobs_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {

    }

    private void lstSessions_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {

    }

    private void lstBreaks_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {

    }
    }
}

Upvotes: 2

Related Questions