Mutiple Refreshable CollectionViewSources of single Observable Collection - c#

Is there any way that I can have multiple views on a single Observable Collection, with different filters, that I can Refresh?
I have an Observable Collection called Proficiencies. I have three list boxes, each should display a subset of the Proficiencies items, filtered on a value within the Proficiencies items. i.e. One list displays items of category A, one list displays items of category B & one list displays items of category C.
I am trying to filter the collection using CollectionViewSources (called the SkillsView, ToolsView, and LanguagesView, one for each list box, each with it's own filter. They are properties in my ViewModel class that the lists boxes Bind to. they are declared in the form of:
protected ICollectionView theSkillsView;
public ICollectionView SkillsView
{
get { return theSkillsView; }
protected set
{
theSkillsView = value;
OnPropertyChanged("SkillsView");
}
}
I have two ways of initialising them either (1):
theSkillsView = new CollectionViewSource { Source = Proficiencies }.View;
theToolsView = new CollectionViewSource { Source = Proficiencies }.View;
theLanguagesView = new CollectionViewSource { Source = Proficiencies }.View;
Or an alternative Ive found (2):
theSkillsView = CollectionViewSource.GetDefaultView(Proficiencies);
theToolsView = CollectionViewSource.GetDefaultView(Proficiencies);
theLanguagesView = CollectionViewSource.GetDefaultView(Proficiencies);
I then Apply the Filtering:
theLanguagesView.Filter = p => ((ProficiencyData) p).Category == ProficiencyCategoryType.Language;
theSkillsView.Filter = p => ((ProficiencyData) p).Category == ProficiencyCategoryType.Skill;
theToolsView.Filter = p => ((ProficiencyData) p).Category == ProficiencyCategoryType.Tool;
The problem is:
if I use Option (1) all the views have the same filter (which ever is applied last)
If I use option (2) when I call Refresh() I get the following error:
System.NullReferenceException
HResult=0x80004003
Message=Object reference not set to an instance of an object.
Source=PresentationFramework
StackTrace:
at System.Windows.Data.ListCollectionView.PrepareLocalArray()
I could create three seperate Collections, but that a) means I don't have all the items in a single collection and b) seems to be not in the spirit of things when CollectionViewSources are available precisely for Sorting, grouping and Filtering.
Many thanks in advance for any suggestions.

You should create three separate views of the AllItemsInDataBase source collection:
theSkillsView = new ListCollectionView(AllItemsInDataBase);
theToolsView = new ListCollectionView(AllItemsInDataBase);
theLanguagesView = new ListCollectionView(AllItemsInDataBase);
You can then filter the views independently of each other.

Related

uwp limit the number of items in AdvancedCollectionView

I have two advanced collection views from windows community toolkit and both of them are bound to same ObservableCollection with different filters and sorting, basically in one of them I need to show just the recent and limited number of items. How can I achieve that?
PeoplePrivate = new ObservableCollection<Person>();
var People = new AdvancedCollectionView(PeoplePrivate, true) { Filter = x => true };
People.SortDescriptions.Add(new SortDescription(nameof(Person.Name), SortDirection.Ascending));
var RecentPeople = new AdvancedCollectionView(PeoplePrivate, true) { Filter = x => true };
RecentPeople.SortDescriptions.Add(new SortDescription(nameof(Person.Modified), SortDirection.Descending));
As you can see in the code above recentPeople should only show recent 20 people according to the modified date. There doesn't seem to be any property to set max size on the advancedCollection view or do anything like "Take(20)".
I tried to return a new advancedCollection by creating a IEnumerable first with Take(20) but that doesn't look the right way because I need to keep it linked to the same ObservableCollection.
view or do anything like "Take(20)" I tried to return a new advancedCollection by creating a IEnumeralbe first with Take(20)
Currently AdvancedCollectionView has not provide this method to get recent number items. But you could remove all the items except top 20 of the source.
public static class AdvancedCollectionViewEx
{
public static void GetTopRang(this AdvancedCollectionView acv, int Range)
{
do
{
var LastIndex = acv.Source.Count - 1;
acv.Source.RemoveAt(LastIndex);
} while (acv.Source.Count > Range);
}
}
Usage
RecentPeople.GetTopRang(20);
I like the WPF answer provided here and use a Binding Converter to chop the end-result of the collection view when it's bound to the ListView. Then it should get updated when the collection changes and re-filter?

