I am currently working on a WPF project in .NET Core 3.1. During the execution I have to read some file names to a combobox. I do that by first adding them to a list. After that I iterate through that list and add them to the combobox.
Problem
Because they should only be accessible if an Item from another combobox is selected, I need to disable the items I just added. Because of that, and to later enable them, I need to name them. But I could not figure our how to do that.
Here is my code:
string[] allAircraft;
allAircraft = Directory.GetFiles(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + #"\ApplicationName\Aircraft\", "*.lxml", SearchOption.TopDirectoryOnly);
foreach (var currentAircraft in allAircraft)
{
aircraft.Add(System.IO.Path.GetFileNameWithoutExtension(currentAircraft));
}
foreach (string currentListItem in aircraft)
{
comboAircraft.Items.Add(currentListItem);
}
foreach (ComboBoxItem currentComboItems in comboAircraft.Items)
{
currentComboItems.Name = "comboAircraftItems" + currentComboItems.Content;
}
At the line foreach (ComboBoxItem currentItems in comboAircraft.Items) I get the Exception:
System.InvalidCastException: 'Unable to cast object of type 'System.String' to type 'System.Windows.Controls.ComboBoxItem'.'
I am really running out of ideas. I googled one entire day but could not find anything.
Have a nice day everyone!
# t-kldw, Your task is very difficult to solve when working directly with UI elements.
But it is very simple to solve, if you separate the Data and their Presentation, as it should be in MVVM.
Unfortunately, what you wrote contains few details for an example with full implementation.
I can give only partial advice.
It is necessary to create an additional container-class for the data needed in one combo box.
This class MUST have an INotifyPropertyChanged implementation.
To simplify, take implementation from the topic OnPropertyChanged and instance property
/// <summary>Base class implementing INotifyPropertyChanged.</summary>
public abstract class BaseINPC : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>Called AFTER the property value changes.</summary>
/// <param name="propertyName">The name of the property.
/// In the property setter, the parameter is not specified. </param>
public void RaisePropertyChanged([CallerMemberName] string propertyName = "")
=> PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
An example class with two properties: File Name and IsEnabled to allow selection.
public class FileItem : BaseINPC
{
private string _fileName;
private bool _isEnabled;
public string FileName { get => _fileName; set { _fileName = value; RaisePropertyChanged(); } }
public bool IsEnabled { get => _isEnabled; set { _isEnabled = value; RaisePropertyChanged(); } }
}
So that I can give further advice, you need to provide more details of your task.
For example, I don’t even have a clue why currentComboItems.Name might be needed ....
I think that you are clearly doing something wrong.
You are trying to convert String to ComboBoxItem. When you populate your combobox with data, you add String, not ComboBoxItem. So you should use String in the last foreach loop.
foreach (string currentComboItems in comboAircraft.Items)
{
currentComboItems.Name = "comboAircraftItems" + currentComboItems;
}
Another solution is to add ComboBoxItem to your combobox instead of String.
foreach (string currentListItem in aircraft)
{
ComboBoxItem item = new ComboBoxItem()
{
Content = currentListItem,
Name = "comboAircraftItems" + currentListItem
};
comboAircraft.Items.Add(item);
}
ItemsControl.Items only holds data objects. In order to render this items, each is wrapped into a container e.g., ComboBoxItem. These UI containers are generated by a ItemContainerGenerator, which is associated with the current ItemsControl. The ItemContainerGenerator is also responsible to track the mapping between the data item and its UI container.
To obtain the container of an item you use the ItemsControl.ItemContainerGenerator:
DependencyObject itemContainer = ItemContainerGenerator.ContainerFromItem(item);
Your simplified code becomes:
List<string> allAircrafts = Directory.EnumerateFiles(
Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
#"\ApplicationName\Aircraft\"),
"*.lxml",
SearchOption.TopDirectoryOnly)
.Select(Path.GetFileNameWithoutExtension)
.ToList();
comboAircraft.ItemsSource = allAircrafts;
foreach (string item in allAircrafts)
{
var itemContainer = comboAircraft.ItemContainerGenerator.ContainerFromItem(item) as ComboBoxItem;
itemContainer.Name = "comboAircraftItems" + itemContainer.Content;
}
But note, that the approach you chose has issues. When UI virtualization is enabled for ItemsCopntrol, then only the visible items have their containers generated. Items out side the viewport of the scroll viewer are not generated and can't be returned by the ItemContainerGenerator.
Since UI virtualization is not enabled for the ComboBox by default, this still can work, as long you don't enable this performance optimization feature.
The optimal solution would need you to create a filtered source collection for the ComboBox that displays the filtered items. This way you won't clutter the control with items that are not of interest for the user and are not selectable anyway.
Create a collection of type ObservableCollection<string> which contains the selected items of the primary ComboBox you named comboAircraft.
Set this collection as ItemsSource of the second filtered ComboBox.
Alternatively create a CollectionViewSource of the original source collection and bind it to the secondary ComboBox. ICollectionView supports filtering.
The proper solution
Using either of the two recommended solutions eliminates the requirement to care about item containers. This adds flexibility and simplifies your code.
Related
Could someone help me, how can I update ObservableCollection, which is binded to ListView ItemSource, without blinking? When I do:
Contacs = _contacs;
the whole ListView is blinking. I would like to search in ListView too, but always after replacing the old results with new one, the listview blinks.
The problem here is, that you are reassigning the whole collection. This does not take advantage of the observability and forces the ListView to reload all items. Try to remove/add the items instead so the ListView only needs to update the Items that actually changed.
In the case of searching hiding the unmatched results might be a viable solution too. To do that create a property on your Contact type (called "IsVisible" for example) and bind it to the ListViewItems Visibility Property. (You might need the build in BooleanToVisibility Converter here)
Update
As pointed out in the comments using a CollectionViewSource is the correct wpf way of implementing a search filter. See this answer for details on how to use it.
If you want to enable filtering in your collection then there is no actual need to perform operations directly on your collection.
Use ICollectionView and CollectionViewSource for this purpose.
As you have an ObservableCollection so you can do something like this.
ICollectionView contactsView;
public ICollectionView ContactsView
{
get { return contactsView; }
set
{
if(contactsView != value)
{
contactsView = value;
}
}
}
And in the setter of the ObservableCollection
public ObservableCollection<ContactType> Contacs
{
get { return _contacs; }
set
{
if(_contacs != value)
{
_contacs = value;
ContactsView = CollectionViewSource.GetDefaultView(value);
ContactsView.Filter = ContactsFilter;
}
}
}
where ContactsFilter is a function with following definition.
bool ContactsFilter(object item)
{
var contact = item as ContactType;
if(condition)
return true; //show this item in ListView.
return false; //Do not show this item in ListView
}
and whenever you want to filter items you can do that just by
ContactsView.Refresh();
which I think will be in the TextChanged Event of your text box in which you are entering search query.
More detailed article is at CollectionViewSource
I am using a ListView control to display some lines of data. There is a background task which receives external updates to the content of the list. The newly received data may contain less, more or the same number of items and also the items itself may have changed.
The ListView.ItemsSource is bound to an OberservableCollection (_itemList) so that changes to _itemList should be visible also in the ListView.
_itemList = new ObservableCollection<PmemCombItem>();
_itemList.CollectionChanged += new NotifyCollectionChangedEventHandler(OnCollectionChanged);
L_PmemCombList.ItemsSource = _itemList;
In order to avoid refreshing the complete ListView I do a simple comparison of the newly retrieved list with the current _itemList, change items which are not the same and add/remove items if necessary. The collection "newList" contains newly created objects, so replacing an item in _itemList is correctly sending a "Refresh" notification (which I can log by using the event handler OnCollectionChanged of the ObservableCollection`)
Action action = () =>
{
for (int i = 0; i < newList.Count; i++)
{
// item exists in old list -> replace if changed
if (i < _itemList.Count)
{
if (!_itemList[i].SameDataAs(newList[i]))
_itemList[i] = newList[i];
}
// new list contains more items -> add items
else
_itemList.Add(newList[i]);
}
// new list contains less items -> remove items
for (int i = _itemList.Count - 1; i >= newList.Count; i--)
_itemList.RemoveAt(i);
};
Dispatcher.BeginInvoke(DispatcherPriority.Background, action);
My problem is that if many items are changed in this loop, the ListView is NOT refreshing and the data on screen stay as they are...and this I don't understand.
Even a simpler version like this (exchanging ALL elements)
List<PmemCombItem> newList = new List<PmemCombItem>();
foreach (PmemViewItem comb in combList)
newList.Add(new PmemCombItem(comb));
if (_itemList.Count == newList.Count)
for (int i = 0; i < newList.Count; i++)
_itemList[i] = newList[i];
else
{
_itemList.Clear();
foreach (PmemCombItem item in newList)
_itemList.Add(item);
}
is not working properly
Any clue on this?
UPDATE
If I call the following code manually after updating all elements, everything works fine
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
But of course this causes the UI to update everything which I still want to avoid.
After a change, you can use the following to refresh the Listview, it's more easy
listView.Items.Refresh();
This is what I had to do to get it to work.
MyListView.ItemsSource = null;
MyListView.ItemsSource = MyDataSource;
I know that's an old question, but I just stumbled upon this issue. I didn't really want to use the null assignation trick or the refresh for just a field that was updated.
So, after looking at MSDN, I found this article:
https://learn.microsoft.com/en-us/dotnet/api/system.componentmodel.inotifypropertychanged?redirectedfrom=MSDN&view=netframework-4.7.2
To summarize, you just need the item to implement this interface and it will automatically detect that this object can be observed.
public class MyItem : INotifyPropertyChanged
{
private string status;
public string Status
{
get => status;
set
{
OnPropertyChanged(nameof(Status));
status = value;
}
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
So, the event will be called everytime someone changes the Status. And, in your case, the listview will add a handler automatically on the PropertyChanged event.
This doesn't really handle the issue in your case (add/remove).
But for that, I would suggest that you have a look at BindingList<T>
https://learn.microsoft.com/en-us/dotnet/api/system.componentmodel.bindinglist-1?view=netframework-4.7.2
Using the same pattern, your listview will be updated properly without using any tricks.
You should not reset ItemsSource of ListView each time observable collection changed. Just set proper binding that will do your trick. In xaml:
<ListView ItemsSource='{Binding ItemsCollection}'
...
</ListView>
And in code-behind (suggest to use MVVM) property that will be responsible for holding _itemList:
public ObservableCollection<PmemCombItem> ItemsCollection
{
get
{
if (_itemList == null)
{
_itemList = new ObservableCollection<PmemCombItem>();
}
return _itemList;
}
}
UPDATE:
There is similar post which most probably will Answer your question: How do I update an ObservableCollection via a worker thread?
I found a way to do it. It is not really that great but it works.
YourList.ItemsSource = null;
// Update the List containing your elements (lets call it x)
YourList.ItemsSource = x;
this should refresh your ListView (it works for my UAP :) )
An alternative on Xopher's answer.
MyListView.ItemsSource = MyDataSource.ToList();
This refreshes the Listview because it's a other list.
Please check this answer:
Passing ListView Items to Commands using Prism Library
List view Items needs to notify about changes (done is setter)
public ObservableCollection<Model.Step> Steps
{
get { return _steps; }
set { SetProperty(ref _steps, value); }
}
and UpdateSourceTrigger need to be set in xaml
<Image Source="{Binding ImageData, UpdateSourceTrigger=PropertyChanged}" />
I am dynamically creating ListViewItem descendants:
class Application : ListViewItem {
. . .
class LegacyApplication : Application {
I store these first in a List of obects:
private List<object> legacyApps = new List<object>();
..this way:
if (ldbc.appIsLegacy(sPathOfExe)) {
legacyApp = new LegacyApplication(sApp, sTitle, sDesc, sIconFileName, sPathOfExe, appCategoriesForCurrentApp);
}
legacyApps.Add(legacyApp);
...and then I add them to the ListView on the main form this way:
foreach (LegacyApplication LegApp in legacyApps) {
this.listApplications.Items.Add(LegApp);
}
...but the ListView does not display them. It displays the ListView Groups I've created (and each ListViewItem is assigned to one of those groups), but not the ListViewItems themselves...
Updated with requested info:
The constructor for the ListViewItem descendant looks like so:
public LegacyApplication(String AAppName, String ATitle, String ADesc, String AIconFileName, String APathOfExe, List<String> ACategories) {
base.Name = String.Format("legapplvi{0}", AAppName);
base.Text = ATitle; // "Title" is a short description - between exe name and Description
base.ToolTipText = ADesc;
base.EnsureVisible();
// "base" above means ListViewItem; "base" below refers to our Application class*
base.Categories = ACategories;
base.ExePath = APathOfExe;
base.IconFileName = AIconFileName;
}
which adds the following properties to ListViewItem:
public string ExePath
public string IconFileName
public string Category
public List Categories
LegacyApplication adds no further properties to (our) Application class.
I'm not sure what the respondent below means by "subitems" - the ListViewItems are subitems of the ListView Groups, perhaps...?
Updated with unrequested info (TMI?):
OK, I'm thinking I can add columns this way, once all the Groups are assigned to the ListView:
for(var item in listApplications.Groups) {
listApplications.Columns.Add(item)
}
...but now, how do I add specific ListViewItems to particular columns?
Updated after getting it to (sort of) work:
Commenting out this:
listApplications.View = View.Details;
...gets the Items to show. HOWEVER, the Text is truncated, which caused me to pose another question here:
I need to display the entire Text of my ListViewItems (not truncated)
Did you try to create a ListViewItem first and then add into the List instead of using
this.listApplications.Items.Add(LegApp);
Can you provide more details with a code snippet
I have a list of Strings.
Well, conceptually. They are stored somewhere else, but I want provide an object which acts like a list (and provides any necessary events on top of that), with properties that I could bind to.
I want to establish a two-way binding over this data, to display it as a modifiable column in a DataGrid. I have the following problems with that:
I can't make a two-way binding because the binding needs a path (i.e. I can't have it look like {Binding} or {Binding Path=.} in the column, must be {Binding Path=someField"} to be made modifiable if I got this right, which sounds reasonable).
I don't exactly know how the proxy collection object should look like, in terms of interfaces (would IEnumerable + INotifyCollectionChanged sufficient?)
Is there any solution which doesn't involve creating one proxy object per every String in the collection? Could you suggest an efficient design?
To keep the discussion on the rails, let's assume I want to bind to something like this:
class Source {
public String getRow(int n);
public void setRow(int n, String s);
public int getCount();
public void addRow(int position, String s);
public void removeRow(int position);
}
That's not exactly my case, but when I know how to bind to this, I think I'll be able to handle any situation like this.
I'm OK with having to provide an adapter object on top of that Source, with any necessary interfaces and events, but I don't want to have one adapter object per row of data.
While making an adapter for the Source is relatively clear, then, unfortunatelly, the core of the second problem ('not wrapping every string in a miniobject') is a clash built into the .Net and WPF..
The first thing is that the WPF does provide you with many ways of registering 'on data modified' callbacks, but provides no way of registering callbacks that would provide a value. I mean, the "set" phase is only extendable, not interceptable, and the "get" - nothing at all. WPF will simply keep and return whatever data it has once cached.
The second thing is that in .Net the string is ... immutable.
Now, if ever you provide a string directly as a pathless binding or as a datacontext to any control, you are screwed in a dead end. The problem is, that WPF actually passes only the actual value of the binding, without the information of "where it came from". The underlying control will be simply given the string instance, and will have no sane way of modifying it as the string cannot change itself. You will not be even notified about such attempt, just like with read-only properties. What's more - if you ever manage to intercept such a modification attempt, and if you produce a proper new string, the WPF will never ask you again for the new value. To update the UI, you'd have to mannually, literally, force the WPF to re-ask you by for example changing the original binding so it points elsewhere (to the new value) or set the datacontext (to the new instance). It is doable with some VisualTree scanning, as every 'changed' callback gives you the DependencyObjects (Controls!), so yo ucan scan upwards/downwards and tamper with their properties.. Remember that option - I'll refer to this in a minute.
So, everything boils down to the fact that to get a normal 2-way binding you do not have to have a Path, you "just" have to have a mutable underlying data object. If you have immutable one - then you have to use a binding to a mutable property that holds the immutable value..
Having said that, you simply have to wrap the strings some how if you want to modify them.
The other question is, how to do that. There's a plenty of ways to do it. Of course, you can simply wrap them like Joe and Davio suggested (note to Joe: INotify would be needed there also), or you can try to do some XAML tricks with attached properties and/or behaviours and/or converters to do that for you. This is completely doable, see for example my other post - I've shown there how to "inject a virtual property" that pulled the data completely from elsewhere (one binding+converter performed the wrapping on the fly, second binding extracted the values from the attached-wrapper). This way you could create a "Contents" property on the string, and that property could simply return the string itself, and it'd be completely 2-way bindable with no exceptions.
But.. it would NOT work 2-way-ish.
Somewhere at the root of your binding/behaviour/conveter chain, there will be an immutable string. Once your smart autowrapping binding chain fires with 'on modified' callback you will be notified with pair of old/new values. You will be able to remap the values to new and old strings. If you implemented everything perfectly, the WPF will simply use the new value. If you tripped somewhere, then you will have to push the new value artificially back to the UI (see the options I'd asked you to remember). So, it's ok. No wrapper, old value was visible, it was changeable, you've got new value, the UI displays new value. How about storage?
Somewhere in the meantime you've been given a old/new value pair. If you analyze them, you'll get old/new strings. But how do you update the old immutable string? Can't do. Even if autowrapping worked, even if UI worked, even if editing seemed to work, you are now standing with the real task: you onmodified callback was invoked and you have to actually update that immutable string piece.
First, you need your Source. Is it static? Phew. What a luck! So surely it is instanced. In the on-modified callback we got only a old+new string.. how to get the Source instance? Options:
scan the VisualTree and search for it in the datacontexts and use whatever was found..
add some more attached properties and binding to bind a virtual "Source" property to every string and read that property from the new value
Well doable, but smells, but no other options.
Wait, there's more: not only the old/new value and an instance of Source are needed! You also need the ROW INDEX. D'oh! how to get that from the bound data? Again, options:
scan the VisualTree and search for it (blaargh)...
add some more attached properties and bindings to bind a virtual "RowIndex" property to every (blaaergh)...
At this point of time, while I see that all of this seems implementable and actually might be working properly, I really think that wrapping each string in a small
public class LocalItem // + INotifyPropertyChanged
{
public int Index { get; }
public Source Source { get; }
public string Content
{
get { Source...}
set { Source... }
}
}
will simply be more readable, elegant and .. SHORTER to implement. And less error-prone, as more details will be explicit instead of some WPF's binding+attached magic..
I find your approach a little weird.
DataGrids are usually used to display Rows. Rows consist of data that belongs together.
You could for instance easily map a row to a certain class. This means that the columns in your datagrid represent properties in your class.
What you're trying to do is the opposite, you're trying to get a relation between the column values instead of the row values.
Wouldn't it be easier to have a collection of your class which you can then bound the column to?
For instance
class MyClass : INotifyPropertyChanged
{
// Remember to actually implement INotifyPropertyChanged
string Column;
}
If you would have an ObservableCollection of MyClass you could bind the DataGrid to this collection. Whenever the property which I called "Column" changes, you could update your special list.
You can do this by hooking up some events. With the implementation of INotifyPropertyChanged, your columns will be updated if you update the "Column"-value directly.
I have this bit of code I use to bind a list of custom object to a DataContextMenu. You can alter it to use a list of strings and bind it to what you need
class SampleCode
{
class Team
{
private string _TeamName = "";
private int _TeamProperty1 = 0;
ObservableCollection<Territory> _Territories = new ObservableCollection<Territory>();
public Team(string tName)
{
this.TeamName = tName;
}
public ObservableCollection<Territory> Territories
{
get { return _Territories; }
set { _Territories = value; }
}
public string TeamName
{
get { return _TeamName; }
set { _TeamName = value; }
}
public int TeamProperty1
{
get { return _TeamProperty1; }
set { _TeamProperty1 = value; }
}
}
class Territory
{
private string _TerritoryName = "";
Team _AssociatedTeam = null;
public Territory(string tName, Team team)
{
this.TerritoryName = tName;
this.AssociatedTeam = team;
}
public Team AssociatedTeam
{
get { return _AssociatedTeam; }
set { _AssociatedTeam = value; }
}
public string TerritoryName
{
get { return _TerritoryName; }
set { _TerritoryName = value; }
}
public void Method1()
{
//Do Some Work
}
}
class MyApplication
{
ObservableCollection<Team> _Teams = new ObservableCollection<Team>();
ContextMenu _TeritorySwitcher = new ContextMenu();
public MyApplication()
{
}
public void AddTeam()
{
_Teams.Add(new Team("1"));
_Teams.Add(new Team("2"));
_Teams.Add(new Team("3"));
_Teams.Add(new Team("4"));
foreach (Team t in _Teams)
{
t.Territories.Add(new Territory("1", t));
t.Territories.Add(new Territory("2", t));
t.Territories.Add(new Territory("3", t));
}
SetContextMenu();
}
private void SetContextMenu()
{
HierarchicalDataTemplate _hdtTerritories = new HierarchicalDataTemplate();
_hdtTerritories.DataType = typeof(Territory);
HierarchicalDataTemplate _hdtTeams = new HierarchicalDataTemplate();
_hdtTeams.DataType = typeof(Team);
FrameworkElementFactory _TeamFactory = new FrameworkElementFactory(typeof(TreeViewItem));
_TeamFactory.Name = "txtTeamInfo";
_TeamFactory.SetBinding(TreeViewItem.HeaderProperty, new Binding("TeamProperty1"));
FrameworkElementFactory _TerritoryFactory = new FrameworkElementFactory(typeof(TreeViewItem));
_TerritoryFactory.Name = "txtTerritoryInfo";
_TerritoryFactory.SetBinding(TreeViewItem.HeaderProperty, new Binding("TerritoryProperty1"));
_hdtTeams.ItemsSource = new Binding("Territories");
_hdtTeams.VisualTree = _TeamFactory;
_hdtTerritories.VisualTree = _TerritoryFactory;
_hdtTeams.ItemTemplate = _hdtTerritories;
_TeritorySwitcher.ItemTemplate = _hdtTeams;
_TeritorySwitcher.ItemsSource = this._Teams;
}
}
}
Lazy solution
Derive from ObservableCollection<string> and let that collection be populated from the Source. In the derived class, register to collection change events and update the source accordingly. Bind the DataGrid column to the observable collection.
This should be pretty straightforward to write, but has a big drawback of duplicating all data in the collection.
More efficient solution
Create an adapter (as you suggested) and implement IList<string> and INotifyCollectionChanged. Let the list operations fall through directly to the source. Bind the DataGrid column to the adapter.
This approach would require some tedious boilerplate, but it's a thin layer between the WPF control and your Source.
This really depends on how you're implementing the UI. Bea Stollnitz did an excellent post of virtualizing the ItemsSource for the WPF DataGrid at http://bea.stollnitz.com/blog/?p=344 . With work I used this to edit as well as display data.
The easiest way is by placing the string in a wrapper class.
public class Wrapper
{
public string Content{get;set;}
}
Then you use the string via the wrapper class. This was the list items remain the same but the content changes.
The problem is when you do this without that then an old string is being deleted and a new one is created and the collection is confused.
Start with an ObservableCollection<string>. Then set the bindable control's ItemsSource to the ObservableCollection.
I have an ItemsControl displaying a collection of files. Those files are sorted by most recent modification, and there's a lot of them.
So, I want to initially only show a small part (say, only 20 or so) of them, and display a button labelled "Show More" that would reveal everything when clicked.
I already have a solution, but it involves using a good old LINQ Take on my view model's source property. I was wondering if there was a cleaner way.
Thanks.
Why not have the object that you assign to the ItemsSource handle this logic - on first assignment, it would report a limited subset of the items. When Show More is clicked, the object is updated to show more (or all entries) and then notifies the framework that the property has changed (e.g. using the IPropertyNotifyChanged).
public class MyItemSource
{
private List<string> source = { ... };
public MyItemSource()
{
this.ShowThisMany = 20;
}
public int ShowThisMany
{
get;
set; // this should call\use the INotifyPropertyChanged interface
}
public IEnumerable<string> this[]
{
return this.source.Take(this.ShowThisMany);
}
}
...
MyItemsSource myItemsSource = new MyItemsSource();
ItemsControl.Source = myItemsSource;
...
void OnShowMoreClicked(...)
{
myItemsSource.ShowThisMany = 50;
}
In order to do this, you need to create some sort of 'view' on your data. There is nothing within the WPF framwork that will give you this functionality for free. In my opinion, a simple bit of Linq, Take(), is a clean and simple solution.