So, I attempting to, very simply, display items in a Windows 10 listview, and then seperate them by group. Everything is working fine, except that I can't seem to bind the title of the group.
Here is my current xaml:
<ListView ItemsSource="{Binding Source={StaticResource cvsEpisodes}}"/>
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<TextBlock Text="{Binding EpisodeNB}"/>
<TextBlock Text="{Binding EpisodeTT}"/>
<TextBlock Text="{Binding EpisodeDESC}"/>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<Grid>
<TextBlock Text="{Binding SEASONNB}"/>
</Grid>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</ListView.GroupStyle>
</ListView>
<Page.Ressources>
<CollectionViewSource x:Name="cvsEpisodes" IsSourceGrouped="True"/>
</Page.Ressources>
And the C#, that is executed by the OnNavigatedTo event:
List < EPISODELST > Episodes = new List < EPISODELST > ();
var episodes = root.episodes.Select(m = >new EPISODELST {EpisodeTT = m.title, EpisodeNB = m.episode.ToString(), EpisodeDESC = m.overview, SEASONNB = m.season.ToString()}).ToList();
foreach(EPISODELST s in episodes)
{
Episodes.Add(new EPISODELST {EpisodeTT = s.EpisodeTT, EpisodeDESC = s.EpisodeDESC, EpisodeNB = "EPISODE " + s.EpisodeNB, SEASONNB = s.SEASONNB });
}
var result = from EPISODELST in Episodes group EPISODELST by EPISODELST.SEASONNB into grp orderby grp.Key select grp;
cvsEpisodes.Source = result;
(EPISODELST and episodes are two classes, but it isn't necessary to paste them here)
I have seen various other implementations of grouped listviews online, but they are all way more complex than this, and I'm guessing that this should work, because I can tell the code can succesfuly sort all the data correctly.
The problem probably just has to do with the TextBlock's binding, but I have tried various other things I found online, such as {Binding=Name}, or {Binding Key.Name}, but nothing seems to work.
So, In the end, this was really simple. I found the awnser burried deep down Microsoft's UWP Github sample page.
It has to be binded to {Binding Key}
Inside GroupStyle:
<TextBlock Text="{Binding Key}"/>
Related
I'm trying to bind a list to a listbox in WPF. But it doesn't seem to work, I just see nothing on screen.
Here is my code:
WPF
<ListBox x:Name="listBox" HorizontalAlignment="Left" Height="453" VerticalAlignment="Top" Width="119" Margin="0,43,0,0" ItemsSource="{Binding orderlist}">
<ListBoxItem Content="{Binding orderlist.ID}"></ListBoxItem>
</ListBox>
C#
Order order = new Order();
Klantgegevens klantgegevens = new Klantgegevens();
XmlReader rdr = XmlReader.Create(#"C:\Users\Gebruiker\Desktop\EDI\Rekening.xml");
rdr.ReadToFollowing("datum");
order.DatumOntvangst = rdr.ReadElementContentAsString();
rdr.ReadToFollowing("volgnr");
order.Status = "Aangenomen";
order.Opmerkingen = "";
rdr.ReadToFollowing("naam");
order.Afzender = rdr.ReadElementContentAsString();
rdr.ReadToFollowing("naam");
klantgegevens.Naam = rdr.ReadElementContentAsString();
rdr.ReadToFollowing("straat");
klantgegevens.Straat = rdr.ReadElementContentAsString();
rdr.ReadToFollowing("huisnr");
klantgegevens.Huisnummer = rdr.ReadElementContentAsInt();
rdr.ReadToFollowing("plaats");
klantgegevens.Woonplaats = rdr.ReadElementContentAsString();
rdr.ReadToFollowing("postcode");
klantgegevens.Postcode = rdr.ReadElementContentAsString();
rdr.ReadToFollowing("telefoonnr");
klantgegevens.Telefoonnummer = rdr.ReadElementContentAsString();
order.Klantgegevens = klantgegevens;
orderlist.Add(order);
listBox.DataContext = orderlist;
As you probably know, Order is a custom class, and so is Klantgegevens.
I'm pretty new to binding and WPF in general so excuse me for my stupidness :)
You need to set or bind the ItemsSource property of ListView to an IEnumerable. Since you have set the DataContext property to your "orderlist" you should bind the ItemsSource property directly to the DataContext (ItemsSource="{Binding}"). You should also use an ItemTemplate as suggested by Fruchtzwerg :
<ListBox x:Name="listBox" HorizontalAlignment="Left" Height="453" VerticalAlignment="Top" Width="119" Margin="0,43,0,0" ItemsSource="{Binding}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding ID}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Also note that the DataContext of the ItemTemplate is an item in your ItemsSource, i.e. an Order object in this case. So to bind to the "ID" property of the Order object you use the binding syntax above. "ID" must be a public property of the Order class.
With
<ListBoxItem Content="{Binding orderlist.ID}"></ListBoxItem>
you are adding an item in XAML. But your plan is to create a template to present bound items. The simplest solution is to use
<ListBox x:Name="listBox" DisplayMemberPath="ID"/>
if only one property needs to be presented. Multiple properties can be showed by creating a template like
<ListView x:Name="listBox">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding ID}" />
<TextBlock Text="{Binding datum}"/>
<!-- ... -->
</WrapPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Furthermore you should use a property like
public ObservableCollection<Klantgegevens> Items { get; } =
new ObservableCollection<Klantgegevens>();
to bind on. Set the DataContext of the whole Window with the ListView to the object, with this property. After that you can bind the ListView with
<ListView ItemsSource="{Binding Items}"/>
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.
I have a ListBox element,
which purpose is to show the users the activities,
that are registered on the Database
so that they can choose from them to modify or delete them.
After consulting two very useful answers about using DataContext and DataTemplates,
I decided to implement that knowledge in my project,
unfortunately, it's not working.
When I run it and I select the text on the ListBox,
I only see: DataTemplate templ = new DataTemplate(typeof(Activities));
as its content, and I didn't mark it up as code,
because I want to stress the fact that it appears as a string,
if you will.
I get that there could be more than one workaround for what I'm trying to achieve.
however I really want to understand this, as it appears to be very useful.
Here's the code:
//This is the connection instance to the database
Connection c = new Connection();
DataTemplate templ = new DataTemplate(
typeof(Activities)
);
//The ListActivities method returns
//an ObservableCollection<Activities> list
libTest.DataContext = c.ListActivities(
objSem.getId()
);
libTest.SetBinding(
ItemsControl.ItemsSourceProperty, new Binding()
);
FrameworkElementFactory sp = new FrameworkElementFactory(
typeof(StackPanel)
);
sp.SetValue(StackPanel.OrientationProperty, Orientation.Horizontal);
sp.Name = "myTemplate";
FrameworkElementFactory date = new FrameworkElementFactory(
typeof(Label)
);
date.SetBinding(Label.ContentProperty, new Binding("date"));
sp.AppendChild(date);
FrameworkElementFactory nameAct = new FrameworkElementFactory(
typeof(Label)
);
nameAct.SetBinding(Label.ContentProperty, new Binding("nameAct"));
sp.AppendChild(nameAct);
FrameworkElementFactory descr = new FrameworkElementFactory(
typeof(Label)
);
descr.SetBinding(Label.ContentProperty, new Binding("descr"));
sp.AppendChild(descr);
FrameworkElementFactory quantity = new FrameworkElementFactory(typeof(Label));
quantity.SetBinding(Label.ContentProperty, new Binding("quantity"));
sp.AppendChild(quantity);
templ.VisualTree = sp;
libTest.ItemTemplate = templ;
i dont like code definition for such thing so here is the xaml one
<DataTemplate DataType="{x:Type local:Activities}">
<StackPanel Orientation="Horizontal">
<Label Content="{Binding date}"/>
<Label Content="{Binding nameAct}"/>
<Label Content="{Binding descr}"/>
<Label Content="{Binding quantity}"/>
</StackPanel>
</DataTemplate>
just put this into your resources and all Activities will render like this
pls read something more about binding in WPF, maybe MVVM stuff too. so you would better understand what you need when you do binding with WPF.
a little example
create a class which will be your DataContext and put a public property for your List in it.
public class SampleViewModel
{
public ObservableCollection<Activities> MyActivities {get;set;}
}
xaml.cs: set the DataContext for your View to your Viewmodel class
public partial class SampleWindow : Window
{
private SampleViewModel _data;
public SampleWindow()
{
_data = new SampleViewModel();
InitializeComponent();
this.DataContext = _data;
}
}
xaml: define your Bindings for your controls
<ListBox ItemsSource="{Binding MyActivities}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Label Content="{Binding date}"/>
<Label Content="{Binding nameAct}"/>
<Label Content="{Binding descr}"/>
<Label Content="{Binding quantity}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
or
<ListBox ItemsSource="{Binding MyActivities}">
<ListBox.Resources>
<DataTemplate DataType="{x:Type local:Activities}">
<StackPanel Orientation="Horizontal">
<Label Content="{Binding date}"/>
<Label Content="{Binding nameAct}"/>
<Label Content="{Binding descr}"/>
<Label Content="{Binding quantity}"/>
</StackPanel>
</DataTemplate>
</ListBox.Resources>
</ListBox>
For my Windows Phone 8 App I have a Listbox element like below;
when I press multiselect icon on AppBar, I want to show checkboxes inside DataTemplate.
So users can make multiselect on items.
I have 50 elements bound on this Listbox and always at the index 11 ItemContainerGenerator.ContainerFromIndex returns null, plus some other items at rest of the list. So around 10 items out of 50 as returning as null.
There are some answers for WPF like applying Dispatcher.BeginInvoke or UpdateLayout, ScrollIntoView but none of them is working.
On the other hand if I scroll through list and then press AppBar icon it just works fine. But users can directly press on icon right after data bound and they will not see some of the checkboxes.
Is there any workaround for this issue for Windows Phone 8?
<ListBox Name="ResultListBox" ItemsSource="{Binding}"
SelectionChanged="ResultListBox_OnSelectionChanged">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Margin="0,0,0,10" Orientation="Horizontal">
<StackPanel Orientation="Horizontal">
<CheckBox Name="CheckBox" Visibility="Collapsed">
</CheckBox>
<Image Source="{Binding url}"
Width="125"
Height="125"
VerticalAlignment="Top"
Margin="0,0,5,0"></Image>
</StackPanel>
<StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding title}"
VerticalAlignment="Top"
FontFamily="Portable User Interface"></TextBlock>
</StackPanel>
<StackPanel>
<TextBlock Text="{Binding description}"
FontFamily="Portable User Interface"></TextBlock>
</StackPanel>
</StackPanel>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
void appBarButtonSelect_Click(object sender, EventArgs e)
{
//Dispatcher.BeginInvoke(delegate
//{
//});
for (int i = 0; i < ResultListBox.Items.Count; i++)
{
//ResultListBox.UpdateLayout();
//ResultListBox.ScrollIntoView(i);
DependencyObject item = ResultListBox.ItemContainerGenerator.ContainerFromIndex(i);
if (item != null)
{
CheckBox checkBox = FindFirstElementInVisualTree<CheckBox>(item);
if (checkBox != null)
{
checkBox.Visibility = Visibility.Visible;
}
}
else
{
Debugger.Break();
}
}
}
I think you're using ScrollIntoView + UpdateLayout incorrectly,
You're passing it an index, when it needs an object that is directly related to the ItemsSource
So if your ItemsSource is an ObservableCollection do this:
object o = ((ObservableCollection<sample_model>)this.myListBox.ItemsSource)[INDEX];
this.myListBox.ScrollIntoView(o); // call this first
this.myListBox.UpdateLayout(); // call this second
Then your ItemContainerGenerator.ContainerFromIndex(INDEX) will not be NULL.
I have this List box and I want to search for its items which were selected (IsChecked=true) by user
<CheckBox Style="{StaticResource ResourceKey=CheckBoxes}"
Name="chkBoxSelectAllStaff" Content="Select All">
</CheckBox>
<ListBox Name="lstStaffs" MaxHeight="250" MinHeight="50" Margin="0,5,5,5" Width="350"
ScrollViewer.VerticalScrollBarVisibility="Auto" HorizontalAlignment="Right"
HorizontalContentAlignment="Right">
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox Style="{StaticResource ResourceKey=CheckBoxes}" IsChecked="{Binding ElementName=chkBoxSelectAllStaff, Mode=OneWay, Path=IsChecked}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding FirstName}" Margin="0,0,3,0"></TextBlock>
<TextBlock Text="{Binding LastName}" Margin="0,0,3,0"></TextBlock>
<TextBlock Text="{Binding CellphoneNumber}" Margin="0,0,3,0"></TextBlock>
</StackPanel>
</CheckBox>
</DataTemplate>
</ListBox.ItemTemplate>
I want to do something like this
foreach(var item in lstStaff.Items){
if((CheckBox) item).IsChecked){
//do something
}
}
And also I am binding the data this way :
//staff is my entity object containing Id, FirstName, LastName, CellphoneNumber
lstStaffs.ItemsSource = args.Result; // comes from webservice call and is Staff[]
lstStaffs.UpdateLayout();
But I get Staff object in lstStaffs.Items!!, So how can I iterate over selected(IsChecked=true) items(staffs) ...
Tnx
From the How to: Find DataTemplate-Generated Elements page at MSDN:
// Getting the currently selected ListBoxItem
// Note that the ListBox must have
// IsSynchronizedWithCurrentItem set to True for this to work
ListBoxItem myListBoxItem = (ListBoxItem)(myListBox.ItemContainerGenerator.
ContainerFromItem(myListBox.Items.CurrentItem));
// Getting the ContentPresenter of myListBoxItem
ContentPresenter myContentPresenter = FindVisualChild<ContentPresenter>(myListBoxItem);
// Finding textBlock from the DataTemplate that is set on that ContentPresenter
DataTemplate myDataTemplate = myContentPresenter.ContentTemplate;
TextBlock myTextBlock = (TextBlock)myDataTemplate.FindName("textBlock",
myContentPresenter);
// Do something to the DataTemplate-generated TextBlock
MessageBox.Show("The text of the TextBlock of the selected list item: "
+ myTextBlock.Text);
This shows you how to get access to elements defined in a DataTemplate. However, if you just want to get access to the items from the collection that have been selected, there is a much simpler way:
var selectedItems = lstStaffs.SelectedItems;
You must set the SelectionMode to Multiple or Extended for this to work.