Databinding with a listbox - c#

I have a bit of code that reads a json response from an HTTP server, it then parses this and inserts the data into a ListBox control.
The event I fire off when the download is complete is the following:
void webClient_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
{
DataContractJsonSerializer ser = null;
try
{
ser =
new DataContractJsonSerializer(typeof(ObservableCollection<UserLeaderboards>));
ObservableCollection<UserLeaderboards> users =
ser.ReadObject(e.Result) as ObservableCollection<UserLeaderboards>;
foreach (UserLeaderboards em in users)
{
int Fid = em.id;
string Fusername = em.username;
int Fscore = em.score;
lstbLeaders.Items.Add(Fid + Fusername + Fscore);
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
Now, when I do the items.add I presume it's just joining up the 3 variables and adding it to one column in the ListBox. This works fine and I see all 3 items joined up and displayed.
I want to separate this and make it look a bit nicer so I've created some XAML to try and bind the variables to textblocks. The following is just binding the username. I also have a public class that get/sets all 3 variables.
<ListBox Height="346" HorizontalAlignment="Left" Margin="5,221,0,0"
Name="lstbLeaders" VerticalAlignment="Top" Width="446">
<DataTemplate>
<TextBlock Text="{Binding Source=Fusername}" />
</DataTemplate>
</ListBox>
When running the above I get nothing displayed at all. I have a feeling it's something simple?
Thanks.

To display a simple string your xaml should look like this:
<ListBox Height="346" HorizontalAlignment="Left" Margin="5,221,0,0"
Name="lstbLeaders" VerticalAlignment="Top" Width="446">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}" />
</DataTemplate>
<ListBox.ItemTemplate>
</ListBox>
and you have to provide an object instead of a simple string if you want to split the properties to make it look nicer. If you just add Fid + Fusername + Fscore you will end up with a plain string.
<ListBox Height="346" HorizontalAlignment="Left" Margin="5,221,0,0"
Name="lstbLeaders" VerticalAlignment="Top" Width="446">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Id}" />
<TextBlock Text="{Binding Name}" />
<TextBlock Text="{Binding Score}" />
</StackPanel>
</DataTemplate>
<ListBox.ItemTemplate>
</ListBox>
You will need a view class:
public class UserView
{
public string Id {get;set;}
public string Name {get;set;}
public int Score {get;set;}
}
in your code behind:
var usersList = new List<UserView>();
foreach (UserLeaderboards em in users)
{
int Fid = em.id;
string Fusername = em.username;
int Fscore = em.score;
usersList.Add(new UserView { Id = Fid, Name = Fusername, Score = Fscore} );
}
lstbLeaders.ItemsSource = usersList;
Further notes:
Why not bind the ObservableCollection<UserLeaderboards> direcectly to the list box?
If there is no reason to convert to an other type skip the foreach part of the code and simply set lstbLeaders.ItemsSource = users;.
void webClient_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
{
try
{
var ser = new DataContractJsonSerializer(
typeof(ObservableCollection<UserLeaderboards>));
var users = ser.ReadObject(e.Result)
as ObservableCollection<UserLeaderboards>;
lstbLeaders.ItemsSource = users;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
Take look at the MVVM pattern. If you want to work with XAML you should know about this. It simplifies your work and creates cleaner code.
If you want to add edit functionality or data can change you may need to implement INotifyPropertyChanged on the View class.
You can use type inference which especially helps when working with cumbersome class names. var list = new ObservableCollection<SomeLongTypeName>() saves much typing and screen estate.
Hungarian notation makes me cringe ;)

I think you missed the ItemTemplate.
Try this
<ListBox Height="346" HorizontalAlignment="Left" Margin="5,221,0,0" Name="lstbLeaders" VerticalAlignment="Top" Width="446">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Source=Fusername}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>

Related

C# UWP create check-list table programmatically

I have task to create in C# UWP user created check-list.
But I have stuck from the beginning cause XAML is new for me, so I have no idea what to start from.
So, I have textbox to enter title, task or subtask to in listbox (priviously added to) selected task.
this is my xaml how it looks like now:
<Page
x:Class="Table1.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Table1"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid>
<TextBox x:Name="txt" HorizontalAlignment="Left" Height="71" Margin="71,247,0,0" Text="TextBox" VerticalAlignment="Top" Width="395"/>
<RadioButton x:Name="title" Content="Add Title" HorizontalAlignment="Left" Margin="71,86,0,0" VerticalAlignment="Top"/>
<RadioButton x:Name="task" Content="Add Task" HorizontalAlignment="Left" Margin="71,123,0,0" VerticalAlignment="Top"/>
<RadioButton x:Name="subtask" Content="Add Subtask" HorizontalAlignment="Left" Margin="71,155,0,0" VerticalAlignment="Top"/>
<ListBox x:Name="listbox" HorizontalAlignment="Left" Height="68" Margin="71,354,0,0" VerticalAlignment="Top" Width="395"/>
<Button x:Name="btn" Content="Button" HorizontalAlignment="Left" Margin="401,483,0,0" VerticalAlignment="Top" Click="btn_Click"/>
</Grid>
</Page>
There are the code:
public class subtasks
{
public string parent { get; set; }
public string subtask { get; set; }
public subtasks(string parenti, string subtaski)
{
parent = parenti;
subtask = subtaski;
}
public void setsub(string parenti, string sub)
{
parent = parenti;
subtask = sub;
}
}
List<string> Tasks = new List<string>();
List<subtasks> sub = new List<subtasks>();
private void btn_Click(object sender, RoutedEventArgs e)
{
string parent = "";
string Title;
string Task;
string Subtask;
if (title.IsChecked==true)
{
Title = txt.Text;
adding(Title, parent, 1);
}
else if (task.IsChecked==true)
{
Task = txt.Text;
adding(Task, parent, 2);
}
else if (subtask.IsChecked==true)
{
parent = listbox.SelectedItem.ToString();
Subtask = txt.Text;
adding(Subtask, parent, 3);
}
else
{
}
}
private void adding(string str, string par, int x)
{
subtasks subi = new subtasks(par,str);
RowDefinition row = new RowDefinition();
TextBlock text = new TextBlock();
if (x==1)
{
print(str);
}
else if (x==2)
{
Tasks.Add(str);
listbox.Items.Add(str);
text.Text = str;
print(str);
}
else
{
sub.Add(subi);
print(str);
}
}
private void print(string title)
{
int step = 0;
Grid gridwin = new Grid();
gridwin.Children.Clear();
RowDefinition row = new RowDefinition();
TextBlock text = new TextBlock();
text.Text = title;
Grid.SetColumn(text, 0);
Grid.SetRow(text, step);
step++;
for (int i = 0; i < Tasks.Count; i++)
{
text.Text = Tasks[i].ToString();
gridwin.Children.Add(text);
Grid.SetColumn(text, 0);
Grid.SetRow(text, step);
step++;
for (int k = 0; k < sub.Count; k++)
{
if (sub[k].parent == Tasks[i])
{
text.Text = sub[k].subtask.ToString();
gridwin.Children.Add(text);
Grid.SetColumn(text, 0);
Grid.SetRow(text, step);
step++;
}
}
}
}
As you see I need to clear and put data every time the button is clicked, cause you never know when user will decide to add new subtask for previously added task. So, the question is, how to make the table with column1 with tasks and subtasks and column2 which is chekbox.
What you want to probably do is to create a DataTemplate. You use this to specify how list items should be displayed and formatted. This way you can specify you want to lay them out as a Grid with two columns like description and CheckBox. Take a look into the documentation to see some examples of DataTemplates. You can also see the Azure Mobile Apps quickstart for UWP, because although it is focused on demonstrating Microsoft Azure integration to UWP, it is actually a to-do app, which should give you some inspiration for building your own.
The layout could look like this:
<ListBox x:Name="listbox" HorizontalAlignment="Left" Height="68" Margin="71,354,0,0" VerticalAlignment="Top" Width="395">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Text}" />
<CheckBox Grid.Column="1" IsChecked="{Binding IsChecked, Mode=TwoWay}" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
You can see my code is also using {Binding} syntax, which you will also need to learn a bit about to be able to know when the user has checked a to-do item in the list. I suggest you to take a look at a simple tutorial sample like here. In fact, data-binding is one of the most important things when building XAML-based apps and when you get to understand this concept, it will help you a lot on the way to becoming a UWP ninja :-) .
Why dont use the UWP DataGrid with CheckBox?
XAML
<toolkit:DataGrid Grid.Column="0" ItemsSource="{x:Bind myItemsToBind}"
x:Name="dgwDeviceSPNs" MinWidth="100"
VerticalAlignment="Stretch" HorizontalAlignment="Stretch"
HorizontalScrollBarVisibility="Visible"
VerticalScrollBarVisibility="Visible"
AlternatingRowBackground="Transparent"
AreRowDetailsFrozen="False"
AreRowGroupHeadersFrozen="True"
AutoGenerateColumns="False"
CanUserSortColumns="False"
CanUserReorderColumns="True"
RowGroupHeaderPropertyNameAlternative=""
CanUserResizeColumns="True"
MaxColumnWidth="200"
FrozenColumnCount="0"
GridLinesVisibility="Horizontal"
HeadersVisibility="None"
IsReadOnly="True"
RowDetailsVisibilityMode="Collapsed"
SelectionMode="Single">
<toolkit:DataGrid.Columns>
<toolkit:DataGridTemplateColumn MinWidth="10">
<toolkit:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Padding="2">
<CheckBox ToolTipService.ToolTip="{Binding Name}" IsChecked="{Binding IsSelected, Mode=TwoWay}" Content="{Binding Name}"></CheckBox>
</StackPanel>
</DataTemplate>
</toolkit:DataGridTemplateColumn.CellTemplate>
</toolkit:DataGridTemplateColumn>
</toolkit:DataGrid.Columns>
</toolkit:DataGrid>

