C# Tie ListView items with objects - c#

Whats the best way to tie ListView item with a object so when i move the item form one listview to another then i would be still able to tell to what object its assigned.
For example, i have object Cards. All these are listed in a allCards ListView. I have another selectedCards ListView and a button what moves selected items from one listview to another. When im done my selection i need to get the list of the Card objects what moved to the selectedCards ListView.

To expand on #CharithJ's answer, this is how you would use the tag property:
ListView allCardsListView = new ListView();
ListView selectedCardsListView = new ListView();
List<Card> allCards = new List<Card>();
List<Card> selectedCards = new List<Card>();
public Form1()
{
InitializeComponent();
foreach (Card selectedCard in selectedCards)
{
ListViewItem item = new ListViewItem(selectedCard.Name);
item.Tag = selectedCard;
selectedCardsListView.Items.Add(item);
}
foreach (Card card in allCards)
{
ListViewItem item = new ListViewItem(card.Name);
item.Tag = card;
allCardsListView.Items.Add(new ListViewItem(card.Name));
}
Button button = new Button();
button.Click += new EventHandler(MoveSelectedClick);
}
void MoveSelectedClick(object sender, EventArgs e)
{
foreach (ListViewItem item in allCardsListView.SelectedItems)
{
Card card = (Card) item.Tag;
//Do whatever with the card
}
}
Obviously you'll need to adapt it to your own code, but that should get you started.

You could use observable collections, and create a datatemplate for your Card class. Then you just bind your ListView to the collection and it does all the work for you. When you add an item to the ObservableCollection the ListView automatically redraws.
using System.Collections.ObjectModel;
<ListView Name="allCardsView" Source="{Binding}">
<ListView.ItemTemplate>
<DataTemplate DataType="{x:Type yourXmlns:Card}">
//Your template here
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<ListView Name="selectedCardsView" Source="{Binding}">
<ListView.ItemTemplate>
<DataTemplate DataType="{x:Type yourXmlns:Card}">
//Your template here
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
ObservableCollection<Card> allCards = new ObservableCollection<Card>();
ObservableCollection<Card> selectedCards = new ObservableCollection<Card>();
allCardsView.DataContext = allCards;
selectedCardsView.DataContext = selectedCards;
public void ButtonClickHandler(object sender, EventArgs e)
{
if (allCardsView.SelectedItem != null &&
!selectedCards.Contains(allCardsView.SelectedItem))
{
selectedCards.Add(allCardsView.SelectedItem);
}
}

1st way.
Assign the object to the Tag property of the ListViewItem. Get the tags of the selected items.
2nd Way.
Add invisible subItem to the listView that holds the ID of the Card object. Then find the card by using the selected item IDs.

Better use ObjectListView. It is a perfect way to add and use objects with ListView. With features like Hot tracking and easy to use drag and drop your listview becomes lot simpler to manipulate.

Related

Adding list items to observable collection list

Hi I am trying to add list items to an observable collection list.
I have a model where I setup a list property
public class DisplayList
{
public List<string> listItem { get; set; }
}
then on my main page I have an observable collection
private ObservableCollection<DisplayList> ListDisplay;
which I instantiate on page load
public MainPage()
{
this.InitializeComponent();
location = new ObservableCollection<storeLocations>();
ListDisplay = new ObservableCollection<DisplayList>();
// location = manager.getStoreLocations();
var dbList = db.Bales.Where(b => b.Location != null).Select(b => b.Location).ToList();
InitialLoad(dbList, null);
}
I am using suggestion boxes and want to filter results based on the selection made. The filtered results then display in a list on screen and this is where I am having a bit of trouble. I get it to display on screen, but it is displaying
System.Collection.Generic.List'1[S....... instead of the actual item in the list.
I am thinking I am not enumerating properly, but cant seem to pin point the error in my ways.
This is the method that is meant to populate the list based on selection of suggestion box.
public ObservableCollection<DisplayList>BaleList(List<string> CatNo)
{
foreach (var item in CatNo)
{
ListDisplay.Add(new DisplayList {listItem = CatNo.ToList()});
}
lstBales.IsItemClickEnabled = true;
return ListDisplay;
}
it takes in a parameter of type list which is gotten from the suggestion box. so the parameter value is basically what I want to display in the list on screen. e.g. CP1354-2 and second item CP1355-3 So those values come into the method. I want to apply those values to the observable collection as the listbox control is bound to the observable collection.
EDIT
adding binding in XAML
<ListView x:Name="lstBales" ItemsSource="{x:Bind ListDisplay}">
<ListView.ItemTemplate>
<DataTemplate x:Name="TemplateListName" x:DataType="data:DisplayList">
<Grid>
<TextBlock Text="{x:Bind listItem}"/>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
ListDisplay.Add(new DisplayList {listItem = CatNo.ToList()});
and <TextBlock Text="{x:Bind listItem}"/>
You are binding a list to a TextBlock due to which ToString()is implicitly called and you don't get the actual values. According to your requirements, you can either change listItem to a string or create a nested ListView

