I have created a "Listview" and a "Gridview" to show some data, and everything looks fine, but whenever I add data to the "Listviews ItemsSource", it doesn't show it, the only thing I can see are marked lines (see below).
The XAML code for the build up is as follows:
<ListView Name="lstView">
<ListView.View>
<GridView AllowsColumnReorder="True" ColumnHeaderToolTip="Info">
<GridViewColumn DisplayMemberBinding="{Binding Path=Item}" Header="Item" Width="100"/>
<GridViewColumn DisplayMemberBinding="{Binding Path=Qty}" Width="100">
<GridViewColumnHeader>Qty
<GridViewColumnHeader.ContextMenu>
<ContextMenu
Name="Quantity">
<MenuItem Header="Ascending" />
<MenuItem Header="Descending" />
</ContextMenu>
</GridViewColumnHeader.ContextMenu>
</GridViewColumnHeader>
</GridViewColumn>
<GridViewColumn DisplayMemberBinding="{Binding Path=Price}"
Header="Price" Width="100"/>
</GridView>
</ListView.View>
</ListView>
CS part:(forgot to add this part, the class used to store the data)
Class used:
public class MaterialsPicked
{
public string Item;
public double Qty;
public double Price;
}
And the data inserted:
ObservableCollection<MaterialsPicked> materialsPicked = new ObservableCollection<MaterialsPicked>
{
new MaterialsPicked { Item = "Test", Qty = 1, Price = 200 },
new MaterialsPicked { Item = "Test2", Qty = 2, Price = 400 }
};
lstView.ItemsSource = materialsPicked;
And here is a picture of window, clearly it does add 2 lines to the listview, but the data itself is not showing up, and I am unsure what exactly I am missing.
#Clemens
Had the simple solution.
I was using fields instead of properties, changing it to public properties fixed the issue.
Related
I have a function which updates every 5 seconds. I would like to keep the selected item but the item does not get selected.
The function below updates every 5 seconds and updates the list in the list view:
C#
public void festJSONUpdateEventHandler()
{
var tempfest = Workspace.This.festStats.Selectedfest;
//REFRESH BINDINGS HERE!!
Workspace.This.festStats.festItems = MainWindow._fest.festData.fest_Items;
Workspace.This.festStats.Selectedfest = tempfest;
}
XAML
<DataTemplate>
<StackPanel Orientation="Vertical">
<ListView x:Name="lvfest" ItemsSource="{Binding festItems}" SelectedItem="{Binding Selectedfest, Mode=TwoWay}" IsSynchronizedWithCurrentItem="True" >
<ListView.View>
<GridView>
<GridViewColumn Width="100" DisplayMemberBinding="{Binding id}" >
<GridViewColumn.Header>
<GridViewColumnHeader Tag="ID" Click="lvfestColumnHeader_Click">ID</GridViewColumnHeader>
</GridViewColumn.Header>
</GridViewColumn>
<GridViewColumn Width="100" DisplayMemberBinding="{Binding formatType}" >
<GridViewColumn.Header>
<GridViewColumnHeader Tag="Format" Click="lvfestColumnHeader_Click">Format</GridViewColumnHeader>
</GridViewColumn.Header>
</GridViewColumn>
<GridViewColumn Width="100" DisplayMemberBinding="{Binding modifiedIso8601}" >
<GridViewColumn.Header>
<GridViewColumnHeader Tag="Date" Click="lvfestColumnHeader_Click">Date</GridViewColumnHeader>
</GridViewColumn.Header>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
</StackPanel>
</DataTemplate>
More C#
class festViewModel : ToolViewModel
{
public festItem _selectedfest;
public festItem Selectedfest
{
get { return _selectedfest; }
set
{
if (_selectedfest != value)
{
_selectedfest = value;
RaisePropertyChanged("Selectedfest");
}
}
}
private List<festItem> _festItems;
public List<festItem> festItems
{
get { return _festItems; }
set
{
if (_festItems != value)
{
_festItems = value;
RaisePropertyChanged("festItems");
}
}
}
}
From comments:
I have a library that extracts JSON data and in there, there is a list
of objects called fest_Items. from this list, I send the objects to
festItems list in the festViewModel which is bound to my list view.
Is the list being recreated at each function call (ergo 5 seconds)? It may be because your items do not contains the same reference to the object.
Two or more objects sharing the same values may be not the same references. If you are recreating the list, that may be the cause of the binding failing to retreive the item.
As a solution, I suggest finding an unique property (like the ID property of your model) and search the item in the newly created list.
Exemple:
public void festJSONUpdateEventHandler()
{
var tempfestID = Workspace.This.festStats.Selectedfest.ID;
//REFRESH BINDINGS HERE!!
Workspace.This.festStats.festItems = MainWindow._fest.festData.fest_Items;
// Find the first festStat that has the same ID as the old selected one.
Workspace.This.festStats.Selectedfest = Workspace.This.festStats.FirstOrDefault(x => x.ID == tempfestID);
}
Let me know if it is not working.
I have the following in my dgridtext.xaml file.
XAML
<Grid x:Name="grid1">
<StackPanel>
<ListView Name="listview1" IsTextSearchEnabled="True" TextSearch.TextPath="Enquiry_Number">
<ListView.View>
<GridView ColumnHeaderToolTip="Multiple Category Information">
<GridViewColumn DisplayMemberBinding="{Binding Path=Enquiry_Number}" Header="Enquiry number"/>
<GridViewColumn DisplayMemberBinding="{Binding Path=Consignee_Ref}" Header="Consignee reference"/>
<GridViewColumn DisplayMemberBinding="{Binding Path=Booking_Reference}" Header="Booking reference"/>
</GridView>
</ListView.View>
</ListView>
</StackPanel>
</Grid>
Something like this.
dgridtest.xaml.cs
for (int i = 0; i < listview1.Items.Count; i++)
{
MessageBox.Show(listview1.Items[i].ToString());
}
But all that returns is System.Data.DataRowView
Please help.
Update:
In My dgridtextxaml.cs file, I call the DataManager.cs class passing a dataset object which is the source of my listview(listview1).
DataManager.BindFilteredData(dts);
listview1.ItemsSource = dts.Tables[0].DefaultView;
And this is what I have in my DataManager.cs class
public static void BindFilteredData(DataSet dts)
{
using (SqlConnection connection = new SqlConnection(ConfigurationManager.ConnectionStrings["ConString"].ConnectionString))
{
string sql = "SELECT Enquiry_Number, Consignee_Ref, Booking_Reference FROM ConsHead";
using (SqlDataAdapter adapter = new SqlDataAdapter(sql, connection))
{
adapter.Fill(dts);
}
}
}
You can cast the item to whatever class you use in the ListView and then use any property you need from it. Something like:
var item = listView1.Items[i] as YourClassHere;
Edit
In your case, since you bind the ItemsSource to a DataSet directly you probably can use DataRowView class, and then use the properties from it:
var firstItem = listview1.Items[0] as DataRowView;
var firstCellValue = firstItem.Row[0];
I'm making an WPF-application that has a databound ListView. When loading the application, the width of the ListViewColumns will auto-resize, but when adding or changing an item it doesn't auto-resize. I've tried refreshing the listview, setting the width of the column to auto, -1 or -2 in both xaml and VB-code and I've tries changing the itemssource to nothing before refilling it with items. This is the xaml code:
<ListView x:Name="lsvPersons" Margin="5,5,5,35" ItemsSource="{Binding Persons}">
<ListView.View>
<GridView>
<GridViewColumn DisplayMemberBinding="{Binding Name}" Header="Name"/>
<GridViewColumn DisplayMemberBinding="{Binding Gender}" Header="Gender"/>
<GridViewColumn DisplayMemberBinding="{Binding Age}" Header="Age"/>
</GridView>
</ListView.View>
</ListView>
<Button x:Name="btnAddPerson" Content="Add" Height="25" Margin="0,0,200,5" Width="80"/>
The binding works with a controller, which gets the persons with Person.getPersons from a SQL-database:
Private oController As New MainController()
Public Sub New()
MyBase.New()
Me.InitializeComponent()
Me.DataContext = oController
End Sub
After pressing the button, a window will open to add a person. After the window is closed, the item is added to the listview with following code, which refreshes the items in the listview:
lsvPersons.ItemsSource = Person.getPersons()
So, what do I need to do to auto-resize the columns of the listview when an item is added or editted?
GridView gv = lvSrchResulsGrid.View as GridView;
if (gv != null)
{
int colNum = 0;
foreach (GridViewColumn c in gv.Columns)
{
// Code below was found in GridViewColumnHeader.OnGripperDoubleClicked() event handler (using Reflector)
// i.e. it is the same code that is executed when the gripper is double clicked
// if (adjustAllColumns || App.StaticGabeLib.FieldDefsGrid[colNum].DispGrid)
// if (adjustAllColumns || fdGridSorted[colNum].AppliedDispGrid)
// {
if (double.IsNaN(c.Width))
{
c.Width = c.ActualWidth;
}
c.Width = double.NaN;
// }
}
}
I saw ICollectionView being introduced with WPF to handle situations when you need sorting and filtering enabled. I even saw few articles which sort items, but my main concern is why my approach is failing. Lets see my code :
<ListView ItemsSource="{Binding}" x:Name="lvItems" GridViewColumnHeader.Click="ListView_Click">
<ListView.View>
<GridView AllowsColumnReorder="True">
<GridViewColumn Header="Id" DisplayMemberBinding="{Binding Id}" />
<GridViewColumn Header="Name" DisplayMemberBinding="{Binding Name}" />
<GridViewColumn Header="Developer">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Developer}" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Salary">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Salary}" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
In codebehind, when the Item is clicked I am doing like this :
ICollectionView Source { get; set; }
private void ListView_Click(object sender, RoutedEventArgs e)
{
GridViewColumnHeader currentHeader = e.OriginalSource as GridViewColumnHeader;
if(currentHeader != null && currentHeader.Role != GridViewColumnHeaderRole.Padding)
{
//using (this.Source.DeferRefresh())
//{
SortDescription currentPropertySort = this.Source.SortDescriptions.FirstOrDefault<SortDescription>(item => item.PropertyName.Equals(currentHeader.Column.Header.ToString()));
if (currentPropertySort != null)
{
if (currentPropertySort.Direction == ListSortDirection.Ascending)
currentPropertySort.Direction = ListSortDirection.Descending;
else
currentPropertySort.Direction = ListSortDirection.Ascending;
}
else
this.Source.SortDescriptions.Add(new SortDescription(currentHeader.Column.Header.ToString(), ListSortDirection.Ascending));
//}
this.Source.Refresh();
this.lvItems.DataContext = this.Source;
this.lvItems.UpdateLayout();
}
}
So whenever the header for the ListBox is clicked, the item need to be sorted. I am holding the collection using a property called Source and then using it by calling lvItems.DataContext = this.Source. But the code does not seem to be working.
Here's an updated version of your ListView_Click method that somewhat works. I'm not sure exactly what sorting behavior you were looking for but the version below "stacks up" a set of sort descriptions, making the last clicked column as the "primary sort description". I hope this makes sense and I hope the code below helps. =)
private void ListView_Click(object sender, RoutedEventArgs e)
{
GridViewColumnHeader currentHeader = e.OriginalSource as GridViewColumnHeader;
if(currentHeader != null && currentHeader.Role != GridViewColumnHeaderRole.Padding)
{
if (this.Source.SortDescriptions
.Count((item) => item.PropertyName.Equals(currentHeader.Column.Header.ToString())) > 0)
{
SortDescription currentPropertySort = this.Source
.SortDescriptions
.First<SortDescription>(item => item.PropertyName.Equals(currentHeader.Column.Header.ToString()));
//Toggle sort direction.
ListSortDirection direction =
(currentPropertySort.Direction == ListSortDirection.Ascending)?
ListSortDirection.Descending : ListSortDirection.Ascending;
//Remove existing sort
this.Source.SortDescriptions.Remove(currentPropertySort);
this.Source.SortDescriptions.Insert(0, new SortDescription(currentHeader.Column.Header.ToString(), direction));
}
else
{
this.Source.SortDescriptions.Insert(0, new SortDescription(currentHeader.Column.Header.ToString(), ListSortDirection.Ascending));
}
this.Source.Refresh();
}
}
EDIT:
By the way, one of the problems in your code above is your call to "FirstOrDefault" to query an existing SortDescription. See, SortDescription is a struct, which is non-nullable so the call to FirstOrDefault will never be null and will always return an instance. Therefore, the "else-statement" in your code above will never get called.
Im usign a Ribbon Window and in the "content area beneath" I have a grid in which I will be displaying UserControls. To demonstrate my problem lets take a look at this simple UserControl:
<ListView x:Name="lvPersonList">
<ListView.View>
<GridView>
<GridViewColumn Width="120" Header="Name" DisplayMemberBinding="{Binding Name}"/>
<GridViewColumn Width="120" Header="Height" DisplayMemberBinding="{Binding Height}"/>
</GridView>
</ListView.View>
</ListView>
And the code:
public partial class MyUserControl: UserControl
{
private List<Person> personList;
public TestSnpList()
{
InitializeComponent();
this.personList = new List<Person>();
this.personList.Add(new Person { Name = "Chuck Norris", Height = 210 });
this.personList.Add(new Person { Name = "John Rambo", Height = 200 });
this.lvPersonList.ItemsSource = personList;
}
}
public class Person
{
public string Name { get; set; }
public int Height { get; set; }
}
The parent Window:
<Grid x:Name="grdContent" DockPanel.Dock="Top">
<controls:MyUserControl x:Name="myUserControl" Visibility="Visible"/>
</Grid>
I don't understant why this binding doesn't work. Instead of values (Name and Height) I get full class names. If I use this code in a Window it works fine.
Any ideas? I would like this user contorl works for itself (it gets the data form the DB and represents it in a ListView)...
Thanks!
It seems the problem is with RibbonWindow.
If I use Window and UserControl binding works fine, but if I use RibbonWindow (Odyssey Ribbon) binding doesn't work. What I don't understand is that in design mode I can see proper values and in running mode I see only class names:
http://i977.photobucket.com/albums/ae255/HekoSLO/designModeVSrunning.png