UWP update a itemscontrol with an array that keeps updating

I have an array that keeps changing its values, because of this I want to have the apps UI refreshing every time the array's values do. I have this bound with an itemsControl. I can show the first array's values but then I can't update them I have tried .items.Clear() but its not working. Here are snippets of the .xaml and the xaml.cs. I actually took the code of the .xaml from a question from this site.
.xaml
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBox Text="Testing" IsReadOnly="True"></TextBox>
<ItemsControl x:Name="itemsControl"
ItemsSource="{Binding itemsControl}"
FontSize="24">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Width="Auto"
Margin="0 12"
HorizontalAlignment="Center">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<StackPanel Grid.Column="0"
Grid.Row="0"
Orientation="Horizontal">
<TextBlock Name="txtblk0" Text="{Binding}" />
</StackPanel>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
.xaml.cs
String c = (new String(cArray));
string[] arr = null;
string[] data = null;
if (c != null)
{
arr = c.Split('\n');
if (arr.Length > 0)
{
data = arr[0].Split(',');
}
}
for(int index = 0; index < 4; index++)
{
itemsControl.Items.Add(float.Parse(data[index]));
}
itemsControl.Clear();
If anyone has an idea of how I can do this I will be very grateful, thanks in advance and I will try to answer any questions as soon as possible!
What you're missing is an understanding of how bindings are triggered to update.
The INotifyPropertyChanged interface contains a method (PropertyChanged) and when called and passed the name of a property will tell the binding system that the property has changed and the binding should be updated.
INotifyCollectionChanged is the equivalent for collections, and communicates when a collection has changed. i.e. something added, removed, or the list cleared.
ObservableCollection<T> contains an implementation of INotifyCollectionChanged that makes it easy to work with lists, collections, etc. that change.
If you used an ObservableCollection<float> instead of an array you'd be able to modify the list and have the UI updated to reflect this easily.
As a starter, see the following which demonstrates how easy it is to use an ObservableCollection.
XAML:
<StackPanel>
<Button Click="Button_Click">add an item</Button>
<ItemsControl ItemsSource="{Binding Items}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
code behind;
public MainPage()
{
this.InitializeComponent();
// Initialize the property
this.Items = new ObservableCollection<string>();
// Use self as datacontext (but would normally use a separate viewmodel)
this.DataContext = this;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
// add a new item to the UI
this.Items.Add(DateTime.Now.ToString());
}
// The "collection" that is shown in the UI
public ObservableCollection<string> Items { get; set; }