ListBox filled with binding doesn't select item on click

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/

WPF MVVM combine two collections in one view model

I'm struggling with something and hope somebody can help with a tip on how to accomplish this:
I wrote a UserControl that looks like a File Explorer with on the left side a TreeView with diskdrives and folders (each item has a checkbox) and as soon as a TreeViewItem on the left side is selected, the right side should display the contents of that directory in a ListView, also with a checkbox for each file.
The TreeView is bound in a ViewModel and has many children and subchildren. This works well. The right side however, should only be 1 level of children and no subchildren.
I get the "IsSelected" event within the ViewModel, but of course I then have a reference to the current selected TreeViewItem on the left side, which is totally unrelated to the main level ListView on the right side, which I need to fill with data at that point. How can I reach the main level ListView on the right side, is there any way to create a global lvMain or something that I can access from within the ViewModel or am I appoaching all of this the wrong way and do I need 2 ViewModels instead? Any tips are welcome!
Edit: Here is an image of the desired result, it is the non MVVM version of it, which I am now converting to a control that uses a viewmodel in order to add extra functionality:
enter image description here
Some XAML code snippets:
<TreeView Name="myTreeView" ItemsSource="{Binding tvChildren}" ItemTemplate="{StaticResource CheckBoxItemTemplate}" Height="Auto" Width="Auto"></TreeView>
...
<ListView x:Name="myListView" FontWeight="Normal" ItemsSource="{Binding lvChildren}" Width="Auto" HorizontalAlignment="Stretch">
UserObject code snippets:
public FileExplorer()
{
InitializeComponent();
try
{
// Initially fill TreeView (left side of FileExplorer) with local drives:
DriveInfo[] drives = DriveInfo.GetDrives();
ViewModel dummyNode = null;
int i = 0;
foreach (DriveInfo drive in drives)
{
if (drive.DriveType != DriveType.CDRom)
{
ViewModel node = new ViewModel();
tvMainNode.tvChildren.Add(node);
node.Text = drive.Name;
node.FullDir = drive.Name;
node._parent = tvMainNode;
// Add a blanc child so the toggle buttons are generated:
tvMainNode.tvChildren[i].tvChildren.Add(dummyNode);
i++;
}
}
}
catch { return; }
DataContext = this;
}
ViewModel code snippet:
private readonly ObservableCollection<ViewModel> _tvChildren = new ObservableCollection<ViewModel>();
private readonly ObservableCollection<ViewModel> _lvChildren = new ObservableCollection<ViewModel>();
void SetIsSelected(bool value)
{
if (value == _isSelected) { return; }
_isSelected = value;
// This is obviously not working:
ViewModel test = new ViewModel();
this.lvChildren.Clear();
test.Naam = "bla";
this.lvChildren.Add(test);
}

How to remove an item from a Xamarin Forms ListView?

