UWP ObservableCollection with data from webservice not updating GUI - c#

I'm using an ObservableCollection on a listview. The ObservableCollection get its data correctly from a web service using an async method then parses it and fill the ObservableCollection with the Add method on the NewsProxyParser(string thing, out ObservableCollection newsList) method, but i'm not able to update the GUI with the binded ObservableCollection.
I'm new to UWP, so i suppose that the async method runs on a different thread that the GUI. How can i do correctly the bind to update the GUI with the data fetched from the web service?
My XAML:
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<ListView Name="ListViewNews" ItemsSource="{x:Bind NewsCollection, Mode=OneWay}" IsItemClickEnabled="True" ItemClick="ListView_ItemClick">
<ListView.ItemTemplate>
<DataTemplate x:DataType="data:News">
<RelativePanel Margin="0,16" >
<Image Name="x:Thumb" Source="{x:Bind thumbnailUrl}" Width="96" Height="96" RelativePanel.AlignLeftWithPanel="True" Margin="0,0,16,0"></Image>
<TextBlock Name="x:Title" Text="{x:Bind title}" FontSize="22" FontWeight="Bold" RelativePanel.RightOf="x:Thumb"></TextBlock>
<TextBlock Name="x:Date" Text="{x:Bind dateString}" FontSize="12" FontWeight="Light" Foreground="Gray" RelativePanel.RightOf="x:Thumb" RelativePanel.Below="x:Title"></TextBlock>
<TextBlock Name="x:Text" Text="{x:Bind newsText}" Foreground="Gray" RelativePanel.RightOf="x:Thumb" RelativePanel.Below="x:Date"></TextBlock>
</RelativePanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
And the cs:
public sealed partial class NewsPage : Page
{
private ObservableCollection<News> NewsCollection;
public NewsPage()
{
NewsRequest();
this.InitializeComponent();
}
private async void NewsRequest()
{
//Fetch News from web
string response = await NewsProxy.GetNews("TokenX");
//Parse News to add to NewsCollection
bool success = NewsProxy.NewsProxyParser(response, out NewsCollection);
// success is true and NewsCollection has new updated values
if (success)
{
News n = new News();
n.title = "Just to trigger collectionchanged";
n.newsText = "dadada";
n.order = 1;
NewsCollection.Add(n);
Debug.WriteLine("Still No GUI updated!!!");
}
}
}

ObservableCollection<> only notifies of collection changes, such as adding and removing items. As long as you replace the whole collection, you will need to make NewsCollection a dependency property to notify XAML UI about the changes.
public ObservableCollection<News> NewsCollection
{
get { return (ObservableCollection<News>)GetValue(NewsCollectionProperty); }
set { SetValue(NewsCollectionProperty, value); }
}
public static readonly DependencyProperty NewsCollectionProperty =
DependencyProperty.Register("NewsCollection", typeof(ObservableCollection<News>), typeof(NewsPage), new PropertyMetadata(null));
private async void NewsRequest()
{
//Fetch News from web
string response = await NewsProxy.GetNews("TokenX");
//Parse News to add to NewsCollection
ObservableCollection<News> newsCollection;
bool success = NewsProxy.NewsProxyParser(response, out newsCollection);
NewsCollection = newsCollection;
// success is true and NewsCollection has new updated values
if (success)
{
News n = new News();
n.title = "Just to trigger collectionchanged";
n.newsText = "dadada";
n.order = 1;
NewsCollection.Add(n);
Debug.WriteLine("Still No GUI updated!!!");
}
}

Related

WPF updating ListBox items from method called in different class