ListView Keep ListViewHeaderItem On Top

I start by explaining what I want to achieve:
The Letter "A" is one ListViewHeaderItem in my Listview. Without Scrolling the top of the List is looking like this.
After I am Scrolling the ListViewHeaderItem "A" is moving downwards with the rest of the items -
but how can I achieve that the Header is staying on top as Kind of the first item until the Letter "B" with ist subitems is coming? An example of the behaviour I want to achieve is the official "Mail" app for Windows 10 by Microsoft. It is keeping the datetime at the top until emails are coming which have been written one day earlier.
I don't know if this question is already existing but I don't know how it is called and I don't know what to Google for.
According to your description, I think what you want is a grouped ListView. The key points here is using CollectionViewSource as ItemsSource and setting GroupStyle to specify how groups are displayed. Following is a simple sample:
In XAML
<Page.Resources>
<CollectionViewSource x:Name="groupInfoCVS" IsSourceGrouped="True" />
</Page.Resources>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<ListView ItemsSource="{Binding Source={StaticResource groupInfoCVS}}">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Margin="15" Text="{Binding Path=Text}" />
</DataTemplate>
</ListView.ItemTemplate>
<ListView.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<Grid Background="LightGray">
<TextBlock Margin="10" Foreground="Black" Text="{Binding Key}" />
</Grid>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</ListView.GroupStyle>
</ListView>
</Grid>
And in code-behind
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
List<TestDemo> list = new List<TestDemo>();
for (int i = 0; i < 6; i++)
{
list.Add(new TestDemo { Key = "A", Text = $"Test A {i}" });
list.Add(new TestDemo { Key = "B", Text = $"Test B {i}" });
}
var result = from t in list group t by t.Key;
groupInfoCVS.Source = result;
}
}
public class TestDemo
{
public string Key { get; set; }
public string Text { get; set; }
}
And it looks like:
For more info, please see How to group items in a list or grid (XAML) and Simple ListView Sample in ListView and GridView sample on GitHub.

