WPF ListView Key Search Issue - wpf

I am using MVVM to show a list data in ListView. The ListView is very simple as follow:
<ListView HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
ItemsSource="{Binding Customers}">
<ListView.View>
<GridView>
<GridViewColumn DisplayMemberBinding="{Binding FirstName}">
<GridViewColumnHeader Width="100" Content="First Name" />
</GridViewColumn>
<GridViewColumn DisplayMemberBinding="{Binding LastName}">
<GridViewColumnHeader Width="100" Content="Last Name" />
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
I bind a list of customer objects into ListView and the issue comes when I highlight a item in the ListView and start typing T. The ListView will highlight the next item each time you type a T character.
By spending some time to find out the issue, I track down it is because that my customer object has namespace TestMVVMProject so I think the ToString() function for customer object is always return TestMVVMProject.Customer
Can anyone give me some idea how to stop this behaviour?

This is because by default the ListView implements text searching ability. You can stop this behavious byusing the IsTextSearchEnabled property on the list box. Try
<ListView HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
ItemsSource="{Binding Customers}"
IsTextSearchEnabled="False">
This should disable the text search when you do not want it.

Edit:
After some investigation I found next solution:
<ListView
TextSearch.TextPath="FirstName"
...>
Hope this will help.
Original answer:
Just override ToString() on View Model and you will get proper behavior.
public class Customer
{
public string Name { get; set; }
public override string ToString()
{
return this.Name;
}
}

Related

Bind a custom class of controls to Listview/GridView as ListViewItem

I am facing some problem with binding the data to the controls in ListView/GridView as ListViweItem.
Requirement: Have to show a checkbox, Label, DropDown and a textbox per row which get data from all different sources.Here is the sample class:
class Data {
public CheckBox chk_box;
public Label lbl;
public ComboBox cmb_box;
public TextBox t_box;
public Data(String lbl_Data,List<String> cmb_box_Data) {
chk_box=new CheckBox();
lbl=new Label();
lbl.Content=lbl_Data;
cmb_box=new ComboBox();
cmb_box.ItemsSource=cmb_box_Data;
chk_box.Click += new RoutedEventHandler(chk_box_clicked);
...
}
private void chk_box_clicked(object Sender, RoutedEventArgs e){
if(chk_box.IsChecked == true) cmb_box.IsEnabled = false;
else cmb_box.IsEnabled = true;
}
}
Now i need to add this class' object as a ListViewItem, per control per column in ListView/GridView - dynamically, row by row and then after user makes selections access all the rows once again row by row and get values from these controls.
XAML code:
<ListView x:Name="TestGrid">
<ListView.View>
<GridView>
<GridViewColumn Header=" Select "/>
<GridViewColumn Header=" Label "/>
<GridViewColumn Header=" cmb_box "/>
<GridViewColumn Header=" t_box "/>
</GridView>
</ListView.View>
</ListView>
Since, the Items Source for all the controls is different, i cannot use ListView.ItemsSorce to bind data. Please suggest suitable options, or ways to accomplish the desired UI. I would like to make everything out from the controls given in VS only without using a third party dll(if possible).
Thank you for your time and suggestions in advance.
Check msdn.microsoft.com for binding
public class Datum
{
public bool Select { get; set; }
public string Name { get; set; }
public List<string> Items { get; set; }
}
<ListView ItemsSource="{Binding}">
<ListView.View>
<GridView>
<GridView.Columns>
<GridViewColumn Header="Select">
<GridViewColumn.CellTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding Path=Select, Mode=OneWay}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Name">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Name, Mode= OneWay}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Name">
<GridViewColumn.CellTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding Path=Items}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView.Columns>
</GridView>
</ListView.View>
</ListView>

Binding textbox in ListView's header datatemplate to a filter property

