I could do it with a call to the data base specifying the DISTINCT keyword and bind my drop down list to the results, but LINQ has a distinct method so I could utilize that against the entire data table.
I don't really like this approach because you end up with LINQ generic enumerables that are difficult to navigate. But it does work up to a point.
I wrote this demo against the AdventureWorks database from Microsoft. Start a new WPF application using Framework 4.0 or later and C#. I'm using Visual Studio 2019. Call the solution BindToAnonymousClasses.
The MainWindow simply defines a drop down list and a data grid. The application starts by loading a data table of Employees from the AdventureWorks' vEmployee view and populating the drop down list with distinct job titles. Whenever the user selects a job title, all the employees with that job title are listed in the grid. The XAML is trivial.
<Window x:Class="BindToAnonymousClasses.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:BindToAnonymousClasses"
mc:Ignorable="d"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
Title="MainWindow" SizeToContent="WidthAndHeight">
<StackPanel Orientation="Vertical">
<ComboBox ItemsSource="{Binding JobTitles}"
DisplayMemberPath="JobTitle" SelectedValuePath="JobTitle" SelectedValue="{Binding SelectedJobTitle}" IsEditable="False" Width="300"/>
<DataGrid ItemsSource="{Binding DisplayEmployees}" AutoGenerateColumns="True" Height="200"/>
</StackPanel>
</Window>
using System.ComponentModel;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Windows;
namespace BindToAnonymousClasses
{
public partial class MainWindow : Window, INotifyPropertyChanged
{
public DataTable Employees;
public object
DisplayEmployees { get; set; }
public object
JobTitles { get; set; }
private string
_SelectedJobTitle;
public string
SelectedJobTitle
{
get { return
_SelectedJobTitle; }
set
{
_SelectedJobTitle = value;
DisplayEmployees = from e in Employees.Select("JobTitle='" + SelectedJobTitle + "'")
select new { FirstName = e["FirstName"], LastName = e["LastName"], JobTitle = e["JobTitle"] };
NotifyPropertyChanged("DisplayEmployees");
}
}
public MainWindow()
{
Employees = LoadEmployees();
JobTitles = from jt in (Employees.Select().Select((r) => r["JobTitle"]).Distinct().OrderBy((jt)
=> jt))
select new { JobTitle = jt };
InitializeComponent();
}
private DataTable LoadEmployees()
{
SqlDataAdapter da;
DataTable dt = new DataTable();
string sConn = "Server=localhost;Database=AdventureWorks2017;Trusted_Connection=True;";
string sSQL = "SELECT * FROM
[HumanResources].[vEmployee]";
SqlConnection oConn;
oConn = new SqlConnection(sConn);
oConn.Open();
da = new SqlDataAdapter(sSQL, oConn);
da.Fill(dt);
oConn.Close();
return dt;
}
public event
PropertyChangedEventHandler PropertyChanged;
public void
NotifyPropertyChanged(string name)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
}
Convert the data table to an enumeration of data rows.
Employees.Select()
Then project the enumeration of data rows into an enumeration of objects containing job titles.
Employees.Select().Select((r) => r["JobTitle"])
Get an enumeration of distinct job titles.
Employees.Select().Select((r) => r["JobTitle"]).Distinct()
Sort the enumeration in ascending sequence
Employees.Select().Select((r) => r["JobTitle"]).Distinct().OrderBy((jt) => jt)
Once we have the list, we can select from it creating a new anonymous class with a single property called JobTitle. Note it is easiest to declare JobTitles as a var or object because the class has no name. WPF binding figures it out.
JobTitles = from jt in (Employees.Select().Select((r) => r["JobTitle"]).Distinct().OrderBy((jt) => jt))
select new { JobTitle = jt };
We can use a similar technique whenever the selected job title changes to populate an enumerable of a class with multiple properties.
DisplayEmployees = from e in Employees.Select("JobTitle='" + SelectedJobTitle + "'")
select new { FirstName = e["FirstName"], LastName = e["LastName"], JobTitle = e["JobTitle"] };
No comments:
Post a Comment