Я работаю над созданием собственного 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>
Он просто добавляет кнопку фильтра в заголовок столбца, который использует контекстное меню для фактического отображения параметров фильтра.
выглядит так
Тогда код для пользовательского элемента управления это ... ну, просто часть этого
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, если он работает, я добавляю в фильтр все параметры, которые есть в моем пользовательском элементе управления.
Все работает, кроме случаев, когда я пытаюсь отфильтровать несколько столбцов. Я считаю, что это потому, что метод фильтра в пользовательском элементе управления является тем же именем, даже если это другой экземпляр. чтобы убедиться, что я скопировал свой пользовательский элемент управления и изменил его имя, и когда я применил фильтр к двум столбцам (каждый из которых использует свою копию элемента управления), фильтрация работала как ожидалось.
Я ищу некоторые идеи или предложения о том, как заставить это работать. Идея здесь состояла в том, чтобы фильтровать все в пользовательском элементе управления, чтобы я мог просто использовать его всякий раз, когда мне это нужно. Я чувствовал себя так близко. Пожалуйста помоги.