C# Listview DisplayMemberBinding shows "wrong" value - c#

In my WPF app I have a Listview with a couple columns and one column with a checkbox inside.
The xaml is as follows:
<ListView x:Name="listviewImported">
<ListView.View>
<GridView>
<GridViewColumn Header="Check" Width="50">
<GridViewColumn.CellTemplate>
<DataTemplate>
/*This line may be the prob.*/ <CheckBox IsChecked="{Binding MarkedForCheck}" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Pos." Width="75" DisplayMemberBinding="{Binding PositionAsString}" />
<GridViewColumn Header="Value" Width="175" DisplayMemberBinding="{Binding ImportValue}" />
<GridViewColumn Header="Type" Width="175" DisplayMemberBinding="{Binding ImportValueTypeDescription}" />
</GridView>
</ListView.View>
</ListView>
The ItemSource is assigned like this:
listviewImported.ItemsSource = _catalog.ImportedList;
The ImportedList in the _catalog is of Type ImportedElem:
private List<ImportedElem> _importedList = new List<ImportedElem>();
This is the ImportedElem class:
public class ImportedElem : BaseElem {
public ImportedElem(int Position, string ImportValue) : base(Position, ImportValue) {
}
//MARKED FOR CHECK
public bool MarkedForCheck = true;
}
This is the problem:
When I add an ImportedElem to the List, then the Listview will update and the columns "Pos.", "Value" and "Type" contain the correct Data. However, the Column with the checkbox ("Check") always shows a nonchecked Checkbox after adding it to the List. The Data is correct, the column just shows the wrong Data. I can modify the checkbox in the list and it will update the Data, that is good. But the List should contain the correct Data right after adding the Element to the list, wich it is not doing. It would be very interesting to know why 3 columns are correct and the checkbox doesn't work.

change public bool MarkedForCheck = true; TO public bool MarkedForCheck {get;set;}

Related

Binding confusion in xaml