I am creating a customized listview header that has the header text but also has a textbox that you can enter to filter the content of that column. My code currently looks like this:
<UserControl.Resources>
<DataTemplate x:Key="myHeaderTemplate">
<StackPanel>
<TextBlock FontSize="14" Foreground="DarkBlue" Margin="20,4" Text="{Binding}" />
<TextBox Text="" Margin="4,2" />
</StackPanel>
</DataTemplate>
</UserControl.Resources>
which is the definition for the header datatemplate containing the texbox; and the listview
<ListView ItemsSource="{Binding Path=MyData}" IsSynchronizedWithCurrentItem="True">
<ListView.View>
<GridView>
<GridViewColumn Header="Last Name" HeaderTemplate="{StaticResource myHeaderTemplate}"
DisplayMemberBinding="{Binding Path=Something}" />
<GridViewColumn Header="First Name" HeaderTemplate="{StaticResource myHeaderTemplate}"
DisplayMemberBinding="{Binding Path=Something}" />
<GridViewColumn Header="Address" HeaderTemplate="{StaticResource myHeaderTemplate}"
DisplayMemberBinding="{Binding Path=Tube}" />
</GridView>
</ListView.View>
</ListView>
I want to be able to build up a filter statement that I can apply to the listview rows, but to do that I have to get the data from each filter textbox in the header template.
Can I somehow bind the textboxes in the headers to properties on my viewmodel? If not is there some other way to get the text?
Thanks for any help.
You should be able to bind the header to a property like this:
<GridViewColumn
Header="{Binding LastNameFilter, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListView}}}"
HeaderTemplate="{StaticResource myHeaderTemplate}"
DisplayMemberBinding="{Binding Path=Something}" />
The RelativeSource is needed to get to the DataContext of the ListView - you could also give it a name and use ElementName instead.
Now you can make a HeaderFilter class:
public class HeaderFilter
{
public string Name { get; set; }
public string Filter { get; set; }
}
Obviously you would need to extend that class to hook into the event when Filter is changed to perform the filtering.
Put a property for each column header on the object which is the DataContext for your ListView (same object which provides MyData probably)
public class SomeClass
{
....
public HeaderFilter LastNameFilter { get; set; }
....
}

How to specify hard-coded data for a multi-column WPF ListView?

Hey,
I have a ListView with mutliple columns. I wish to attach hard-coded values using a xaml construct like ListViewItem but can not work out how to specifiy multiple columns of fixed data. For example, in the grid shown below I'd like to see two columns, 'Animal' and 'IQ', and pre-populate the data for both columns, but the code shown sets all columns to the same value.
<ListView>
<ListView.View>
<GridView>
<GridViewColumn Header="Animal" />
<GridViewColumn Header="IQ" />
</GridView>
</ListView.View>
<ListViewItem Content="Pig"/>
<ListViewItem Content="Dog"/>
</ListView>
BTW: This is for a simple, sample screen. As such, I do not really want to bind programatically to data.
Thanks in advance.
Dave.
Not sure how to accomplish this declaratively in XAML, but if you don't want to bind data to the control, you could add programatically the items on the code-behind.
this.AnimalsList.Items.Add(new ListViewItem { Content = "Pig" });
this.AnimalsList.Items.Add(new ListViewItem { Content = "Dog" });
Just in case someone is still looking for an answer; the first answer is incorrect and will still print the same value in each column, just like the XAML example. This will do the trick:
<ListView>
<ListView.View>
<GridView>
<GridViewColumn Header="Animal" DisplayMemberBinding="{Binding Animal}" />
<GridViewColumn Header="IQ" DisplayMemberBinding="{Binding IQ}" />
</GridView>
</ListView.View>
<ListViewItem>
<local:Animals Animal="Pig" IQ="70" />
<local:Animals Animal="Dog" IQ="60" />
<local:Animals Animal="Frog" IQ="30" />
</ListViewItem>
However you do need a class like this, I don't think it can be done with pure XAML:
public class Animals
{
public string Animal { get; set; }
public int IQ { get; set; }
}

WPF - How to bind generic method in XAML?

I have a class with a static method that returns a Dictionary. The signature of the method is as follows:
public static Dictionary<int, string> CodeLookup<T>() where T : EntityCodeBase
At the moment, I'm using this method to bind to my comboboxes in the code behind, like so:
this.cboState.ItemsSource = CodeCache.CodeLookup<StateCode>();
Would someone please be able to point me in the correct direction for doing this in XAML so that I can remove this kind of stuff from my codebehind?
Thanks,
Sonny
Not directly binding to a particular method. You should create a property and bound that to it.
public Dictionary<int, string> Code {
get { return CodeCache.CodeLookup<StateCode>(); }
}
looks like you cannot do it for generic methods
more info
Here is a property in my viewModel:
public ObservableCollection<Contact> AllContacts
{
get { return _applicationViewModel.CurrentContacts; }
}
And here is my XAML:
<ListView Margin="5" ItemsSource="{Binding Path=AllContacts}">
<ListView.View>
<GridView>
<GridViewColumn Header="Last Name" DisplayMemberBinding="{Binding Path=LastName}" />
<GridViewColumn Header="First Name" DisplayMemberBinding="{Binding Path=FirstName}" />
<GridViewColumn Header="Work Phone" DisplayMemberBinding="{Binding Path=OfficePhone, Converter={StaticResource phoneConverter}}" />
<GridViewColumn Header="Cell Phone" DisplayMemberBinding="{Binding Path=CellPhone, Converter={StaticResource phoneConverter}}" />
<GridViewColumn Header="Email Address" DisplayMemberBinding="{Binding Path=PrimaryEmail}" />
</GridView>
</ListView.View>
</ListView>
Just set the DataContext to the ViewModel and you get everything you need. Check out MVVM pattern for more info.
There are ways to bind to a static method but if all you are doing is basic databinding then it's a bit of overkill. Take a look at Actions if you are interested.

