Capturing the contents of a Listview row as variables - c#

C# WPF
I have a list view with 3 columns in it. What I want to happen is when I double click on a row, it will capture the values of the columns for the selected row and store them as variables so I can pass them off to another page or simply manipulate them as needed. What I have here is my practice code. For right now, I am just populating a message box to see if I can get the variables out correctly. What is happening is it is not grabbing anything from the row. I figured this out by removing the if block and saw that string name = selectedObject.ilName; was null. an additional question I have is in regards to the statement ingListLV.SelectedItems[0] as InventoryList the [0], what exactly does that refer to? I initally thought it referred to the values it returned, so [0] would be the value in column 1, [1] would be column 2 etc. but i know that is wrong.
Here is my XAML
<ListView x:Name="ingListLV" HorizontalAlignment="Center" Height="100" Margin="0,145,0,0" VerticalAlignment="Top" Width="Auto"
IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding}"
SelectedValuePath="InventoryName"
Style="{DynamicResource listViewStyle}"
MouseDoubleClick="Open_Edit">
<ListView.View>
<GridView>
<GridViewColumn x:Name="InvName" Width="100" Header="Name" DisplayMemberBinding="{Binding Path=InventoryName}" />
<GridViewColumn Width="50" Header="Qty" DisplayMemberBinding="{Binding Path=Qty}" />
<GridViewColumn Width="50" Header="Type" DisplayMemberBinding="{Binding Path=Type}" />
</GridView>
</ListView.View>
</ListView>
and my code behind
private void Open_Edit(object sender, RoutedEventArgs e)
{
var selectedObject = ingListLV.SelectedItems[0] as InventoryList;
if (selectedObject == null)
{
return;
}
string name = selectedObject.ilName;
MessageBox.Show(name);
}
public class InventoryList
{
public string ilName { get; set; }
public decimal ilQty { get; set; }
public string ilType { get; set; }
}
EDIT
Here is where i am loading data in to the listview
private void LoadLV()
{
auroraDataEntities = new AuroraDataEntities();
ObjectQuery<Inventory> inventories = auroraDataEntities.Inventories;
//Returns only opjects with a quantity greater than 0, so it won't show anything you are out of
var fillList = from q in inventories
where q.Qty > 0
select q;
ingListLV.ItemsSource = fillList.ToList();
}

In ListView.SelectionMode Single (which is default) use SelectedItem not SelectedItems.
var selectedObject = ingListLV.SelectedItem as Inventory;
The [0] refers to the first selected item (row) in a multiple selection.

Related

Expand columns vertically when adding content in a ListView

