Filtering bound controls in WPF

I have the following XAML inside a user control:

<UserControl.Resources>
    <ObjectDataProvider x:Key="DataSources" 
       ObjectInstance="{x:Static data:Sql.SqlDataSourceEnumerator.Instance}" 
       MethodName="GetDataSources" 
       IsAsynchronous="True"/>
</UserControl.Resources>

  …

<ComboBox Name="cboServer" IsEditable="True" 
      ItemsSource="{Binding Source={StaticResource DataSources}, Mode=OneWay}"
      DisplayMemberPath="ServerName" />

<ComboBox Name="cboInstance" IsEditable="True" 
      ItemsSource="{Binding Source={StaticResource DataSources}, Mode=OneWay}"
      DisplayMemberPath="InstanceName" />

This works, but now what I want to do is to filter the second box based on the first; so when the server is selected, the instances are filtered to show only those for that server.

Is there a way to do this without having to manually populate the second control?

Answers


Normally you might be able to do filtering like this using a CollectionViewSource, but unfortunately it looks like this won't work here because SqlDataSourceEnumerator.GetDataSources() returns a DataTable, and the collection view type used with those doesn't seem to support filtering.

As I believe some of the other answers were suggesting, the best way to do this is probably to replace the ObjectDataProvider with your own view model class that can filter things appropriately. This will also let you do things like filtering out duplicate server names if a particular server has multiple instances.

Here's something that might work for you or at least get you started:

public class ViewModel : INotifyPropertyChanged
{
    private string _selectedServerName;
    private DataTable _dataSources;

    public IEnumerable<string> ServerNames
    {
        get
        {
            if (_dataSources == null)
            {
                _dataSources = SqlDataSourceEnumerator.Instance.GetDataSources();
            }
            return _dataSources.Rows.Cast<DataRow>()
                .Where(row => !row.IsNull("ServerName"))
                .Select(row => (string)row["ServerName"]).Distinct();
        }
    }

    public string SelectedServerName
    {
        get { return _selectedServerName; }
        set
        {
            _selectedServerName = value;
            NotifyOfPropertyChange("SelectedServerName");
            NotifyOfPropertyChange("Instances");
        }
    }

    public IEnumerable<string> Instances
    {
        get
        {
            if (_dataSources == null || _selectedServerName == null) return new string[0];
            return _dataSources.Rows.Cast<DataRow>()
                .Where(row => !row.IsNull("InstanceName") && _selectedServerName.Equals(row["ServerName"]))
                .Select(row => (string)row["InstanceName"]);
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    private void NotifyOfPropertyChange(string propertyName)
    {
        var handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }
}

You'd then need to change your XAML as follows (replacing the namespace declaration on the ViewModel with something appropriate for your project):

<UserControl.Resources>
    <WpfApplication1:ViewModel x:Key="ViewModel" />
</UserControl.Resources>

<ComboBox Name="cboServer"
          IsEditable="True" 
          ItemsSource="{Binding ServerNames, Source={StaticResource ViewModel}, Mode=OneWay, IsAsync=True}"
          SelectedItem="{Binding SelectedServerName, Source={StaticResource ViewModel}, Mode=TwoWay}"/>

<ComboBox Name="cboInstance" 
          IsEditable="True" 
          ItemsSource="{Binding Instances, Source={StaticResource ViewModel}, Mode=OneWay}" />

Note that you're binding both the ItemsSource and the SelectedItem properties on cboServer to properties on the ViewModel, and that the SelectedItem binding is two-way. This will feed the selected server name back to the ViewModel, which will then notify WPF that the Instances property has changed. The Instances property filters out any row that doesn't match the selected server name.


Need Your Help

MathJax Test Page Warning

javascript web latex mathjax

I download the latest MathJax package, and deploy it in my own website(IIS).

Linear discriminant analysis plot

r plot

How can I add the sample ID (row number) as labels to each point in this LDA plot?

About UNIX Resources Network

Original, collect and organize Developers related documents, information and materials, contains jQuery, Html, CSS, MySQL, .NET, ASP.NET, SQL, objective-c, iPhone, Ruby on Rails, C, SQL Server, Ruby, Arrays, Regex, ASP.NET MVC, WPF, XML, Ajax, DataBase, and so on.