Get item from custom control / C# WPF - c#

I have 2 classes:
class MultiSwitch : ListBox
{
protected override bool IsItemItsOwnContainerOverride(object item)
{
return item is MultiSwitchItem;
}
protected override DependencyObject GetContainerForItemOverride( )
{
return new MultiSwitchItem( );
}
}
class MultiSwitchItem : ListBoxItem {...}
Basically, I want to place items depending on items count. So I subscribed to collection changed event and i'm trying to recieve MultiSwitchItem from Items collection. I found out that Items collection doesn't contain MultiSwitchItems. Then I found solution that looks like that:
private void RearrangeItems(
object sender,
NotifyCollectionChangedEventArgs notifyCollectionChangedEventArgs)
{
foreach (var item in Items)
{
MultiSwitchItem item2 = ItemContainerGenerator.ContainerFromItem(item) as MultiSwitchItem;
...
}
}
But item2 is always null. What am I doing wrong?
EDIT
Ok, I have a little progress with that. This issue appears only when control is initialized (so I think, wrappers for items are created not instantly). So the question now: how to force creating of MultiSwitchItems, or how to iterate over items AFTER wrappers was created?

instead of this
ItemContainerGenerator.ContainerFromItem(item as TextBlock)
do this
ItemContainerGenerator.ContainerFromItem(item)
item wont be a TextBlock, it will be the underlying data item

Related

How to find items in listbox using Textbox( localdatabase ) [duplicate]

This question already has an answer here:
How can I find an item in a WPF ListBox by typing?
(1 answer)
Closed 8 years ago.
I have a dictionary app and I have the word's database connected to my application, and now I want to make a simple search box (using a textBox and a Button) to search in my list box for a word.
I found this in the button code which is useful for me, but what about the rest?
private void searchBTN_Click_1(object sender, RoutedEventArgs e)
{
foreach (Wordst item in mainlist.Items)
{
if (item.listwordsw == txtSearch.Text)
{
//What should I have Here?
}
foreach (Wordst subItem in mainlist.Items)
{
if (subItem.Listwords2== txtSearch.Text)
{
//What should I have Here?
}
}
}
}
From MSDN
CollectionViewSource is a proxy for a CollectionView class, or a class derived from CollectionView. CollectionViewSource enables XAML code to set the commonly used CollectionView properties, passing these settings to the underlying view. CollectionViewSource has a View property that holds the actual view and a Source property that holds the source collection.
You can think of a collection view as the layer on top of the binding source collection that allows you to navigate and display the collection based on sort, filter, and group queries, all without having to manipulate the underlying source collection itself. If the source collection implements the INotifyCollectionChanged interface, the changes raised by the CollectionChanged event are propagated to the views.
Because views do not change the underlying source collections, each source collection can have multiple views associated with it. For example, you may have a collection of Task objects. With the use of views, you can display that same data in different ways. For example, on the left side of your page you may want to show tasks sorted by priority, and on the right side, grouped by area.
You want to use CollectionViewSource in order to support Filtering (finding items in ListBox. where lvDictionary is your ListBox
public partial class FilteringSample : Window
{
public FilteringSample()
{
InitializeComponent();
List<Word> items = new List<Word>();
items.Add(new User() { Name = "Apple"});
items.Add(new User() { Name = "Orange"});
items.Add(new User() { Name = "Pineapple" });
items.Add(new User() { Name = "Define",});
lvDictionary.ItemsSource = items;
CollectionView view = (CollectionView)CollectionViewSource.GetDefaultView(lvDictionary.ItemsSource);
view.Filter = UserFilter;
}
private bool UserFilter(object item)
{
if(String.IsNullOrEmpty(txtFilter.Text))
return true;
else
return ((item as Word).Name.IndexOf(txtFilter.Text, StringComparison.OrdinalIgnoreCase) >= 0);
}
private void txtFilter_TextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e)
{
CollectionViewSource.GetDefaultView(lvDictionary.ItemsSource).Refresh();
}
}