Binding a wpf listview to a Dataset....Possible..?

I was struggling moving to Wpf,I am just stuck while trying out databinding to a lsitview.I want to databind a listview to a dataset(dataset because the data i want to display in columns belongs to different tables).I am attaching a sample code that i am trying with.It works alright but the listliew only shows one row.What could be wrong.Can anyone guide me through.All the samples available are using datatables.None specifies about binding to a dataset.Pls help..any input will be highly appreciated...thanks in advance
My Xaml
<Grid>
<TextBox Text="" Height="20" Width="100" HorizontalAlignment="Left" Margin="15,13,0,0" VerticalAlignment="Top"></TextBox>
<TextBox Text="" Height="20" Width="100" HorizontalAlignment="Left" Margin="15,42,0,0" VerticalAlignment="Top"></TextBox>
<ListView Margin="15,89,63,73" Name="lst" ItemsSource="{Binding}">
<ListView.View>
<GridView>
<GridViewColumn Header="Name" DisplayMemberBinding="{Binding Path=T1/Name}"></GridViewColumn>
<GridViewColumn Header="Place" DisplayMemberBinding="{Binding Path=T2/Name}"></GridViewColumn>
</GridView>
</ListView.View>
</ListView>
<!--<Button Height="19" HorizontalAlignment="Right" Name="button2" VerticalAlignment="Top" Width="46" Margin="0,42,63,0" Click="button2_Click">Add</Button>-->
<Button Height="19" HorizontalAlignment="Right" Name="button1" VerticalAlignment="Top" Width="46" Click="button1_Click" Margin="0,43,63,0">Add</Button>
My Code
Dt1 = new DataTable("T1");
Dt1.Columns.Add("Name");
Dt1.Rows.Add("abc1");
Dt1.Rows.Add("abc2");
Dt2 = new DataTable("T2");
Dt2.Columns.Add("Name");
Dt2.Rows.Add("xyz1");
Dt2.Rows.Add("xyz1");
Ds = new DataSet();
Ds.Tables.Add(Dt1);
Ds.Tables.Add(Dt2);
lst.DataContext = Ds;
Hi am in full accord with Andy and Thomas. They both have explained the concept elegantly.
I am only showing the steps of doing the same only with dataset.
The MVVM (ModelView ViewModel) I am not discussing here.
The Xaml looks like this
<Grid Name="myGrid" ShowGridLines="False">
<Label Height="28" Margin="12,5,0,0" Name="lblName" VerticalAlignment="Top" HorizontalAlignment="Left" Width="55">Name</Label>
<TextBox Height="23" Margin="73,8,85,0" Name="txtName" VerticalAlignment="Top" />
<Label Height="28" Margin="12,39,0,0" Name="lblPlace" VerticalAlignment="Top" HorizontalAlignment="Left" Width="55">Place</Label>
<TextBox Height="23" Margin="73,44,85,0" Name="txtPlace" VerticalAlignment="Top" />
<Button Height="23" HorizontalAlignment="Left" Margin="20,82,0,0" Name="btnAddRecord" VerticalAlignment="Top" Width="75" Click="btnAddRecord_Click">Add Record</Button>
<ListView Margin="31,119,27,45" Name="listView" *ItemsSource="{Binding}"*>
<ListView.View>
<GridView>
<GridViewColumn Header="Name" DisplayMemberBinding="{Binding Name}"/>
<GridViewColumn Header="Place" DisplayMemberBinding="{Binding Place}"/>
</GridView>
</ListView.View>
</ListView>
</Grid>
In the .CS file create a dataset
private DataSet MyDataSet()
{
DataTable dtInformation1 = new DataTable();
dtInformation1.Columns.Add("Name");
dtInformation1.Columns.Add("Place");
dtInformation1.Rows.Add(txtName.Text, txtPlace.Text);
DataTable dtInformation2 = new DataTable();
dtInformation2.Columns.Add("Name");
dtInformation2.Columns.Add("Place");
dtInformation2.Rows.Add(txtName.Text + "2", txtPlace.Text + "2");
DataSet Ds = new DataSet();
Ds.Tables.Add(dtInformation1);
Ds.Tables.Add(dtInformation2);
return Ds;
}
Next in the Button's click event write the following
private void btnAddRecord_Click(object sender, RoutedEventArgs e)
{
**listView.ItemsSource = MyDataSet().Tables[0].DefaultView;
- OR -
listView.ItemsSource = MyDataSet().Tables[1].DefaultView;**
}
N.B.~ You cannot assign the source of the ListView a dataset.
Why ? You may ask?
A dataset, in simple terms , is a collection of data tables.
Suppose you have 5 different datatables. And say none of their column names as well as column numbers are same.
Now you have assigned all those to your dataset. How will the controls source know that which source it has to bind?
Inorder to overcome such a situation, either make a custom datatable that will have all the columns of those discreet datatables and assign the values to this custom one and then bind to the source.
Or you need to explicitly specify the datatable in the datasource
But I always prefer to use MVVM pattern for this kind of operations.
I'm not sure what you're trying to do here... Is that the result you expect ?
abc1 xyz1
abc2 xyz2
There is no relation between your tables, so the binding system can't guess which row of T1 you want to associate with which row of T2... You should either put all data in the same table, or use a DataRelation between the two tables (but that would require extra fields for the join). You would then set the DataTable as the ItemsSource, not the DataSet.
Alternatively, you could create a dedicated class to hold the data, as suggested by Andy
WPF Binding works off of properties. For example, consider the following Person object:
class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
You could then modify the XAML for your ListView to display a collection of Person objects by doing the following:
<ListView Margin="15,89,63,73" Name="lst" ItemsSource="{Binding}">
<ListView.View>
<GridView>
<GridViewColumn Header="First Name"
DisplayMemberBinding="{Binding Path=FirstName}" />
<GridViewColumn Header="Last Name"
DisplayMemberBinding="{Binding Path=LastName}" />
</GridView>
</ListView.View>
</ListView>
This will display the object's first name in the first column, and their last name in the second column (assuming that the DataContext of the ListView is a collection of Person objects).
In theory, to bind the values in a DataTable to a ListView, you could set the ItemsSource to the DataTable's Rows property. The problem becomes that the DataRow class doesn't expose properties for its columns - there is only the Item property that takes an argument specifying the row. To my knowledge, XAML does not support properties that take arguments, so I don't think that it is possible to use a DataTable as the ItemsSource for a ListView.
You do have some other options, however. You could create a strongly typed DataSet, which would expose a DataTable with a property for each column. You can then bind each GridViewColumn to the correct property.
Another approach would be to not use a DataTable at all. Your data layer would still load the data from your source into the DataTable, but it would then convert that data into normal objects. You could create an instance of ObservableCollection, add each of the objects into it, and then bind the ListView to that. Each GridViewColumn would just bind to the corresponding property of the objects.
Updated:
In answer to OP's further question:
Can I use the xsd for the purpose?.It
holds a property for each datatable..
You need more than just a property for a DataTable. You'd also need a property for each value in each row of the DataTable. Otherwise there is no property for the ListView's columns to bind to.
This worked for me.
public partial class MainWindow : Window
{
public ObservableCollection<DataTable> _observableCollection =
new ObservableCollection<DataTable>();
public MainWindow()
{
InitializeComponent();
DataTable dt1 = new DataTable();
dt1.Columns.Add("OrderID");
dt1.Columns.Add("CustomerID");
dt1.Columns.Add("ProductID");
dt1.Rows.Add("test1", "test2", "test3");
DataTable dt2 = new DataTable();
dt2.Columns.Add("OrderID");
dt2.Columns.Add("CustomerID");
dt2.Columns.Add("ProductID");
dt2.Rows.Add("test4", "test5", "test6");
_observableCollection.Add(dtInformation1);
_observableCollection.Add(dtInformation2);
}
public ObservableCollection<DataTable> _Collection
{ get { return _observableCollection; } }
ListView XAML
<ListView Height="Auto"
HorizontalAlignment="Stretch"
Name="somename"
ItemsSource="{Binding _Collection}" VerticalAlignment="Stretch" Width="Auto" >
<GridViewColumn Header="OrderID" Width="auto" >
<GridViewColumn.CellTemplate>
<DataTemplate>
<Button Tag="{Binding OrderID}" Content="{Binding OrderID}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Width="auto" DisplayMemberBinding="{Binding CustomerID}" Header="CustomerID" />
<GridViewColumn Width="auto" DisplayMemberBinding="{Binding ProductID}" Header="ProductID" />

Resources