LINQ Query - exclude the values of one list from another list

I need some help with a LINQ query please.
Here's my code :
private void ReturnSensitivitiesSearchResults()
{
PatientRecord PMR = new PatientRecord();
// Return the list of sensitivities (Ingredient objects), but do not include any where the "SuitableForSensitivityChecking" is false - as we wouldn't want to include ingredients in the list where
// they can't be screened for sensitivities. - This returns an array of Ingredient[]
var ListOfSensitivities = FDBSystem.Navigation.GetIngredientsByName(txtSearchText.Text + "*", sensitivityType).Where(n => n.SuitableForSensitivityChecking != false);
// Return a list of all of the Sensitivities logged on a PatientRecord (which is a list of Ingredient objects)
var PatientSensitivities = PMR.Sensitivities;
// Populate the drug information into the grid control.
this.dgvSearchResults.DataSource = ListOfSensitivities.ToArray();
}
class PatientRecord
{
public List<Ingredient> Sensitivities = new List<Ingredient>();
}
What I need is a LINQ statement that returns the list of sensitivities, but not including the ones that are in the PatientRecord Sensitivities list.
What I'm trying to do is list all sensitivities, in a datagridview control.... the user can then drag and drop one of these onto a treeview control which adds it to the PatientRecord Sensitivities list. I'm then wanting the datagridview to refresh with the sensitivites minus the ones already in the Patient Record, so that the same sensitivity can't be added to the treeview twice.
Hope you can help.
Assuming PatientSensitivities and ListOfSensitivities are actually a List and not some custom type, I think an Except() will do what you're looking for.
this.dgvSearchResults.DataSource = ListOfSensitivities.Except(PatientSensitivities).ToArray();

Silverlight C#: How to filter a combobox based on another combobox?

