Add index column on listview - c#

I'm using WPF, I have a window with a listview which is binded to an ObservableCollection.
So It's looks like that:
public ObservableCollection<Task> TaskList { get; set; }
Task being a model
public class Task
{
public int Id { get; set; }
public string Name { get; set; }
....
}
The XAML of the ListView.ItemTemplate:
<ListView.ItemTemplate>
<DataTemplate>
<WrapPanel>
<TextBlock Text="{Binding Name}" />
</WrapPanel>
</DataTemplate>
</ListView.ItemTemplate>
I cannot use the Id because items in the collection can be deleted or sorted, I need the index in the collection (with update if the collection get changed).
I would like to find a way to add an "index column" to the listview, so the index of the Task in the ObservableCollection would be before the name in every row (see below).
| 0 Task
| 1 TaskTest
| 2 OtherTask
| 3 LastTask
Thanks for your help!

As far as I know it's not that easy to add index :(
If I was You, I'll add a property to item, that is shown in Your listview. That property would be filled with ViewModel and incremented. But if You sort or delete it should be rewritten.
You can also try making a property inside Your ViewModel like:
private int counter;
public int Index
{
get
{
counter++;
return counter;
}
}
and Bind it to each element using RelativeSource FindAncestor. but again - it should be refreshed on collectionChange

try this,crate a view model
public class TaskViewModel : Task
{
private int _Index;
public int Index
{
get { return _Index; }
set { _Index = value; }
}
}
ItemTemplate:
<ListView.ItemTemplate>
<DataTemplate>
<WrapPanel Orientation="Horizontal">
<TextBlock Text="{Binding Index}" Width="Auto" Height="Auto" Margin="0,0,4,0"/>
<TextBlock Text="{Binding Name}" Width="Auto" Height="Auto"/>
</WrapPanel>
</ListView.ItemTemplate>
code behind:
TaskViewModel vm = new TaskViewModel();
vm.Id = 0;
vm.Index = 1;
vm.Name = "sad";
TaskList.Add(vm);

Related

Combobox Itemsource Binding does not work