I'm trying to remove items/rows from a ListView but the difficulty is that I need to also pass in some delegate or fire some event or something, so when a person clicks a button to remove that row, my code handles some other logic, elsewhere (eg. remove the item from the DB or whatever).
I have a custom control I made:
public class SportsTeam : StackLayout { .. }
Inside this control, one of the elements is a ListView which lists all the people in a sporting team.
var viewModel = teamMembers.Select(x => new SportsTeamViewModel(x));
return new ListView
{
HasUnevenRows = true,
ItemSource = viewModel,
ItemTemplate = new DataTemplate(typeof(SportsTeamViewCell));
};
Inside the SportsTeamViewCell I have the following:
private Grid CreateContent()
{
var grid = new Grid();
// Setup row and column definitions.
// Add items to the Grid
grid.Children.Add(...);
var removeButton = RemoveButton;
grid.Children.Add(removeButton);
Grid.SetRowSpan(removeButton, 2);
return grid;
}
private Button RemoveButton
{
get
{
var button = new Button
{
Image = "Icons/remove.png"
};
return button;
}
}
From here, I don't know how to make it so that the button fires an event or some delete could be passed in via the constructor, so some custom logic is performed against the individual cell/row/item that is to be removed.
Here is what you could do :
This be my model class :
public class Item
{
public string ItemName { get; set; }
public string ItemDetails { get; set; }
}
And in my XAML or you can write this in code as well, bind to the Command Parameter of your Item template :
<Button Text="Delete" CommandParameter="{Binding ItemName}" Clicked="DeleteClicked"></Button>
Full Item Template will be like below :
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.View>
<StackLayout Orientation="Horizontal">
<Label Text="{Binding ItemName}" HorizontalOptions="StartAndExpand" FontSize="30"></Label>
<Button Text="Delete" CommandParameter="{Binding ItemName}" Clicked="DeleteClicked">
</Button>
</StackLayout>
</ViewCell.View>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
And in you code file you can do this :
public void DeleteClicked(object sender, EventArgs e)
{
var item = (Xamarin.Forms.Button)sender;
Item listitem = (from itm in allItems
where itm.ItemName == item.CommandParameter.ToString()
select itm)
.FirstOrDefault<Item>();
allItems.Remove(listitem);
}
IMPORTANT : This would only delete the item from the bound collection. To delete it from the original list you need to use ObservableCollection
Here is the full source code of the explained scenario - Handling Child Control Event in Listview using XAMARIN.FORMS.
Also the Tutorial - How to handle Row selection and delete Button in Row For Custom ListView using Xamarin.Forms explain deletion from a listview as well.
I've found a similar approach and I want to share it. I filled the list with an ObservableCollection<MyObject>. Then I filled the CommandParameter with just CommandParameter="{Binding .}". So I got the whole Object back. Then you can just cast the CommandParameterto your Object and remove it from the ObservableCollection<MyObject> List
XAML:
CommandParameter="{Binding .}"
Filling my List:
savingExpensesCollection = new ObservableCollection<SavingsExpensesEntry> ();
savingExpensesCollection .Add (new SavingsExpensesEntry ("1000 mAh Akku", "Dampfgarten", new DateTime (635808692400000000), 8.95));
savingExpensesCollection .Add (new SavingsExpensesEntry ("Cool-Mint Aroma", "Dampfgarten", new DateTime (635808692400000000), 3.95));
savingExpensesCollection .Add (new SavingsExpensesEntry ("Basis", "Dampfgarten", new DateTime (635808692400000000), 13.65));
savingExpensesList.ItemsSource = savingExpenses;
EventHandler:
void OnDelete(object sender, EventArgs e)
{
var menuItem = ((MenuItem)sender);
SavingsExpensesEntry see ((SavingsExpensesEntry)menuItem.CommandParameter);
savingExpensesCollection .Remove (see);
}
I've using a MenuItem but it's the same approach with a Button
I just did using delete button
public void OnDelete(object sender, EventArgs e)
{
var mi = ((MenuItem)sender);
PhotoViewModel photo= ((photoViewModel)mi.CommandParameter);
photoModel.Remove(photo);
}

WPF Listbox with checkboxes appearing blank, added dynamically

I'm trying to populate a listbox with a series checkbox entries, however once running the code below the listbox has blank entries in it, which are selectable, i.e. a blue bar appears. However neither the text or checkbox appears.
for (int num = 1; num <= 10; num++)
{
CheckBox checkBox = new CheckBox();
checkBox.Text = "sheet" + num.ToString();
checkBox.Name = "checkbox" + num.ToString();
thelistbox.Items.Add(checkBox);
}
The best way to handle this is to create a list of data -- in your case, a list of numbers (or a list of strings (sheet1, sheet2, etc). You can then assign that list of numbers to thelistbox.ItemsSource. Inside the XAML of your listbox, set the ItemTemplate to include a CheckBox and bind the number to the text of the checkbox.
Try changing
checkBox.Text = "sheet" + num.ToString();
to
checkBox.Content = "sheet" + num.ToString();
With that change, I was able to use your example successfully.
To follow up on Brian's comment, here is an outline of a simple checkbox list in C# wpf. This will need more code to handle checking/unchecking boxes and general post-interaction handlers. This setup presents the difference in elements on two lists of objects (defined elsewhere) in a checkbox list.
The XAML
...
<ListBox Name="MissingNamesList" ItemsSource="{Binding TheMissingChildren}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<CheckBox Content="{Binding Path=Name}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
...
The supporting C# code:
...
public partial class MissingNamesWindow : Window
{
// Make this accessible from just about anywhere
public ObservableCollection<ChildName> TheMissingChildren { get; set; }
public MissingNamesWindow()
{
// Build our collection so we can bind to it later
FindMissingChildren();
InitializeComponent();
// Set our datacontext for this window to stuff that lives here
DataContext = this;
}
private void FindMissingChildren()
{
// Initialize our observable collection
TheMissingChildren = new ObservableCollection<ChildName>();
// Build our list of objects on list A but not B
List<ChildName> names = new List<ChildName>(MainWindow.ChildNamesFromDB.Except(
MainWindow.ChildNamesFromDisk).ToList());
// Build observable collection from out unique list of objects
foreach (var name in names)
{
TheMissingChildren.Add(name);
}
}
}
...
Hope that clarifies a bit.

Categories