Multiple ListBox/Collection Items Selected for MVVM

I have a listbox that is bound to an ObservableCollection of Names. Some of the items in the list will have a checkbox that is toggled on/off, indicating the item has been selected.
How do I create an ObservableCollection from the selected items of the first listbox following the Master-Details concept?
(I plan to use my MasterViewModel as the DataContext for my DetailsView which displays the selected items Collection.)
Thanks in advance!
Yeah, I've come across this before as well. ListBoxes and the like have a dependency property called 'SelectedItem', but the 'SelectedItems' (with an 's') property is not implemented as one.
The cleanest solution I've found is just to subclass the listbox, and create my own dependency property called 'SelectedItems'. No fun, but it's I think the best solution.
UPDATE
First our ViewModel:
class ViewModel : INotifyPropertyChanged
{
// Set up our collection to be read from the View
public ObservableCollection<String> Collection { get; private set; }
// This collection will maintain the selected items
public ObservableCollection<String> SelectedItems { get; private set; }
public ViewModel()
{
// Instantiate
this.Collection = new ObservableCollection<String>();
this.SelectedItems = new ObservableCollection<String>();
// Now let's monitor when this.SelectdItems changes
this.SelectedItems.CollectionChanged += SelectedItems_CollectionChanged;
// Fill our collection with some strings (1 to 10).
// (1) Generate the numbers 1 - 10
// (2) Convert each number to a string
// (3) Cast into a list so we can use foreach
// (4) Add each item to the collection.
Enumerable.Range(1, 10)
.Select(number => number.ToString())
.ToList()
.ForEach(this.Collection.Add);
// Remember! Never reset the ObservableCollection.
// That is, never say this.Collection = new... (or you'll break the binding).
// instead use this.Collection.Clear(), and then add the items you want to add
}
void SelectedItems_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add)
{
foreach (String str in this.SelectedItems)
System.Diagnostics.Debug.WriteLine("New item added {0}", str);
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
Then our extended ListBoxEx:
class ListBoxEx : ListBox
{
// Use the 'new' keyword so that we 'hide' the base property.
// This means that binding will go to this version of SelectedItems
// rather than whatever the base class uses. To reach the base 'SelectedItems' property
// We just need to use base.SelectedItems instead of this.SelectedItems
// Note that we register as an observable collection.
new DependencyProperty SelectedItemsProperty =
DependencyProperty.Register("SelectedItems", typeof(ObservableCollection<String>), typeof(ListBoxEx));
// Accessor. Again, note the 'new'.
new public ObservableCollection<String> SelectedItems
{
get { return (ObservableCollection<String>) GetValue(SelectedItemsProperty); }
set { SetValue(SelectedItemsProperty, value); }
}
protected override void OnSelectionChanged(SelectionChangedEventArgs e)
{
// Guard against ViewModel being null
if (this.SelectedItems != null)
{
// Clear the list
this.SelectedItems.Clear();
// (1) On selection changed. Get the new base.SelectedItems
// (2) Cast each item to a String ("Make a string collection")
// (3) Cast to list, and use foreach to add each item to
// this.SelectedItems (note this is different from the original base.SelectedItems)
base.SelectedItems.Cast<String>()
.ToList()
.ForEach(this.SelectedItems.Add);
}
}
}
And finally our View:
<Window.DataContext>
<lol:ViewModel />
</Window.DataContext>
<Grid>
<lol:ListBoxEx ItemsSource="{Binding Collection}" SelectedItems="{Binding SelectedItems}"
SelectionMode="Multiple"/>
</Grid>

How to notify the UI that a change has taken place in one or several of a List's members

I have the following problem:
I am adding an element to a favorites list (List<Item>) through a ContextMenu. Each Item has a derived property IsFavorite that changes depending on the favorites list - like so:
public bool IsFavorite
{
get { return ItemController.FavoriteList.Contains( this ); }
}
When I add something (or delete it) from the ContextMenu, the ContextMenu must be immediately updated.
Now, I know this is possible through using an ObservableCollection, but due to a few factors out of my control, I must make due with List objects. Now, is there any way I can get this to refresh?
public void DeleteFromFavorites(Item item)
{
Item itemInMainList = MainList.First(item);
itemInMainList.Refresh();
}
Item.cs:
public bool IsFavorite
{
get { return ItemController.FavoriteList.Contains( this ); }
}
public void Refresh()
{
NotifyPropertyChanged("IsFavorite");
}

How to fluidly reorder a collection in a listbox?

The situation is like this: I have an Observable Collection that has a bunch of objects in it which I then display to a listbox using a binding. Then periodically, I get a message from an external server that gives me a new (or possibly the same) ordering for these objects. Right now, I just clear the observable collection, and add each item back in using the specified ordering.
This doesn't look too nice. Is there a better way to go about doing this? I'd be really awesome if I could somehow get the listbox to reorder and have a nice reordering animation with it, but that might be asking too much.
I thought about adding the ordering as an attribute to each object in the ObservableCollection and then calling a sort on it. Would this look clean? My assumption is that it would be almost the same effect as clearing it and readding everything.
Thanks for any help!
I created a class that inherits from ObservableCollection. This class has a SetItems method where you need to pass in the newly ordered items. Key in this class is that it will suppress the collectionchanged event and thus will not refresh the listbox each time an item is added. It looks better and loads a lot faster.
public class SuperObservableCollection<T> : ObservableCollection<T>
{
public void SetItems(IEnumerable<T> items)
{
suppressOnCollectionChanged = true;
Clear();
AddRange(items);
}
private bool suppressOnCollectionChanged;
public void AddRange(IEnumerable<T> items)
{
suppressOnCollectionChanged = true;
if (items != null)
{
foreach (var item in items)
Add(item);
}
suppressOnCollectionChanged = false;
NotifyCollectionChanged();
}
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (!suppressOnCollectionChanged)
base.OnCollectionChanged(e);
}
public void NotifyCollectionChanged()
{
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
}

Cancel collection changed event on an observable collection

How can i cancel the collection changed event on an observable collection?
When my collection changes it invokes methods on a third party dll.These methods may or may not fail.
If they fail, i want dont want the item to be added to or removed from the collection. Looking at the name, it looks like the collection changed event is fired after something has been added or deleted, but how could i achieve my functionality?
Too late but it might help someone else :
class ExtendedObservableCollection<T> : ObservableCollection<T>
{
private bool _suppressNotification = false;
public bool AllowNotifications { get { return _suppressNotification; } }
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (!_suppressNotification)
base.OnCollectionChanged(e);
}
protected override void OnPropertyChanged(PropertyChangedEventArgs e)
{
if (!_suppressNotification)
base.OnPropertyChanged(e);
}
public void ActivateNotifications()
{
_suppressNotification = false;
}
public void DesactivateNotifications()
{
_suppressNotification = true;
}
public void AddRange(IEnumerable<T> list)
{
if (list == null)
throw new ArgumentNullException("list");
_suppressNotification = true;
foreach (T item in list)
{
Add(item);
}
_suppressNotification = false;
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add));
}
}
I'll share the homely hack I came up with for my WPF/MVVM solution. In the methods I call for e.Action == NotifyCollectionChangedAction.Remove or e.Action == NotifyCollectionChangedAction.Add I check for failure and take any actions required to undo the change and set a boolean member _updateObservable to true. Since I can't modify the ObservableCollection during the change event, I have to defer it. Setting the boolean seemed like the easiest thing.
Then in the view model I have a property used for binding the selected item on the observable. I added to that property get method if (_updateObservable) UpdateObservable(); It appears the selected item bound property always fires its getter even if the item added or deleted does not directly affect the selected item. UpdateObservable() adds back or removes any items required from the collection and sets the flag to false.
You could simply override that particular method using the new command .
If you can manage to handle the event in your code, NotifyCollectionChangedEventArgs.NewItems returns an IList of the new items involved in the change. You could then remove these items from the collection if the methods in the third party dll failed.

Categories