I'm trying to fill my Combobox ItemsSource using Binding in Xaml with a collection of data ObservableCollection but I always get the Combobox ItemsSource null .
WPF UserControl Cs Code : ( Update the code )
public ObservableCollection<User> users { get; set; }
public partial class MainWindow : Window
{
InitializeComponent();
User user1 = new User("Mariah", 15);
User user2 = new User("John", 19 );
users = new ObservableCollections<User>();
users.Add(user1);
users.Add(user2);
this.DataContext = this;
Console.WriteLine(ComboBoxUsers.Items.Count); // always 0
Console.WriteLine(ComboBoxUsers.ItemsSource); // always null
}
WPF UserControl Xaml Code : ( Updated my code )
<ComboBox SelectedIndex = "0" x:Name="ComboBoxUsers" ItemsSource="{Binding users, UpdateSourceTrigger=PropertyChanged}" FontFamily="Arial" FontSize="11" Grid.Row="3" Height="30" Margin="10,5,5,5">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" >
<Image IsEnabled="False" Source="{Binding Image}"/>
<TextBlock x:Name="textblock" IsEnabled="False" Text="{Binding Name} />
</StackPanel>
<DataTemplate.Resources>
<Style TargetType="ComboBoxItem">
<Setter Property="IsEnable" Value="{Binding IsEnable}"/>
</Style>
</DataTemplate.Resources>
</DataTemplate>
</ComboBox.ItemTemplate>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
<ComboBox.ItemContainerStyle>
<Style TargetType="ComboBoxItem" BasedOn="{StaticResource {x:Type ComboBoxItem}}">
<Setter
Property="Visibility"
Value="{Binding IsHidden}" />
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>
Class User
public class User
{
public string Name { get; set; }
public int Age { get; set; }
public User(string name, int age)
{
this.Name = name;
this.Age = age;
}
}
What is the source of this problem?
Discarding unnecessary (which is not relevant to the question or has syntax errors), your example with a little formatting works fine.
XAML:
<ComboBox x:Name="ComboBoxUsers"
ItemsSource="{Binding Users}"
FontFamily="Arial"
FontSize="11"
Grid.Row="3"
Height="30"
Margin="10,5,5,5">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" >
<TextBlock IsEnabled="False"
Text="{Binding Name}" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
Code-behind:
public class User
{
public string Name { get; }
public int Age { get; }
public User(string name, int age)
{
Name = name;
Age = age;
}
}
public partial class MainWindow : Window
{
public ObservableCollection<User> Users { get; }
public MainWindow()
{
InitializeComponent();
// Initialize collection with some items
Users = new ObservableCollection<User>
{
new User("Mariah", 15),
new User("John", 19)
};
DataContext = this;
}
}
Result:
Remarks:
Your
Console.WriteLine(ComboBoxUsers.Items.Count); // always 0
Console.WriteLine(ComboBoxUsers.ItemsSource); // always null
because you use Binding. You needn't access ItemsSource or Items.Count through ComboBox - you should use binded collection Users (e.g. Users.Count) to manipulate or get ComboBox content.
EDIT.
About SelectedItem. You should define for yourself, you want use Bindings or work with ComboBox directly.
Binding push you to NOT use ComboBox.SelectedItem/Index/Value whatever. Even not access ComboBoxUsers to get some data. Binding is closely related to, for example, the MVVM Pattern. If you decided to use Bindings - forget about accessing directly to ComboBox or it data properties SelectedItem/Index/Value or similar.
If you use Bindings - you should create a property (e.g. SelectedUser) for SelectedItem (same as you create property Users for your ItemsSource ) and bind to it:
XAML (introducing binding for SelectedItem property and SelectionChanged handler):
<ComboBox x:Name="ComboBoxUsers"
ItemsSource="{Binding Users}"
SelectedItem="{Binding SelectedUser}"
SelectionChanged="OnUserSelectionChanged"
FontFamily="Arial"
FontSize="11"
Grid.Row="3"
Height="30"
Margin="10,5,5,5">
</ComboBox>
Code-behind (introducing property SelectedUser and OnUserSelectionChanged handler):
public partial class MainWindow : Window
{
public ObservableCollection<User> Users { get; }
// Here would be stored your Binded selected item
public User SelectedUser { get; set; }
// And here is your handler when selection changed
private void OnUserSelectionChanged(object sender, SelectionChangedEventArgs e)
{
// SelectedUser property stores selected in ComboBox item,
// so you can use it directly
_ = MessageBox.Show(SelectedUser.Name);
// Even if you wish to get directly - it is possible
// (thanks to #Clemens):
var selectedUser = (sender as ComboBox).SelectedItem as User;
var selectediIndex = (sender as ComboBox).SelectedIndex;
}
public MainWindow()
{
InitializeComponent();
Users = new ObservableCollection<User>
{
new User("Mariah", 15),
new User("John", 19)
};
DataContext = this;
}
}
Repeat same algorithm for each property you wish to Bind (e.g. SelectedIndex):
SelectedIndex="{Binding SelectedUserIndex}"
public int SelectedUserIndex { get; set; }
Result:
Decide for yourself, what you need. Nice modern Bindings or old boring (sender as ComboBox).SelectedItem.

Some Issue with Two-way Binding - Not working as expected in UWP

