I can't seem to get this to work. Anyone have any ideas why?
Here is the markup:
<ListView Width="210" Height="83" Margin="0 0 5 0" Name="FiltersListView">
<ListView.View>
<GridView>
<GridViewColumn Header="Column" Width="Auto" DisplayMemberBinding="{Binding FilterColumn}"></GridViewColumn>
<GridViewColumn Header="Rule" Width="Auto" DisplayMemberBinding="{Binding FilterRule}"></GridViewColumn>
<GridViewColumn Header="String" Width="Auto" DisplayMemberBinding="{Binding FilterString}"></GridViewColumn>
</GridView>
</ListView.View>
</ListView>
Here is the window initializer:
public SelectionWindow()
{
InitializeComponent();
rows = new List<Row>();
// Create a new binding object and set the binding of this list view to it.
Binding myBinding = new Binding();
myBinding.Source = rows;
FiltersListView.SetBinding(ItemsControl.ItemsSourceProperty, myBinding);
}
Here is my rows object and class:
List<Row> rows;
public class Row
{
public string FilterColumn;
public string FilterRule;
public string FilterString;
}
But when I click this button, I don't see it getting added to the list:
private void AddButtonClick(object sender, RoutedEventArgs e)
{
Console.WriteLine("Adding row.");
rows.Add(new Row { FilterColumn = "1", FilterRule = "2", FilterString = "3" });
Console.WriteLine("Row added.");
}
This is because List<int> doesn't implement INotifyCollectionChanged, so the control doesn't know that the list has actually been updated.
Try to make rows an ObservableColelction<Row>.
Edit: as the OP mentioned, the other problem (empty rows) was due to using fields, not properties, on Row.
Related
I'm still learning WPF and have searched for a way to do this that is not beyond my level of learning, but have not found an answer. Hopefully someone can help me out!
The purpose of my application was to be a simple check-in/check-out program. In my main application I list employees by name along with 2 buttons (in and out) as well as a comment field.
So far I've managed to open a new window when the 'In' or 'Out' buttons are clicked and prompt the user for a comment. I've managed to pass that comment back to my MainWindow, but now I am at a loss as to how to display that in the GridView.
Here is what the MainWindow.cs looks like:
private void menuIn_Click(object sender, RoutedEventArgs e)
{
var item = (sender as FrameworkElement).DataContext;
int rowNumber = lvUsers.Items.IndexOf(item) + 1;
// MessageBox.Show(String.Format("Button row is {0}", rowNumber));
string userCommentString = "";
SubWindow subWindow = new SubWindow(userCommentString, rowNumber);
subWindow.Show();
}
So the 'rowNumber' is the row that contains the comment field I want to change. The 'userCommentString' is what I want to go into that row. Is there not a simple way to insert a string into the grid if I know the row and column number?
Here is a small example how you could do that. Beside this, I would also recommend to learn MVVM.
XAML:
<ListView x:Name="lvUsers">
<ListView.View>
<GridView>
<GridViewColumn Header="Name" Width="120" DisplayMemberBinding="{Binding Path=Name}" />
<GridViewColumn Header="Comment" Width="150" DisplayMemberBinding="{Binding Path=Comment}" />
<GridViewColumn Header="Button">
<GridViewColumn.CellTemplate>
<DataTemplate>
<StackPanel Margin="6,2,6,2">
<Button Content="Click" Click="Button_Click" />
</StackPanel>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
Add items to ListView
lvUsers.Items.Add(new Test() { Name = "User A" });
lvUsers.Items.Add(new Test() { Name = "User B" });
lvUsers.Items.Add(new Test() { Name = "User C" });
Manipulate row on button click:
private void Button_Click(object sender, RoutedEventArgs e)
{
//Get row number
var item = (sender as FrameworkElement).DataContext;
int rowNumber = lvUsers.Items.IndexOf(item);
//Use row number to manipulated the right row
//This should be done after comment is passed back to MainWindow
Test t = (Test)lvUsers.Items[rowNumber];
t.Comment = "new comment";
lvUsers.Items.Refresh();
}
Test class:
public class Test
{
public string Name { get; set; }
public string Comment { get; set; }
}
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...
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;}
I've already search for any solutions but I just couldn't find one. I've found several post in several websites/forum that discusses my problem, but I want a very simple solution.
while (SQLDataReader.Read())
{
var view = new GridView();
view.Columns.Add(new GridViewColumn { Header = "User", DisplayMemberBinding = new Binding("User") });
view.Columns.Add(new GridViewColumn { Header = "hostNamedb", DisplayMemberBinding = new Binding("hostNamedb") });
view.Columns.Add(new GridViewColumn { Header = "fullName", DisplayMemberBinding = new Binding("fullName") });
view.Columns.Add(new GridViewColumn { Header = "location", DisplayMemberBinding = new Binding("location") });
view.Columns.Add(new GridViewColumn { Header = "department", DisplayMemberBinding = new Binding("department") });
view.Columns.Add(new GridViewColumn { Header = "position", DisplayMemberBinding = new Binding("postion") });
view.Columns.Add(new GridViewColumn { Header = "message", DisplayMemberBinding = new Binding("message") });
view.Columns.Add(new GridViewColumn { Header = "status", DisplayMemberBinding = new Binding("status") });
view.Columns.Add(new GridViewColumn { Header = "ip", DisplayMemberBinding = new Binding("ip") });
ListView2.View = view;
ListView2.Items.Add(new
{
User = SQLDataReader["User"].ToString(),
hostNamedb = SQLDataReader["hostNamedb"].ToString(),
fullName = SQLDataReader["fullName"].ToString(),
location = SQLDataReader["location"].ToString(),
department = SQLDataReader["department"].ToString(),
position = SQLDataReader["position"].ToString(),
message = SQLDataReader["message"].ToString(),
status = SQLDataReader["status"].ToString(),
ip = SQLDataReader["ip"].ToString(),
});
}
userName_click = System.Convert.ToString(ListView2.Items[var2].User.Text.ToString());
hostName_click = System.Convert.ToString(ListView2.Items[var2].hostNamedb.Text.ToString());
fullName_form = System.Convert.ToString(ListView2.Items[var2].SubItems[2].Text.ToString());
location_form = System.Convert.ToString(ListView2.Items[var2].SubItems[3].Text.ToString());
department_form = System.Convert.ToString(ListView2.Items[var2].SubItems[4].Text.ToString());
position_form = System.Convert.ToString(ListView2.Items[var2].SubItems[5].Text.ToString());
message_Form = System.Convert.ToString(ListView2.Items[var2].SubItems[6].Text.ToString());
status_form = System.Convert.ToString(ListView2.Items[var2].SubItems[7].Text.ToString());
id_form = System.Convert.ToString(ListView2.Items[var2].SubItems[8].Text.ToString());
userIP_click = System.Convert.ToString(ListView2.Items[var2].SubItems[9].Text.ToString());
Basically, the codes here works like this: the program will get the data from my database and then add them to my Listview2.
(Also, can someone check if my adding items to listview2 will work? I can't run the program right now because I really have to do lots of re-coding. I'm migrating from VB.NET to C# WPF).
Going on: each variable, like userName_click will have the data from the
listview2-row-VAR2 with columns 0 to 8.
I tried replacing the subitems[n] with binding names like User from -->
(new binding("user")) but it didn't work.
by the way, here is my XAML code in listview2.
<ListView x:Name="ListView2" HorizontalAlignment="Left" Height="34" Margin="-10,446,-22,-50" VerticalAlignment="Top" Width="702" Grid.ColumnSpan="3" ClipToBounds="True">
<ListView.View>
<GridView ColumnHeaderContainerStyle="{DynamicResource CustomHeaderStyle}">
<l:FixedWidthColumn Header="username" DisplayMemberBinding="{Binding User}" FixedWidth="65"/>
<l:FixedWidthColumn Header="Host Name" DisplayMemberBinding="{Binding hostNamedb}" FixedWidth="65"/>
<l:FixedWidthColumn Header="fullname" DisplayMemberBinding="{Binding fullName}" FixedWidth="85"/>
<l:FixedWidthColumn Header="location" DisplayMemberBinding="{Binding location}" FixedWidth="65"/>
<l:FixedWidthColumn Header="department" DisplayMemberBinding="{Binding department}" FixedWidth="65"/>
<l:FixedWidthColumn Header="position" DisplayMemberBinding="{Binding position}" FixedWidth="65"/>
<l:FixedWidthColumn Header="message" DisplayMemberBinding="{Binding message}" FixedWidth="65"/>
<l:FixedWidthColumn Header="status" DisplayMemberBinding="{Binding status}" FixedWidth="65"/>
<l:FixedWidthColumn Header="id" DisplayMemberBinding="{Binding id}" FixedWidth="65"/>
<l:FixedWidthColumn Header="ip" DisplayMemberBinding="{Binding ip}" FixedWidth="65"/>
</GridView>
</ListView.View>
</ListView>
Your solution is not following, but since you know about binding I think you already know that. I can not help you making your existing code work, but instead suggest the following: Use an observable collection to hold your DB-Objects with fields like user, hostNamedb...
public DbEntry(){
user = SQLDataReader["User"].ToString();
hostNamedb = SQLDataReader["hostNamedb"].ToString();
fullName = SQLDataReader["fullName"].ToString();
location = SQLDataReader["location"].ToString();
...
}
These will fill your observable collection. Next you bind to that collection via ItemsSource in your XAML.
<ListView ItemsSource="{Binding DBObjectsCollection}">
Filling your variables with data is now only a matter of specifiying the index of the "row" from which to get the data, and getting the right field or property, e.g.
userName_click = DBObjectsCollection[2].user;
Mind you, this code will not work. This is only meant to be a basic pointer to how to solve your problem in WPF. There is already tons on information on WPF, but feel free to ask questions, should any arise.
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.