Фильтровать элементы ListBox на основе текста TextBox, используя только XAML в WPF - PullRequest
7 голосов
/ 17 августа 2011

У меня в настоящее время ListBox привязан к коллекции элементов. Поскольку коллекция большая, мы хотим отфильтровать отображаемые элементы на основе текста, введенного в TextBox.

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

Надеюсь, это ясно,

спасибо!

Ответы [ 6 ]

6 голосов
/ 17 августа 2011

Вы можете использовать CollectionViewSource для применения фильтрации, другой пример можно найти здесь и здесь .

5 голосов
/ 07 ноября 2012

Как CodeNaked и devdigital сказали вам CollectionViewSource / CollectionView / ICollectionView ключи к вашей цели

Это паттер MVVM, но это проблема, связанная только с View, поэтому я не хочу этот код в ViewModel.

это не правильный путь, потому что вид показывает только то, что она получает, но не должна быть модифицирована так что должен / должен быть ваш ViewModel, кто меняет handel

так что теперь некоторые фрагменты кода:

    public class myVM
    {
        public CollectionViewSource CollViewSource { get; set; }
        public string SearchFilter
        {
            get;
            set
            {
              if(!string.IsNullOrEmpty(SearchFilter))
                 AddFilter();

                CollViewSource.View.Refresh(); // important to refresh your View
            }
        }
        public myVM(YourCollection)
        {
            CollViewSource = new CollectionViewSource();//onload of your VM class
            CollViewSource.Source = YourCollection;//after ini YourCollection
        }
    }

Xaml Snip:

    <StackPanel>
        <TextBox Height="23" HorizontalAlignment="Left"  Name="tB" VerticalAlignment="Top" 
                 Width="120" Text="{Binding SearchFilter,UpdateSourceTrigger=PropertyChanged}" />
        <DataGrid Name="testgrid" ItemsSource="{Binding CollViewSource.View}"/>
    </StackPanel>

Редактировать я забыл фильтр

private void AddFilter()
{
    CollViewSource.Filter -= new FilterEventHandler(Filter);
    CollViewSource.Filter += new FilterEventHandler(Filter);  

}

private void Filter(object sender, FilterEventArgs e)
{
    // see Notes on Filter Methods:
    var src = e.Item as YourCollectionItemTyp;
    if (src == null)
        e.Accepted = false;
    else if ( src.FirstName !=null && !src.FirstName.Contains(SearchFilter))// here is FirstName a Property in my YourCollectionItem
        e.Accepted = false;
}
2 голосов
/ 17 августа 2011

Нет способа сделать это только в XAML. Но есть и два других способа: 1) используя конвертер

<TextBox x:Name="text"/>
<ListBox Tag="{Binding ElementName=text}">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="Visibility" Value="{Binding RelativeSource={RelativeSource AncestorType=ListBox},Path=Tag, Converter={StaticResource filterLogicConverter}}"/>
</Style>
</ListBox.ItemContainerStyle>
<LixtBox/>

2) лучшим и более естественным способом является использование свойства CollectionView.Filter. Он не изменяет основную коллекцию.

var collectionView = CollectionViewSource.GetDefaultView(your_collection);
collectionView.Filter = filter_predicate
2 голосов
/ 17 августа 2011

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

1 голос
/ 17 августа 2011

Единственное, что на самом деле делает XAML, это инкапсулирует логику декларативным способом.Используя расширения разметки , вы можете сделать довольно много, вот пример:

<StackPanel>
    <StackPanel.Resources>
        <CollectionViewSource x:Key="items" Source="{Binding Data}">
            <CollectionViewSource.Filter>
                <me:Filter>
                    <me:PropertyFilter PropertyName="Name"
                            RegexPattern="{Binding Text, Source={x:Reference filterbox}}" />
                </me:Filter>
            </CollectionViewSource.Filter>
        </CollectionViewSource>
    </StackPanel.Resources>
    <TextBox Name="filterbox" Text="Skeet">
        <TextBox.TextChanged>
            <me:ExecuteActionsHandler ThrowOnException="false">
                <me:CallMethodAction>
                    <me:CallMethodActionSettings MethodName="Refresh"
                            TargetObject="{Binding Source={StaticResource items}}" />
                </me:CallMethodAction>
            </me:ExecuteActionsHandler>
        </TextBox.TextChanged>
    </TextBox>
    <!-- ListView here -->
