【问题标题】:Filtering bound controls in WPF过滤 WPF 中的绑定控件
【发布时间】:2011-11-30 12:56:30
【问题描述】:

我在用户控件中有以下 XAML:

<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" />

这可行,但现在我想做的是根据第一个框过滤第二个框;因此,选择服务器时,将过滤实例以仅显示该服务器的实例。

有没有办法做到这一点而不必手动填充第二个控件?

【问题讨论】:

    标签: .net wpf xaml data-binding


    【解决方案1】:

    通常您可以使用 CollectionViewSource 进行这样的过滤,但不幸的是,这似乎在这里不起作用,因为 SqlDataSourceEnumerator.GetDataSources() 返回一个 DataTable,而与这些一起使用的集合视图类型似乎并不支持过滤。

    正如我相信其他一些答案所暗示的那样,最好的方法可能是将 ObjectDataProvider 替换为您自己的可以适当过滤事物的视图模型类。如果特定服务器有多个实例,这还可以让您执行过滤重复服务器名称等操作。

    以下内容可能对您有用,或者至少可以帮助您入门:

    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));
        }
    }
    

    然后您需要按如下方式更改您的 XAML(将 ViewModel 上的命名空间声明替换为适合您项目的内容):

    <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}" />
    

    请注意,您将 cboServer 上的 ItemsSource 和 SelectedItem 属性绑定到 ViewModel 上的属性,并且 SelectedItem 绑定是双向的。这会将选定的服务器名称反馈给 ViewModel,然后 ViewModel 将通知 WPF Instances 属性已更改。 Instances 属性会过滤掉任何与所选服务器名称不匹配的行。

    【讨论】:

      【解决方案2】:

      您需要将第一个 ComboBox 的选定值绑定到 ObjectDataProvider 上的参数,以将其提供给选定的服务器。要查看这方面的一个很好的例子,请查看 Paul Sheriff 博客上的 this article

      【讨论】:

        【解决方案3】:

        您的 ComboBox 已绑定到 ObjectDataProvider 的一个实例。
        要过滤第一个 ComboBox 必须更新代表 SelectedServer 的属性,第二个 ComboBox 您必须绑定到代表您想要的特定过滤实例列表的属性。 在您的 XAML 中:

        <ComboBox Name="cboServer"
                  ItemsSource="{Binding Source={StaticResource DataSources}, Mode=TwoWay, 
                      Path=SelectedServer}" />
        
        
        <ComboBox Name="cboInstance" 
                  ItemsSource="{Binding Source={StaticResource DataSources}, Mode=OneWay,
                      Path=FilteredInstances}" />
        

        在 ObjectDataProvider 类中:

            // get a list a instances of the current selected server
            public IEnumerable<string> FilteredInstances
            {
                get
                {
                    return SelectedServer.Instances;
                }
            }
        
            private Server _selectedServer;
            public Server SelectedServer
            {
                get
                {
                    return _selectedServer;
                }
                set
                {
                    _selectedServer = value;
                    OnPropertyChanged("SelectedServer");
                    // update the list of instances when the selected server changes
                    OnPropertyChanged("FilteredInstances");
                }
            }
        

        我希望我的解释尽可能清楚。

        【讨论】:

        • 我可能在这里遗漏了一些东西,但这似乎没有使用 SqlDataSourceEnumerator.GetDataSources()。您是否建议我在 this 和 WPF 绑定之间实现第二个类?
        • 我的示例使用 DataSources,它是您的 ObjectDataProvider。无需实现另一个类,只需向您的 ViewModel 添加一些属性。如果没有,可以使用 UserControl 的代码隐藏。
        • 我没有,我实际上只是使用 SqlDataSourceEnumerator 作为数据源。那么如何在后面的代码(或视图模型或任何地方)中扩展它?
        • 在 UserControl 的构造函数中将 View 定义为 View 本身的 VM: this.DataContext = this;并在后面的UserControl代码中创建属性FilteredInstances和SelectedServer。
        猜你喜欢
        • 1970-01-01
        • 2017-07-05
        • 1970-01-01
        • 2013-06-20
        • 1970-01-01
        • 1970-01-01
        • 2023-03-16
        • 2020-12-04
        • 2023-03-06
        相关资源
        最近更新 更多