The source code can be found here : https://www.codeproject.com/Articles/24973/TreeListView
The way the original author has set it up, is the data is filled in the xaml itself. I need to create the TreeViewList inside of my ViewModel, but I can't figure out how to bind my own TreeViewList within the xaml to display it properly.
Here's an example of me creating a tree in the code behind and calling the window.
public TreeListView TreeList { get; set; } = new TreeListView();
private void generateTree()
{
TreeList.Columns.Add(new GridViewColumn() { Header = "Col1" });
TreeList.Columns.Add(new GridViewColumn() { Header = "Col2" });
TreeList.Columns.Add(new GridViewColumn() { Header = "Col3" });
}
public ICommand AssemblyTreeCommand => new RelayCommand(AssemblyTree, p => CanAssemblyTree);
public bool CanAssemblyTree { get; set; } = true;
private void AssemblyTree(object parameter)
{
generateTree();
AssemblyTreeDialogWindow dialog = new AssemblyTreeDialogWindow()
{
DataContext = this,
Topmost = true
};
dialog.ShowDialog();
}
AssemblyTreeDialog Window class looks like this:
<local:TreeListView AllowsColumnReorder="True" ItemsSource="{Binding TreeList}">
<!--Create an item template to specify the ItemsSource-->
<local:TreeListView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children}" />
</local:TreeListView.ItemTemplate>
<local:TreeListView.Columns>
<!--Create the first column containing the expand button and the type name.-->
<GridViewColumn Header="Name" Width="200">
<GridViewColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<!--The Expander Button (can be used in any column (typically the first one))-->
<local:TreeListViewExpander/>
<!--Display the name of the DataElement-->
<TextBlock Text="{Binding}"/>
</StackPanel>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<!--Create a second column containing the number of children.-->
<GridViewColumn Header="Children" Width="100">
<GridViewColumn.CellTemplate>
<DataTemplate>
<!--Display the size of the DataElement-->
<TextBlock Text="{Binding Children.Count}" HorizontalAlignment="Right"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<!--Create a third column containing the brush of the material.-->
<GridViewColumn Header="Brush" Width="100">
<GridViewColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<!--Border showing the actual color-->
<Border Background="{Binding Brush}" CornerRadius="2"
Width="16" Height="16"
BorderThickness="1" BorderBrush="DarkGray"/>
<!--Display the brush-->
<TextBlock Text="{Binding Brush}"/>
</StackPanel>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</local:TreeListView.Columns>
<!--Create some sample data-->
<MaterialGroup>
<MaterialGroup>
<DiffuseMaterial Brush="Blue"/>
<DiffuseMaterial Brush="Red"/>
<SpecularMaterial Brush="Orange"/>
</MaterialGroup>
<EmissiveMaterial Brush="AliceBlue"/>
</MaterialGroup>
</local:TreeListView>
Interestingly if I bind the line <GridViewColumn Header="Name" Width="200"> so that it reads <GridViewColumn Header="{Binding TreeList}" Width="200">it gives me this:
I'll explain my end goal as best as possible.
The System is a giant list of parts. A main table displays all of the parts while a subtable displays all of the parts which make up that part. All parts (including those which are used to create other parts) exist within the MainTable. So a Parent part might have a set of children parts, which each individually have children parts which they are made up of. This is the relationship i'm trying to model using this tool.
The code that I've written maps the parts list out into a list of class objects which contain the data. I'll post it below. It's working to map to a TreeView right now.
A datastructure I've based it off is here : Treeview with multiple columns
private void generateTree(string PN)
{
Proces selectedRow = new Proces() { procesId = (int)Vwr.Table.SelectedRow.Row["PID"], procesName = (string)Vwr.Table.SelectedRow.Row["PN"], subProcesses = generateSubtable(PN) };
processes.Add(selectedRow);
}
public List<Proces> generateSubtable(string PN)
{
List<Proces> subTable = new List<Proces>();
foreach (DataRow mplrow in Vwr.Table.Tbl.Rows) if (mplrow["PN"].ToString() == PN)
MainVM.Modules.AllModules[0].SubVwr.Tables[0].LoadTableQuery.Prms[0].Val = mplrow[0];
MainVM.Modules.AllModules[0].SubVwr.Tables[0].Tbl = Db.GetTable(MainVM.Modules.AllModules[0].SubVwr.Tables[0].LoadTableQuery);
foreach (DataRow sub in MainVM.Modules.AllModules[0].SubVwr.Tables[0].Tbl.Rows)
{
Proces subItem = new Proces() { procesId = (int)sub["ItemNo"], procesName = sub["PN"].ToString(), subProcesses = generateSubtable(sub["PN"].ToString()) };
subTable.Add(subItem);
}
return subTable;
}
Found the answer! After some pretty extensive searching, and trying many different solutions. Thought i'd post incase someone else might also be trying to do the same.
credit:
http://dlaa.me/blog/post/9898803

Combobox binding WPF inside ListView