How to filter a combobox based on another combobox? ... again :)
I'm writing an web app to learn. I'm using Visual Studio 2012, Silverlight 5, C#, and SQl Server for the data source.
I have one table loading into a datagrid and comboboxes to filter the datagrid. Up to this point everything is working just right.
The comboboxes are "FilterState" and "FilterWaterWay". Note they are not in the datagrid.
I want to select a state and re-populate the FilterWaterWay with only those waterways in the state.
I've seen a lot of ways to do this but none of them seem to match my setup. I could be wrong and just not know it.
From a learning standpoint, I would like to know how to implement this in all 3 of the following query data examples but I'll settle for just one. The last one is my favorite.
Thanks for any and all help.
I would not mind using the following to load comboboxes, filtered or not, but I can't firgure out how to
Restirct the GetQuery to only one field
Make that field distinct
This loads all data from the GetQuery to the datagrid.
LoadOperation<MASTER_DOCKS> loadOp = this._DocksContext.Load(this._DocksContext.GetMASTER_DOCKSQuery());
DocksGrid.ItemsSource = loadOp.Entities;
This loads all data from the GetQuery to the datagrid after it's been filtered
EntityQuery<MASTER_DOCKS> query = _DocksContext.GetMASTER_DOCKSQuery();
query = query.Where(s => s.WTWY_NAME == WaterwaytoFilterBy && s.STATE == StateToFilterBy);
LoadOperation<MASTER_DOCKS> loadOp = this._DocksContext.Load(query);
DocksGrid.ItemsSource = loadOp.Entities;
This is how I am currently loading the comboboxes. This works fine for the load but I don't see how to filter.
The DomainService.cs does not know my other combobox (FilterState) that I want to use as the filter for this combobox (FilterWaterway).
If I could query the ObservableCollection in the xaml I might be able to get it to work but it seems kind of chunky.
Adapted from http://www.jonathanwax.com/2010/10/wcf-ria-services-datagrid-filters-no-domaindatasource-2/
XAML =
private ObservableCollection<string> waterWayFilterList;
public ObservableCollection<string> WaterWayFilterList
{
get { return waterWayFilterList; }
set { waterWayFilterList = value; }
}
private void DoPopulateFilter()
{
//Call Invoke Method to get a list of distinct WaterWays
InvokeOperation<IEnumerable<string>> invokeOp = _DocksContext.FillWaterWayList();
invokeOp.Completed += (s, e) =>
{
if (invokeOp.HasError)
{
MessageBox.Show("Failed to Load Category Filter");
}
else
{
//Populate Filter DataSource
WaterWayFilterList = new ObservableCollection<string>(invokeOp.Value);
//Add a Default "[Select]" value
WaterWayFilterList.Insert(0, "[Select WaterWay]");
FilterWaterWay.ItemsSource = WaterWayFilterList;
FilterWaterWay.SelectedItem = "[Select WaterWay]";
}
};
}
DomainService.cs =
[Invoke]
public List<string> FillWaterWayList()
{
return (from r in ObjectContext.MASTER_DOCKS
select r.WTWY_NAME).Distinct().ToList();
}
Here's the closest I've gotten so far and it seems straight forward.
It returns no errors but the displayed result reads System.Collections.Generic.List'1[System.Char]
The record count in the dropdown is correct which leads me to think it's on the right track.
Only what is displayed is wrong. A casting problem perhaps?
I would still have to get the result from the FilterState Combo box in where "TX" is.
var filter = from r in _DocksContext.MASTER_DOCKS
where r.STATE.Equals("TX")
select r.WTWY_NAME.Distinct().ToList();
MyComboBox.ItemsSource = filter;
Without parentheses, you're doing the .Distinct().ToList() on the string (which implements IEnumerable<char>, which is why those operations work), which results in a List<char> (which isn't what you're looking for). You need to add parentheses so you get the distinct waterways:
var filter = (from r in _DocksContext.MASTER_DOCKS
where r.STATE.Equals("TX")
select r.WTWY_NAME).Distinct().ToList();
Note that if two waterways might have the same name, but actually be distinct, you'll need to instead select distinct r, and then differentiate them in the dropdown somehow, e.g.
var filter = (from r in _DocksContext.MASTER_DOCKS
where r.STATE.Equals("TX")
select r).Distinct().ToList();
// generated classes are partial, so you can extend them in a separate file
public partial class MASTER_DOCKS
{
// the dropdown uses the ToString method to show the object
public override string ToString()
{
return string.Format("{0} ({1})", WTWY_NAME, ID);
}
}

How to add differnt sub ObservableCollections to main ObservableCollection

Example
I have
ObservableCollection<Employee> // 1
ObservableCollection<Boss>// 2
ObservableCollection<Department> //3
ObservableCollection<T> // main >>>I want 1, 2, 3 ObservableCollection to main ObservableCollection
How to do?
edited1: I want to add them to be a list. Not for each item.
edited2: I have to display 3 lists of field on the wpf application. the 2nd list can add/remove item in the list.
** please let me know if it unclear.
Since what you want to do is to add a collection to a collection, but those collection types are not compatible, I'd try this
ObservableCollection<Employee> _employees = ...
ObservableCollection<Boss> _bosses = ...
ObservableCollection<Department> _departments = ...
ObservableCollection<IList> _collections = ...
_collections.Add(_employees);
_collections.Add(_bosses);
_collections.Add(_departments);
Note that the generic argument to the _collections collection is IList. ObservableCollection<T> implements IList and is therefore assignable to things of that type, even for different Ts among the sets.
Observable Collection does not support something like AddRange functionality where in you can add your entire list to an already existing list.
By the way, if all your observable collection is of type T, you could use foreach to iterate through and do the adding, something like below:
var observableCollection1 = new ObservableCollection<string>();
var observableCollection = new ObservableCollection<string>();
foreach (var element in observableCollection1)
{
observableCollection.Add(element);
}
But, what you are trying to do, as from my understanding, is trying to add an observable collection of type department to a different type T, which is not possible unless you try to add it to an observable collection of simply, objects. Warning: Note, you might need to box/unbox afterwards.
Code Snippet:
var observableCollection_Department = new ObservableCollection<Department>();
var observableCollection_Employee = new ObservableCollection<Employee>();
var observableCollection_Boss = new ObservableCollection<Boss>();
var observableCollection = new ObservableCollection<object>();
foreach (var element in observableCollection_Department)
{
observableCollection.Add(element);
}
foreach (var element in observableCollection_Employee)
{
observableCollection.Add(element);
}
foreach (var element in observableCollection_Boss)
{
observableCollection.Add(element);
}

