i am currently trying to remove items from a bound list.
Here is where it is bound in the xaml.
<ListBox Height="362" HorizontalAlignment="Left" Margin="6,245,0,0" Name="lstHoldCategories" VerticalAlignment="Top" Width="462" SelectionChanged="list_SelectionChanged_1" BorderThickness="0,0,0,0">
<ListBox.ItemTemplate>
<DataTemplate>
<!--This positions the Text eg name etc-->
<StackPanel Orientation ="Vertical">
<!--This changes the size of the photo on the left-->
<Image Width="445" Height="300" HorizontalAlignment="Center" Stretch="UniformToFill" >
<Image.Source>
<BitmapImage UriSource="{Binding imgSource}"/>
</Image.Source>
</Image>
<TextBlock Text="{Binding Name}" Style="{StaticResource PhoneTextLargeStyle}" Width="1000" />
<TextBlock Text="{Binding Type}" Style="{StaticResource PhoneTextLargeStyle}" Width="1000" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I have then made a seperate generic list to be held in a seperate unbound listBox so that i can select a "Type" and load up all the animals of that type.
Here is the code where i set up the unbound list
public CategorySearch()
{
InitializeComponent();
observablePets = new ObservableCollection<Shop>();
temp = new ObservableCollection<Shop>();
MyList.Add("Dog");
MyList.Add("Cat");
MyList.Add("Fish");
MyList.Add("Lizard");
lstCategory.ItemsSource = MyList;
}
and this is where i have done the SelectedIndex of the unbound listBox to add in the animals of the selected "Type"
private void lstCategory_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (lstCategory.SelectedIndex == 0)
{
foreach (Shop pet in thisApp.myshop)
{
if (pet.Type == "Dog")
{
//lstHoldCategories.Items.Clear();
temp.Add(pet);
lstHoldCategories.ItemsSource = temp;
}
}
}
if (lstCategory.SelectedIndex == 1)
{
foreach (Shop pet in thisApp.myshop)
{
if (pet.Type == "Cat")
{
//lstHoldCategories.Items.Clear();
temp.Add(pet);
lstHoldCategories.ItemsSource = temp;
}
}
}
if (lstCategory.SelectedIndex == 2)
{
foreach (Shop pet in thisApp.myshop)
{
if (pet.Type == "Fish")
{
//lstHoldCategories.Items.Clear();
temp.Add(pet);
lstHoldCategories.ItemsSource = temp;
}
}
}
if (lstCategory.SelectedIndex == 3)
{
foreach (Shop pet in thisApp.myshop)
{
if (pet.Type == "Lizard")
{
//lstHoldCategories.Items.Clear();
temp.Add(pet);
lstHoldCategories.ItemsSource = temp;
}
}
}
}
As you can see in this piece of code, I have commented out the piece of code that i believed would empty the listBox on the selectedIndex and reload the listBox with the new selection. Unfortunately it doesn't work and crashes the app when you select an index.
If there is a different way to empty the listBox that is bound, i would appreciate someone advising me how to do it,
Thanks in advance,
Jason
////Pics\\
This is what the page looks like before an index is selected
This is what the bound listBox will look like when you select an index
You need to clear the collection itself, rather than the object bound to the collection. A quick search showed up this... Delete all items from listobox
Just to clarify, the Items collection lives on the ListBox and that property is readonly. So you need to remove the items from the collection your ListBox is actually bound to.
you should just be able to call clear on temp prior to adding you new items. But you will need to make sure your collection source implements the INotifyCollectionChanged to see the changes reflected in the UI.
Related
I'm trying to use a ListBox to choose an entry and then display a picture belonging to this selected entry. But just at the beginning I got my first problem: filling the ListBox with binding is working, but if I click on one line in my running program, it doesn't select the line. I can just see the highlighted hover effect, but not select a line. Any ideas what my mistake could be?
This is my XAML:
<ListBox x:Name="entrySelection" ItemsSource="{Binding Path=entryItems}" HorizontalAlignment="Left" Height="335" Margin="428,349,0,0" VerticalAlignment="Top" Width="540" FontSize="24"/>
And in MainWindow.xaml.cs I'm filling the ListBox with entries:
private void fillEntrySelectionListBox()
{
//Fill listBox with entries for active user
DataContext = this;
entryItems = new ObservableCollection<ComboBoxItem>();
foreach (HistoryEntry h in activeUser.History)
{
var cbItem = new ComboBoxItem();
cbItem.Content = h.toString();
entryItems.Add(cbItem);
}
this.entrySelection.ItemsSource = entryItems;
labelEntrySelection.Text = "Einträge für: " + activeUser.Id;
//show image matching the selected entry
if (activeUser.History != null)
{
int index = entrySelection.SelectedIndex;
if (index != -1 && index < activeUser.History.Count)
{
this.entryImage.Source = activeUser.History[index].Image;
}
}
}
So I can see my ListBox correctly filled, but not select anything - so I can't go on with loading the picture matching the selected entry.
I'm still quite new to programming, so any help would be great :)
EDIT: If someone takes a look at this thread later: here's the - quite obvious -solution
XAML now looks like this
<ListBox x:Name="entrySelection" ItemsSource="{Binding Path=entryItems}" HorizontalAlignment="Left" Height="335" Margin="428,349,0,0" VerticalAlignment="Top" Width="540" FontFamily="Siemens sans" FontSize="24">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Text}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Code behind to fill it:
//Fill listbox with entries for selected user
DataContext = this;
entryItems = new ObservableCollection<DataItem>();
foreach (HistoryEntry h in selectedUser.History)
{
var lbItem = new DataItem(h.toString());
entryItems.Add(lbItem);
}
this.entrySelection.ItemsSource = entryItems;
labelEntrySelection.Text = "Einträge für: " + selectedUser.Id;
And new Class DataItem:
class DataItem
{
private String text;
public DataItem(String s)
{
text = s;
}
public String Text
{
get
{
return text;
}
}
}
You are filling it with ComboBoxItem, which is not relevant to the ListBox, and also wrong by definition.
You need to have the ObservableCollection filled with data items.
Meaning, make a class that contains the data you want to store, and the ListBox will generate a ListBoxItem automatically per data item.
http://www.wpf-tutorial.com/list-controls/listbox-control/
I have a list view that is bound to an Items collection in the view model behind. This works quite well, in that it shows the data correctly and groups it properly.
<!-- The list view for the shoeprint images -->
<ListView Grid.Row="0"
Margin="3,-3,-3,3"
Background="Transparent"
BorderThickness="0"
ItemsSource="{Binding Items"}
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
SelectionMode="Single">
</ListView
/// <summary>
/// The collection that handles the grouping for the list view
/// </summary>
public ICollectionView Items
{
get { return _items; }
private set
{
_items = value;
if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs("Items"));
}
}
private void UpdateDisplay()
{
var lst = new CollectionViewSource { Source = new ObservableCollection<ShoeprintImage>(concatList) };
lst.GroupDescriptions.Add(new PropertyGroupDescription("Type"));
/*OK UNTIL THIS LINE*/ Items = lst.View;
}
Up until the third line in Update display there are minimal database calls to generate the items collection and it is all quite speedy and efficient.
However each list view item consists of an image and a text block which holds a text string depending on certain properties of the item.
When I bind to the text block I use a value converter that takes the item, and checks these properties to see what string it should display.
The problem is that for every item it makes a round trip to the database to get its information. I would like to have it preload this information for every item before the converter as I believe it could be done in one SQL statement instead of 1000s of round trips.
could someone give me an idea of how to proceed? if you need more information I will post it but trying to keep it as short as possible as I hate reading long essay questions :-)
EDIT - Converter code
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
//do nothing if value is null
if (value == null) return Binding.DoNothing;
//get the value
var image = (ShoeprintImage)value;
bool isPartofGroup = true;
//we only want the word Group to appear on the search results
if (image.Type == Enumerations.ImageType.GroupMembers || image.Type == Enumerations.ImageType.Shortlist) isPartofGroup = false;
else
{
var group = image.Item.ItemGroups.First().Group;
if (group.GroupID == 0) isPartofGroup = false;
else
{
if (group.ItemGroups.Count() <= 1) isPartofGroup = false;
}
}
//get the string
if (isPartofGroup) return Strings.GroupString;
else return string.Empty;
}
EDIT - binding (this is in the list view item template
<!-- put a stack panel so the image is above the text -->
<StackPanel HorizontalAlignment="Center"
VerticalAlignment="Center"
Background="White">
<!-- The image to display -->
<Image x:Name="MainImage"
Width="75"
Height="125"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
MouseLeftButtonDown="Image_MouseLeftButtonDown"
MouseRightButtonDown="Image_MouseRightButtonDown"
Source="{Binding ImageFromFile}" />
<!-- Displays the word "Group" if the image is part of a group -->
<TextBlock HorizontalAlignment="Center"
FontStyle="Italic"
FontWeight="Bold"
Foreground="ForestGreen"
Padding="2"
Text="{Binding Converter={StaticResource GroupConverter},
Mode=OneWay}"
TextAlignment="Center" />
</StackPanel>
I am working on a Windows Phone 8.1 app in XAML/C#.
I have a listview, whose item source is set to a CollectionViewSource called MusicSource. On the backend in C#, I have an ObservableCollection called source and the following code populates it by getting getting all the music files on the phone, groups it by artist and then puts them in the CollectionViewSource, which shows them in the listview:
var folders = await folder.GetFoldersAsync();
if (folders != null)
foreach (var fol in folders)
await getMusic(fol);
var files = await folder.GetFilesAsync();
foreach (var file in files)
{
MusicProperties musicProperties = await file.Properties.GetMusicPropertiesAsync();
this.source.Add(new Music((musicProperties.Artist.Length > 0) ? musicProperties.Artist : "Custom", (musicProperties.Title.Length > 0) ? musicProperties.Title : file.Name, (musicProperties.Album.Length > 0) ? musicProperties.Album : "Custom Album", file.Path));
}
itemSource = AlphaKeyGroup<Music>.CreateGroups(source, CultureInfo.CurrentUICulture, s => s.Artist, true);
this.MusicSource.Source = itemSource;
The following is the XAML side of it:
<Page.Resources>
<DataTemplate x:Key="GroupTemplate">
<Grid Grid.Column="1">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="1">
<TextBlock x:Name="SongTitle" Text="{Binding Title}"
Style="{ThemeResource ListViewItemTextBlockStyle}"/>
<TextBlock x:Name="ArtistName" Text="{Binding Album}"
Style="{ThemeResource ListViewItemContentTextBlockStyle}"/>
</StackPanel>
</Grid>
</DataTemplate>
<CollectionViewSource x:Name="MusicSource" IsSourceGrouped="true" />
<DataTemplate x:Key="headerTemplate">
<StackPanel HorizontalAlignment="Stretch" Width="{Binding ActualWidth, ElementName=contentList}">
<TextBlock Text="{Binding Key}" />
</StackPanel>
</DataTemplate>
</Page.Resources>
<Grid>
<SemanticZoom>
<SemanticZoom.ZoomedInView>
<ListView
x:Name="contentList"
SelectionMode="Multiple"
ItemsSource="{Binding Source={StaticResource MusicSource}}"
ItemTemplate="{StaticResource GroupTemplate}">
<ListView.GroupStyle>
<GroupStyle HidesIfEmpty="True" HeaderTemplate="{StaticResource headerTemplate}"/>
</ListView.GroupStyle>
</ListView>
</SemanticZoom.ZoomedInView>
</SemanticZoom>
<Border
x:Name="SearchBorder"
Background="White">
<TextBox
x:Name="Search" TextChanged="TextBox_TextChanged" />
</Border>
</Grid>
So I get something like the following in the listview:
Michael Jackson
Bad
Dangerous
Thriller
Monster
Eminem
Not Afraid
The Monster
When the user types in the search textbox, the listview should be filtered and only show the items that match the text in the search textbox. So for example, if I type "Monster" in the searchbox, the listview is immediately filtered and only shows "Monster" within the "Michael Jackson" group header and "The Monster" within the "Eminem" group header.
How would I achieve this?
I've got similar task for Windows 8 Store Application - live filtering of the grouped list view when user type sample text. I've made a view model that holds FilterText and Groups. Group has two observable collections - AllItems (complete list of items) and Items (visible on screen). When FilterText is changed I'm going through each item inside each group and determine whether to keep it in Items or not. Here is some code:
...
var rule = x => GetTextToFilter(x).IndexOf(filterText,
StringComparison.CurrentCultureIgnoreCase) >= 0;
foreach (var group in Groups)
{
group.UpdateVisibleItems(rule);
}
...
void UpdateVisibleItems(Func<ItemViewModel, bool> rule)
{
for (int i = 0, j = 0; i < AllItems.Count; i++)
{
var item = AllItems[i];
if (rule(item))
{
if (j == _Items.Count || (j < _Items.Count && _Items[j] != item))
{
_Items.Insert(j, item);
}
j++;
}
else
{
if (j < _Items.Count && _Items[j] == item)
{
_Items.RemoveAt(j);
}
}
}
}
This way selection stays intact if item is still visible after filtering. Animations looks correct because system see it as series of insert/remove on observable collection (complete list refresh will make whole list removed and restored which will be animated wrong).
It works great (provided that AllItems is not millions of rows), but I'm hitting strange exception in specific case - when ListView.GroupStyle.HidesIfEmpty=True and some (or all?) groups are cleared in the process of update - process crashes within Windows.UI.Xaml.dll. No exception is trapped in C# code (UnhandledException and TaskScheduler.UnobservedTaskException are silent). Nothing usable in Event Log. Debugger fails to display details or even attach to the failing process. If I add await Task.Delay() it will be way more stable, but still may fail from time to time. If I set ListView.GroupStyle.HidesIfEmpty=False - all is stable working!
Hmm, it doesn't seem hard at all , all you have to do is to first make your songs list then search among it's items and make unwanted item COLLAPSED!
for ease of work use listview control and search in its items .
Note that first u have to make a cache of all listview controls in mother control for future searches , to avoid getting all musics again and also disabling search mode.
I have an observable collection bound to a list box.
The collection has 2 items, but the list box is showing 3 items (e.g. the 2 items that are actually in the observable collection and an additional item for the NewItemPlaceholder.
I want it only to show the 2 items.
Below is my XAML.
<ListBox MinHeight="20" MinWidth="20" Name="MultipleSelectionsMultipleWagersListBox" Visibility="{Binding Path=Coupon.BarcodeText, Converter={StaticResource CouponBarcodeToVisibilityConverter1}, ConverterParameter=994450_994550}" Height="AUto" Width="Auto" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Margin="5"
ItemsSource="{Binding Path=BetViewModels}" Grid.Row="1" Grid.Column="1" >
<ListBox.ItemTemplate>
<DataTemplate>
<View:BetView DataContext="{Binding}" Name="ThisBet" Margin="5"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Here is the c#
private ObservableCollection<BetViewModel> _betViewModels = new ObservableCollection<BetViewModel>();
public ObservableCollection<BetViewModel> BetViewModels
{
get { return _betViewModels; }
set
{
if (Equals(value, _betViewModels)) return;
_betViewModels = value;
OnPropertyChanged("BetViewModels");
}
}
Here is the code to populate the betViewModels:
var betViewModel = new BetViewModel { Bet = new Bet() };
betViewModel.Bet.SelectionName = "Chelsea";
betViewModel.Bet.Price = "4/9";
betViewModel.Bet.Market = "90 Minutes";
betViewModel.Bet.ExpectedOdd = DateTime.Now;
BetViewModels.Add(betViewModel);
betViewModel = new BetViewModel { Bet = new Bet() };
betViewModel.Bet.SelectionName = "Chelsea";
betViewModel.Bet.Price = "4/9";
betViewModel.Bet.Market = "90 Minutes";
betViewModel.Bet.ExpectedOdd = DateTime.Now;
BetViewModels.Add(betViewModel);
How Do I switch of this from showing the additional item for the new item place
Here is an image of it displaying the placeholder
The DataGrid supports adding new rows, which have to start out blank. If your ItemsSource is bound to both a ListBox/ItemsControl and a DataGrid, you need to set the DataGrid 'CanUserAddRows' property to 'False'.
Where I found the answer: http://www.mindstick.com/Forum/1519/How%20do%20I%20remove%20a%20listbox%20new%20item%20placeholder
There's nothing in your code that should be adding an extra empty item. There may be some other code adding to BetViewModels or there may be a change happening to the generated ICollectionView for the collection if you have it bound to something else that you're not showing, like an editable DataGrid.
did your sample code also provide this issue?
how much items contains your _betViewModels.count in debugging there are really only 2 Items?
it seems you added an empty BetViewModel at the End
i would suggest check your logic which provides populates your items
if it is a loop it should (counter<yourDatasource.Count) just for example
I have a listbox with a data template bound to a list<class> in the program.
<DataTemplate x:Key="pTemplate">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Ref}" Padding="5,0,0,0"/>
<StackPanel Name="taggedA" Tag="{Binding A}" Orientation="Horizontal">
<TextBlock Name="selectedA" Text="{B}" />
</StackPanel>
<Image Name="ind" Width="40" Height="40" />
</StackPanel>
</DataTemplate>
On button click, I want to go over all the elments of the listbox and check if the stackPanel taggedA's tag == textblock selectedA's text.
This is to be done for each of the items in the list box and the data template is as above. How can this be done?
Easier to compare the binding source directly:
ListBox l = myListBox;
for (int i = 0; i < l.Items.Count; i++)
{
var boundObject = (MyClass)l.Items[i];
MessageBox.Show("They are equal? " + (boundObject.A == boundObject.B));
}
I would agree with #dbaseman. But if you are set on doing it you could do the following:
private void button_click(object sender, RoutedEvent e)
{
foreach(var item in MyListBox.Items)
{
ListBoxItem lbi = MyListBox.ItemContainerGenerator.ContainerFromItem(item);
StackPanel taggedApanel = (lbi.Content as StackPanel).Children[1];
//Do whatever you need to do here
}
}