I have been banging my head against WPF ListBox item binding for hours and am hoping to get some advice. My application has three main elements:
A Player class that sends and receives data over a TcpClient.
A MainWindow that handles the GUI and exposes methods that Player can call to provide data for updating the UI based on network messages.
A UserControl called HostLobby that contains 1) a ListBox called gamesListBox for displaying new games as they are added by clients via Player and 2) UI elements for adding a new game to be broadcast to all clients.
I have confirmed that the "upstream" piece works. You can enter new game information on HostLobby, submit, and it propagates to clients as expected. In addition, clients respond properly to server messages telling them a new game has been added.
The problem is, I cannot get gameListBox to update. I rigged up test buttons on both the HostLobby control and MainWindow to verify that binding is working properly - which it is. I just can't seem to update by passing data from Player. Any ideas what I'm doing wrong?
Relevant code:
Player.cs
public void AddGameToLobby(string name, int mp)
{
// name and mp are provided by the network message handler and work fine
WriteToLog("Attempting to add game to host lobby", true);
mainWindow.AddGameToLobby(name, mp);
}
MainWindow.cs
public void AddGameToLobby(string n, int mp)
{
hostLobby.AddGameToList(n, mp);
}
HostLobby.cs
private MainWindow parent; // used to call an AddGame event when client adds a game
public ObservableCollection<Game> games;
public class Game
{
public string Name { get; set; }
}
public HostLobby(MainWindow mw)
{
InitializeComponent();
parent = mw;
games = new ObservableCollection<Game>();
// add some test items - this works. the ListBox updates properly
games.Add(new Game() { Name = "game1" });
games.Add(new Game() { Name = "game2" });
games.Add(new Game() { Name = "game3" });
gamesListBox.ItemsSource = games;
}
public void AddGameToList(string n, int maxp)
{
// called to announce a new game added by another client
// call stack is Player.AddGameToLobby -> MainWindow.AddGameToLobby -> this.AddGameToList
string msg = String.Format("{0} (0/{1})", n, maxp.ToString());
games.Add(new Game() { Name = msg });
}
HostLobby.xaml
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0" Margin="50" Orientation="Vertical" VerticalAlignment="Center">
<StackPanel Orientation="Horizontal">
<Label HorizontalContentAlignment="Right" Width="80">Game Name:</Label>
<TextBox Name="gameNameTextBox" VerticalContentAlignment="Center" Width="200"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,10,0,0">
<Label HorizontalContentAlignment="Right" Width="80">Max Players:</Label>
<TextBox Name="maxPlayersTextBox" VerticalContentAlignment="Center" Width="200"/>
</StackPanel>
<Button Name="addGameButton" Click="addGameButton_Click" Margin="0,20,0,0" Width="200" Height="30">Add Game</Button>
</StackPanel>
<StackPanel Orientation="Vertical" VerticalAlignment="Center" Margin="50" Grid.Column="1">
<Label>Current Games</Label>
<ListBox Name="gamesListBox" MinHeight="200">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<TextBlock Text="{Binding Name}"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</Grid>
Your ListBox is not being updated because you're modifying the ObservableCollection on a worker thread, which means that the collection's CollectionChanged event is being raised on the worker thread as well. To remedy this, you need to modify your list on the UI thread. To achieve this, you have a few options, but the first ones that come to mind are:
Using Dispatcher.BeginInvoke
In AddGameToList in HostLobby.cs, put the statement that modifies the list inside a Dispatcher.BeginInvoke:
Application.Current.Dispatcher.BeginInvoke((MethodInvoker)(() => games.Add(new Game() { Name = msg })));
Using BindingOperations.EnableCollectionSynchronization (.NET 4.5 or later)
First, create a lock object as a private member of your HostLobby class. Then, after initializing your ObservableCollection, call the following:
BindingOperations.EnableCollectionSynchronization(games, _yourLockObj);
Then, use the lock whenever you modify the list within HostLobby. So in this instance, you'd want to change the list modification in AddGameToList such that it uses the lock:
lock (_yourLockObj)
{
games.Add(new Game() { Name = msg });
}
The latter seems like a better choice in my opinion, but it is only available if you're using .NET 4.5 or later.

How to pass a ListView value to another page using an item button click?

