In Wpf Can You Filter a Collectionviewsource Without Code Behind

In WPF can you filter a CollectionViewSource without code behind?

You can do pretty much anything in XAML if you "try hard enough", up to writing whole programs in it.

You will never get around code behind (well, if you use libraries you don't have to write any but the application still relies on it of course), here's an example of what you can do in this specific case:

<CollectionViewSource x:Key="Filtered" Source="{Binding DpData}"
xmlns:me="clr-namespace:Test.MarkupExtensions">
<CollectionViewSource.Filter>
<me:Filter>
<me:PropertyFilter PropertyName="Name" Value="Skeet" />
</me:Filter>
</CollectionViewSource.Filter>
</CollectionViewSource>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Markup;
using System.Windows.Data;
using System.Collections.ObjectModel;
using System.Windows;
using System.Text.RegularExpressions;

namespace Test.MarkupExtensions
{
[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);
}
    // Sketchy Example Filter
public class PropertyFilter : DependencyObject, IFilter
{
public static readonly DependencyProperty PropertyNameProperty =
DependencyProperty.Register("PropertyName", typeof(string), typeof(PropertyFilter), new UIPropertyMetadata(null));
public string PropertyName
{
get { return (string)GetValue(PropertyNameProperty); }
set { SetValue(PropertyNameProperty, value); }
}
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register("Value", typeof(object), typeof(PropertyFilter), new UIPropertyMetadata(null));
public object Value
{
get { return (object)GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}
public static readonly DependencyProperty RegexPatternProperty =
DependencyProperty.Register("RegexPattern", typeof(string), typeof(PropertyFilter), new UIPropertyMetadata(null));
public string RegexPattern
{
get { return (string)GetValue(RegexPatternProperty); }
set { SetValue(RegexPatternProperty, value); }
}

public bool Filter(object item)
{
var type = item.GetType();
var itemValue = type.GetProperty(PropertyName).GetValue(item, null);
if (RegexPattern == null)
{
return (object.Equals(itemValue, Value));
}
else
{
if (itemValue is string == false)
{
throw new Exception("Cannot match non-string with regex.");
}
else
{
return Regex.Match((string)itemValue, RegexPattern).Success;
}
}
}
}
}

Markup extensions are your friend if you want to do something in XAML.

(You might want to spell out the name of the extension, i.e. me:FilterExtension as the on-the-fly checking in Visual Studio may complain without reason, it still compiles and runs of course but the warnings might be annoying.

Also do not expect the CollectionViewSource.Filter to show up in the IntelliSense, it does not expect you to set that handler via XML-element-notation)

CollectionViewSource filtering only in xaml

I want to put the filtering logic not in my viewmodel or code behind but only in XAML

Not possible. You can't do this in pure XAML. XAML is a markup language. You should implement your logic in a programming language.

Givne your current setup, you should call Refresh() on the CollectionViewSource whenever the TextBox changes, for example in a TextChanged event handler in the code-behind.

If you want to refresh the filter from the view model, you should perform the actual filtering there as well. You may for example expose an ICollectionView that the view binds to. It makes no sense to define the filtering logic in the view but try to refresh it from the view model.

Can I filter a collection from xaml?

You can use a CollectionViewSource and make it a property of your view model, and bind to that instead of your ImportMessageList collection directly from the XAML. Set your ImportMessageList collection as the Source of the CollectionViewSource, and then configure a predicate to do your filtering on the CollectionViewSource.

Something like:

private ICollectionView messageListView;
public ICollectionView MessageListView
{
get { return this.messageListView; }
private set
{
if (value == this.messageListView)
{
return;
}

this.messageListView = value;
this.NotifyOfPropertyChange(() => this.MessageListView);
}
}

...

this.MessageListView = CollectionViewSource.GetDefaultView(this.ImportMessageList);
this.MessageListView.Filter = new Predicate<object>(this.FilterMessageList);

...

public bool FilterMessageList(object item)
{
// inspect item as message here, and return true
// for that object instance to include it, false otherwise
return true;
}

Filter CollectionViewSource by search string - bound to itemscontrol (WPF MVVM)

If you are creating the CollectionViewSource in the view, you should filter it there as well:

private void GameSearchFilter(object sender, FilterEventArgs e)
{
GameList game = e.Item as GameList;
e.Accepted = game != null && game.Title?.Contains(txtSearchString.Text);
}

The other option would be to bind to an ICollectionView and filter this one in the view model:

_view = CollectionViewSource.GetDefaultView(sourceCollection);
_view.Filter = (obj) =>
{
GameList game = obj as GameList;
return game != null && game.Title?.Contains(_searchString);
};
...
public string SearchString
{
...
set { _searchString = value; _view.Refresh(); }
}

Or sort the source collection itself directly.

CollectionViewSource, how to filter data?

Al last I have found a solution, as posted also in this question
to explicitly declare the type of the Collection:

CollectionViewType="ListCollectionView"

So in XAML added the Collection type:

<CollectionViewSource x:Key="tSCHEDEViewSource" d:DesignSource="{d:DesignInstance my:TSCHEDE,  CreateList=True}" CollectionViewType="ListCollectionView">
</CollectionViewSource>

And in code now the Event Handler works:

myCollectionViewSource.Filter += new FilterEventHandler(filterSource);

The only regret is that I did not understand why, for something apparently so simple, I have to force it "by hand" in XAML ???
To me this seems like an hack, and also very error prone...

Filtering CollectionViewSource

Yes your assumption is correct.

I'm assuming with your translations,

public ObservableCollection<Channel> myListChannels;

is actually

public ObservableCollection<Canal> miListaDeCanales;

with the class Canal in the namespace Unico

Update:

In your filter try using the property that is rendered in the ComboBox than use the ToString() on the object(o) if you've not overridden ToString() from System.Object.

try switching

if (o.ToString().Contains(myTextBox.Text))

to

if (((Canal)o).NameProperty.Contains(myTextBox.Text))

^^ that should fix your issue.

Do you have a DataTemplate for ComboBox.ItemTemplate in xaml. That will explain why you see the valid value rendered in the ComboBox, else all the ComboBoxItem's will also render as Unico.Canal



Related Topics



Leave a reply



Submit