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
ASP.NET MVC Dropdownlistfor with Model of Type List<String>
What Is the Correct Way to Read a Serial Port Using .Net Framework
App.Config for a Class Library
Nice & Universal Way to Convert List of Items to Tree
How to Transfer Authentication from Webbrowser to Webrequest
How to Enumerate Through a Jobject
How to Seed an Admin User in Ef Core 2.1.0
Determine If Current Application Is Activated (Has Focus)
Asp .Net MVC Disable Client Side Validation at Per-Field Level
How to Detect a Process Start & End Using C# in Windows
Different Like Behaviour Between My Application and the Access Query Wizard
Differencebetween Manualresetevent and Autoresetevent in .Net
Wrapping Stopwatch Timing with a Delegate or Lambda