WPF Xaml updating listView to List<string> using INotifyPropertyChanged - wpf

I have a simple problem. I have a listView and it's datasource is a List. It is not updated immediately, but only when the codeflow ends.
What's the problem?
Below xaml:
<ListView x:Name="listView" ItemsSource="{Binding InfoBancaDatiAttuale}" HorizontalAlignment="Left" Height="130" Margin="212,34,0,0" VerticalAlignment="Top" Width="310">
<ListView.View>
<GridView>
<GridViewColumn/>
</GridView>
</ListView.View>
</ListView>
Below xaml.cs:
private List<string> _infoBancaDatiAttuale;
public List<string> InfoBancaDatiAttuale
{
get { return _infoBancaDatiAttuale; }
set
{
_infoBancaDatiAttuale = value;
onPropertyChanged("InfoBancaDatiAttuale");
}
}
private void AddToListAndNotify(List<string> list, string value, string propertyNotify)
{
List<string> tempList = list;
tempList.Add(value);
InfoBancaDatiAttuale = tempList;
}
In xaml.cs file I have also a procedure that perform instruction and should refresh UI. Percentage refresh a ProgressBar and I see that is update instantanely but InfoBancaDatiAttuale not refresh until the method finish.
public void performInstruction() {
Percentage = (int)((1 * 100) / 11);
AddToListAndNotify(InfoBancaDatiAttuale, "1) Eseguo login", "InfoBancaDatiAttuale");
//...instruction
Percentage = (int)((2 * 100) / 11);
AddToListAndNotify(InfoBancaDatiAttuale, "2) Another operation", "InfoBancaDatiAttuale");
//...instruction
Percentage = (int)((3 * 100) / 11);
AddToListAndNotify(InfoBancaDatiAttuale, "3) Another operation", "InfoBancaDatiAttuale");
}
What's the problem?

