Im create collection for show in ListBoxTransactions and binding as Description. But in result i have only name collection in ListBoxTransactions.ItemsSource, but not value. adn a cant use ListBox.ItemTemplate
XAML
<phone:PivotItem Header="Journal">
<Grid>
<ListBox Name="ListBoxTransactions">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Description}" FontSize="35"/>
</StackPanel>
</ListBox>
</Grid>
</phone:PivotItem>
C#
public class TransactHelper
{
public string Description { get; set; }
}
public void ShowTransactions()
{
ListBoxTransactions.Items.Clear();
var transactFulls = _workerDb.GeTransactFull();
var list = new List<TransactHelper>();
foreach (var t in transactFulls)
{
list.Add(new TransactHelper { Description = t.Description });
}
this.ListBoxTransactions.ItemsSource = list; // dont view collection. only name collection
You should implement ItemContainerStyle for your items.
<ListBox Name="ListBoxTransactions" ItemContainerStyle="{DynamicResource MyItemStyle}">
<ListBox.Resources>
<Style x:Key="MyItemStyle" TargetType="{x:Type ListBoxItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Description}" FontSize="35"/>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.Resources>
</ListBox>
And once note:
Don't use this pair
ListBoxTransactions.Items.Clear();
this.ListBoxTransactions.ItemsSource = list;
You need
ListBoxTransactions.ItemsSource = null;
this.ListBoxTransactions.ItemsSource = list;
Or Implement your collection as ObservableCollection
Related
I'm trying to modify the WinUI 3 default TabItem Header Text to Icon+Text while its bound via TabItemsSource like so:
<TabView Grid.Column="1" DataContext="{x:Bind ReportViewModel}"
TabItemsSource="{x:Bind ReportViewModel.ReportTabSources}"
TabCloseRequested="{x:Bind ReportViewModel.TabClosedRequest}"
TabWidthMode="SizeToContent"
IsAddTabButtonVisible="False"
CanDragTabs="False"
CanReorderTabs="False">
<TabView.TabItems>
<TabViewItem>
<TabViewItem.IconSource>
<SymbolIconSource Symbol="Document" />
</TabViewItem.IconSource>
<TabViewItem.HeaderTemplate>
<DataTemplate>
<TextBlock Text="{Binding}"></TextBlock>
</DataTemplate>
</TabViewItem.HeaderTemplate>
</TabViewItem>
</TabView.TabItems>
</TabView>
ReportTabSources is a ReadOnlyObservableCollection that contains a following Model:
public class ReportTabSource
{
public string? Name
{
get; set;
}
public long Id
{
get; set;
}
public Report Report
{
get;
set;
}
public override string ToString() => Name;
}
But the Icon+Text isn't shown anywhere within the TabItemHeader...
How to fix this?
You can set the header template like this.
<TabView
Grid.Column="1"
DataContext="{x:Bind ReportViewModel}"
TabItemsSource="{x:Bind ReportViewModel.ReportTabSources}"
TabCloseRequested="{x:Bind ReportViewModel.TabClosedRequest}"
TabWidthMode="SizeToContent"
IsAddTabButtonVisible="False"
CanDragTabs="False"
CanReorderTabs="False">
<TabView.Resources>
<Style TargetType="TabViewItem">
<Setter Property="HeaderTemplate">
<Setter.Value>
<DataTemplate x:DataType="local:ReportTabSource">
<StackPanel Orientation="Horizontal">
<SymbolIcon Symbol="Document" />
<TextBlock Text="{x:Bind Name}" />
</StackPanel>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</TabView.Resources>
</TabView>
I use a combobox and I would like to proceed as follows:
I choose an element in cmb1, this allows to display in cmb2 a Collection.
This code allows me to retrieve the data I want ( result A = ObservableCollectionA, result B = ObservableCollection B...)
<ComboBox Name="cmbResultatListe"
Margin="0,10,0,10"
Grid.Row="4"
Grid.Column="2"
Height="25"
Width="250" >
<ComboBox.Style>
<Style TargetType="{x:Type ComboBox}">
<Setter Property="ItemsSource" Value="{Binding Sections}"></Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding SelectedChoiceList}" Value="Etablissement">
<Setter Property="ItemsSource" Value="{Binding Etablissements}"></Setter>
</DataTrigger>
<DataTrigger Binding="{Binding SelectedChoiceList}" Value="Service">
<Setter Property="ItemsSource" Value="{Binding Services}"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</ComboBox.Style>
</ComboBox>
I would now like to divide my combobox into three grids, so that I can proceed as follows:
If result A is selected => cmb2 grid0 = ObservableCollectionA.ID, cmb2 grid1 = observableCollectionA.Name...
If result B is selected => cmb2 grid0 = ObservableCollectionB.Name, cmb2 grid1 = observableCollectionB.Years...
And i don't know how can i do that.
Any tips ?
Thank you for your help.
Edit :
c# code :
private ObservableCollection<Etablissement> _EtablissementsUtilisateur;
public ObservableCollection<Etablissement> EtablissementsUtilisateur
{
get
{
return _EtablissementsUtilisateur;
}
set
{
if (value != _EtablissementsUtilisateur)
{
_EtablissementsUtilisateur = value;
RaisePropertyChanged(nameof(EtablissementsUtilisateur));
}
}
}
private ObservableCollection<ServiceSectionModel> _Services;
public ObservableCollection<ServiceSectionModel> Services
{
get
{
return _Services;
}
set
{
if (value != _Services)
{
_Services = value;
RaisePropertyChanged(nameof(Services));
}
}
}
private ObservableCollection<ServiceSectionModel> _Sections;
public ObservableCollection<ServiceSectionModel> Sections
{
get
{
return _Sections;
}
set
{
if (value != _Sections)
{
_Sections = value;
RaisePropertyChanged(nameof(Sections));
}
}
}
private string _SelectedChoiceList;
public string SelectedChoiceList
{
get
{
return _SelectedChoiceList;
}
set
{
if (value != _SelectedChoiceList)
{
_SelectedChoiceList = value;
RaisePropertyChanged(nameof(SelectedChoiceList));
}
}
}
Etablissements = new ObservableCollection<Etablissement>((await _dataService.GetEtablissements().ConfigureAwait(false)));
Services = await _dataService.GetServicesAsync(false).ConfigureAwait(false);
Sections = await _dataService.GetSectionsAsync(_dataService.ParamGlobaux.IDEtablissement).ConfigureAwait(false);
Etablissement contain ID, Name, Years.
Service contain Color, ID, Name.
Section contain Color, ID, SectionName.
Edit 2 : I would like something like this exemple :
<ComboBox Name="CbService" HorizontalAlignment="Left" Margin="115,67,0,0" VerticalAlignment="Top" Width="150" ItemsSource="{Binding}" SelectionChanged="CbRecherche_SelectionChanged" KeyboardNavigation.TabIndex="1" >
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Libelle}" />
</DataTemplate>
</ComboBox.ItemTemplate>
<ComboBox.ItemContainerStyle>
<Style TargetType="{x:Type ComboBoxItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid x:Name="gd" TextElement.Foreground="Black" Background="White">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50"/>
<ColumnDefinition Width="Auto" MinWidth="50" />
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Rectangle Fill="{Binding Fond}" Grid.Column="0"/>
<TextBlock Margin="5" Grid.Column="0" Text="{Binding ID}"/>
<TextBlock Margin="5" Grid.Column="1" Text="{Binding Libelle}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ComboBox.ItemContainerStyle>
Currently my combobox displays a string. I want something like this :
In this example, there is an ID, a color only in the ID part, and a Name. I can't do that with my string at the moment.
I belive you may be able to reduce the size of your codes by removing the RaisePropertyChanged event as ObservableCollections already contain the INotifyPropertyChanged interface, I have made a simple example of how to use Datatemplate to display information from ObservableCollections.
Step 1: C# codes:
namespace WpfApp1
{
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
ComboBox1.ItemsSource = Services;
Services.Add(new ServiceSectionModel { Color = Brushes.Red, ID = "Clem", Name = "Clementine" });
Services.Add(new ServiceSectionModel { Color = Brushes.White, ID = "011", Name = "Logistique" });
Services.Add(new ServiceSectionModel { Color = Brushes.Green, ID = "MBT", Name = "Montbrilland" });
}
public class ServiceSectionModel
{
public string ID { get; set; }
public string Name { get; set; }
public SolidColorBrush Color { get; set; }
}
ObservableCollection<ServiceSectionModel> Services = new ObservableCollection<ServiceSectionModel>();
}
}
Step 2: XAML codes:
<ComboBox x:Name="ComboBox1" HorizontalAlignment="Center" VerticalAlignment="Center">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Background="{Binding Color}" Text="{Binding ID}" Margin="0,0,10,0" Padding="5,2,10,2"></TextBlock>
<TextBlock Text="{Binding Name}"></TextBlock>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
I'm looking for a way to add GroupDescriptions dynamically, in particular in the viewmodels, I actually add GroupDescription behind code like this:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
List<Country> items = new List<Country>();
items.Add(new Country() { Name = "Italy", League = new League { Name = "Serie A" } }); // Added "new League"
items.Add(new Country() { Name = "Italy", League = new League { Name = "Serie B" } });
items.Add(new Country() { Name = "England", League = new League { Name = "Premiere League" } });
items.Add(new Country() { Name = "Spain", League = new League { Name = "Primeira Division" } });
lvUsers.ItemsSource = items;
CollectionView view = (CollectionView)CollectionViewSource.GetDefaultView(lvUsers.ItemsSource);
PropertyGroupDescription groupNations = new PropertyGroupDescription("Name");
view.GroupDescriptions.Add(groupNations);
PropertyGroupDescription groupCompetions = new PropertyGroupDescription("League.Name"); // Changed "League" to "League.Name"
view.GroupDescriptions.Add(groupCompetions); // Fixed the variable name here
lvUsers.ItemsSource = view;
}
}
public struct League
{
public string Name { get; set; }
}
public struct Country
{
public string Name { get; set; }
public League League { get; set; } // added getter and setter
}
and this is my xaml:
<Window x:Class="GroupBox_Header.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:GroupBox_Header"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid Margin="10">
<ListView Name="lvUsers">
<ListView.View>
<GridView>
<GridViewColumn Header="Home Team" Width="50" DisplayMemberBinding="{Binding HomeTeam}" />
<GridViewColumn Header="Away Team" Width="50" DisplayMemberBinding="{Binding AwayTeam}" />
</GridView>
</ListView.View>
<ListView.GroupStyle>
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Expander IsExpanded="True">
<Expander.Header>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}" FontWeight="Bold" Foreground="Gray" FontSize="22" VerticalAlignment="Bottom" />
<TextBlock Text="{Binding ItemCount}" FontSize="22" Foreground="Green" FontWeight="Bold" FontStyle="Italic" Margin="10,0,0,0" VerticalAlignment="Bottom" />
<TextBlock Text=" Items" FontSize="22" Foreground="Silver" FontStyle="Italic" VerticalAlignment="Bottom" />
</StackPanel>
</Expander.Header>
<ItemsPresenter />
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Expander IsExpanded="True">
<Expander.Header>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding League}" FontWeight="Bold" Foreground="Gray" FontSize="22" VerticalAlignment="Bottom" />
</StackPanel>
</Expander.Header>
<ItemsPresenter />
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</ListView.GroupStyle>
</ListView>
</Grid>
what I want to do is Bind in the ListView a collection that have the group description automatically.
If I understand your question right, you could either control the GroupDescriptions directly from the ViewModel, from code behind or create a custom ListView which let you bind to an ObservableCollection.
ViewModel
Use CollectionViewSource to get a hold of the CollectionView from the ViewModel. You could argue that this is an anti-pattern, but I think it is okay.
Code behind
Create a dependency property in you code behind that on change will update the group descriptors for the view. Then in you xaml bind that property to you ViewModel.
Custom ListView
Inherit from ListView and create a dependency property that let's you bind to an ObservableCollection.
Either way, you need to control different scenarios like, property
changed and collection changed.
I'd like to have a header element in my ListView, but I need the possibility to decide which item is simple and which one is header when I bind data to the ListView. This header should not be clickable and have a different color at least. In Android we do it in a custom adapter.
At the moment I get this logic of data binding to the ListView:
<ListView
x:Name="drawerListOptions"
SelectionChanged="onSelectDrawerItem"
SelectionMode="Single">
<ListView.ItemTemplate>
<DataTemplate>
<!--for simplicity I put only one view in as a list item -->
<TextBlock
Text="{Binding titleItemMenu}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
and C# code:
drawerListOptions.ItemsSource = myListOfData;
So I need to check in my myListOfData if item is simple or header. Is there a way to do it?
by using CollectionViewSource
although i have the same problem, i searched over web but couldn't find solution so i create this one to load data from server and show in list as group separated as StickyHeaderListView in android.
in XAML
<Page
....
>
<RelativePanel ... >
<Page.Resources>
<CollectionViewSource x:Key="cvs" x:Name="cvs" SourceGrouped="True"/>
</Page.Resources>
<ListView x:Name="listView" IsItemClickEnabled="True" ItemsSource="{Binding Source={StaticResource `cvs` }}">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="Padding" Value="0"/>
<Setter Property="Margin" Value="0"/>
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate >
<RelativePanel HorizontalAlignment="Stretch" VerticalAlignment="Center" BorderBrush="DarkGray" BorderThickness="0,0,0,1" Background="White" Padding="10">
<TextBlock Text="{Binding Description}" RelativePanel.AlignVerticalCenterWithPanel="True" VerticalAlignment="Center" Foreground="Gray" FontSize="20"></TextBlock>
<StackPanel Orientation="Horizontal" RelativePanel.AlignRightWithPanel="True" RelativePanel.AlignVerticalCenterWithPanel="True" Margin="0,0,8,0" >
<TextBlock Text="{Binding Amount}" HorizontalAlignment="Right" VerticalAlignment="Center" Foreground="{StaticResource primary}" FontSize="16"/>
</StackPanel>
</RelativePanel>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate >
<DataTemplate>
<TextBlock Text="{Binding Key}" FontSize="14" Foreground="#FF222222" />
</DataTemplate>
</GroupStyle.HeaderTemplate>
<GroupStyle.ContainerStyle>
<Style TargetType="ListViewHeaderItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="Padding" Value="0"/>
<Setter Property="Margin" Value="0"/>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</ListView.GroupStyle>
</ListView>
</RelativePanel> </Page>
and in source file
bind source
var obj = JsonConvert.DeserializeObject<List<Model.YourModelHere>>(ApiResponseHere);
var groups = from Data in obj group Data by Data.Date;
this.cvs.Source = groups;
Have an property in you collection item called IsSimple. In your xaml bind that variable to Header property of list view with converter. That Converter converts bool to Visibility. If value us true converter should return Visibility.Collapsed.
In UWP, we can use CollectionViewSource to provides a data source that adds grouping and current-item support to collection classes.
Use CollectionViewSource when you want to bind list controls to collections, but you want to display those collections in groups and maintain a current item independent from the list control.
For more info, see CollectionViewSource.
In my words, when you want bind to the collections and display those collections in groups without manipulating, we can use CollectionViewSource in this scenario.
You can use LINQ query to get results from the ObservableCollection. And you can set the results to the CollectionViewSource.Source.
In ListView, we can set the ListView.ItemSource bind to CollectionViewSource.View. It can get the view object that is currently associated with this instance of CollectionViewSource.
For example:
Create GroupInfoList class:
public class GroupInfoList : List<object>
{
public object Key { get; set; }
}
Create Text class:
public class Text : INotifyPropertyChanged
{
private string _title;
public string Title
{
get { return _title; }
set
{
_title = value;
RaisePropertyChanged("Title");
}
}
public Text(string name)
{
this.Title = name;
}
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Add Header class:
public class Header
{
public string HeaderTitle { get; set; }
public Header()
{
HeaderTitle = string.Empty;
}
public static ObservableCollection<Text> GetTexts()
{
ObservableCollection<Text> myListOfData = new ObservableCollection<Text>();
myListOfData.Add(new Text("Product1"));
myListOfData.Add(new Text("Product2"));
myListOfData.Add(new Text("Product3"));
myListOfData.Add(new Text("Setting1"));
myListOfData.Add(new Text("Setting2"));
myListOfData.Add(new Text("Setting3"));
myListOfData.Add(new Text("Setting4"));
return myListOfData;
}
public static ObservableCollection<GroupInfoList> GetItemsGrouped()
{
ObservableCollection<GroupInfoList> groups = new ObservableCollection<GroupInfoList>();
var query = from item in GetTexts()
group item by item.Title[0] into g
orderby g.Key
select new { GroupName = g.Key, Items = g };
foreach (var g in query)
{
GroupInfoList info = new GroupInfoList();
if (g.GroupName.ToString() == "P")
{
info.Key = "Products";
}
else if (g.GroupName.ToString() == "S")
{
info.Key = "Settings";
}
else
{
info.Key = g.GroupName;
}
foreach (var item in g.Items)
{
info.Add(item);
}
groups.Add(info);
}
return groups;
}
}
In XAML:
<Page.Resources>
<CollectionViewSource x:Name="MyItems" IsSourceGrouped="True" />
<DataTemplate x:Name="ItemListViewTemplate">
<TextBlock Text="{Binding Title}"></TextBlock>
</DataTemplate>
</Page.Resources>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<ListView ItemsSource="{x:Bind MyItems.View}"
SelectionMode="Single"
ShowsScrollingPlaceholders="True"
Grid.Row="1"
ItemTemplate="{StaticResource ItemListViewTemplate}"
Grid.ColumnSpan="2">
<ListView.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<TextBlock Foreground="Red" Text="{Binding Key}" />
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</ListView.GroupStyle>
</ListView>
</Grid>
Set the CollectionViewSource source in code behind:
public MainPage()
{
this.InitializeComponent();
MyItems.Source = Header.GetItemsGrouped();
}
I have a list view which I populate it from an API.. I want it fill a text box with the value which is found in the text block when I click..
My Listview...
<ListView Width="300" Height="134" x:Name="lsvObjectives" IsItemClickEnabled="True" SelectionMode="Multiple" ItemsSource="{Binding Source={StaticResource cvsObjectives}}" ItemClick="lsvObjectives_ItemClick">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<WrapGrid Orientation="Horizontal" HorizontalChildrenAlignment="left"/>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="Padding" Value="0"/>
<Setter Property="Margin" Value="-7"/>
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical" Margin="0,0,0,0" HorizontalAlignment="Center" >
<StackPanel Orientation="Horizontal" Width="310" Height="33" Background="#FFE9D5F0" HorizontalAlignment="Left">
<StackPanel Width="270" VerticalAlignment="Center" Margin="10,5,0,0">
<TextBlock Text="{Binding objective}" Style="{StaticResource ContentTextBlockStyle}" Foreground="Black" VerticalAlignment="Center" HorizontalAlignment="Left" FontSize="13"/>
</StackPanel>
</StackPanel>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
This is how I populate the list:
private async void getObjectives()
{
string getObjectives = baseAddress + "unitPlansDetailsByUnit/1";
var content = await httpClient.GetStringAsync(new Uri(getObjectives));
objectivesHelper data = JsonConvert.DeserializeObject<objectivesHelper>(content);
foreach (var item in data.result)
{
cvsObjectives.Source = data.result;
}
}
My Classes:
public class objectives
{
public int id { get; set; }
public string objective { get; set; }
}
class objectivesHelper
{
public List<objectives> result { get; set; }
}
I am unable to read the value from the text block which is found in my list view..
Someone kindly help me do this..
Any kind of help is appreciated....
You can read the value in your lsvObjectives_ItemClick method by casting the ItemClickEventArgs.ClickedItem to your type objectives.
For example:
private void lsvObjectives_ItemClick(object sender, ItemClickEventArgs e)
{
objectives item = e.ClickedItem as objectives;
var itemText = item.objective;
youtTextBox.Text = item.Description.ToString();
}