I have ListView which is populating Id based on username:
<Entry Text="{Binding username, Mode=TwoWay}"/>
<Button Command="{Binding SearchCommand}" />
<ListView ItemsSource="{Binding SearchId}"
HasUnevenRows="False"
x:Name="list"
ItemTapped="LstItems_OnItemTapped">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell >
<StackLayout Orientation="Vertical" >
<Label x:Name="ident"
Text="{Binding Id}"></Label>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
private async void LstItems_OnItemTapped(object sender, ItemTappedEventArgs e)
{
var item = (UserDetail)e.Item;
var newpage = new Contact(item.Id);
await Navigation.PushAsync(newpage);
}
public Command SearchCommand
{
get
{
return new Command(async () =>
{
var employeesServices = new EmployeesServices();
SearchId= await employeesServices.GetUserDetailAsync(_username);
});
}
}
private List<UserDetail> _SearchId;
public List<UserDetail> SearchId
{
get { return _SearchId; }
set
{
_SearchId = value;
OnPropertChanged();
}
}
I can successfully pass the id to another page, now I have another issue, I can only pass Id from listview to another page when I tap on the listview.
Is it possible if I click on button Id directly pass on next page without tap on ListView?
You can use SelectedItem inside your List View like this:
<ListView ItemsSource="{Binding SearchId}" HasUnevenRows="False" x:Name="list" ItemTapped="LstItems_OnItemTapped" SelectedItem="{Binding SelectedListItem,Mode=TwoWay}">
After using this you will have current selected item of your list and you can pass that to any page!
Hope this may solve your issue.

TextBlock with text highlighting