Pulling my hair out here. I can't get my combobox within my listview to bind to a list in the code behind.
Also the combobox isn't even appearing within the column..
Want a combobox in listview to show numbers 0-24.
XAML:
<ListView Grid.Row="0" Margin="0,0,0,0" Height="250" Width="540" SelectionMode="Single" dd:DragDrop.IsDragSource="True" dd:DragDrop.IsDropTarget="True" x:Name="TasksList">
<ListView.View>
<GridView>
<GridViewColumn Header ="Day 1" Width="50">
<GridViewColumn.CellTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding Path=ComboBox1}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
And Code behind:
public partial class TaskHoursRemaining : Page {
List<int> hourOfDay = new List<int>();
public TaskHoursRemaining() {
InitializeComponent();
LoadData();
DataContext = this;
}
private void LoadData() {
for (int i = 0; i < 25; i++) {
hourOfDay.Add(i);
}
this.ComboBox1.ItemsSource= hourOfDay;
}
}
but ComboBox1 does not exist in the current context.
In your XAML, you're binding to a non-existent property ComboBox1:
<ComboBox ItemsSource="{Binding Path=ComboBox1}"/>
In your code-behind, you're accessing a non-existent field ComboBox1:
this.ComboBox1.ItemsSource= hourOfDay;
The DataContext = this; statement does nothing useful for you here.
To create fields via XAML, you should use the x:Name attribute. This wouldn't help you anyway, since, the ComboBox resides in a template.
#un-lucky is correct that you should bind the list view to the collection (which is in fact what you're trying to do in your code-behind). Then again, the ComboBox also wants a collection, so you should properly have a data model that is a collection of collections. (Sort of -- all the comboboxes want the same collection; only the selected item will differ.)
Let's first make this work with a TextBox instead of a ComboBox. The list binds to hourOfDay, while the TextBox displays the int:
private readonly List<int> hourOfDay = new List<int>();
public MainWindow()
{
InitializeComponent();
for (int i = 0; i < 25; i++)
{
this.hourOfDay.Add(i);
}
this.TasksList.ItemsSource = this.hourOfDay;
}
XAML:
<ListView Grid.Row="0" Margin="0,0,0,0" Height="250" Width="540" SelectionMode="Single" x:Name="TasksList">
<ListView.View>
<GridView>
<GridViewColumn Header ="Day 1" Width="50">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBox Text="{Binding Mode=OneWay}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
Result:
What you want, though, is a list of somethings, where each combobox has a dropdown with 1-24. I don't know what the somethings might be -- perhaps something like this:
public class Entry
{
private static readonly List<int> hourOfDay;
static Entry()
{
hourOfDay = new List<int>();
for (int i = 0; i < 25; i++)
{
hourOfDay.Add(i);
}
}
public IEnumerable<int> HourOfDaySource => hourOfDay;
}
In the window/page constructor:
InitializeComponent();
this.TasksList.ItemsSource = new List<Entry>
{
new Entry(),
new Entry(),
new Entry(),
new Entry(),
new Entry(),
};
XAML:
<ListView Grid.Row="0" Margin="0,0,0,0" Height="250" Width="540" SelectionMode="Single" x:Name="TasksList">
<ListView.View>
<GridView>
<GridViewColumn Header ="Day 1" Width="60">
<GridViewColumn.CellTemplate>
<DataTemplate DataType="wpf:Entry">
<ComboBox
ItemsSource="{Binding HourOfDaySource, Mode=OneWay}"
SelectedIndex="12"
Width="42"
/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
Result:
There's a goodly amount of plumbing required for this to become useful, but at least you've got your ComboBoxes populated...

Get the selected value

I am attempting to pull information from my listview but cannot seem to select a specific dataset. When pulling information from this I always get the entire contents back but can never read just one set.
I will be pulling the row data as:
chartViewList2.SelectedItem
I added information to the list view via
private void getServices(string ComputerName)
{
chartListView2.Items.Clear();
ServiceController[] services = ServiceController.GetServices(ComputerName);
foreach (ServiceController service in services)
{
chartListView2.Items.Add(new { Service = service.ServiceName, Status = service.Status, Desc = service.DisplayName });
}
}
List View XML:
ListView x:Name="chartListView2" Margin="12,59,46,6"
GridViewColumnHeader.Click="GridViewColumnHeaderClickedHandler2">
<ListView.View>
<GridView>
<GridViewColumn Header="Service" DisplayMemberBinding="{Binding Service}" Width="100"/>
<GridViewColumn Header="Status" DisplayMemberBinding="{Binding Status}" Width="60"/>
<GridViewColumn Header="Desc" DisplayMemberBinding="{Binding Desc}" Width="190"/>
</GridView>
</ListView.View>
</ListView>
All I want to get from this is the Service name field back.
Any help will be appreciated!
Reflection may help here.
var item = chartViewList2.SelectedItem;
var type = item.GetType();
var propertyInfo = type.GetProperty("Service");
var value = propertyInfo.GetValue(item, null);
Then you will have a value of Service field in 'value' variable.
Since .NET 4 and dynamic keyword even shorter way exists:
dynamic item = chartViewList2.SelectedItem;
dynamic propertyValue = item.Service;
If you are binding your ListView to a List<TypedObject> then you can do this:
var item = chartViewList2.SelectedItem as TypedObject
You can set the ListView SelectedvaluePath to Service and bind to your model or access using the ListView SelectedValue.
Example:
<ListView x:Name="chartListView2" SelectedValuePath="Service" SelectedValue="{Binding SelectedServiceName}" >

Dynamically Adding Items To a WPF List View