I'm creating a windows WPF interface for an XML API of a Music catalog.
So in creating the list of albums, songs, playlists, etc, I created list boxes with columns (with visual studio 'cause I have no experience on windows programming):
<Window x:Class="AmpacheWPF.Programacion"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:AmpacheWPF"
mc:Ignorable="d"
Title="Programacion" Height="917.833" Width="1206.666">
<Grid>
<ComboBox x:Name="comboBox" HorizontalAlignment="Left" Margin="26,39,0,0" VerticalAlignment="Top" Width="120" SelectionChanged="comboBox_SelectionChanged"/>
<ListView x:Name="albums" HorizontalAlignment="Left" Height="146" Margin="26,99,0,0" VerticalAlignment="Top" Width="549" SelectionChanged="albums_SelectionChanged">
<ListView.View >
<GridView AllowsColumnReorder="False">
<GridView.ColumnHeaderContextMenu>
<ContextMenu/>
</GridView.ColumnHeaderContextMenu>
<GridViewColumn Header="Album"
DisplayMemberBinding="{Binding Album}" Width="120"/>
<GridViewColumn Header="Artist"
DisplayMemberBinding="{Binding Artist}"/>
<GridViewColumn Header="Year"
DisplayMemberBinding="{Binding Year}"/>
</GridView>
</ListView.View>
</ListView>
<Label x:Name="label" Content="Label" HorizontalAlignment="Left" Margin="160,54,0,0" VerticalAlignment="Top"/>
<ListView x:Name="Songs" HorizontalAlignment="Left" Height="257" Margin="26,286,0,0" VerticalAlignment="Top" Width="549" SelectionChanged="Songs_SelectionChanged">
<ListView.View>
<GridView>
<GridViewColumn Header="#"
DisplayMemberBinding="{Binding Pista}"/>
<GridViewColumn Header="Cancion"
DisplayMemberBinding="{Binding Titulo}"/>
<GridViewColumn Header="Compositor"
DisplayMemberBinding="{Binding Compositor}"/>
<GridViewColumn Header="Artista"
DisplayMemberBinding="{Binding Artist}"/>
<GridViewColumn Header="Tiempo"
DisplayMemberBinding="{Binding Tiempo}"/>
<GridViewColumn Header="Genero"
DisplayMemberBinding="{Binding Genero}"/>
</GridView>
</ListView.View>
</ListView>
</Grid>
It has Columns (Album, Artist, Year)
All the info is auto-filled with the XML API of my music server.
I have a combo box with Genres (Latin, Salsa, etc)
Every time I click a genre it loads the info in the ListView albums, and when I click an album it fills the ListView songs.
What I'm trying to do with no success is to fix the columns' size (that is the only thing I can do with "width") and want the columns to expand vertically when the text doesn't fit on the column width (like Microsoft excel does) (so I have a 2 or 3 line rows) but please tell me where to put it on the code because really I have no experience programming on windows.
Here is some example of my code in case it helps:
public Programacion()
{
InitializeComponent();
serverurl = Loginwindow.serverurl;
string ampuser = Loginwindow.ampuser;
token = Loginwindow.token;
//obteniendo lista de tags
string tagurl = "http://" + serverurl + "/server/xml.server.php?action=tags&auth=" + token;
string[] tagarray = { tagurl, "1", "tag", "name", "albums" };//Creando array con datos a utilizar
tagsinfo = xmlcs2.Xmlparser(tagarray);//Llamando afuncion que lee XML
//Obteniendo total de espacios musicales contando el array
totalgenres = tagsinfo.GetUpperBound(0);
//Lennado ComboBox1
int g = Programacion.totalgenres;
for (int y = 0; y < g; y++)
{
comboBox.Items.Add(tagsinfo[y][1]); //
}
}
private void comboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
int index = comboBox.SelectedIndex;
id = tagsinfo[index][0];
string totalalbums = tagsinfo[index][2];
//obteniendo lista de albums
string tagurl = "http://" + serverurl + "/server/xml.server.php?action=tag_albums&auth=" + token + "&filter=" + id;
string[] tagarray = { tagurl, totalalbums, "album", "name", "tracks", "artist", "year" };//Creando array con datos a utilizar
albumsinfo = xmlcs2.Xmlparser(tagarray);//Llamando afuncion que lee XML
//ordenando alfabeticamente
Sort(albumsinfo, 1);
//Lennado ListView Albums
albums.ItemsSource = null;
int g = Convert.ToInt32(totalalbums);
List<Albumlist> items = new List<Albumlist>();
for (int y = 0; y < g; y++)
{
if (albumsinfo[y][4] == "0")
{
items.Add(new Albumlist() { Album = albumsinfo[y][1], Artist = albumsinfo[y][3], Year = "" });
}
else
{
items.Add(new Albumlist() { Album = albumsinfo[y][1], Artist = albumsinfo[y][3], Year = albumsinfo[y][4], Id = albumsinfo[y][0] });
}
}
albums.ItemsSource = items;
}
Thanks in advance for your help.
It looks like what you need to do is change out the DisplayMemberBinding in your GridView for CellTemplate as per this answer, which will allow you to customize the presenter object into a custom TextBlock with wrapping (this will then also do the right thing with regards to letting the grid layout know it needs more space).
You might not get Excel-behavior right away, and you may need to play around with the other TextBlock settings to get it to work exactly how you want.

How to get the column value of the selected row in wpf listview

