пользовательский элемент управления для автофильтра данных в столбцах с несколькими столбцами - PullRequest
0 голосов
/ 07 сентября 2018

Я работаю над созданием собственного Excel-подобного автофильтра для сетки данных. Я хотел иметь возможность повторно использовать код. Я почти закончил с тем, чтобы заставить его работать, но столкнулся с проблемой .. я объясню, что у меня есть и где проблема возникает

Во-первых, у меня есть простая форма wpf с сеткой данных и некоторыми тестовыми столбцами, я сделал простой testdatalist и в XAML добавил его в качестве источника коллекции для связывания

- отредактировано за комментарий, чтобы предоставить только необходимый код. -

Вот некоторые строки из XAML

        xmlns:local="clr-namespace:WPFFilterDesign"

    <local:testDatalist x:Key="testdatalist"/>
    <CollectionViewSource x:Key="cvstestdatalist" Source="{StaticResource testdatalist}"></CollectionViewSource>

<DataGrid.Columns>
    <DataGridTextColumn Binding="{Binding Path=propTest}">
        <DataGridTextColumn.HeaderStyle>
              <Style TargetType="{x:Type DataGridColumnHeader}">
                  <Setter Property="HorizontalContentAlignment" Value="Stretch" />
               </Style>
         </DataGridTextColumn.HeaderStyle>
         <DataGridTextColumn.Header>
              <local:textFilterControl x:Name="testcol1" HeadingName="Test"/>
          </DataGridTextColumn.Header>
      </DataGridTextColumn>
            <DataGridTextColumn Binding="{Binding Path=propStatus}">
                <DataGridTextColumn.HeaderStyle>
                    <Style TargetType="{x:Type DataGridColumnHeader}">
                        <Setter Property="HorizontalContentAlignment" Value="Stretch" />
                    </Style>
                </DataGridTextColumn.HeaderStyle>
                <DataGridTextColumn.Header>
                    <local:textFilterControl x:Name="testStatus" HeadingName="Status"/>
                </DataGridTextColumn.Header>
            </DataGridTextColumn>
         </DataGrid.Columns>
    </DataGrid>

и вот тот же код позади .. Я вырезал и упростил некоторые вещи

public class testData
{
    public string propTest { get; set; }
    public string propStatus { get; set; }
    public string propTest2 { get; set; }
}

public class testDatalist : ObservableCollection<testData>
{
    // Creating the Tasks collection in this way enables data binding from XAML.
}

public partial class MainWindow : Window
{
    public static CollectionViewSource CVS_DG { get; set; }
    public MainWindow()
    {
        InitializeComponent();
        //get a referenceto the collection
        testDatalist _testdatalist = (testDatalist)this.Resources["testdatalist"];
        CVS_DG = (CollectionViewSource)(this.Resources["cvstestdatalist"]);
        //add some data 
        testData temp = new testData() ;
        temp.propTest = "testasdfa";
        temp.propStatus = "Sold";
        temp.propTest2 = "sadfa";
        _testdatalist.Add(temp);
        _testdatalist.Add(new testData() { propTest = "test1", propStatus = "Active", propTest2 = "afsa" });
        _testdatalist.Add(new testData() { propTest = "test8", propStatus = "Sold", propTest2 = "afdfsa" });
        _testdatalist.Add(new testData() { propTest = "test6", propStatus = "Sold", propTest2 = "afdahhfsa" });
        _testdatalist.Add(new testData() { propTest = "test4", propStatus = "Complete", propTest2 = "affjsa" });
        _testdatalist.Add(new testData() { propTest = "test9", propStatus = "Canceled", propTest2 = "afytsa" });
        _testdatalist.Add(new testData() { propTest = "test8", propStatus = "Sold", propTest2 = "afsfdgytresa" });
        _testdatalist.Add(new testData() { propTest = "test2", propStatus = "Active", propTest2 = "afferg5463sa" });
        _testdatalist.Add(new testData() { propTest = "test3", propStatus = "Canceled", propTest2 = "a699kljfsa" });
        _testdatalist.Add(new testData() { propTest = "test7", propStatus = "Active", propTest2 = "afsgfha" });
        _testdatalist.Add(new testData() { propTest = "test2", propStatus = "Canceled", propTest2 = "afsatryrwe-0" });
        _testdatalist.Add(new testData() { propTest = "test9", propStatus = "Active", propTest2 = "afshghjaq21a" });
    }
}

