How to read data from a WPF ListView?
Here is my code.
<ListView x:Name="LVR" AllowDrop="True" PreviewDrop="LVR_PreviewDrop" RenderTransformOrigin="0.505,0.506" Margin="0,0,0,0" Grid.Row="1" Grid.ColumnSpan="3" MouseEnter="LVR_MouseEnter" >
<ListView.View>
<GridView >
<GridViewColumn Header="Status" Width="40">
<GridViewColumn.CellTemplate>
<DataTemplate>
<Image Source="index.png" Width="26"></Image>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="File Name">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding TBtxt}" FontWeight="Bold" Foreground="Blue" Cursor="Hand" Height="30" TextAlignment="Left" HorizontalAlignment="Center"></TextBlock>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
And i am inserting items to the List view like this.
void Insert()
{
WinForms.OpenFileDialog ofd = new WinForms.OpenFileDialog();
ofd.Multiselect = true;
ofd.Title = "Select .TXT File";
ofd.FileName = "";
ofd.Filter = "TXT | *.txt";
if (ofd.ShowDialog() == WinForms.DialogResult.OK)
{
foreach (var filename in ofd.FileNames)
{
if (System.IO.Path.GetExtension(filename).ToUpperInvariant() == ".txt")
{
LVR.Items.Add(new StackItems { TBtxt = filename });
}
}
}
}
class StackItems
{
public string TBtxt { get; set; }
public Image imgg { get; set; }
}
Once I completed adding files, my ListView will looks like this.
|Status | File Name|
|[Image]| test.txt |
|[Image]| test1.txt|
(Sorry. I don't have enough reputation to post image)
Now how will I read the 'File Name' from second column?
I am very new to WPF.
Thanks in advance.
In short, you should be data binding a collection of items (one for each row) to the ListView.ItemsSource property:
<ListView ItemsSource="{Binding SomeCollection}">
<ListView.View>
<!-- Define your view here -->
</ListView.View>
</ListView>
If you do this, then accessing the items is as simple as this (using Linq):
var firstItem = SomeCollection.First();
An improvement on this situation would be to data bind another property of the same type as the objects on the data bound collection to the ListView.SelectedItem property:
<ListView ItemsSource="{Binding SomeCollection}" SelectedItem="{Binding CurrentItem}">
<ListView.View>
<!-- Define your view here -->
</ListView.View>
</ListView>
Doing this will enable you to access properties from the currently selected item from the ListView like this:
int someValue = CurrentItem.SomeProperty;
Please refer to the ListView Class page on MSDN for further help.
Related
This question already has answers here:
"Items collection must be empty before using ItemsSource."
(19 answers)
Closed 2 years ago.
For the life of me I can't get this binding to work.
On the top is my attempt, on the bottom is an example I found online, which works.
<StackPanel Orientation="Horizontal" Height="100" Width="200" Background="White">
<ItemsControl x:Name="shortcutsItems" Width="100" ItemsSource="{Binding}">
Hello world
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Name}"/>
<TextBlock Text="Normal text"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<ListView x:Name="shortcutsList" Width="100">
<ListView.View>
<GridView x:Name="gridShortcuts">
<GridViewColumn Header="Name" DisplayMemberBinding="{Binding Name}"/>
</GridView>
</ListView.View>
</ListView>
</StackPanel>
public partial class ExamShortcuts : UserControl {
public ExamShortcuts() {
InitializeComponent();
ObservableCollection<Shortcut> shortcuts = new ObservableCollection<Shortcut>();
shortcuts = new ObservableCollection<Shortcut>();
shortcutsList.ItemsSource = shortcuts;
shortcutsItems.ItemsSource = shortcuts; // ERROR!!!
//shortcuts.Add(new Shortcut() { Name = "Shortcut Exam 1" });
//shortcuts.Add(new Shortcut() { Name = "Shortcut Exam 2" });
}
}
public class Shortcut {
public string Name { get; set; }
}
When I run, it says "Items collection must be empty before using ItemsSource" for shortcutsItems.ItemsSource = shortcuts;
Note that is IS EMPTY!!!!! And of course it doesn't work when the Add lines are uncommented, either.
I've tried using DataContext = shortcuts and ItemsControl ItemsSource="{Binding]" with the same result.
Also, all examples I've seen have the ItemSource assignment in the code after the items are added anyway!
The shortcutsList works fine.
Remove "Hello World" and the ItemSource binding parts prior to setting the ItemsSource in your codebehind. You are trying to set it dynamically, but have already set it in your xaml which is why it think's the source is not empty. I bet that's causing your issue.
Change
<ItemsControl x:Name="shortcutsItems" Width="100" ItemsSource="{Binding}">
Hello world
to
<ItemsControl x:Name="shortcutsItems" Width="100">
I have first and second ListView, as shown in the image:
I populated the first ListView, using this code
class CategoriesList
{
public string Category_Names { get; set; }
public double Category_Amount { get; set; }
public static List<CategoriesList> get_CategoryList()
{
try
{
SQLiteConnection con = new SQLiteConnection(" Data Source=system.sqlite; Version=3; Compress=True; ");
con.Open();
string query = " SELECT category_id, category_name, amount FROM acc_income_category WHERE deleted = 0 ORDER BY category_name ASC ";
SQLiteCommand cmd = new SQLiteCommand(query, con);
SQLiteDataReader dr = cmd.ExecuteReader();
var categories = new List<CategoriesList>();
while (dr.Read())
{
CategoriesList cl = new CategoriesList();
cl.Category_Names = dr.GetString(1);
cl.Category_Amount = dr.GetDouble(2);
categories.Add(cl);
}
con.Close();
return categories;
}
catch (Exception ex)
{
System.Windows.MessageBox.Show(ex.Message);
return null;
}
}
}
This is my Xaml Code
<ListView x:Name="ListBox_Category_Names" VerticalAlignment="Stretch" HorizontalAlignment="Left"
Width="auto" Height="300" SelectionMode="Single" Grid.Column="0" Margin="0,0,10,0"
ScrollViewer.HorizontalScrollBarVisibility="Auto" ScrollViewer.VerticalScrollBarVisibility="Auto" SelectionChanged="ListBox_Category_Names_SelectionChanged"
>
<ListView.View>
<GridView>
<GridViewColumn Header="Category Name" DisplayMemberBinding="{Binding Category_Names}" Width="280" />
<GridViewColumn Header="Amount " DisplayMemberBinding="{Binding Category_Amount, ConverterCulture=ig-NG, StringFormat=\{0:C\}}" Width="130" />
</GridView>
</ListView.View>
</ListView>
<ListView x:Name="ListBox_Selected_Category" VerticalAlignment="Stretch" HorizontalAlignment="Stretch"
Width="auto" Height="300" SelectionMode="Single" Grid.Column="1" Margin="10,0,0,0"
ScrollViewer.HorizontalScrollBarVisibility="Auto" ScrollViewer.VerticalScrollBarVisibility="Auto"
ItemsSource="{Binding SelectedItem, ElementName=ListBox_Category_Names}" >
<ListView.View>
<GridView>
<GridViewColumn Header="Category Name" DisplayMemberBinding="{Binding Category_Names}" Width="250" />
<GridViewColumn Header="Amount" >
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBox x:Name="txtBox_amount" Text="{Binding Category_Amount, ConverterCulture=ig-NG, StringFormat=\{0:C\}}" Width="200" Height="35" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
This is the Code thats Loads the First ListBox
ListBox_Category_Names.ItemsSource = CategoriesList.get_CategoryList();
Not MVVM
Now I'm stuck cause I need to bind the First ListView Selected Items to the Second ListView. Please, I really need help, been stuck with this for the past three (3) weeks. Thanks in advance.
You can do it in two ways: 1) code behind 2) mvvm
First way:handle the button click in the code behind:
public void Button_Click(object sender, EventArguments arg)
{
List<ListViewItem> mySelectedItems = new List<ListViewItem>();
foreach(ListViewItem item in myListView.SelectedItems)
{
mySelectedItems.Add(item);
}
}
Second way bind the multiple selection in the pattern of mvvm, for that look at:
Part 1
Part 2
Part 3
I'm Using WPF.
I have ListView with TextBox column and two checkboxs columns.
I want to edit the TextBox text by double click or something else.
What is the simple way to do that?
...
<GridViewColumn Header="Name" DisplayMemberBinding={Binding Path=fullName}" Width=500>
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBox Name="txtName"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
...
Here is a sample way of doing this...
public partial class MainWindow : Window
{
public List<string> Items { get; set; }
public MainWindow()
{
InitializeComponent();
Items = new List<string>();
LoadItems();
DataContext = this;
}
private void txtName_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
TextBox currentTextBox = (TextBox)sender;
if (currentTextBox.IsReadOnly)
currentTextBox.IsReadOnly = false;
else
currentTextBox.IsReadOnly = true;
}
private void LoadItems()
{
Items.Add("Coffee");
Items.Add("Sugar");
Items.Add("Cream");
}
}
<Grid>
<ListView ItemsSource="{Binding Items}">
<ListView.View>
<GridView>
<GridViewColumn Header="Name">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBox Name="txtName" Text="{Binding Mode=OneTime}" IsReadOnly="True" MouseDoubleClick="txtName_MouseDoubleClick" Width="100"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
</Grid>
Here is an example I have from an application that I wrote. The column JobName was to be user editable. This example allows the column to be editable and also gets rid of the border and lets the background blend into the row so it doesn't appear to have a text box in it.
Those can be edited out (BorderThickness="0" Background="Transparent").
My example binds to an MVVM ViewModel property called JobName and is set to be "TwoWay" so that changes to the view model will also reflect on the UI.
<ListView x:Name="lvJobs" HorizontalAlignment="Left" Height="628" Margin="30,62,0,0" ItemsSource="{Binding Jobs}"
SelectedItem="{Binding SelectedJob, Mode=TwoWay}" VerticalAlignment="Top" Width="335">
<ListView.View>
<GridView>
<GridViewColumn Header="Active" Width="50">
<GridViewColumn.CellTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding IsActive, Mode=TwoWay}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Job Name" Width="150">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBox Text="{Binding JobName, Mode=TwoWay}" BorderThickness="0" Background="Transparent"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn DisplayMemberBinding="{Binding User}" Header="User" Width="125"/>
</GridView>
</ListView.View>
</ListView>
I have a 2 column ListView and I'm trying to fill it using and IDictionary with the following code.
System.Collections.IDictionary entryList = Environment.GetEnvironmentVariables(EnvironmentVariableTarget.User);
foreach (System.Collections.DictionaryEntry de in entryList)
{
Row row = new Row();
row.Name1 = (string)de.Key;
row.Name2 = (string)de.Value;
this.list1.Items.Add(row);
}
public class Row
{
public string Name1 { get; set; }
public string Name2 { get; set; }
}
XAML:
<ListView x:Name="varList"
Grid.ColumnSpan="1"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Height="400"
Width="500"
Margin="0, 30, 0, 0">
<ListView.View>
<GridView>
<GridViewColumn Width="150" Header="Name" />
<GridViewColumn Width="350" Header="Path" />
</GridView>
</ListView.View>
</ListView>
But every row and column gets filled with "Project.Views.Row".
Anyone got any idea on how to fix it? Thank you very much.
A ListView (and every other control for that matter) will display the results of calling ToString when given an object to display.
For a standard class, thats its qualified name; Project.Views.Row in your example.
There are two ways to fix this:
Don't add an object. Instead, format the string as you want it ie:
list1.Items.Add(String.Format({0}:{1}, row.Name1, row.Name2));
Do this the right way and use MVVM. In this case your XAML needs a data template:
<ListView ItemsSource="{Binding Rows}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name1}"/>
<TextBlock Text="{Binding Name2}"/>
</StackPanel>
</DataTemplate>
<ListView.ItemTemplate>
</ListView>
For a grid view:
<ListView.View>
<GridView>
<GridViewColumn Header="Name" DisplayMemberBinding="{Binding Name1}" />
<GridViewColumn Header="Path" DisplayMemberBinding="{Binding Name2}"/>
</GridView>
</ListView.View>
Binding the ItemsSource is not actually necessary, but since we are doing everything the right way, you should do it so you are not directly manipulating the UI from code.
I am working with a WPF control that I created and I am trying to only show certain rows of my list by values of a property. An example is the following, I have a User class that holds a property of Active. How do I tell the .xaml that the list should only show the people that are Active?
Right now I am basically using linq to generate a new list and hand it to the listview based on what I want. However, I would rather just hand the ListView my entire list and let it do the work for me.
Here is my ListView code.
<ListView ItemsSource="{Binding}" DataContext="{Binding }" >
<ListView.View>
<GridView>
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Index}"/>
<TextBlock Text=". " />
<TextBlock Text="{Binding FirstName}" />
<TextBlock Text="{Binding LastName}" />
</StackPanel>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
You'll need some code behind to add a filter:
See: WPF filtering
ICollectionView view = CollectionViewSource.GetDefaultView(lstMovies.ItemsSource);
view.Filter = null;
view.Filter = new Predicate<object>(FilterMovieItem);
private bool FilterMovieItem(object obj)
{
MovieItem item = obj as MovieItem;
if (item == null) return false;
string textFilter = txtFilter.Text;
if (textFilter.Trim().Length == 0) return true; // the filter is empty - pass all items
// apply the filter
if (item.MovieName.ToLower().Contains(textFilter.ToLower())) return true;
return false;
}