Faced the need to select a fragment of text in TextBlock, namely certain keywords on which the ListBox was filtered, this text block itself and containing
XAML variant, title property is not bound
<ListBox Name="ProcedureList" ItemsSource="{Binding Path=ProceduresView.View}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Name="ProcedurePanel" PreviewMouseDown="ProcedurePanel_OnPreviewMouseDown">
<DockPanel Width="{c:Binding ElementName=MainPanel, Path=Width-40}">
<!--<TextBlock Name="MainText" TextWrapping="Wrap" FontSize="16" Text="{Binding Path=title}" HorizontalAlignment="Left" />-->
<htb:HighlightTextBlock Name="MainText" TextWrapping="Wrap" FontSize="16" Text="{Binding Path=title}" HorizontalAlignment="Left">
<htb:HighlightTextBlock.HighlightRules>
<htb:HighlightRule
IgnoreCase="{Binding IgnoreCase, Source={StaticResource SourceVm}}"
HightlightedText="{Binding Path=title, Converter={StaticResource getFilter}}">
<htb:HighlightRule.Highlights>
<htb:HighlightBackgroung Brush="Yellow"/>
</htb:HighlightRule.Highlights>
</htb:HighlightRule>
</htb:HighlightTextBlock.HighlightRules>
</htb:HighlightTextBlock>
</DockPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
A component written by our compatriot with open source is used
Component
Description of component
The commented code is an old TexBlock with no selection
The new HighlightTextBlock component perfectly selects the text if you use a static resource, as in the example, but when I try to bind it to the current text it can not find this field :(, I'm new in WPF help figure it out
HightlightedText="{Binding Path=title, Converter={StaticResource getFilter}}"
How correctly to anchor this property to title?
DataContext structure
public ObservableCollection<Procedure> Procedures { set; get; }
public CollectionViewSource ProceduresView { set; get; } = new CollectionViewSource();
....
Procedures = new ObservableCollection<Procedure>();
ProceduresView.Filter += Procedures_Filter;
ProceduresView.Source = Procedures;
....
public class Procedure : ObservableObject
{
....
public String title { get; set; }
....
}
....
// Simple filtering
void Procedures_Filter(object sender, FilterEventArgs e)
{
Procedure procedure = (Procedure) e.Item;
Boolean flag = false;
if (!string.IsNullOrEmpty(filter))
{
Setting.Filter sfilter = new Setting.Filter();
sfilter.type = "искать везде";
sfilter.text = filter;
ObservableCollection<Setting.Filter> arr = new ObservableCollection<Setting.Filter>();
arr.Add(sfilter);
if (Utils.AssignedProcedureFromFilter(procedure, arr)) flag = true;
}
else flag = true;
e.Accepted = flag;
}
Video with problem description
Simplified project emitting my functional
On the Russian-speaking forum they explained to me that:
Your case, in fact, is more serious. DataContext you, apparently, the
right one. But your Binding expression is inside the HighlightRules
property setter, which is not part of the visual tree (because it is
not available as a Child element of your control). And elements that
are not inside the visual tree, participate in bindings are only
limited: they do not inherit DataContext, nor access by name through
ElementName. As a solution, bind to an element via x: Reference. In my
(heavily cut) test case, HightlightedText = "{Binding Path =
DataContext.title, Source = {x: Reference MainText}} is triggered."
But, if directly replaced by this, a strange error works: 'Can not
call MarkupExtension. ProvideValue because of a cyclic dependency. The
properties inside the MarkupExtension can not reference objects that
reference the MarkupExtension result.
The workaround for the error was found here: you need to put your element in resources. We get this:
XAML, modified according to the recommendations
<ListBox Name="ProcedureList" ItemsSource="{Binding Path=ProceduresView.View}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Name="ProcedurePanel" PreviewMouseDown="ProcedurePanel_OnPreviewMouseDown">
<DockPanel Width="{c:Binding ElementName=MainPanel, Path=Width-40}">
<!--<TextBlock Name="MainText" TextWrapping="Wrap" FontSize="16" Text="{Binding Path=title}" HorizontalAlignment="Left" />-->
<htb:HighlightTextBlock Name="MainText" TextWrapping="Wrap" FontSize="16"
Text="{Binding Path=title}" HorizontalAlignment="Left">
<htb:HighlightTextBlock.Resources>
<htb:HighlightRule x:Key="HR"
IgnoreCase="{Binding IgnoreCase, Source={StaticResource SourceVm}}"
HightlightedText="{Binding Path=DataContext.title, Source={x:Reference MainText}, Converter={StaticResource getFilter}}">
<htb:HighlightRule.Highlights>
<htb:HighlightBackgroung Brush="Yellow"/>
</htb:HighlightRule.Highlights>
</htb:HighlightRule>
</htb:HighlightTextBlock.Resources>
<htb:HighlightTextBlock.HighlightRules>
<htb:HighlightRulesCollection>
<StaticResource ResourceKey="HR"/>
</htb:HighlightRulesCollection>
</htb:HighlightTextBlock.HighlightRules>
</htb:HighlightTextBlock>
</DockPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I was given advice on the restructuring of XAML, through resources, this partially solved the problem (I successfully got the title text in the converter), but the element ceased to perform its functions (allocation) During the discussion, it was suggested that the component itself should be finalized
#iRumba: In theory, the whole trick should not be necessary if you put
the HighlighRule collection (also) in a visual tree. Then the
DataContext will be automatically inherited and on idea the binding
through ElementName too will work.
#iRumba: I do not remember exactly. It seems, it is necessary to
specify to add all HighlightRule as LogicalChildren (for this purpose
on idea it is necessary to redefine protected internal override
IEnumerator LogicalChildren). This is a complicated, advanced
technique, yes.
Sorry for Google Translator
Found a solution
public class SearchHightlightTextBlock : TextBlock
{
public SearchHightlightTextBlock() : base() { }
public String SearchText
{
get { return (String)GetValue(SearchTextProperty); }
set { SetValue(SearchTextProperty, value); }
}
private static void OnDataChanged(DependencyObject source,
DependencyPropertyChangedEventArgs e)
{
TextBlock tb = (TextBlock)source;
if (tb.Text.Length == 0)
return;
string textUpper = tb.Text.ToUpper();
String toFind = ((String)e.NewValue).ToUpper();
int firstIndex = textUpper.IndexOf(toFind);
String firstStr = "";
String foundStr = "";
if (firstIndex != -1)
{
firstStr = tb.Text.Substring(0, firstIndex);
foundStr = tb.Text.Substring(firstIndex, toFind.Length);
}
String endStr = tb.Text.Substring(firstIndex + toFind.Length,
tb.Text.Length - (firstIndex + toFind.Length));
tb.Inlines.Clear();
tb.FontSize = 16;
var run = new Run();
run.Text = firstStr;
tb.Inlines.Add(run);
run = new Run();
run.Background = Brushes.Yellow;
run.Text = foundStr;
tb.Inlines.Add(run);
run = new Run();
run.Text = endStr;
tb.Inlines.Add(run);
}
public static readonly DependencyProperty SearchTextProperty =
DependencyProperty.Register("SearchText",
typeof(String),
typeof(SearchHightlightTextBlock),
new FrameworkPropertyMetadata(null, OnDataChanged));
}
Use
<parser:SearchHightlightTextBlock SearchText="{Binding Path=title, Converter={StaticResource getFilter}}" Text="{Binding title}"/>