Your issue is nothing to do with INotifyPropertyChanged, INotifyCollectionChanged and you dont need to use an ObservableCollection (although it would be slightly more efficient)
Your problem is that your performInstruction Method is running on the UI Thread and the Dispatcher is unable to update the user interface until the method has completed, which is why nothing happens until the method has completed.
What you could to is:
public void performInstruction() {
Task.Run(() => {
Percentage = (int)((1 * 100) / 11);
AddToListAndNotify(InfoBancaDatiAttuale, "1) Eseguo login",
"InfoBancaDatiAttuale");
//...instruction
Percentage = (int)((2 * 100) / 11);
AddToListAndNotify(InfoBancaDatiAttuale, "2) Another operation",
"InfoBancaDatiAttuale");
//...instruction
Percentage = (int)((3 * 100) / 11);
AddToListAndNotify(InfoBancaDatiAttuale, "3) Another operation",
"InfoBancaDatiAttuale");
};
}
And before anyone suggests that you need to marshal the property changes up to the dispatcher thread, I can assure you that it isnt necessary.

You should use ObservableCollection<string>. It will fire the CollectionChanged event of the INotifyCollectionChanged interface whenever an item is added to or removed from the collection.
private ObservableCollection<string> _items;
public ObservableCollection<string> Items => _items ?? (_items = new ObservableCollection<string>());

Related

After WPF Polygon binding, the graphics remain unchanged

I used to assign directly to the set of points, the graphics can be displayed properly, and later found that the performance of doing that is very poor, I thought of using the binding method to do it.
After WPF Polygon binding, the set of points has changed, but the graphics have not changed.
xaml code
<Canvas Background="Black" Name="map">
<Polygon Name="pl" Points="{Binding sendPoints,Mode=TwoWay}"></Polygon>
</Canvas>
backend code
DrawLinesClass drawLinesClass = new DrawLinesClass();
pl.DataContext = drawLinesClass;//bind
pl.Stroke = Brushes.Red;
pl.StrokeThickness = 2;
Thread td = new Thread(() =>
{
double index = 0,sum=0;
while (true)
{
Thread.Sleep(50);
if (isRun)
{
sum+=0.01;
pl.Dispatcher.Invoke(new Action(() =>
{
if (sum * 2 >= map.ActualHeight - 40)
{
sum = 0;
index += 1;
//drawLinesClass.sendPoints.Clear();
}
drawLinesClass.sendPoints.Add(new Point(index * sum, sum * 2));
//pl.Points = drawLinesClass.sendPoints;//no bind
}));
}
}
});
td.IsBackground = true;
td.Start();
bind model
public class DrawLinesClass : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private PointCollection _sendPointsList;
public PointCollection sendPoints
{
get
{
if (_sendPointsList == null) _sendPointsList = new PointCollection();
return _sendPointsList;
}
set
{
//this._sendPointsList = new PointCollection();
this._sendPointsList = value;
OnPropertyChanged("sendPoints");
}
}
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
Now the set of binding points has changed, I hope the graphics can change automatically.
The Binding
Points="{Binding sendPoints}"
where (Mode=TwoWay was pointless) only updates its target property Points if you assign a new value to its source property sendPoints. Just adding points to the existing sendPoints collection as in
drawLinesClass.sendPoints.Add(new Point(index * sum, sum * 2));
won't make the Binding update.
In other scenarios like binding the ItemsSource property of an ItemsControl you would simply use an ObservableCollection for the source property. However, this won't work here. You really have to create a new PointCollection whenever you add a point.
Besides that, better use a DispatcherTimer instead of a Thread with Sleep and Dispatcher.Invoke:
double index = 0,sum=0;
var timer = new DispatcherTimer
{
Interval = TimeSpan.FromMilliseconds(50)
};
timer.Tick += (s, e) =>
{
sum += 0.01;
if (sum * 2 >= map.ActualHeight - 40)
{
sum = 0;
index += 1;
}
var points = new PointCollection(drawLinesClass.sendPoints);
points.Add(new Point(index * sum, sum * 2));
drawLinesClass.sendPoints = points;
};
timer.Start();
To stop the the timer, just call
timer.Stop();
Also better write
private void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
As a final remark, the Bindings in your view should usually use a common view model, and hence a common DataContext that is assigned at the MainWindow instance. When you explicitly set a Polyline's DataContext in code behind like
pl.DataContext = drawLinesClass;
it is questionably why you would at all bind the Polylines's Points property. Instead of Binding and setting the DataContext, you could as well simply write
pl.Points = drawLinesClass.sendPoints;
and thus even avoid the need for implementing INotifyPropertyChanged. You would also avoid the need for creating a new PointsCollection on each update, because
drawLinesClass.sendPoints.Add(new Point(index * sum, sum * 2))
will now magically work.

How to change datacontext at runtime with Mvvm

I have a graph that I want to change some ViewModel property so the whole graph would change accordingly.
The only property that I want to change here is "year", I tried to implement the INotifyPropertyChanged so the binding will cause the graph to change automatically, but it didn't work.
This is the model:
public class Model
{
public double rate { get; set; }
public string date { get; set; }
}
This is the ViewModel:
public class ViewModel :INotifyPropertyChanged
{
private string _year;
public string Year { get { return _year; } set { _year = value;UpdateData(); OnPropertyChanged("Year"); } }
public ViewModel()
{
_year = "2017";
UpdateData();
}
public void UpdateData()
{
int i,j;//Indexs that holds actuall api retrived values
string cR, urlContents;// cR- current rate in string format, urlContents - the whole Api retrived data
string c;//For api syntx, add 0 or not, depends on the current date syntax
this.CurrenciesHis = new ObservableCollection<Model>();//Model objects collection
HttpClient client = new HttpClient();
for (int l = 1; l < 13; l++)
{
if (l < 10)
c = "0";
else
c = "";
urlContents = client.GetStringAsync("http://data.fixer.io/api/"+(_year)+"-"+ c + l + "-01?access_key=&base=USD&symbols=EUR&format=1").Result;
i = urlContents.IndexOf("EUR");//Finds the desired value from api recived data
j = urlContents.IndexOf("}");
cR = urlContents.Substring(i + 5, (j - 2) - (i + 5));
CurrenciesHis.Add(new Model() { rate = Convert.ToDouble(cR), date = "01/" + l.ToString() });
}
}
public ObservableCollection<Model> CurrenciesHis { get; set; }
#region "INotifyPropertyChanged members"
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
#endregion
This is the View that based on third party control (I deleted alot of XAML and used bold letters to mark where is the actuall binding located):
<layout:SampleLayoutWindow x:Class="AreaChart.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
ResizeMode="CanResizeWithGrip"
xmlns:chart="clr-namespace:Syncfusion.UI.Xaml.Charts;assembly=Syncfusion.SfChart.WPF"
xmlns:local="clr-namespace:PL"
xmlns:layout="clr-namespace:Syncfusion.Windows.SampleLayout;assembly=Syncfusion.Chart.Wpf.SampleLayout"
UserOptionsVisibility="Collapsed"
WindowStartupLocation="CenterScreen" Height="643.287" Width="1250.5"
Title="2017">
<Grid>
<Grid.Resources>
...........................................
<chart:AreaSeries x:Name="AreaSeries" EnableAnimation="True"
**XBindingPath="date"
Label="Favourite"
YBindingPath="rate"
ItemsSource="{Binding CurrenciesHis}"**
ShowTooltip="True" >
<chart:AreaSeries.AdornmentsInfo>
<chart:ChartAdornmentInfo AdornmentsPosition="Bottom"
HorizontalAlignment="Center"
VerticalAlignment="Center"
ShowLabel="True">
<chart:ChartAdornmentInfo.LabelTemplate>
<DataTemplate>
....................................
<TextBox HorizontalAlignment="Left" Height="30" Margin="28,231,0,0" TextWrapping="Wrap" Name="Text1" VerticalAlignment="Top" Width="76" Text="{Binding Year, UpdateSourceTrigger=PropertyChanged}"/>
</Grid>
This is the code behaind and the event of the Textbox that I want to change with it's help that year property of the viewmodel:
public partial class MainWindow : SampleLayoutWindow
{
PL.ViewModel newInstance;
public MainWindow()
{
InitializeComponent();
newInstance = new PL.ViewModel();
this.DataContext = newInstance;
}
}
What I understand is that from this point the mechanism of WPFwill change the values on the chart using the binding and the "notification" of INotifyPropertyChanged but it doesn't work for me..
year should be a private field, but it is public. You're setting the value of the field, which naturally does not execute any of the code in the setter for the property.
Make year and all of your backing fields private, and rename all of your private fields with a leading underscore (for example, year should be renamed to _year) to prevent accidents like this.
And make it a policy in your viewmodel code always to set the property, never the field, except of course inside the actual property setter for that field.
Also, use bindings to set viewmodel properties from UI. Don't do it in codebehind. Get rid of that textchanged handler.
<TextBox
HorizontalAlignment="Left"
Height="30"
Margin="28,231,0,0"
TextWrapping="Wrap"
VerticalAlignment="Top"
Width="76"
Text="{Binding Year, UpdateSourceTrigger=PropertyChanged}"
/>
Finally, it seems that you intended for changes to Year to have some effect on the contents of CurrenciesHis, but there's no mechanism for that in your code, and no explanation of what you want to have happen or how you expect it to happen.
And here's an updated version of your viewmodel.
public class ViewModel
{
public ViewModel()
{
// DO NOT, DO NOT EVER, DO NOT, SERIOUSLY, EVER, EVER, EVER UPDATE A
// PROPERTY'S BACKING FIELD OUTSIDE THE PROPERTY'S SETTER.
Year = DateTime.Now.Year - 1;
UpdateCurrencies();
}
protected void UpdateCurrencies()
{
// Indexs that holds actuall api retrived values
int i, j;
// cR- current rate in string format, urlContents - the whole Api retrived data
string cR, urlContents;
// For api syntx, add 0 or not, depends on the current date syntax
string c;
CurrenciesHis = new ObservableCollection<Model>();//Model objects collection
HttpClient client = new HttpClient();
for (int l = 1; l < 13; l++)
{
if (l < 10)
c = "0";
else
c = "";
// Use the public property Year, not the field _year
var url = "http://data.fixer.io/api/" + Year + "-" + c + l + "-01?access_key=&base=USD&symbols=EUR&format=1";
urlContents = client.GetStringAsync(url).Result;
i = urlContents.IndexOf("EUR");//Finds the desired value from api recived data
j = urlContents.IndexOf("}");
cR = urlContents.Substring(i + 5, (j - 2) - (i + 5));
CurrenciesHis.Add(new Model() { rate = Convert.ToDouble(cR), date = "01/" + l.ToString() });
}
OnPropertyChanged(nameof(CurrenciesHis));
}
// Year is an integer, so make it an integer. The binding will work fine,
// and it'll prevent the user from typing "lol".
private int _year;
public int Year
{
get { return _year; }
set
{
if (_year != value)
{
_year = value;
OnPropertyChanged(nameof(Year));
UpdateCurrencies();
}
}
}
public ObservableCollection<Model> CurrenciesHis { get; private set; }
// -----------------------------------------------------
// YearsList property for ComboBox
// 30 years, starting 30 years ago.
// You could make this IEnumerable<int> or ReadOnlyCollection<int> if you
// want something other than the ComboBox to use it. The ComboBox doesn't care.
// Year MUST be an int for the binding to SelectedItem (see below) to work,
// not a string.
public System.Collections.IEnumerable YearsList
=> Enumerable.Range(DateTime.Now.Year - 30, 30).ToList().AsReadOnly();
}
XAML for YearsList combobox (which I prefer to the text box btw):
<ComboBox
ItemsSource="{Binding YearsList}"
SelectedItem="{Binding Year}"
/>
Your CurrenciesHis property doesn't implement INPC so WPF doesn't realize that you changed it (UpdateData() has "this.CurrenciesHis = new ObservableCollection();")
Your current property is:
public ObservableCollection<Model> CurrenciesHis { get; set; }
Should be something like this:
private ObservableCollection<Model> _CurrenciesHis;
public ObservableCollection<Model> CurrenciesHis { get { return _CurrenciesHis; } set { if (_CurrenciesHis != value) { _CurrenciesHis = value; OnPropertyChanged("CurrenciesHis"); } } }

How i can filter dataGrid on the basis of Column header in wpf? [duplicate]

I search an example or sample to filter the WPF DataGrid column elements by a textbox.
Something similar to this (the given example uses a WPFToolkit... apparently abandoned by Microsoft...)
XAML
<Canvas>
<DataGrid Height="200" Name="dataGrid1" Width="200" Canvas.Top="23" />
<TextBox Name="textBox1" Width="120" />
</Canvas>
cs:
public partial class MainWindow : Window
{
private List<Personne> persons;
ICollectionView cvPersonnes;
public MainWindow()
{
InitializeComponent();
persons = new List<Personne>();
persons.Add(new Personne() { Id = 1, Nom = "Jean-Michel", Prenom = "BADANHAR" });
persons.Add(new Personne() { Id = 1, Nom = "Gerard", Prenom = "DEPARDIEU" });
persons.Add(new Personne() { Id = 1, Nom = "Garfild", Prenom = "THECAT" });
persons.Add(new Personne() { Id = 1, Nom = "Jean-Paul", Prenom = "BELMONDO" });
cvPersonnes = CollectionViewSource.GetDefaultView(persons);
if (cvPersonnes != null)
{
dataGrid1.AutoGenerateColumns = true;
dataGrid1.ItemsSource = cvPersonnes;
cvPersonnes.Filter = TextFilter;
}
}
public bool TextFilter(object o)
{
Personne p = (o as Personne);
if (p == null)
return false;
if (p.Nom.Contains(textBox1.Text))
return true;
else
return false;
}
}
public class Personne
{
public int Id { get; set; }
public string Nom { get; set; }
public string Prenom { get; set; }
}
You can filter the Items in the DataGrid by binding it to an ICollectionView that supports filtering.
Details here for .NET 4. The process is the same for .NET 4.5, but it seems the documentation has been lost. There's a small mention to it here under the "Grouping, Sorting, and Filtering" heading.
edit: at the time this was originally written, the WPF toolkit had not been abandoned by Microsoft. The controls that used to be part of it are now in the framework, and the toolkit was alive and doing well here
I have seen at various sites much ado about this matter...
To filter the latter being a datagrid using a datatable as the source, which is quite common to make the code below:
DataTable dt = new DataTable("Table1");
//fill your datatable...
//after fill...
dataGrid1.DataContext = dt;
IBindingListView blv = dt.DefaultView;
blv.Filter = "NAME = 'MOISES'";
There are several solutions, but in my opinion, the best solutions are the ones which uses only DataGrid styles without inventing a new inherited DataGird type. The followings are the best I found:
Option 1: which I personally use: Automatic WPF Toolkit DataGrid Filtering
Option 2: Auto-filter for Microsoft WPF DataGrid
I have written my own FilterDataGrid Control, it's much more flexible than the ones provided on CodeProject or elsewhere. I can neither post the full code here, nor can I publish it.
But: Since your datasource is most likely wrapped into a ICollectionView, you can do something like this:
public void ApplyFilters()
{
ICollectionView view = CollectionViewSource.GetDefaultView(ItemsSource);
if (view != null)
{
view.Filter = FilterPredicate;
}
}
private bool FilterPredicate(object item)
{
var yourBoundItemOrRow = item as BoundItemType;
return aFilterObject.Matches(yourBoundItemOrRow);
}
You can implement any filter logic easily based on this concept. Even very, very powerful filters. Note: I have those methods in my own class derived from datagrid. They can be adapted to work outside of the grid, too, for example in a UserControl

Update the location of pushpins on Silverlight bing maps control

My question is about using bing maps with windows phone 7. Here is a summary of what I need to do
poll a service every x seconds to retrieve a set of coordinates
if this is the first time the service is polled
plot these coordinates as custom pushpins on the map (I am using Image and MapLayer)
PinObject pin = new PinObject() //Custom object
{
Id = groupMember.Id,
PushPin = new Image()
{
Source = new System.Windows.Media.Imaging.BitmapImage(new Uri("blackpin.png", UriKind.Relative)),
Opacity = 0.8,
Stretch = System.Windows.Media.Stretch.None
},
PinLocation = new GeoCoordinate(groupMember.Latitude, groupMember.Longitude)
};
imageLayer.AddChild(pin.PushPin, pin.PinLocation); //Initialized in constructor
pinObjects.Add(pin);// Add pin object to a list to provide a handle to the objects
auto set the map zoomlevel so that all the plotted points are visible (I would assume using a LocationRect.CreateLocationRect should do)
var coords = pinObjects.Select(p => p.PinLocation).ToList();
myMap.SetView(LocationRect.CreateLocationRect(coords));
else based on the new coordinates obtained, update the locations of each of the pushpins on the map
PinObject pObj = pinObjects.FirstOrDefault(p => p.Id == groupMember.Id);
MapLayer.SetPosition(pObj.PushPin, new GeoCoordinate(groupMember.Latitude, groupMember.Longitude));
The pins load fiine and the call to the service to get their new locations loads fine as well, the problem is that their location on the map is never updated so basically they just sit still even though all this work is going on in the background, I have debugged so I know it works. How do I reset the location of the pins, if using an Image won't do, can I use a Pushpin object? How would this work?
Thanks in advance.
I've found that the best way to ensure the pushpins get updated is to call call SetView() on the map again. You can pass in the existing view to basically force a refresh. Eg; MyMapControl.SetView(MyMapControl.BoundingRectangle);
Here is an option similar to Dispatcher.BeginInvoke but it works better for me in some cases. When I really need to get off the current thread with some work I will use a private static class UICallbackTimer to offset execution just a slight amount. (typos and untested code, just pulling out pieces here you'll have to debug in your code)
UICallbackTimer is not my code but it's available on the Internet. You can get information on this class by searching "private static class UICallbackTimer"
Here is the code to execute it.
UICallbackTimer.DelayExecution(TimeSpan.FromSeconds(.01),
() => (
MapLayer.SetPosition(pObj.PushPin, new GeoCoordinate(groupMember.Latitude, groupMember.Longitude))
);
and here is the class ( I place it inside the current object, so that it remains private to my class) Add using statement for System.Threading
private static class UICallbackTimer
{
private static bool _running = false;
private static int runawayCounter = 0;
public static bool running()
{
if (_running && runawayCounter++ < 10)
return _running;
runawayCounter = 0;
_running = false;
return _running;
}
public static void DelayExecution(TimeSpan delay, Action action)
{
_running = true;
System.Threading.Timer timer = null;
SynchronizationContext context = SynchronizationContext.Current;
timer = new System.Threading.Timer(
(ignore) =>
{
timer.Dispose();
_running = false;
context.Post(ignore2 => action(), null);
}, null, delay, TimeSpan.FromMilliseconds(-1));
}
}
Great Question!
Here is some realllly ugly code, but at least its working and something to start from.
I got the main structure from here. I'd appreciate it if someone could post an answer with proper binding and less code behind.
<Grid x:Name="LayoutRoot" Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!--RESOURCES-->
<Grid.Resources>
<DataTemplate x:Key="LogoTemplate">
<maps:Pushpin Location="{Binding PinLocation}" />
</DataTemplate>
<maps:MapItemsControl x:Name="GroupAPins"
ItemTemplate="{StaticResource LogoTemplate}"
ItemsSource="{Binding PinsA}">
</maps:MapItemsControl>
</Grid.Resources>
<Grid x:Name="ContentPanel" Grid.Row="0" Margin="12,0,12,0"/>
</Grid>
public partial class MapPage : PhoneApplicationPage
{
private ObservableCollection<PinData> _pinsA = new ObservableCollection<PinData>();
private Map MyMap;
public ObservableCollection<PinData> PinsA { get { return this._pinsA; } }
public MapPage()
{
InitializeComponent();
this.DataContext = this;
//Create a map.
MyMap = new Map();
MyMap.CredentialsProvider = new ApplicationIdCredentialsProvider("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
//Remove the List of items from the resource and add it to the map
this.LayoutRoot.Resources.Remove("GroupAPins");
MyMap.Children.Add(GroupAPins);
MyMap.Center = new GeoCoordinate(40.74569634433956, -73.96717071533204);
MyMap.ZoomLevel = 5;
//Add the map to the content panel.
ContentPanel.Children.Add(MyMap);
loadAPins_fromString();
}
//ADD PIN TO COLLECTION
private void addPin(String lat, String lon)
{
PinData tmpPin;
tmpPin = new PinData()
{
PinLocation = new GeoCoordinate(System.Convert.ToDouble(lat), System.Convert.ToDouble(lon))
};
_pinsA.Add(tmpPin);
var timer = new DispatcherTimer();
timer.Interval = TimeSpan.FromSeconds(1);
timer.Tick += delegate(object sender, EventArgs args)
{
PinsA.Remove(tmpPin);
tmpPin.PinLocation.Latitude += 1;
PinsA.Add(tmpPin);
};
timer.Start();
}
//LOAD PINS ONE BY ONE
private string loadAPins_fromString()
{
//BAD
addPin("42.35960626034072", "-71.09212160110473");
//addPin("51.388066116760086", "30.098590850830067");
//addPin("48.17972265679143", "11.54910385608672");
addPin("40.28802528051879", "-76.65668606758117");
var coords = PinsA.Select(p => p.PinLocation).ToList();
MyMap.SetView(LocationRect.CreateLocationRect(coords));
return "A PINS LOADED - STRING";
}
}
public class PinData
{
public GeoCoordinate PinLocation{get;set;}
}

Filter a DataGrid on a Text box

I search an example or sample to filter the WPF DataGrid column elements by a textbox.
Something similar to this (the given example uses a WPFToolkit... apparently abandoned by Microsoft...)
XAML
<Canvas>
<DataGrid Height="200" Name="dataGrid1" Width="200" Canvas.Top="23" />
<TextBox Name="textBox1" Width="120" />
</Canvas>
cs:
public partial class MainWindow : Window
{
private List<Personne> persons;
ICollectionView cvPersonnes;
public MainWindow()
{
InitializeComponent();
persons = new List<Personne>();
persons.Add(new Personne() { Id = 1, Nom = "Jean-Michel", Prenom = "BADANHAR" });
persons.Add(new Personne() { Id = 1, Nom = "Gerard", Prenom = "DEPARDIEU" });
persons.Add(new Personne() { Id = 1, Nom = "Garfild", Prenom = "THECAT" });
persons.Add(new Personne() { Id = 1, Nom = "Jean-Paul", Prenom = "BELMONDO" });
cvPersonnes = CollectionViewSource.GetDefaultView(persons);
if (cvPersonnes != null)
{
dataGrid1.AutoGenerateColumns = true;
dataGrid1.ItemsSource = cvPersonnes;
cvPersonnes.Filter = TextFilter;
}
}
public bool TextFilter(object o)
{
Personne p = (o as Personne);
if (p == null)
return false;
if (p.Nom.Contains(textBox1.Text))
return true;
else
return false;
}
}
public class Personne
{
public int Id { get; set; }
public string Nom { get; set; }
public string Prenom { get; set; }
}
You can filter the Items in the DataGrid by binding it to an ICollectionView that supports filtering.
Details here for .NET 4. The process is the same for .NET 4.5, but it seems the documentation has been lost. There's a small mention to it here under the "Grouping, Sorting, and Filtering" heading.
edit: at the time this was originally written, the WPF toolkit had not been abandoned by Microsoft. The controls that used to be part of it are now in the framework, and the toolkit was alive and doing well here
I have seen at various sites much ado about this matter...
To filter the latter being a datagrid using a datatable as the source, which is quite common to make the code below:
DataTable dt = new DataTable("Table1");
//fill your datatable...
//after fill...
dataGrid1.DataContext = dt;
IBindingListView blv = dt.DefaultView;
blv.Filter = "NAME = 'MOISES'";
There are several solutions, but in my opinion, the best solutions are the ones which uses only DataGrid styles without inventing a new inherited DataGird type. The followings are the best I found:
Option 1: which I personally use: Automatic WPF Toolkit DataGrid Filtering
Option 2: Auto-filter for Microsoft WPF DataGrid
I have written my own FilterDataGrid Control, it's much more flexible than the ones provided on CodeProject or elsewhere. I can neither post the full code here, nor can I publish it.
But: Since your datasource is most likely wrapped into a ICollectionView, you can do something like this:
public void ApplyFilters()
{
ICollectionView view = CollectionViewSource.GetDefaultView(ItemsSource);
if (view != null)
{
view.Filter = FilterPredicate;
}
}
private bool FilterPredicate(object item)
{
var yourBoundItemOrRow = item as BoundItemType;
return aFilterObject.Matches(yourBoundItemOrRow);
}
You can implement any filter logic easily based on this concept. Even very, very powerful filters. Note: I have those methods in my own class derived from datagrid. They can be adapted to work outside of the grid, too, for example in a UserControl

Resources