I am trying to programatically add items to a ListView in WPF. I have done a lot of reading (including some questions here) and thought I was doing it correctly but the items aren't showing up. As I understand it I create the ListViewe and bind it to a data source, in this case an ObservableCollection. I have verified the ObservableCollection is getting items added to it, but they aren't getting displayed on the ListView. If it matters, the ListView is already instantiated by the time I run the LINQ query and attempt to add items to it.
Here is the XAML that defines the list view:
<TabPanel Name="ResultsTab" Height="200" Width ="500" DockPanel.Dock="Top" HorizontalAlignment="Left">
<TabItem Name="Default_Tab" Header="Default">
<ListView Name="DefaultListView" ItemsSource="Binding FCPortCollection">
<ListView.View>
<GridView x:Name="DefaultGridView">
<GridViewColumn Width="Auto" Header="FC Port" DisplayMemberBinding="{Binding Path=FCPort}" />
<GridViewColumn Width="Auto" Header="WWPN" DisplayMemberBinding="{Binding Path=WWPN}"/>
<GridViewColumn Width="Auto" Header="FCID" DisplayMemberBinding="{Binding Path=FCID}" />
<GridViewColumn Width="Auto" Header="SwitchName" DisplayMemberBinding="{Binding Path=SwitchName}">
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
</TabItem>
And here is the code that is supposed to load it.
public class PortResult
{
public string SwitchName;
public string FCPort;
public string FCID;
public string WWPN;
public PortResult(string name, FCPort port)
{
SwitchName = name;
FCPort = String.Format("fc{0}/{1}", port.SlotNum, port.PortNum);
WWPN = port.WWPNList[0].WWPNValue;
FCID = port.WWPNList[0].FCIDValue;
}
}
ObservableCollection<PortResult> FCPortCollection = new ObservableCollection<PortResult>();
// results is an IEnumerable collection of FCPort result from a LINQ query that has been turned into a Dictionary
foreach (KeyValuePair<string, List<FCPort>> resultspair in results)
{
foreach (FCPort port in resultspair.Value)
{
// create a new PortResult and add it to the ObservableCollection
PortResult pr = new PortResult(resultspair.Key, port);
FCPortCollection.Add(pr);
}
}
There are several problems in the code you posted:
The binding syntax for your ItemsSource is missing the {} braces - it needs to be ItemsSource="{Binding FCPortCollection}"
You can only bind to properties, however you only expose fields in your PortResult class. Change those fields to be properties.
Also make sure the DataContext of the ListView is set to the object which contains the FCPortCollection. Also make sure the collection is a property of the object and not a field (same reason as point 2. above).
This:
ItemsSource="Binding FCPortCollection"
Is not a binding, you forgot the braces {} and hence assigned a char[] as ItemsSource instead.

WPF ListView Binding + Error

I am getting an exception "Object reference not set to an instance of an object." on the "songs.DataContext =" line. If I add songs = new ListView(); before it my listview is empty even though the list of audiofiles is not
XAML:
<ListView Height="Auto" HorizontalAlignment="Center" ItemsSource="{Binding}"
VerticalAlignment="Center" Name="songList" Width="Auto" MinHeight="300" MinWidth="600">
<ListView.View>
<GridView>
<GridViewColumn Width="Auto" Header="Title" DisplayMemberBinding="{Binding Path=Title}" />
<GridViewColumn Width="Auto" Header="Artist" DisplayMemberBinding="{Binding Path=Artist}" />
<GridViewColumn Width="Auto" Header="Album" />
<GridViewColumn Width="Auto" Header="Length" />
</GridView>
</ListView.View>
</ListView>
C#
public struct AudioFile
{
public String Artist;
public String Title;
public String Album;
public String fileLocation;
public String Length;
}
//...
private List<AudioFile> songs = new List<AudioFile>();
//code that adds to array
songList.DataContext = songs;
I suspect your code to be in the constructor, in a place where songList is not yet created.
//...
private List<AudioFile> songs = new List<AudioFile>();
//code that adds to array
songList.DataContext = songs;
Try to move it in the Loaded event instead.
Your songs are clearly instatiated, but what about the songList?
I think you may be trying to set songList.ItemsSource = list in your constructor and obviously the object is not built yet.
In your UI classes, best place to initialize will be only in Loaded event or after InitializeComponent method.
Even better approach will be to use MVVM.
Are you writing this code -
//...
private List<AudioFile> songs = new List<AudioFile>();
//code that adds to array
songList.DataContext = songs;
before IniitializeComponent() method is called for a view?? Can you provide a bit more of insight regarding your code placement that will help in understanding the situation better.
And just a suggestion, not related though. I would say use class instead of structure objects because WPF data binding considers only properties, not fields. Definitely, this is not a cause of an error though.

Categories

Resources