What is difference between System.Linq.Enumerable.WhereListIterator & System.Linq.Enumerable.WhereSelectListIterator?

What is difference between System.Linq.Enumerable.WhereListIterator & System.Linq.Enumerable.WhereSelectListIterator?
One difference I hav noticed is Type WhereListIterator reflects changes on collection object but WhereSelectListIterator does not
I will make it more clear for eg.
I hav a scenario where I fetch my Domain Object from Repository
var buckets = testRepository.GetBuckets(testIds);
Then I select certain buckets from the above collection inside a loop
var bucketsForTest = buckets.Where(bucket => bucket.TestID == test.testId);
Then I change a single property of all the Bucket Objects inside the method of LooserTrafficDisributor object.
ITrafficDistributor distributor = new LooserTrafficDisributor(bucketsForTest);
IEnumerable<Bucket> updatedBuckets = distributor.Distribute(test.AutoDecision);
Constructor of LooserTrafficDisributor
public LooserTrafficDisributor(IEnumerable<Bucket> allBuckets)
{
this.allBuckets = allBuckets;
}
The distribute method inside LooserTrafficDistributor looks like this
private IEnumerable<Bucket> DistributeTraffic(bool autoDecision)
{
// allBuckets is class variable in LooserTrafficDistributor object which is set through constructor shown above .
// Omitted other details
allBuckets.Where(bucket=> bucket.IsControl == false).ToList()
.ForEach(bucket => bucket.TrafficPercentage += 10 ));
return allBuckets
}
After this I can see the reflected changes inside the IEnumerable updatedBuckets collection.
But if I do this i.e. instead of fetching Bucket collection from repository do a select & then Update all the Bucket objects in similar manner as follows
var bucketsForTest = testRows.Where(testrow => testrow.url == url.url).Select(currRow => new Bucket
{
TestID = currRow.TestId,
BucketID = currRow.BucketId,
BucketName = currRow.c_bucket_name,
TrafficPercentage = Convert.ToInt32(currRow.i_bucket_percentage),
IsControl = currRow.b_is_control,
IsEnabled = currRow.b_enabled,
UpdatedAdminId = currRow.i_updated_admin_id,
LogAsSection = currRow.i_log_as_section
}) ;
ITrafficDistributor distributor = new LooserTrafficDisributor(bucketsForTest);
IEnumerable<Bucket> updatedBuckets = distributor.Distribute(test.AutoDecision, strategy.GetStatisticallySignificantLoosingBucketIds());
I can't get the changes reflected inside the IEnumerable updatedBuckets collection.
Infact I debugged inside the DistributeTraffic methods even there the changes were not reflected after each loop round.
.Where() makes an IEnumerable of your items containing all elements which fullfil the where criteria. If you run a .Select() on that result set, you will get a IEnumerable of new elements you've created in the select-statement. So changes to the original elements will not reflect on the new elements.
In your example you create for every Bucket in the original list fullfilling your where criteria a new Bucket object, copying the content from the original bucket to the new Bucket.

Categories