Reach a TextBlock from a specific ListViewItem from the ListView in Windows Phone 8.1 XAML programmatically

I am a new developer on Windows Phone 8.1, I am try to reach a specific ListView item from the ListView collection and be able to color it or color the TextBock inside of it, But I can't reach the item or reach any of items inside of ListView, Please take a look for my below code :
protected async override void OnNavigatedTo(NavigationEventArgs e)
{
SQLiteRT db1 = new SQLiteRT();
var db_connection = await db1.Connection("MyDB.sqlite");
List<MyTBL> t_list = db1.GetTable("SELECT * FROM MyTBL LIMIT 4 ORDER BY RANDOM() ;");
db_connection.Close();
LV_Options.ItemsSource = t_list;
}
// my List View called LV_Options
private void LV_Options_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
ListView lv1 = sender as ListView;
if (lv1 == null)
return;
MyTBL wrd = lv1.SelectedItem as MyTBL;
if (wrd == null)
return;
TextBlock tb = lv1.FindName("TB_AMean1") as TextBlock;
tb.FontSize = 17; // here I got debug error (it not worked !!!!!!!)
var item = LV_Options.Items.ElementAt(3); // this seems not work also !!!!
item.BackColor = Color.LightSteelBlue;
}
As you can see above, I tried to reach a specific item by LV_Options.Items.ElementAt(3) but it doesn't work! I also tried to reach the TextBlock from the selected List view item, but also not worked !
(Updated)
XAML code :
<!-- Title Panel -->
<StackPanel Grid.Row="0" Margin="19,0,0,0">
<TextBlock Name="TB_Rslt" Text="Here result of your answer" Style="{ThemeResource TitleTextBlockStyle}" Margin="0,12,0,0"/>
<TextBlock Text="page title" Margin="0,-6.5,0,26.5" Style="{ThemeResource HeaderTextBlockStyle}" CharacterSpacing="{ThemeResource PivotHeaderItemCharacterSpacing}"/>
</StackPanel>
<!--TODO: Content should be placed within the following grid-->
<Grid Grid.Row="1" x:Name="ContentRoot" Margin="19,10,19,15">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock Name="TB_Question" Text="Choose Answer " Margin="0,0,25,0" HorizontalAlignment="Right" FontWeight="Bold" FontSize="22" FontFamily="Verdana" RenderTransformOrigin="0.5,0.5" />
<TextBlock Name="TB_EnWord" Text="" Margin="90,0,15,0" HorizontalAlignment="Left" FontWeight="Bold" FontSize="22" FontFamily="Verdana" RenderTransformOrigin="0.5,0.5" TextAlignment="Right" />
<StackPanel Grid.Row="1" Margin="5,22,0,0">
<ListView Name="LV_Options" SelectionChanged="LV_Options_SelectionChanged">
<ListView.ItemTemplate>
<DataTemplate>
<Grid Margin="6">
<StackPanel VerticalAlignment="Top" Margin="10,0,0,0">
<TextBlock Name="TB_AMean1" Text="{Binding AMean1}" TextWrapping="Wrap"/>
</StackPanel>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackPanel>
<Button Name="Btn_Answer" Content="Ansewr" HorizontalAlignment="Left" Grid.Row="1" VerticalAlignment="Bottom" Click="Btn_Answer_Click"/>
My application is a quiz application that offer 4 choices/options as answers for each question, and when user select a true answer, I want to highlight the true answer(true choice) by make its background to green, and if the user selected wrong answer/option I want to make the background of that answer (a specific List View item) with red.
Any help please ?
You're not going to be able to access an element inside a data template like that. Instead, leverage the binding to a view model to set the color and other view-related properties. First, create a wrapper view model for your data class:
public class MyTBLViewModel : INotifyPropertyChanged
{
public MyTBL Entity
{
get { return _entity; }
}
private readonly MyTBL _entity;
public Brush Highlight
{
get { return _brush; }
set
{
_brush = value;
RaisePropertyChanged("Highlight");
}
}
private Brush _highlight;
public double ItemFontSize
{
get { return _itemFontSize; }
set
{
_itemFontSize = value;
RaisePropertyChanged("ItemFontSize");
}
}
private Brush _itemFontSize;
public MyTBLViewModel(MyTBL entity)
{
_entity = entity;
_highlight = new SolidColorBrush(Colors.Transparent);
_itemFontSize = 12;
}
public event PropertyChangedEventArgs PropertyChanged;
protected void RaisePropertyChanged(string propName)
{
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propName));
}
}
Use this as your ItemsSource:
List<MyTBLViewModel> t_list = db1.GetTable("SELECT * FROM MyTBL LIMIT 4 ORDER BY RANDOM() ;")
.AsEnumerable().Select(entity => new MyTBLViewModel(entity)).ToList();
Now in your view, bind the view elements to "Highlight" and "ItemFontSize", and to any other properties you like:
<ListView.ItemTemplate>
<DataTemplate>
<Grid Margin="6" Background="{Binding Highlight}">
<StackPanel VerticalAlignment="Top" Margin="10,0,0,0">
<TextBlock Name="TB_AMean1" Text="{Binding Entity.AMean1}" TextWrapping="Wrap"
FontSize="{Binding ItemFontSize}"/>
</StackPanel>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
Finally, you can get the data item from the SelectionChangedEventArgs -- use it to update your view-related properties:
private void LV_Options_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
foreach (var item in e.AddedItems.OfType<MyTBLViewModel>())
{
item.Highlight = new SolidColorBrush(Color.LightSteelBlue);
item.ItemFontSize = 17;
}
foreach (var item in e.RemovedItems.OfType<MyTBLViewModel>())
{
item.Highlight = new SolidColorBrush(Colors.Transparent);
item.ItemFontSize = 12;
}
}
var item = LV_Options.Items.ElementAt(3);
This line is incorrect. It will not return you a TextBlock. I don't know what a .BackColor is, and it should not compile. The Items property in a ListView will return you a list of ListViewItems. If you want to access the inside element from a ListViewItem, you'll need to access the ContentTemplateRoot property.
Do not use var ever. It lets you assume that you know the type, whereas if you explicitly typed the declaration you would realize you're doing it wrong.
MyTBL wrd = lv1.SelectedItem as MyTBL;
if (wrd == null)
return;
TextBlock tb = lv1.FindName("TB_AMean1") as TextBlock;
What is a MyTBL type? FindName is only available to framework DependencyObjects so I'm assuming it's a user control? You have to provide a lot more code to show us what you're doing and what you're setting the ListView's ItemsSource and ItemTemplate with and what these errors are and how you have 2 breaking debug errors at once and what the error messages are.
Comprehending runtime error messages is a huge part of being a good developer.