Этот материал довольно стандартный. но в XAML у меня есть три столбца в сетке данных, и для каждого столбца я добавляю свой пользовательский элемент управления "textFilterControl"

Вот пользовательский элемент управления XAML

<UserControl x:Class="WPFFilterDesign.textFilterControl"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         xmlns:local="clr-namespace:WPFFilterDesign"
         mc:Ignorable="d" 
         d:DesignHeight="20" d:DesignWidth="60">
<Grid Margin="0">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*"/>
        <ColumnDefinition Width="16"/>
    </Grid.ColumnDefinitions>
    <TextBlock Grid.Column="0" Text="{Binding HeadingName}" />
    <Button Grid.Column="1" x:Name="btnFilter"  Margin="3,0,0,0" HorizontalAlignment="Right" ContextMenuService.IsEnabled="False" Click="btnFilter_Click">
        <Image Name="filterImage" Source="Resources/filternotapplied.png" />
        <Button.ContextMenu>
            <ContextMenu Name="CtxMenuText" Closed="CtxMenuText_Closed" Opened="CtxMenuText_Opened">
                <ContextMenu.Template>
                    <ControlTemplate>
                        <Border BorderBrush="Black" BorderThickness="1" Width="120" Height="180">
                            <Grid  Background="White">
                                <Grid.RowDefinitions>
                                    <RowDefinition Height="30" />
                                    <RowDefinition Height="30" />
                                    <RowDefinition Height="30"/>
                                    <RowDefinition Height="30"/>
                                    <RowDefinition Height="30"/>
                                    <RowDefinition Height="30"/>
                                </Grid.RowDefinitions>
                                <ComboBox Name="Filter1Type" Grid.Row="0" HorizontalAlignment="Stretch" Margin="5,3,5,3" SelectedIndex="0">
                                    <ComboBoxItem Content="Starts with"/>
                                    <ComboBoxItem Content="Ends with"/>
                                    <ComboBoxItem Content="Contains"/>
                                    <ComboBoxItem Content="Does not contain"/>
                                    <ComboBoxItem Content="Equals"/>
                                    <ComboBoxItem Content="Does not equal"/>
                                </ComboBox>
                                <TextBox Name="Filter1Value" HorizontalAlignment="Stretch" Grid.Row="1"  Margin="5,3,5,3" TextChanged="Filter1Value_TextChanged"/>
                                <ComboBox Name="FilterCombineOperator" Grid.Row="2" Width="60" HorizontalAlignment="Left" SelectedIndex="0"  Margin="5,3,5,3">
                                    <ComboBoxItem Content="Or"/>
                                    <ComboBoxItem Content="And"/>
                                </ComboBox>
                                <ComboBox Name="Filter2Type" Grid.Row="3" HorizontalAlignment="Stretch" SelectedIndex="0" Margin="5,3,5,3" IsEnabled="False">
                                    <ComboBoxItem Content="Starts with"/>
                                    <ComboBoxItem Content="Ends with"/>
                                    <ComboBoxItem Content="Contains"/>
                                    <ComboBoxItem Content="Does not contain"/>
                                    <ComboBoxItem Content="Equals"/>
                                    <ComboBoxItem Content="Does not equal"/>
                                </ComboBox>
                                <TextBox Name="Filter2Value" HorizontalAlignment="Stretch" Grid.Row="4"  Margin="5,3,5,3" IsEnabled="False"></TextBox>
                                <Button Name="btnApply" Content="Apply" Grid.Row="5" Width="50" Margin="5,3,5,5" HorizontalAlignment="Left" Click="btnApply_Click"></Button>
                                <Button Name="btnClear" Content="Clear" Grid.Row="5" Width="50" Margin="5,3,5,5" HorizontalAlignment="Right" Click="btnClear_Click"></Button>
                                <CheckBox Name="checkboxFiltered" Grid.Row="5" Visibility="Hidden"></CheckBox>
                            </Grid>
                        </Border>
                    </ControlTemplate>
                </ContextMenu.Template>
            </ContextMenu>
        </Button.ContextMenu>
    </Button>
</Grid>
</UserControl>

Он просто добавляет кнопку фильтра в заголовок столбца, который использует контекстное меню для фактического отображения параметров фильтра.

выглядит так

enter image description here

Тогда код для пользовательского элемента управления это ... ну, просто часть этого

