根据从TextBoxes到DataGridItemCollectionViewSource的多个值应用过滤器

问题描述

我为数据库制作了一个工具,该工具显示带有某些过滤选项的项目。但是,在阅读了有关WPF和C#的更多信息之后。我使用了此https://www.codeproject.com/Articles/683429/Guide-to-WPF-DataGrid-formatting-using-bindings教程来修改我的应用程序以处理pv.exe

我之前曾用它进行过滤:

import {
  StyleSheet,Text,View,SafeAreaView,FlatList,Image,TouchableOpacity,Modal,TouchableHighlight,} from "react-native";

import { Colors } from "../colors/Colors";

const DATA = [
  {
    id: "1",title: "Calves and Hamstrings: one",section: "Stretches",image: require("../assets/S1.png"),modalTitle: "Calves and Hamstrings: one",modalDesc:
      "Lorem ipsum dolor sit amet,consectetur adipiscing elit,sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",},{
    id: "2",title: "Adductors: one",image: require("../assets/S2.png"),modalTitle: "Adductors: one",{
    id: "3",title: "Adductors: two",image: require("../assets/S3.png"),modalTitle: "Adductors: two",{
    id: "4",title: "Hip Flexors",image: require("../assets/S4.png"),modalTitle: "Hip Flexors",{
    id: "5",title: "Calves and Hamstrings: two",image: require("../assets/S5.png"),modalTitle: "Calves and Hamstrings: two",{
    id: "6",title: "Quads",image: require("../assets/S6.png"),modalTitle: "Quads",{
    id: "7",title: "Calves and Hamstrings: three",image: require("../assets/S7.png"),modalTitle: "Calves and Hamstrings: three",{
    id: "8",title: "glutes (Bum)",image: require("../assets/S8.png"),modalTitle: "glutes (Bum)",{
    id: "9",title: "Abs",image: require("../assets/S9.png"),modalTitle: "Abs",{
    id: "10",title: "Lats (Sides)",image: require("../assets/S10.png"),modalTitle: "Lats (Sides)",{
    id: "11",title: "Deltoid (Shoulder)",image: require("../assets/S11.png"),modalTitle: "Deltoid (Shoulder)",{
    id: "12",title: "Triceps",image: require("../assets/S12.png"),modalTitle: "Triceps",{
    id: "13",title: "pecs (Chest)",image: require("../assets/S13.png"),modalTitle: "pecs (Chest)",];

export default function StretchesScreen() {
  const [modalVisible,setModalVisible] = useState(false);
  const [modalDesc,setModalDesc] = useState([]);
  const [modalTitle,setModalTitle] = useState("");

  const Item = ({ title,image,section,modalTitle,modalDesc }) => (
    <View style={styles.item}>
      <Image source={image} style={styles.exerciseImage} />
      <View style={styles.detailSection}>
        <Text style={styles.title}>{title}</Text>
        <Text style={styles.section}>{section}</Text>
      </View>
      <TouchableOpacity
        style={styles.viewButton}
        onPress={() => {
          openSettingsModal(modalTitle,modalDesc);
        }}
      >
        <Text style={styles.viewText}>View</Text>
      </TouchableOpacity>
    </View>
  );

  const renderItem = ({ item }) => (
    <Item title={item.title} image={item.image} section={item.section} />
  );

  const openSettingsModal = (modalTitle,modalDesc) => {
    setModalTitle(modalTitle);
    setModalDesc(modalDesc);
    setModalVisible(!modalVisible);
  };

  return (
    <SafeAreaView style={styles.container}>
      <View style={styles.tabBackground}>
        <Text style={styles.subTitle}>EXERCISES: STRETCHES</Text>
      </View>
      <Modal
        animationType="slide"
        transparent={true}
        visible={modalVisible}
        onRequestClose={() => {
          Alert.alert("Modal has been closed.");
        }}
      >
        <View style={styles.centeredView}>
          <View style={styles.modalView}>
            <Text style={styles.modalText}>{modalTitle}</Text>
            <Text style={styles.modalText}>{modalDesc}</Text>
            <TouchableHighlight
              style={{ ...styles.openButton,backgroundColor: "#2196F3" }}
              onPress={() => {
                setModalVisible(!modalVisible);
              }}
            >
              <Text style={styles.textStyle}>Hide Modal</Text>
            </TouchableHighlight>
          </View>
        </View>
      </Modal>
      <FlatList
        data={DATA}
        renderItem={renderItem}
        keyExtractor={(item) => item.id}
      />
    </SafeAreaView>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,backgroundColor: "#fff",alignItems: "center",justifyContent: "center",tabBackground: {
    backgroundColor: Colors.primary,width: "100%",height: "10%",top: 0,left: 0,subTitle: {
    position: "absolute",color: "white",fontSize: 10,item: {
    padding: 20,backgroundColor: "#FFF",width: "90%",flex: 1,alignSelf: "center",flexDirection: "row",borderRadius: 5,borderBottomWidth: 1,borderColor: Colors.greyLight,exerciseImage: {
    width: "20%",height: 60,borderRadius: 30,marginRight: 30,detailSection: {
    width: "50%",marginRight: 10,title: {
    fontSize: 12,section: {
    fontSize: 10,viewButton: {
    height: 40,width: "30%",borderWidth: 1,borderColor: Colors.greyMedium,borderRadius: 3,viewText: {
    left: 0,centeredView: {
    flex: 1,marginTop: 22,modalView: {
    margin: 20,backgroundColor: "white",borderRadius: 20,padding: 35,shadowColor: "#000",shadowOffset: {
      width: 0,height: 2,shadowOpacity: 0.25,shadowRadius: 3.84,elevation: 5,openButton: {
    backgroundColor: "#F194FF",padding: 10,elevation: 2,textStyle: {
    color: "white",fontWeight: "bold",textAlign: "center",modalText: {
    textAlign: "center",});

然后这样:

ItemCollectionViewSource

但是看来我现在需要使用 public void FilterSetup() { try { string qry = null; _conditions["name"] = null; if (!string.IsNullOrEmpty(BusinessIDSearch.Text)) { qry = string.Format("LY Like '{0}%'",BusinessIDSearch.Text); } if (!string.IsNullOrEmpty(NameSearch.Text)) { if (!string.IsNullOrEmpty(qry)) qry += " AND "; qry += string.Format("HAKUNIMI Like '%{0}%'",NameSearch.Text); } if (!string.IsNullOrEmpty(GroupSearch.Text)) { if (!string.IsNullOrEmpty(qry)) qry += " AND "; qry += string.Format("KONSERNI Like '{0}%'",GroupSearch.Text); } if (!string.IsNullOrEmpty(LiinosIDSearch.Text)) { if (!string.IsNullOrEmpty(qry)) qry += " AND "; qry += string.Format("YRNRO Like '{0}%'",LiinosIDSearch.Text); } _conditions["name"] = qry; Updatefilter(); //LiinosFICount.Content = DataGrid1.Items.Count; } catch (Exception) { throw; } } 的另一种方法

这是文本框的XAML部分:

    private void Updatefilter()
    {
        try
        {
            var activeConditions = _conditions.Where(c => c.Value != null).Select(c => "(" + c.Value + ")");
            DataView dv = DataGrid1.ItemsSource as DataView;
            dv.RowFilter = string.Join(" AND ",activeConditions);
        }
        catch (Exception)
        {
            //MessageBox.Show(ex.Message);
        }
    }

这里是DataGrid的XAML:

ItemCollectionViewSource

我尝试了一些教程,但没有成功。是否有人可以提供任何类型的教程来建议或提供解决方案,我也可以对其他TextBox实施该解决方案?棘手的是我有几个TextBoxes,我需要根据TextBoxes中的值组合过滤查询

如您所见,我已尝试在此处应用绑定:

    <TextBox Style="{StaticResource TextBox_Style}" x:Name="GroupSearch" HorizontalAlignment="Left" Margin="414,121,0" textwrapping="Wrap" Text="" VerticalAlignment="Top" Width="90" Height="20" TextChanged="GroupSearch_TextChanged"/>
    <TextBox Style="{StaticResource TextBox_Style}" x:Name="NameSearch" HorizontalAlignment="Left" Margin="130,0" textwrapping="Wrap" Text="" VerticalAlignment="Top" Width="279" Height="20" TextChanged="NameSearch_TextChanged"/>
    <TextBox Style="{StaticResource TextBox_Style}" Text="{Binding FilterString,UpdateSourceTrigger=PropertyChanged}" x:Name="LiinosIDSearch" HorizontalAlignment="Left" Margin="20,0" textwrapping="Wrap" VerticalAlignment="Top" Width="105" Height="20"/>
    <TextBox Style="{StaticResource TextBox_Style}" x:Name="BusinessIDSearch" HorizontalAlignment="Left" Margin="509,0" textwrapping="Wrap" Text="" VerticalAlignment="Top" Width="192" Height="20" TextChanged="BusinessIDSearch_TextChanged"/>

我从一些教程中获得了此信息,并尝试将其修改为我的需要而没有成功:

    <DataGrid Margin="0,146,0" Background="{x:Null}" BorderBrush="{x:Null}"
              CanUserAddRows="False" CanUserDeleteRows="False" CanUserResizeRows="False" IsReadOnly="True" 
              HorizontalGridLinesBrush="#FF377A6C" VerticalGridLinesBrush="#FF377A6C" 
          DataContext="{StaticResource ItemCollectionViewSource}"
          ItemsSource="{Binding}"
          AutoGenerateColumns="False" FontFamily="Arial Nova" Foreground="White" >

        <DataGrid.RowStyle>
            <Style targettype="DataGridRow" >
                <Setter Property="Background" Value="{Binding YRNRO,Converter={StaticResource LiinosIDToBackgroundConverter}}" />
            </Style>
        </DataGrid.RowStyle>

        <DataGrid.Resources>
            <Style BasedOn="{StaticResource {x:Type DataGridColumnHeader}}" targettype="{x:Type DataGridColumnHeader}">
                <Setter Property="Background" Value="#377A6C" />
                <Setter Property="Foreground" Value="White" />
                <Setter Property="MinHeight" Value="20" />
            </Style>
            <SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" 
               Color="#5AC37E"/>
        </DataGrid.Resources>

        <DataGrid.Columns>
            <DataGridTextColumn Binding="{Binding YRNRO}">
                <DataGridTextColumn.Header>
                    <TextBlock Text="LIINOS ID" FontWeight="Bold" TextAlignment="Left"/>
                </DataGridTextColumn.Header>
            </DataGridTextColumn>
            <DataGridTextColumn Binding="{Binding HAKUNIMI}">
                <DataGridTextColumn.Header>
                    <TextBlock Text="SEARCH NAME" FontWeight="Bold" TextAlignment="Left"/>
                </DataGridTextColumn.Header>
            </DataGridTextColumn>
            <DataGridTextColumn Binding="{Binding KONSERNI}">
                <DataGridTextColumn.Header>
                    <TextBlock Text="GROUP" FontWeight="Bold" TextAlignment="Left"/>
                </DataGridTextColumn.Header>
            </DataGridTextColumn>
            <DataGridTextColumn Binding="{Binding LY}">
                <DataGridTextColumn.Header>
                    <TextBlock Text="BUSInesS ID" FontWeight="Bold" TextAlignment="Left"/>
                </DataGridTextColumn.Header>
            </DataGridTextColumn>
        </DataGrid.Columns>
    </DataGrid>

编辑:

我正在将当前单击按钮上的数据加载到DataGrid:

<TextBox Style="{StaticResource TextBox_Style}" Text="{Binding FilterString,0" textwrapping="Wrap" VerticalAlignment="Top" Width="105" Height="20"/>

编辑2:

这是我一直在使用的using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Data; using System.Linq; using System.Windows.Data; namespace Liinos_inspector_FilterTest { public class viewmodel : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; private string _filter1 = ""; private string _filter2 = ""; private string _filter3 = ""; public viewmodel() { var List = MainProcess.CustomersInLiinos.AsEnumerable() .GroupBy(x => x.Field<string>("HAKUNIMI")) .Where(x => x.Count() > 1) .SelectMany(x => x) .ToList(); //ItemList = new ObservableCollection<Items>(List); ItemView = (CollectionView)CollectionViewSource.Getdefaultview(List); ItemView.Filter = TextFilter; } private bool TextFilter(object obj) { var data = obj as Items; if (data != null) { return data.Text1.StartsWith(_filter1) && data.Text2.StartsWith(_filter2) && data.Text3.StartsWith(_filter3); } return false; } private void NotifyPropertyChanged(string property) { PropertyChanged?.Invoke(this,new PropertyChangedEventArgs(property)); } public ObservableCollection<Items> ItemList { get; set; } public CollectionView ItemView { get; set; } public string Filter1 { get { return _filter1; } set { _filter1 = value; NotifyPropertyChanged("Filter1"); ItemView.Refresh(); } } public string Filter2 { get { return _filter2; } set { _filter2 = value; NotifyPropertyChanged("Filter2"); ItemView.Refresh(); } } public string Filter3 { get { return _filter3; } set { _filter3 = value; NotifyPropertyChanged("Filter3"); ItemView.Refresh(); } } } public class Items { public string Text1 { get; set; } public string Text2 { get; set; } public string Text3 { get; set; } } } 方法

    private async void Button_Click_1(object sender,RoutedEventArgs e)
    {

        if (MainProcess.CheckForVPNInterface() == true)
        {
            if (MainProcess.Customers != null)
            {
                ProgressBar.IsIndeterminate = true;

                CollectionViewSource itemCollectionViewSource;
                itemCollectionViewSource = (CollectionViewSource)(FindResource("ItemCollectionViewSource"));
                itemCollectionViewSource.source = await LoadMainTableDataAsync();

                ProgressBar.IsIndeterminate = false;

            }
        }
        else
        {
            string caption = "VPN connection missing";

            MessageBox.Show("Please,check your VPN connection!",caption,MessageBoxButton.OK,MessageBoxImage.Exclamation);
        }
    }

这里是数据表的合并:

LoadMainTableDataAsync

解决方法

您不需要CollectionViewSource即可过滤集合。只需绑定到视图模型的集合即可。在WPF中,集合是通过引擎盖下的ICollectionsView消耗的。每个集合都返回一个默认视图。控件在此视图上自动运行。要更改顺序,分组或过滤,请修改集合的当前视图,而不是实际的集合实例。 另外,请勿将DataContext显式设置为CollectionViewSource。直接绑定到它。

请注意,由于绑定到模型项,Items应该实现INotifyPropertyChanged或引发内存泄漏。

请勿在视图模型中处理ProgressBar。设置一个bool属性并将其绑定到ProgressBar.Visibility

为了处理异常,不会在视图模型中显示MessageBox或任何用户对话框。而是抛出一个有意义的完全自定义异常或包装异常(原始异常作为内部异常)并在您的视图中处理它,例如,通过显示一个交互对话框。

在您的视图中不处理数据库或任何其他数据(模型)访问。而是实现ICommand并将其分配给按钮以替换事件处理程序,并在视图模型中执行操作。有关RelayCommand的简单实现,请参见Microsoft Docs: Relaying Command Logic

这是一个基本示例,其中介绍了如何使用集合的默认ICollectionView来过滤集合,并结合了建议的改进。您需要根据需要调整实际的过滤器逻辑:

VpnInterfaceException.cs

class VpnInterfaceException : Exception
{
  public VpnInterfaceException(string message) : base(message)
  {
  }

  public VpnInterfaceException(string message,Exception innerException) : base(message,innerException)
  {
  }
}

Item.cs

class Item : INotifyPropertyChanged
{
  private string text;   
  public string Text 
  {
    get => this.text;
    set 
    { 
      this.text = value; 
      OnPropertyChanged();
    }
  }

  public event PropertyChangedEventHandler PropertyChanged;
  protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
  {
    this.PropertyChanged?.Invoke(this,new PropertyChangedEventArgs(propertyName));
  }
}

ViewModel.cs

class ViewModel : INotifyPropertyChanged
{
  public ObservableCollection<Item> Items { get; set; }
  public ICommand LoadMainTableDataCommand => new RelayCommand(ExecuteLoadMainTableDataAsync);

  // Binding source for the TextBox
  private string searchKey;   
  public string SearchKey
  {
    get => this.searchKey;
    set 
    { 
      this.searchKey = value; 
      OnPropertyChanged();  
      
     // Refresh the ICollectionView to update the filter expression
      CollectionViewSource.GetDefaultView(this.Items).Refresh();
    }
  }

  private bool hasProgress;   
  public bool HasProgress
  {
    get => this.hasProgress;
    set 
    { 
      this.hasProgress = value; 
      OnPropertyChanged();
    }
  }

  public ViewModel()
  {
    this.Items = new ObservableCollection<Item>();
    EnableItemsFiltering();
  }

  public void EnableItemsFiltering()
  {
    // Assign the filter expression which is executed when items are added 
    // or the 'ICollectionView.Refresh()'  was called
    CollectionViewSource.GetDefaultView(this.Items).Filter = FilterPredicate;
  }

  // The filter expression.
  // Returns 'true' to include the current item.
  private bool FilterPredicate(object item) 
    => string.IsNullOrWhiteSpace(this.SearchKey) 
       || ((item as Item)?.Text.StartsWith(this.SearchKey,StringComparison.OrdinalIgnoreCase) ?? false);

  private async Task ExecuteLoadMainTableDataAsync(object commandParameter)
  {
    if (MainProcess.CheckForVPNInterface())
    {
      if (MainProcess.Customers != null)
      {
        this.HasProgress = true;

        IEnumerable<Item> resultItems = await LoadMainTableDataAsync();
        this.Items = new ObservableCollection<Item>(resultItems);
        EnableItemsFiltering();

        this.HasProgress = false;
      }
    }
    else
    {
      // Throw an exception since the operation cannot be completed unexpectedly. 
      // The view can catch this exception to execute the error handling.
      throw new VpnInterfaceException("Please,check your VPN connection!");
    }
  }

  public event PropertyChangedEventHandler PropertyChanged;
  protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    => this.PropertyChanged?.Invoke(this,new PropertyChangedEventArgs(propertyName));
}

MainWindow.xaml.cs

partial class MainWindow : Window
{
  public MainWindow()
  {
    InitializeComponent();
      
    // Handle specific view model exceptions for user interaction
    this.Dispatcher.UnhandledException += (sender,args) =>
    {
      // Only handle selected exceptions,that can be handled through user interaction
      if (args.Exception is VpnInterfaceException exception)
      {
        args.Handled = true;
        string caption = "VPN connection missing";
 
        // Convert exception message to user friendly message
        MessageBox.Show(exception.Message,caption,MessageBoxButton.OK,MessageBoxImage.Exclamation);
      }
    };
  }
}

MainWindow.xaml

<Window>
  <Window.DataContext>
    <ViewModel />
  </Window.DataContext>

  <Window.Resources>
    <BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
  </Window.Resources>
 
  <StackPanel>
    <ProgressBar IsIndeterminate="True"
                 Visibility="{Binding HasProgress,Converter={StaticResource BooleanToVisibilityConverter}}" />

    <Button Command="{Binding LoadMainTableDataCommand}" 
            Content="Load Data" />

    <TextBox Text="{Binding SearchKey}" />
    <DataGrid ItemsSource="{Binding Items}" />
  <StackPanel>
</Window>