我正在创建一个非常简单的 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/