Problem: When location is changed via ComboBox cb1 the related location TextBlock does not change to updated value.
I am self learning and below is experiment code on binding that has
public EmpDeptViewModel vm; its initialize on button click event as below
private void btn2_Click(object sender, RoutedEventArgs e) {
vm = new EmpDeptViewModel();
this.Bindings.Update(); }
The XAML looks like this.
<ListView x:Name="listview3" ItemsSource="{x:Bind vm.InstanceOfDepartmentData}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="classes:Department">
<StackPanel Orientation="Horizontal">
<TextBlock Margin="5">
<Run Text="DeptNo: " /><Run Text="{x:Bind DeptNo}" />
</TextBlock>
<TextBlock Margin="5">
<Run Text="DeptName: " /><Run Text="{x:Bind DeptName}" />
</TextBlock>
<TextBlock Margin="5">
<Run Text="Location: " /><Run Text="{x:Bind Location, Mode=OneWay}" />
</TextBlock>
<ComboBox x:Name="cb1" ItemsSource="{Binding Source={StaticResource MyLocatonList}, Path=ListofLocationsInsideViewModel, Mode=TwoWay}" DisplayMemberPath="LocationName" SelectedValuePath="LocationName" SelectedValue="{x:Bind Location}" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Problem: When location is changed via ComboBox cb1 the related location TextBlock does not change to updated value.
The x:DataType="classes:Department" looks like this.
public class Department : BindableBase
{ private string _location;
public Department(int pdeptNo, string pdeptName, string plocation)
{
DeptNo = pdeptNo;
DeptName = pdeptName;
Location = plocation;
ListOfDeparmentEmployees = new List<Employee>(); }
public int DeptNo { get; set; }
public string DeptName { get; set; }
public string Location {
get { return this._location; }
set { this.SetProperty(ref this._location, value); }
}
public List<Employee> ListOfDeparmentEmployees { get; set; }
}
You may be bound in the wrong position
In your ComboBox, you set TwoWay to the ItemsSource. This does not make sense. You cannot change the Location if you modify the value of the ComboBox.
Try this:
Xaml
...
<ComboBox x:Name="cb1" ItemsSource="{Binding Source={StaticResource MyLocatonList}, Path=ListofLocationsInsideViewModel}"
DisplayMemberPath="LocationName" SelectedValuePath="LocationName" SelectedValue="{x:Bind Location,Mode=TwoWay}" />
...
However, if you write it directly, it will cause an endless loop and then report an error. You need to rewrite the Location property of the Department class.
Department.cs
...
public string Location
{
get { return this._location; }
set
{
if (_location != value)
{
this.SetProperty(ref this._location, value);
}
}
}
...
In addition, please note whether your BindableBase base class implements the INotifyPropertyChanged interface, which is the basis for modifying the UI while modifying the data.
Best regards.

C# Wpf Binding to Collection of Userdefined Class not working as expected

Currently i have an ObservableCollection of MyClass in my Viewmodel. I use the getter of this Collection to fill it from an other Datasource. I can now Display this Data in a Window(Grid) and the correct Data is shown, but when i change the Data the set is not fired(I think it is because not the Collection is changed, only a Element in the Collection). Should i create a Property for every Property of MyClass, so i can react to the changes of a single Value, the Questions i ask myself are:
How do i know what Element is selected at the moment
How to fill the Collection correct when i have a binding to every single item
I also thought of a Event when my Collection is changed, but i am not sure how to implement it right.
public ObservableCollection<MyClass<string>> SelectedParams
{
get
{
//Fill the Collection
}
set
{
//I think here i want to react to changed Textboxvalues in my View
}
}
public class MyClass<T> : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private T _curValue;
private string _value1;
private string _value2;
public string Value1
{
get
{
return _value1;
}
set
{
if (_value1 != value)
{
_value1 = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Value1)));
}
}
}
public string Value2
{
get
{
return _value2;
}
set
{
if (_value2 != value)
{
_value2 = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Value2)));
}
}
}
public T curValue
{
get
{
return _curValue;
}
set
{
_curValue = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(curValue)));
}
}
public MyClass()
{
}
public MyClass(string val1, string val2, T curVal)
{
Value1 = val1;
Value2 = val2;
curValue = curVal;
}
}
The xaml Code looks something like this
<ItemsControl ItemsSource="{Binding SelectedParams}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Grid.Column="0" Content="{Binding Value1}"/>
<Label Grid.Column="1" Content="{Binding Value2}"/>
<TextBox Grid.Column="2" Text="{Binding curValue, Mode=TwoWay}"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Edit1: Changed MyClass to INotifyPropertyChanged now the Collection changes internal Values but the Setter is still not called on change of a Value
You need to implement INotifyPropertChanged interface for MyClass and raise the PropertyChanged in setter to notify UI that the property value changed.
How do i know what Element is selected at the moment
If you want support for item selection you have to use an other control. ItemsControl does not support selection.
Use ListView for example. Bind ItemsSource and SelectedItem to your class. Now every time you click on an item, SelectedValue is updated. And if you change SelectedValue from code the UI updates the selected item in the list. You can also bind other controls to SelectedValue like I did with the TextBlock outside the ListView.
View
<StackPanel>
<ListView ItemsSource="{Binding Values}" SelectedItem="{Binding SelectedValue}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Item1}" />
<TextBlock Text="=" />
<TextBlock Text="{Binding Path=Item2}" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<StackPanel Orientation="Horizontal">
<TextBox Text="Selected:" Background="DarkGray" />
<TextBox Text="{Binding SelectedValue.Item1, Mode=OneWay}" Background="DarkGray" />
</StackPanel>
</StackPanel>
Data
public class ListViewBindingViewModel : INotifyPropertyChanged
{
private Tuple<string,int> _selectedValue;
public ObservableCollection<Tuple<string,int>> Values { get; }
public Tuple<string, int> SelectedValue
{
get { return _selectedValue; }
set
{
_selectedValue = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SelectedValue)));
}
}
public ListViewBindingViewModel()
{
Values = new ObservableCollection<Tuple<string, int>> {Tuple.Create("Dog", 3), Tuple.Create("Cat", 5), Tuple.Create("Rat",1)};
}
}