cascading dropdown in wp8

I'm try to create cascading dropdown when i select first one it's working fine.On SelectionChanged i'm try to bind second drop down on code behind it's show the result fine.But Dropdown show empty.here is my code..
Location ld = new Location();
ld = categoryList.SelectedItem as Location;
string id = "0";
try
{
id = Convert.ToString(ld.id);
}
catch (Exception ex)
{ }
if (id != "0")
{
// lstSublocation.Visibility = Visibility.Visible;
var lst = _lstlocation.Where(z => z.id == id).Select(z => z.sub_location).ToList();
lstSublocation.ItemsSource = lst;
}
In lst show 2 items.
<toolkit:ListPicker x:Name="lstSublocation" Foreground="Black"
BorderThickness="2" SelectionMode="Single"
VerticalAlignment="Bottom" Margin="12,0,10,460" BorderBrush="LightGray" Height="68" >
<toolkit:ListPicker.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding name}" Margin="12 0 0 0" Foreground="Black"/>
</StackPanel>
</DataTemplate>
</toolkit:ListPicker.ItemTemplate>
<toolkit:ListPicker.FullModeItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="0 21 0 20">
<TextBlock Text="{Binding name}"
Foreground="Black"
/>
</StackPanel>
</DataTemplate>
</toolkit:ListPicker.FullModeItemTemplate>
</toolkit:ListPicker>
Here is front end code ..... :( 2 hours struggle with this code like wrestlemania fight... :)
Code should work (as posted), this is probably where the problem is
var lst = _lstlocation.Where(z => z.id == id).Select(z => z.sub_location).ToList();
lstSublocation.ItemsSource = lst;
Put a break point at lstSublocation.ItemsSource = lst; and check what lst is
Make sure it's actually a List<your_model> and that your_model has a Property of name not just a public variable.. like this
public class sample_model
{
public sample_model()
{
this.name = "default";
}
public string name { get; set; } // this is bindable
// public string name; // this is NOT bindable
}

