c# - 在 WPF 应用程序中显示和筛选 XML 数据

标签 c# xml wpf datagrid filter

我正在创建一个非常简单的 WPF 应用程序,它从 XML 文件加载数据并将其显示为表格。但是,我需要使表可以自由文本过滤(按任何值、任何列)。问题是 XML 数据将具有未知数量的属性,因此我无法将数据转换为已知/具体的对象(请注意,对于任何给定的 XML 文件,属性的数量将始终相同)。

我最初尝试了来自 this post的代码,但发现由于我无法将 XML 数据转换为已知对象,因此我无法在 DataGridCollectionView 上使用 Filter 谓词方法(当我传入 DataSet 时,CanFilter 方法设置为 false) 。因此,我发现我必须使用接受 CustomFilter 的 BindingListCollectionView,但它是一个“笨拙”的 SQL 类型过滤器。

这是一个适用于此目的的随机 XML 文件。

<products>
  <product Name="Widget" Amount="123.45" Size="10 in" SomeOtherValue="foo" />
  <product Name="Screw" Amount="8.52" Size="5.1 cm" SomeOtherValue="bar" />
  <product Name="Bolt" Amount="2.66" Size="4 in" SomeOtherValue="con" />
  <product Name="Hinge" Amount="14.00" Size="22 cm" SomeOtherValue="toso" />
</products>

下面的代码可以工作(在某种程度上),但看起来真的很脏。另外,它的行为很奇怪,因为如果我提供无效的过滤器值“xxx”,则不会返回任何结果(正常),但对于“xxxx”,会返回所有结果,而对于“xxxxx”,则不会返回任何结果。这是我需要帮助的 FilterString 方法。我怎样才能清理这段代码并消除我的异常过滤行为?

public partial class MainWindow : INotifyPropertyChanged
{

    private BindingListCollectionView _bindingListCollection;
    private string _filterString;

    public MainWindow()
    {
        InitializeComponent();
        var dataSet = new DataSet();
        dataSet.ReadXml(@"D:\index.xml");
        BindingListCollection = (BindingListCollectionView)CollectionViewSource.GetDefaultView(dataSet.Tables[0]);
        BindingListCollection.CustomFilter = null;
    }

    public BindingListCollectionView BindingListCollection
    {
        get { return _bindingListCollection; }
        set { _bindingListCollection = value; NotifyPropertyChanged("BindingListCollection"); }
    }

    public string FilterString
    {
        get { return _filterString; }
        set
        {
            _filterString = value;
            if (_bindingListCollection == null) return;
            var filterValue = string.Empty;
            foreach (DataRowView dataRowView in _bindingListCollection.SourceCollection)
            {
                // Loop through each column in the underlying data and manually create a SQL filter
                foreach (var columnName in dataRowView.DataView.DataViewManager.DataSet.Tables[0].Columns)
                {
                    filterValue += string.Format("{0} LIKE '%" + _filterString + "%' OR ", columnName);
                }
                // I don't want to iterate through the rows, I just need to get to the raw table columns, so I break here
                break; 
            }

            if (filterValue == string.Empty)
            {
                BindingListCollection.CustomFilter = null;
            }
            else
            {
                // add something so the last "OR" doesn't throw things off
                BindingListCollection.CustomFilter = filterValue + " 0 = 1";
            }

            NotifyPropertyChanged("FilterString");
            _bindingListCollection.Refresh();
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    private void NotifyPropertyChanged(string property)
    {
        if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(property)); }
    }

}

如果您希望它帮助排除故障,这里还有演示代码

<Window x:Class="MyViewer.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="368" Width="1002" Name="UI" >
    <StackPanel DataContext="{Binding ElementName=UI}">
        <TextBox Text="{Binding FilterString, UpdateSourceTrigger=PropertyChanged}" />
        <DataGrid ItemsSource="{Binding BindingListCollection}" />
    </StackPanel>
</Window>

最佳答案

我认为你可以使用ObservableCollection<DataRow> ,并过滤DataSet使用DataRow.ItemArray 。类似于:

我将使用 LINQ 查询原始数据,生成的结果为 IEnumerable<DataRow> ,然后将其转换为DataTable使用 DataTableExtensions.CopyToDataTable 绑定(bind) View 方法。

//Warning not tested.....

public partial class MainWindow : INotifyPropertyChanged
{
private DataSet _dataSet;
private string _filterString;

public MainWindow()
{
    InitializeComponent();
    _dataSet = new DataSet();
    _dataSet.ReadXml(@"D:\index.xml");
    FilterString=null;

}

public DataTable BindingListCollection
{
    get {
         return FilteredList.CopyToDataTable();
        }
}

public IEnumerable<DataRow> FilteredList
{
    get {
         //may need to check _dataSet is not null 
         return  string.IsNullOrEmpty(FilterString)?                        
                    from DataRow dr in _dataSet.Tables[0].Rows select dr
                   :from DataRow dr in _dataSet.Tables[0].Rows
                         where dr.ItemArray.Count(c => c.ToString().IndexOf(FilterString,StringComparison.InvariantCultureIgnoreCase)>=0) > 0
                         select dr;
        }
}

public string FilterString
{
    get { return _filterString; }
    set
    {
        _filterString = value;

        NotifyPropertyChanged("FilterString");
        NotifyPropertyChanged("BindingListCollection");
    }
}

public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string property)
{
    if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(property)); }
}

}

关于c# - 在 WPF 应用程序中显示和筛选 XML 数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26027345/

相关文章:

android - 使用 SAX 解析递归 XML

xml - 更改文件中的数据

wpf 最小高度和高度

c# - 运行 SQL 语句时出现页面 404,然后重定向

c# - Entity Framework 枚举 SqlQuery 结果

python - 从文本文件中删除以 Python 中的特定表达式开头的单词

c# - ViewModels和UI

c# - 在运行时将所有事件处理程序从一个控件复制到另一个控件

c# - 如何返回用户在 PC 中使用的当前文化

c# - 从 C# 中的较长字符串解析此数字的最佳方法是什么?