In my listview, I've three columns, the first column is displayed as text with image and the rest of the columns just text only. The listview is coded as below:
<TabItem x:Name="HistoryTab" Header="History" Style="{StaticResource TabStyle}">
<Grid>
<ListView x:Name="HistoryTabLv" HorizontalAlignment="Left" Height="164" Width="275" VerticalAlignment="Top" SelectionChanged="HistoryTabLv_SelectionChanged" SelectionMode="Single">
<ListView.View>
<GridView>
<GridViewColumn x:Name="TimeColumn" Header="Time" Width="85">
<GridViewColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left" Margin="-5,0,0,0">
<Image x:Name="Img" Height="12" Width="12" Source="{Binding Image}" Stretch="Uniform"/>
<TextBlock Text="{Binding Time}"/>
</StackPanel>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn x:Name="PhoneNumColumn" Header="Phone Number" Width="85" DisplayMemberBinding="{Binding PhoneNum}" />
<GridViewColumn x:Name="DirectionColumn" Header="Direction" Width="95" DisplayMemberBinding="{Binding Direction}" />
</GridView>
</ListView.View>
</ListView>
</Grid>
</TabItem>
If the action statement is true, the relevant data will be binded to each column as coded below.
private void HistoryTabLv_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (myStatement == true)
{
var uri = new Uri(#"/Resources/time.png", UriKind.Relative);
myImg = new BitmapImage(uri);
DateTime myTime = DateTime.Now;
HistoryTabLv.Items.Insert(0, new { Image = myImg, Time = myTime.ToString("hh:mm:ss tt"), PhoneNum = calledNum,
Direction = "Called out" });
}
}
In winform, if I want to get the second column value of the selected row, it is coded like this: (based on what I've searched)
string secondCol = lv.SelectedItems[0].SubItems[1].Text;
I want to get the second column value of the selected row (in my case is the PhoneNum column), how can I do that in WPF. I tried with the code below but it doesn't work. Please help.
string myText = (string)((DataRowView)HistoryTabLv.SelectedItems[0])["PhoneNum"];
In WPF ListViewItem is just a wrapper for your content object and SelectedItem(s) will be of the same type as item in your source collection so normally you would cast HistoryTabLv.SelectedItem to that type but because, as far as I can see, you use anonymous type it makes it a bit more difficult. I think the easiest way is around your problem is to use dynamic
dynamic selectedItem = HistoryTabLv.SelectedItem;
var phoneNum = selectedItem.PhoneNum;
or
dynamic selectedItem = HistoryTabLv.SelectedItems[0];
var phoneNum = selectedItem.PhoneNum;
EDIT
If you would create class for you item like
public class MyItemClass {
public string Image { get; set; }
public string Time { get; set; }
public string PhoneNum { get; set; }
public string Direction { get; set; }
}
and create your item like
new MyItemClass {
Image = myImg,
Time = myTime.ToString("hh:mm:ss tt"),
PhoneNum = calledNum,
Direction = "Called out"
}
then you could cast SelectedItem(s) to your item class like
var selectedItem = (MyItemType)HistoryTabLv.SelectedItem

How do you save TextBox, comboBox, CheckBox inputs to ListView in WPF (C#)?

I'm creating a Valet Parking WPF application. I just want to know how you save user inputs to ListView and how to do remove stored data in ListView.
Thanks for your help!!
Thats up to your wish; You can store in a file or even a database, then show in the GridView within a ListView;
public class CarInfo
{
public string Name { get; set; }
}
Then we need to parse the text file:
List<ProcessInfo> processes = new List<ProcessInfo>();
StreamReader reader = new StreamReader("input.txt');
reader.ReadLine(); //The headers don't matter!
string currentLine;
while (currentLine = reader.ReadLine() != null)
{
ProcessInfo newInfo = new ProcessInfo();
// String separation can be done if needed; Can use String.Split API
processes.Add(newInfo);
}
Finally, we need to set up the XAML:
<ListView ItemsSource="{Binding Processes}">
<ListView.View>
<GridView>
<GridViewColumn DisplayMemberBinding="{Binding Name}" Header="Name"/>
</GridView>
</ListView.View>
</ListView>

Draw resizable table in WPF

I mean how should I display my datastructure for I can work with it like with table?
I want to have a table with rows and columns can be dynamicaly added and removed, but in the rest is should looks like a table.
Now it's represented like IList>, because I should resize it, as I said before. But now i want to display it in DataGrid, and be able to have rows, coulmns, and work with "Cells". But i cannot bind it properly, it it does not display anything, only an empty cell for every row.
What should I do? Or mby use arrays and resize them after each row/column addition?
Please, advice.
Now I have this one:
private void MainWindow_OnLoaded(object sender, RoutedEventArgs e)
{
var storage = new Storage();
storage.Data.Add(new DataRow("Bla-bla")
{
new DataEntity() {Text = "bla-bla", Forces = new[] {ForceEnum.AA}},
new DataEntity() {Text = "bla-bla", Forces = new[] {ForceEnum.UA}}
});
DataListView.DataContext = new StorageModel(storage);
}
public class StorageModel
{
public StorageModel()
{
}
public StorageModel(IStorage storage)
{
DataRowList = new ObservableCollection<DataRow>(storage.Data);
}
public ObservableCollection<DataRow> DataRowList
{
get;
set;
}
}
public class DataRow : IList<DataEntity>
{
public string Name { get; private set; }
private readonly List<DataEntity> _list = new List<DataEntity>();
...
_
<ListView ItemsSource="{Binding DataRowList}" Name="DataListView">
<ListView.ItemTemplate>
<DataTemplate>
<ListView ItemsSource="{Binding}">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
i want be able to create something similar to this, but with 2-way binding...
You seem to be asking for TreeView and not ListView since you have a tree alike data structure.
If you do not want to use a TreeView I would suggest you to use a simple ListView with a small trick. That is you could explose an Expander which will add or remove items underneath parent with an indent to fake tree look and still all cells in column would be resized together.
Btw dont forgot to define columns
Here is how:
<ListView Margin="10" Name="lvUsers">
<ListView.View>
<GridView>
<GridViewColumn Header="Name" Width="120" DisplayMemberBinding="{Binding Name}" />
<GridViewColumn Header="Age" Width="50" DisplayMemberBinding="{Binding Age}" />
<GridViewColumn Header="Mail" Width="150" DisplayMemberBinding="{Binding Mail}" />
</GridView>
</ListView.View>
</ListView>
If you wish to implement that expander trick behavior I would suggest you to listen to event OnExpanded and add or remove the needed rows.

How to concatenate the values of two Columns into One inside ListView

I have a .xaml file which has a listview. Listview has 2 items inside which are bind in a following way:
<ListView Name="listView" ItemsSource="{Binding DeviceList}" SelectedItem="{Binding ConnectedDevice, Mode=TwoWay}" >
<ListView.View>
<GridView>
<GridViewColumn Width="300" Header="Name" DisplayMemberBinding="{Binding Description}" />
<GridViewColumn Width="240" Header="Connection Status" DisplayMemberBinding="{Binding DeviceName}" />
</GridView>
</ListView.View>
</ListView>
Both Description and Devicename are part of ModelClass.In My ViewModel class I am able to extract the Device name as well as Description from the hardware I have connected.
public ObservableCollection<ComDeviceInfo> DeviceList
{
get { return comDevices; }
set
{
comDevices = value;
NotifyPropertyChanged("DeviceList");
}
}
public ComDeviceInfo ConnectedDevice
{
get { return connectedDevice; }
set
{
connectedDevice = value;
NotifyPropertyChanged("ConnectedDevice");
}
}
//Called Inside Constructor
private void checkForDevicesTimer_Elapsed(Object source, ElapsedEventArgs e)
{
DeviceList = ComDeviceManagement.FindDevices();
}
Here ComDeviceManagement is my class which has FINDDevices() which returns me the devicename and description. U can notice DeviceList = ComDeviceManagement.FindDevices() above which indicates both the descrip and name are present inside the list.
Everything is working fine. But What I basically want is to display both the Devicename and Description in One Column rather than two separate columns. Well the problem I am facing here is with Devicename and Description. Even though they both display different values, Isn't their a way where I can concatinate them and display both the values into a single Column??? You may notice another column in .xaml file but I want to display(concatenate) both these inside my single column in listView.
How can i do that?
Please help!!
Use a MultiBinding with a format string.
<GridViewColumn>
<TextBlock>
<!-- the Text property is of type System.String -->
<TextBlock.Text>
<MultiBinding StringFormat="{0} {1}">
<Binding Path="Description "/>
<Binding Path="Name"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</GridViewColumn>
The thing you have to understand with a MultiBinding is that if the target property is not a string then you must provide a converter. If it is a string, you can get away with just using the format string.
So, in your case, you can't use it (easily) via the DisplayMemberBinding, you have to specify the content as in my example above.
Two approaches
In the ComDeviceInfo class just add a property that concatenates
public string DescName
{
get
{
return Description + " " + Name;
}
{
<GridViewColumn Width="300" Header="Name" DisplayMemberBinding="{Binding DescName}" />
Or use a multi-value converter
MultiBinding.Converter
Will provided and example.
I would add a new property, but don't forget to update it too when its components change.
private ComDeviceInfo _model;
public string DeviceName
{
get { return _model.Name; }
set
{
_model.Name = value;
NotifyPropertyChanged("DeviceName");
NotifyPropertyChanged("Combined");
}
}
public string Description
{
get { return _model.Description; }
set
{
_model.Description = value;
NotifyPropertyChanged("Description");
NotifyPropertyChanged("Combined");
}
}
public string Combined
{
get { return string.Format("{0} : {1}", _description, _deviceName; }
}
If you are using LINQ query to get your data you can do this:
var query = from devices in DeviceList
select new
{.DeviceDesc = devices.device + " " devices.desc}
listview.ItemsSource = query;
Then go into the GridView.Column and use:
DisplayMemberBinding="{Binding DeviceDesc}"
and it will bind the concatenated column you just created. Obviously you will need to write the correct query, mine was just an example to go by...

Categories