binding data - inotifypropertychanged does not work

I have a listBox1 in which data are binding from the list. Then I want to when I select any item from listBox1 in listBox2 will binding data from another list.
private void listBox1_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
Teams teams = (Teams)listBox1.SelectedItems[0];
getH2hResults("//td[#class='hell']", teams.Team1, teams.Team2); // add elements to list
getH2hResults("//td[#class='dunkel']", teams.Team1, teams.Team2); // and here also
listBox2.ItemsSource = lists.h2hList;
}
On the first time this work, but for the twice time listBox2 doesn't displays new data.
public class Lists : BindableBase
{
public Lists()
{
_teamsList = new List<Teams>();
_h2hList = new List<H2H>();
}
private List<Teams> _teamsList;
public List<Teams> teamsList
{
get
{
return _teamsList;
}
set
{
if (value != _teamsList)
{
_teamsList = value;
RaisePropertyChanged("teamsList");
}
}
}
private List<H2H> _h2hList;
public List<H2H> h2hList
{
get
{
return _h2hList;
}
set
{
if (value != _h2hList)
{
_h2hList = value;
RaisePropertyChanged("h2hList");
}
}
}
}
And XAML
<ListBox Name="listBox1" Width="300" Height="300"
VerticalAlignment="Top"
HorizontalAlignment="Left"
ItemsSource="{Binding teamsList}" SelectionChanged="listBox1_SelectionChanged">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Foreground="#FF4273CD" Text="{Binding Team1, Mode=TwoWay}"></TextBlock>
<TextBlock Text=" vs " FontWeight="Bold"></TextBlock>
<TextBlock Foreground="#FF4273CD" Text="{Binding Team2, Mode=TwoWay}"></TextBlock>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<ListBox Name="listBox2" Grid.Column="1" Width="300" Height="300"
VerticalAlignment="Top"
HorizontalAlignment="Left"
ItemsSource="{Binding h2hList}" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding date, Mode=TwoWay}"></TextBlock>
<TextBlock Text="{Binding result, Mode=TwoWay}"></TextBlock>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
RaisePropertyChanged("teamList");
is Wrong your propery is named 'teamsList' with an S,
change to:
RaisePropertyChanged("teamsList");
It is the public property you bind to and notify changes of,
edit:
also change your binding:
ItemsSource="{Binding teamList}"
to
ItemsSource="{Binding teamsList}"
Edit 2:
listBox2.DataContext = xxx
Not itemsource = xxx
With the line (in listBox1_SelectionChanged)
listBox2.ItemsSource = lists.h2hList;
you are effectively removing the binding from the ItemsSource property of listBox2.
Instead, you should only update the h2hList property in your Lists class (which presumably happens in getH2hResults) and remove the above line from your code.
Note however that it is not sufficient to clear and re-fill that list. You need to set the h2hList property in order to get a property change notification raised:
var newList = new List<H2H>();
// fill newList before assigning to h2hList property
lists.h2hList = newList;
If you want to keep the list and just change its elements, you would need to use ObservableCollection<H2H> instead of List<H2H> as collection type. This would be the better approach anyway, as you would not have to care for when exactly you add elements to a newly created collection.

Categories

Resources