public partial class textFilterControl : UserControl
{
    //.....
    private static int tempfilter1index;
    private static string tempfilter1Value;
    private static int tempOpteratorTypeIndex;
    private static int tempfilter2Index;
    private static string tempfilter2Value;
    private static bool filterApplied = false;
    private static string propname;

    //https://social.msdn.microsoft.com/Forums/vstudio/en-US/d9a8921f-27a3-4e81-b7fd-d6cf1e23bbdf/usercontrol-passing-parameter?forum=wpf
    public textFilterControl()
    {
        InitializeComponent();
        this.Loaded += new RoutedEventHandler(Filter_Loaded);
    }

    void Filter_Loaded(object sender, RoutedEventArgs e)
    {
    }

    private void btnFilter_Click(object sender, RoutedEventArgs e)
    {
        (sender as Button).ContextMenu.IsEnabled = true;
        (sender as Button).ContextMenu.PlacementTarget = (sender as Button);
        (sender as Button).ContextMenu.Placement = System.Windows.Controls.Primitives.PlacementMode.Bottom;
        (sender as Button).ContextMenu.IsOpen = true;
    }

    private void btnApply_Click(object sender, RoutedEventArgs e)
    {
        //get current values
        ComboBox filter1type = (ComboBox)CtxMenuText.Template.FindName("Filter1Type", CtxMenuText);
        tempfilter1index = filter1type.SelectedIndex;
        TextBox tboxFilter1 = (TextBox)CtxMenuText.Template.FindName("Filter1Value", CtxMenuText);
        tempfilter1Value = tboxFilter1.Text;
        ComboBox OperatorType = (ComboBox)CtxMenuText.Template.FindName("FilterCombineOperator", CtxMenuText);
        tempOpteratorTypeIndex = OperatorType.SelectedIndex;
        ComboBox filter2type = (ComboBox)CtxMenuText.Template.FindName("Filter2Type", CtxMenuText);
        tempfilter2Index = filter2type.SelectedIndex;
        TextBox tboxFilter2 = (TextBox)CtxMenuText.Template.FindName("Filter2Value", CtxMenuText);
        tempfilter2Value = tboxFilter2.Text;
        CheckBox isFiltered = (CheckBox)CtxMenuText.Template.FindName("checkboxFiltered", CtxMenuText);

        if (isFiltered.IsChecked == true)
        {
            filterApplied = true;
        }
        else
        {
            filterApplied = false;
        }

        //change filter image if there is a filter to apply            
        //get specific image
        var parent = this.Parent;
        System.Windows.Controls.Primitives.DataGridColumnHeader column = (System.Windows.Controls.Primitives.DataGridColumnHeader)parent;
        propname = column.Column.SortMemberPath;

        //add filter 
        if(filterApplied == true)
        {
            MainWindow.CVS_DG.Filter -= new FilterEventHandler(CVS_DG_Filter);
            MainWindow.CVS_DG.Filter += new FilterEventHandler(CVS_DG_Filter);
        }
        else
        {
            MainWindow.CVS_DG.Filter += new FilterEventHandler(CVS_DG_Filter);
            filterApplied = true;
        }

        //close context menu
        CtxMenuText.IsOpen = false;
    }

    private void CVS_DG_Filter(object sender, FilterEventArgs e)
    {
        dynamic src = e.Item;
        var propertyInfo = src.GetType().GetProperty(propname);
        var value = propertyInfo.GetValue(src, null);

        if (value.Contains(tempfilter1Value))
        {
        }
        else
        {
            e.Accepted = false;
        }
    }
}

В пользовательском элементе управления есть кнопка «Применить», которая затем добавляет фильтр для просмотра. на данный момент мой фильтр просто выполняет if, если он работает, я добавляю в фильтр все параметры, которые есть в моем пользовательском элементе управления.

Все работает, кроме случаев, когда я пытаюсь отфильтровать несколько столбцов. Я считаю, что это потому, что метод фильтра в пользовательском элементе управления является тем же именем, даже если это другой экземпляр. чтобы убедиться, что я скопировал свой пользовательский элемент управления и изменил его имя, и когда я применил фильтр к двум столбцам (каждый из которых использует свою копию элемента управления), фильтрация работала как ожидалось.

Я ищу некоторые идеи или предложения о том, как заставить это работать. Идея здесь состояла в том, чтобы фильтровать все в пользовательском элементе управления, чтобы я мог просто использовать его всякий раз, когда мне это нужно. Я чувствовал себя так близко. Пожалуйста помоги.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...