Binding data in listview itemtemplate using style

I cannot bind my sample data to textblocks in stackpanel, which I defined in resources. I think that I use style in wrong way, because I receive toString() method instead of class binded fields.
That's my resources with defined style:
<UserControl.Resources>
<ItemsPanelTemplate x:Key="VirtualizingStackPanelTemplate">
<VirtualizingStackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
<ListView x:Key="ListBoxTemplate" HorizontalAlignment="Left" ScrollViewer.HorizontalScrollBarVisibility="Visible">
<ListView.ItemTemplate>
<DataTemplate>
<!--<ListBoxItem Background="DarkOrchid" Margin="1,1, 5,5" Height="400" HorizontalContentAlignment="Stretch" HorizontalAlignment="Stretch">-->
<StackPanel>
<TextBlock FontSize="30" Text="{Binding Title}"/>
<TextBlock FontSize="20" Text="{Binding Desc}"/>
</StackPanel>
<!--</ListBoxItem>-->
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</UserControl.Resources>
Here is my method in which i add listview programatically:
long rowCount = ContentGridFullView.RowDefinitions.LongCount();
if (rowCount > 8) return;
var c1 = new RowDefinition { Height = new GridLength(1, GridUnitType.Star) };
ContentGridFullView.RowDefinitions.Add(c1);
rowCount = ContentGridFullView.RowDefinitions.LongCount();
TextBlock tb = new TextBlock {Text = "TEXTBLOCK ITEM = " + (rowCount - 1), FontSize = 40};
Viewbox vb = new Viewbox { Child = tb };
if (rowCount > 8) return;
Grid.SetRow(vb, Convert.ToInt32(rowCount-1));
Grid.SetColumn(vb, 1);
ListView lb = new ListView();
lb.Style = Resources["ListBoxTemplate"] as Style;
lb.ItemsPanel = (ItemsPanelTemplate) Resources["VirtualizingStackPanelTemplate"];
var products = new ObservableCollection<Product>() { new Product("ASDASDSADAS", "VCBVCBVCBVCBC"), new Product("ASDASDSADAS", "VCBVCBVCBVCBC"), new Product("ASDASDSADAS", "VCBVCBVCBVCBC"), new Product("ASDASDSADAS", "VCBVCBVCBVCBC") };
lb.ItemsSource = products;
ContentGridFullView.Children.Add(lb);
ContentGridFullView.Children.Add(vb);
Grid.SetRow(lb, Convert.ToInt32(rowCount - 1));
Grid.SetColumn(lb, 2);
And my short class that I want to bind:
public class Product
{
public string Title { get; set; }
public string Desc { get; set; }
public Product(string title, string desc)
{
Title = title;
Desc = desc;
}
public override string ToString()
{
return "I see that message instead of Title and Desc";
}
}
Can someone tell me what's wrong with this code? Thank you.
Create your Observable collection as a property (getter/setter):
ObservableCollection<Product> _products;
public ObservableCollection<Product> products
{
get{return _products;}
set
{
_products=value;
PropertyChanged("products");
}
}
The property changed event will be need to indicate that the collection has changed,its needed when your using ObservableCollection. You'll need to read more about it.You can add items to the products collection by using :
products.Add(Product_object)
And your xaml code will have the itemsSource as follows:
<ListView x:Key="ListBoxTemplate" HorizontalAlignment="Left" ScrollViewer.HorizontalScrollBarVisibility="Visible" ItemsSource="{Binding products}">
<ListView.ItemTemplate>
<DataTemplate>
<!--<ListBoxItem Background="DarkOrchid" Margin="1,1, 5,5" Height="400" HorizontalContentAlignment="Stretch" HorizontalAlignment="Stretch">-->
<StackPanel>
<TextBlock FontSize="30" Text="{Binding Title}"/>
<TextBlock FontSize="20" Text="{Binding Desc}"/>
</StackPanel>
<!--</ListBoxItem>-->
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
The following statement is important in your xaml code so that your xaml code will know where to look for the data.
DataContext="{Binding RelativeSource={RelativeSource Self}, Path=x}
First try and create a static list and check if data is getting initialized properly and then you can try creating listviews dynamically. But the code above will be the same thing you will have to do create dynamic listviews.

Categories