How to do Data Binding for Textblock within a LongListselector in windows phone app?

Hi, I am trying to bind the data for text block within a LongListSelector. But I am not getting any Output for it, kindly help me.
This is my XAML code:
<phone:LongListSelector ItemsSource="{Binding ''}" x:Name="longListSelector" HorizontalAlignment="Left" Height="680" VerticalAlignment="Top" Width="446" >
<phone:LongListSelector.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Name="name" Text="{Binding DataContext.TextContent,ElementName=page,Mode=OneWay}" Height="100" Width="100" HorizontalAlignment="Center">
</TextBlock>
</StackPanel>
</DataTemplate>
</phone:LongListSelector.ItemTemplate>
</phone:LongListSelector>
In the C# code I have parsed data which i need to display in the windows phone, in a menu format.
Part of C# code is shown below:
XDocument document = XDocument.Parse(e.Result);
var data1 = from query in document.Descendants("location")
select new Data
{
Lat = (string)query.Element("lat"),
Lag = (string)query.Element("lng")
};
foreach (var d in data1)
{
JsonParsing(d.Lat, d.Lag);
}
data1 = from query in document.Descendants("result")
select new Data
{
Country = (string)query.Element("formatted_address")
};
foreach (var d in data1)
{
// ob.JsonParsing(d.Lat, d.Lag);
//XmlParsing(d.Lat, d.Lag);
val = d.Country;
//listbox.Items.Add(val);
//StringsList.Add(val);
TextContent=val;
I want the value of the country to be shown inside the textblock, kindly help me figure this out as I am pretty new to this field, thanks.
try like this
a good reference
<DataTemplate>
<StackPanel VerticalAlignment="Top">
<TextBlock Text="{Binding Value}" />
</StackPanel>
</LongListSelector>
CodeBehind
**Add a public property only public property can be participate in databinding**
#region Public Properties
private ObservableCollection<YourModel> _collectionofValue;
public ObservableCollection<YourModel> CollectionofValues
{
get
{
return _collectionofValue;
}
set
{
_collectionofValue=value;
raisepropertyChanged("CollectionofValues");
}
}
private string _value;
public string Value
{
get
{
return _errorMessage;
}
set
{
_errorMessage = value;
RaisePropertyChanged("Value");
}
}
#endregion
**Set value to this public property when you get value**
// for single values
public void getValue()
{
value =GetXmlValue(); // your method that will return the value;
}
// as it is a collection
public void getValuestoCollection()
{
Collection.Add(new YourModel(value="SampleValue1");
Collection.Add(new YourModel(value="SampleValue1");
Collection.Add(new YourModel(value="SampleValue1");
Collection.Add(new YourModel(value="SampleValue1");
}
YourModel
// the collection of this model is binded to the LongListSelector.
public class ModelName
{
public string Values {get;set;}
}
reference
<phone:LongListSelector ItemsSource="{Binding Items}" x:Name="longListSelector" HorizontalAlignment="Left" Height="680" VerticalAlignment="Top" Width="446" >
<phone:LongListSelector.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Name="name" Text="{Binding Path=TextContent}" Height="100" Width="100" HorizontalAlignment="Center">
</TextBlock>
</StackPanel>
</DataTemplate>
</phone:LongListSelector.ItemTemplate>
</phone:LongListSelector>
Your C# algm should be:
i) Have a viewmodel class
public class MyViewModel
{
public ObservableCollection<MyDataItem> Items {get; set;}
public MyViewModel()
{
Items=new ObservableCollection<MyDataItem>();
loop //add your items to your 'Items' property so that you can bind this with LongListSelector ItemsSource
{
Items.Add(new MyDataItem("mystring"));
}
}
}
public class MyDataItem
{
public MyDataItem(string s)
{
TextContent=s;
}
public string TextContent {get;set;}
}
ii) Create an instance to ViewModel class and set DataContext
// write this in the constructor of the page which contains the LongListSelector
public MyViewModel vm;
constructor()
{
vm=new MyViewModel();
this.DataContext=vm;
}

Bind item-related variable to listheader in longlistselector (WP8)

I'm new to Windows Phone development and I'm currently facing an issue while using the LongListSelector in WP8, and I don't know how to proceed to achieve the result I want.
I use it to display a list of items as usual. The class used contains 5 items, and one of them is a float value. I want to display, in the list header, the sum of all positive float values contained in the list, but I have no idea whatsoever about how to do this.
I tried to bind another variable (result of the sum) specificly to the listheader in addition to the original binding, or to add another item in the class containing the sum result (hence repeated throughout the list in each list item), but it didn't work.
I guess this is a pretty basic fonctionnality (for instance to count and display the number of elements of the list), but I can't figure out how to do this.
EDIT : I thought showing my code wouldn't help, but here it is. (I took away the formatting that wasn't relevant)
XAML
<phone:LongListSelector x:Name="ListeSolde" LayoutMode="List">
<phone:LongListSelector.ListHeaderTemplate>
<DataTemplate>
<TextBlock Text="{Binding SommeTotale}" />
</DataTemplate>
</phone:LongListSelector.ListHeaderTemplate>
<phone:LongListSelector.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Nom}" />
<TextBlock Text="{Binding DerniereConnexion}" />
<TextBlock Text="{Binding Depuis}" />
<TextBlock Text="{Binding Solde}" />
</DataTemplate>
</phone:LongListSelector.ItemTemplate>
</phone:LongListSelector>
Class definition
public class resume
{
public string Nom { get; set; }
public double Solde { get; set; }
public string Depuis { get; set; }
public string DerniereConnexion { get; set; }
public resume(string nom, double solde, string depuis, string derniereconnexion)
{
this.Nom = nom;
this.Solde = solde;
this.Depuis = depuis;
this.DerniereConnexion = derniereconnexion;
}
}
public class total
{
public double Total { get; set; }
public double calculTotal(List<resume> soldes)
{
double total = new double();
foreach (resume solde in soldes)
{
if (solde.Solde > 0)
total += solde.Solde;
}
return total;
}
public total(double Dtotal)
{
this.Total = Dtotal;
}
}
And code behind
public MainPage()
{
InitializeComponent();
List<resume> soldes = new List<resume>();
Donnees MainData = new Donnees();
soldes = MainData.RefreshResume(soldes); // A method that basically add items to the list
total SommeTotale = new total(1);
SommeTotale.Total = SommeTotale.calculTotal(soldes);
ListeSolde.ItemsSource = soldes;
}
This of course doesn't work (as far as the list header is concerned) and this is how I would do it
This is how I managed to bind other data from my ViewModel to the header of the LongListSelector.ListHeader.
<phone:LongListSelector.ListHeaderTemplate>
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock Grid.Row="2" Foreground="DarkViolet"
Text="{Binding ElementName=LayoutRoot, Path=DataContext.AddressBookList.Count}" />
</Grid>
</DataTemplate>
</phone:LongListSelector.ListHeaderTemplate>
The ListHeader property binds directly to the DataContext of the LongListSelector. This is different that the items contained within the LongListSelector. The items contained within it are bound to each item within the ItemSource. One of the best ways to get the ListHeader to display is to create an object that houses the data for the LongListSelector
public class ResumeContainer
{
public double SommeTotale { get { return Resumes.Sum(r => r.Value); } }
public IEnumerable<Resume> Resumes { get; set; }
}
You would set the DataContext of the LongListSelector to be an instance of the ResumeContainer. This would preferably be a property of your ViewModel. You would need to change your xaml to be
<phone:LongListSelector x:Name="ListeSolde" ItemsSource="{Binding Resumes}">
Your code-behind then changes to
List<resume> soldes = new List<resume>();
Donnees MainData = new Donnees();
soldes = MainData.RefreshResume(soldes);
ListeSolde.DataContext = new ResumeContainer { Resumes = soldes };

Categories