I have a ListView and data source for it which it populate from the Internet. Once it's populate it should remain static unless a user makes a new http request. Now I have something like this:
class MyDataItem {
public int Field1 { get; set; }
public string Field2 { get; set; }
}
class Window1: Window {
private List<MyDataItem> dataSource = new ...
void sendHttpRequest(...) {
dataSource = getFromInternet();
myListView.ItemsSource = dataSource ;
}
}
And say, I have a checkbox. When I click on it, I want to filter the data by some filter.
//.........
// the checkbox is checked
var filterDataSource = dataSource.Where(....)
How can I make my ListView update its data with data source to be filterDataSource? And then when the checkbox is unchecked again, how will I make it show the initial data source?
Here is some code to help you. Please note that this was not tested nor compiled but it can give you some hints on how to handle your case. The trick is to use a CollectionViewSource that lets you filter your data.
class Window1: Window {
private readonly ObservableCollection<MyDataItem> _children;
private readonly CollectionViewSource _viewSource;
public Window1()
{
// ...
_children = new ObservableCollection<MyDataItem>();
_viewSource = new CollectionViewSource
{
Source = _children
};
myListView.ItemsSource = _viewSource;
// ...
}
// This method needs to be called when your checkbox state is modified.
// "filter = null" means no filter
public void ApplyFilter(Func<MyDataItem, bool> filter)
{
if (_viewSource.View.CanFilter)
{
_viewSource.View.Filter = (filter == null) ? (o => true): (o => filter((MyDataItem) o));
}
}
Related
I Follow MVVM model for my application, I have a textbox which acts as an input to filter the collection. I understood observablecollection filter using lambda expression but i couldn't understand collectionviewsource methods. how can i implement collectionviewsource methods using this.
Here is my viewmodel class:
private ObservableCollection<SPFetchCREntity> _CRmappings2 = new ObservableCollection<SPFetchCREntity>();
public ObservableCollection<SPFetchCREntity> CRmappings2
{
get { return _CRmappings2; }
set
{
_CRmappings2 = value;
RaisePropertyChanged("CRmappings2");
}
}
public ICollectionView AllCRSP
{ get; private set;}
public void UpdatePopList()
{
CRmappings2 = new ObservableCollection<SPFetchCREntity>(crentities.Where(p => p.MU_Identifier == selectmu.ToString()).ToList());
}
Bind to the ICollectionView and filter this one:
public void UpdatePopList()
{
CRmappings2 = new ObservableCollection<SPFetchCREntity>(crentities.ToList());
AllCRSP = CollectionViewSource.GetDefaultView(CRmappings2);
AllCRSP.Filter = obj =>
{
SPFetchCREntity entity = obj as SPFetchCREntity;
return entity != null && entity.MU_Identifier == selectmu.ToString();
};
}
private string _selectmu;
public string Selectmu
{
get { return _selectmu; }
set { _selectmu = value; AllCRSP.Refresh(); } //<-- refresh the ICollectionView whenever the selectmu property gets set or when you want to refresh the filter
}
Is it possible to convert IEnumerable to a Custom Class that is inherting from ObservableCollection class?
Reason is I want to select only a filtered set of items on the get. I want to implement it on the get because lots of other properties reference CustomItems and perform processes on the items, but I want to somehow make it process filtered set of items depending if a value is enabled or not.
Below is code to help explain what I want to achieve:
public class CustomItemsCollection : ObservableCollection<ItemViewModel>
{
public ListView ListView { get; set; }
public void ScrollToItem(object item = null)
{
//Some custom Code
}
}
And here is my property that I want to customize:
private CustomItemsCollection _CustomItems = null;
[JsonProperty]
public CustomItemsCollection CustomItems
{
get
{
if (_CustomItems != null)
{
if(SomeValueIsEnabled)
{
var filteredItems = _CustomItems.Where(c => c.Property.equals(SomeValue));
var castedItems = (CustomItemsCollection)filteredItems;
return castedItems;
}
return _CustomItems;
}
_CustomItems = new CustomItemsCollection();
_CustomItemsChangedSource = new CollectionChangedWeakEventSource();
_CustomItemsChangedSource.SetEventSource(_CustomItems);
_CustomItemsChangedSource.CollectionChanged += _CustomItemsChangedSource_CollectionChanged;
return _CustomItems;
}
set { _CustomItems = value; RaisePropertyChanged("CustomItems"); }
}
Specifically, this part:
if(SomeValueIsEnabled)
{
var filteredItems = _CustomItems.Where(c => c.Property.equals(SomeValue));
var castedItems = (CustomItemsCollection)filteredItems;
return castedItems;
}
Is this possible / or maybe wrong? What is the best practice to do it?
Thank you!
You can't just cast it, but you can create an instance of CustomItemsCollection and initialize it with filteredItems.
Add a constructor to your custom class that passes through to the appropriate ObservableCollection constructor:
public class CustomItemsCollection : ObservableCollection<ItemViewModel>
{
public CustomItemsCollection(IEnumerable<ItemViewModel> items)
: base(items) { }
// your other code here
}
Then you can do this:
var filteredItems = _CustomItems.Where(c => c.Property.equals(SomeValue));
var collection = new CustomItemsCollection(filteredItems);
return collection;
Try with this code:
var filteredItems = _CustomItems.Where(c => c.Property.equals(SomeValue))
.Select(pre=> new ItemViewModel(){
//add info here
});
var castedItems = new CustomItemsCollection(filteredItems);
I am trying to use the following code example from the Infragistics site and I'd like edits in the XamDataCards to be reflected in the XamDataGrid. However, my DataSource for the XamDataGrid is an ObservableCollection<Companies> in my ViewModel. How can I also bind to the card and relay updates back to my Companies object in the ViewModel?
<igDP:XamDataGrid x:Name="dgCompanies" Theme="IGTheme" DataSource="{Binding Companies}" SelectedDataItemsScope="RecordsOnly">
<igDP:XamDataGrid.FieldSettings>
<igDP:FieldSettings CellClickAction="SelectCell" AllowEdit="True"/>
</igDP:XamDataGrid.FieldSettings>
</igDP:XamDataGrid>
<igDP:XamDataCards x:Name="XamDataCards1"
Grid.Row="1"
DataSource="{Binding Path=SelectedDataItems, ElementName=dgCompanies}"
Theme="IGTheme">
Edit: Added ViewModel
public class CompanyMgmtViewModel : ViewModelBase
{
private ObservableCollection<Object> _Companies = null;
public ObservableCollection<Object> Companies
{
get { return _Companies; }
set
{
if (_Companies != value)
{
_Companies = value;
RaisePropertyChanged(GetPropertyName(() => Companies));
}
}
}
public CompanyMgmtViewModel()
{
this.LoadData();
}
public void LoadData()
{
ObservableCollection<Object> records = new ObservableCollection<Object>();
var results = from res in AODB.Context.TCompanies
select res;
foreach (var item in results)
if (item != null) records.Add(item);
Companies = records;
}
}
The Model/Context code is just EF Database First generated.
You would need to bind your XamDataGrid's SelectedDataItems property to a property of type object[] ie. SelectedCompanies in your ViewModel and bind to that for your XamDataCards' datasource.
The accepted answer in this thread has a sample that shows how to do this, albeit with a ListBox instead of XamDataCards:
http://www.infragistics.com/community/forums/t/89122.aspx
Just replace that ListBox with your XamDataCards control, it works and updates the XamDataGrid. The ViewModel in the example is contained in the MainWindow code-behind, so it is MVVM like you want.
more info:
http://help.infragistics.com/Help/Doc/WPF/2014.1/CLR4.0/html/xamDataGrid_Selected_Data_Items.html
IG's SelectedDataItems is an object[] :
http://help.infragistics.com/Help/Doc/WPF/2014.1/CLR4.0/html/InfragisticsWPF4.DataPresenter.v14.1~Infragistics.Windows.DataPresenter.DataPresenterBase~SelectedDataItems.html
I couldn't have gotten to this answer without Theodosius' and Ganesh's input - so thanks to them, they both had partial answers.
I first tried to bind the SelectedDataItems of the XamDataGrid to the XamDataCards by way of a property on the ViewModel as Theodosius suggested, but that wasn't enough. Thanks to Ganesh, I implemented INotifyPropertyChanged on my model objects, by inheriting from ObservableObject in MVVMLight (how did I not know the Model needed this?).
Below are the relevant pieces of code to make it work.
I also implemented PropertyChanged.Fody as documented here; that's where the TypedViewModelBase<T> and removal of RaisePropertyChanged() comes from.
I'm also creating my Model objects by using a LINQ/Automapper .Project().To<T>() call which can be found here.
Model
public class Company : ObservableObject
{
public Company() { }
public int id { get; set; }
public string strName { get; set; }
public string strDomicileCode { get; set; }
}
ViewModel
public class CompanyMgmtViewModel : TypedViewModelBase<Company>
{
private ObservableCollection<Object> _Companies = null;
private Object[] _selectedCompany = null;
public Object[] Company
{
get { return _selectedCompany; }
set
{
if (_Company != value)
{
_selectedCompany = value;
}
}
}
public ObservableCollection<Object> Companies
{
get { return _Companies; }
set
{
if (_Companies != value)
{
_Companies = value;
}
}
}
public CompanyMgmtViewModel()
{
this.LoadData();
}
public void LoadData()
{
ObservableCollection<Object> records = new ObservableCollection<Object>();
var results = AODB.Context.TCompanies.Project().To<Company>();
foreach (var item in results)
if (item != null) records.Add(item);
Companies = records;
}
}
View
<igDP:XamDataGrid x:Name="dgCompanies"
Theme="IGTheme"
DataSource="{Binding Companies, Mode=OneWay}"
SelectedDataItemsScope="RecordsOnly"
SelectedDataItems="{Binding Company}">
...
<igDP:XamDataCards x:Name="XamDataCards1"
Grid.Row="1"
DataSource="{Binding ElementName=dgCompanies, Path=SelectedDataItems}"
Theme="IGTheme">
I want to show a ComboBox with OPTGROUP style header gruopings in Silverlight. Every website I find (including questions on SO) that sovle this link to an outdated link and, handily, show no code snippets for me to work from.
E.g.:
So how do I do this?
See my similar question: How to show group header for items in Silverlight combobox?
I put dummy entries in collection source to indicate the group header and then modified DataTemplate to show the group headers in different way and normal entries in separate way.
Here is one generalized approach. It's not bad, and should be flexible enough -- although it has the weakness of requiring an extended collection class (cannot make use of CollectionViewSource).
Step 1 ComboBoxGroupHeader object
This plays the role of "dummy entry" mentioned by #NiteshChordiya.
public class ComboBoxGroupHeader
{
public ComboBoxGroupHeader(object header)
{
Header = header;
}
public object Header { get; protected set; }
}
Step 2 Extended ComboBox
This overrides PrepareContainerForItemOverride, in order to tinker with the dummy items' containers. It also provides an (optional) "HeaderTemplate".
public class GroupedComboBox : ComboBox
{
public DataTemplate HeaderTemplate
{
get { return (DataTemplate)GetValue(HeaderTemplateProperty); }
set { SetValue(HeaderTemplateProperty, value); }
}
public static readonly DependencyProperty HeaderTemplateProperty =
DependencyProperty.Register("HeaderTemplate", typeof(DataTemplate), typeof(GroupedComboBox), new PropertyMetadata(null));
protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
{
base.PrepareContainerForItemOverride(element, item);
var container = element as ComboBoxItem;
if (container != null && item is ComboBoxGroupHeader)
{
// prevent selection
container.IsHitTestVisible = false;
// adjust the container to display the header content
container.ContentTemplate = HeaderTemplate
container.Content = ((ComboBoxGroupHeader)item).Header;
}
}
}
Step 3 Extended collection class
This does the grouping, and adds the dummy "ComboBoxGroupHeader" entries. This implementation is essentially read-only (groupings would break if you tried to add new items), but it would be straight-forward to support operations like "Add", "Insert", etc.
public class GroupedCollection<T, TGroup> : ObservableCollection<object>
{
private Func<T, TGroup> _grouping;
public IEnumerable<T> BaseItems
{
get { return base.Items.OfType<T>(); }
}
public GroupedCollection(IEnumerable<T> initial, Func<T, TGroup> grouping)
: base(GetGroupedItems(initial, grouping))
{
_grouping = grouping;
}
private static IEnumerable<object> GetGroupedItems(IEnumerable<T> items, Func<T, TGroup> grouping)
{
return items
.GroupBy(grouping)
.SelectMany(grp =>
new object[] { new ComboBoxGroupHeader(grp.Key) }
.Union(grp.OfType<object>())
);
}
}
Usage
It works like a normal ComboBox, with the optional HeaderTemplate:
<local:GroupedComboBox ItemsSource="{Binding Source}">
<local:GroupedComboBox.HeaderTemplate>
<TextBlock FontSize="9" TextDecorations="Underline" Foreground="DarkGray"
Text="{Binding}" />
</local:GroupedComboBox.HeaderTemplate>
</local:GroupedComboBox>
For the grouped to take effect, the source must use the "GroupedCollection" above, so as to include the dummy header items. Example:
public class Item
{
public string Name { get; set; }
public string Category { get; set; }
public Item(string name, string category)
{
Name = name;
Category = category;
}
}
public class Items : GroupedCollection<Item, string>
{
public Items(IEnumerable<Item> items)
: base(items, item => item.Category) { }
}
public class ViewModel
{
public IEnumerable<Item> Source { get; private set; }
public ViewModel()
{
Source = new Items(new Item[] {
new Item("Apples", "Fruits"),
new Item("Carrots", "Vegetables"),
new Item("Bananas", "Fruits"),
new Item("Lettuce", "Vegetables"),
new Item("Oranges", "Fruits")
});
}
}
Group header is not supported in ComboBox. Alternate way for to implement it with DataGrid or use TreeView to show grouped data.
However, you can try something like this,
Silverlight Custom ComboBox
How to add one object to ObservableCollection list object? I have class called "Assest" and I have created ObservableCollection list of Asset and I want to maintain it like adding and deleting element from that ObservableCollection list. Now I'm getting error when I try to add single element to ObservableCollection.
Here's my code.
private static ObservableCollection<Assest> _collection = null;
public ObservableCollection<Assest> AssestList
{
get
{
if (_collection == null)
{
_collection = new ObservableCollection<Assest>();
}
return _collection;
}
set { _collection = value; }
}
public static ObservableCollection<Assest> ToObservableCollection(List<Assest> assestList)
{
return new ObservableCollection<Assest>(assestList);
}
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
LoadData();
comboBox1.ItemsSource = AssestList;
}
private void LoadData()
{
Assest assest = new Assest() { AppID = "1", AssestName = "AppName", AppDescription = "Description" };
Assest assest2 = new Assest { AppDescription = "Des2", AppID = "2", AssestName = "hi" };
List<Assest> assList = new List<Assest> {assest, assest2};
ObservableCollection<Assest> generatedAssestList = ToObservableCollection(assList);
AssestList = generatedAssestList;
}
// Here I get an error.
public static void AddAppToObservalCollection(Assest ass)
{
_collection.Add(ass);
}
So How to over come these kind of situations. Thanks everyone.
Your code is a bit messy, it's not clear why you need both AssestList and _collection.
However, I think you need to replace
_collection.Add(ass);
with
AssestList.Add(ass);
_collection object still null while you call the getter of AssestList. So, when you use "_collection.Add(ass);", it can be null (and, btw _collection is private, so you can't access it from static function)
To avoid this, use always AssestList.