</StackPanel>

( Обратите внимание, что это работает, но это отключит каждый дизайнер GUI, также нет IntelliSenseдля событий, которые обычно не устанавливаются с помощью синтаксиса элемента. )

Здесь имеется несколько расширений разметки, из которых два создают обработчики, а одно создает действие:

  • FilterExtension
  • ExecuteActionsHandlerExtension
  • CallMethodActionExtension

Расширения выглядят так:

[ContentProperty("Filters")]
class FilterExtension : MarkupExtension
{
    private readonly Collection<IFilter> _filters = new Collection<IFilter>();
    public ICollection<IFilter> Filters { get { return _filters; } }

    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        return new FilterEventHandler((s, e) =>
            {
                foreach (var filter in Filters)
                {
                    var res = filter.Filter(e.Item);
                    if (!res)
                    {
                        e.Accepted = false;
                        return;
                    }
                }
                e.Accepted = true;
            });
    }
}

public interface IFilter
{
    bool Filter(object item);
}

Довольно просто, просто проходит через фильтры и применяет их.То же самое относится к ExecuteActionsHandlerExtension:

[ContentProperty("Actions")]
public class ExecuteActionsHandlerExtension : MarkupExtension
{
    private readonly Collection<Action> _actions = new Collection<Action>();
    public Collection<Action> Actions { get { return _actions; } }

    public bool ThrowOnException { get; set; }

    public ExecuteActionsHandlerExtension()
    {
        ThrowOnException = true;
    }

    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        return new RoutedEventHandler((s, e) =>
            {
                try
                {
                    foreach (var action in Actions)
                    {
                        action.Invoke();
                    }
                }
                catch (Exception)
                {
                    if (ThrowOnException) throw;
                }
            });
    }
}

Теперь последнее расширение немного сложнее, так как ему действительно нужно сделать что-то конкретное:

[ContentProperty("Settings")]
public class CallMethodActionExtension : MarkupExtension
{
    //Needed to provide dependency properties as MarkupExtensions cannot have any
    public CallMethodActionSettings Settings { get; set; }

    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        return new Action(() =>
            {
                bool staticCall = Settings.TargetObject == null;
                var argsCast = Settings.MethodArguments.Cast<object>();
                var types = argsCast.Select(x => x.GetType()).ToArray();
                var args = argsCast.ToArray();
                MethodInfo method;
                if (staticCall)
                {
                    method = Settings.TargetType.GetMethod(Settings.MethodName, types);
                }
                else
                {
                    method = Settings.TargetObject.GetType().GetMethod(Settings.MethodName, types);
                }
                method.Invoke(Settings.TargetObject, args);
            });
    }
}

public class CallMethodActionSettings : DependencyObject
{
    public static readonly DependencyProperty MethodNameProperty =
        DependencyProperty.Register("MethodName", typeof(string), typeof(CallMethodActionSettings), new UIPropertyMetadata(null));
    public string MethodName
    {
        get { return (string)GetValue(MethodNameProperty); }
        set { SetValue(MethodNameProperty, value); }
    }

    public static readonly DependencyProperty TargetObjectProperty =
        DependencyProperty.Register("TargetObject", typeof(object), typeof(CallMethodActionSettings), new UIPropertyMetadata(null));
    public object TargetObject
    {
        get { return (object)GetValue(TargetObjectProperty); }
        set { SetValue(TargetObjectProperty, value); }
    }

    public static readonly DependencyProperty TargetTypeProperty =
        DependencyProperty.Register("TargetType", typeof(Type), typeof(CallMethodActionSettings), new UIPropertyMetadata(null));
    public Type TargetType
    {
        get { return (Type)GetValue(TargetTypeProperty); }
        set { SetValue(TargetTypeProperty, value); }
    }

    public static readonly DependencyProperty MethodArgumentsProperty =
        DependencyProperty.Register("MethodArguments", typeof(IList), typeof(CallMethodActionSettings), new UIPropertyMetadata(null));
    public IList MethodArguments
    {
        get { return (IList)GetValue(MethodArgumentsProperty); }
        set { SetValue(MethodArgumentsProperty, value); }
    }

    public CallMethodActionSettings()
    {
        MethodArguments = new List<object>();
    }
}

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

0 голосов
/ 17 августа 2011

Используйте триггер данных для некоторого свойства элемента в коллекции, и вы можете сделать все это в xaml.

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