I've currently got a class that gets and dispatches an XML feed using Linq to XML to a ListBox in my XAML page. I took this from a tutorial, and was wondering, would I be able to make it appear in a pivot?
My idea is to load the feed, and create a pivot page just in background code for each item (Something like, foreach item in my data, create a new pivot, with other content)
Is this possible?
I currently get data into a ListBox by Binding the loading and using "TextBlock Text="{Binding Id}"/>" in XAML, and loading the feed in the background code as follows:
myFeed.LoadFeed(//name of the listbox that currently has to exist in XAML)
Here is my code that loads the XML feed and dispatches to a Listbox
public class FeedItem
{
public string Id { set; get; }
public string Text { set; get; }
}
public class Feed
{
ListBox myContext;
public void LoadFeed(ListBox context)
{
myContext = context;
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(new Uri("http://myDataSource"));
request.BeginGetResponse(new AsyncCallback(ReadCallback), request);
}
private static readonly XNamespace m = "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata";
private void ReadCallback(IAsyncResult asynchronousResult)
{
HttpWebRequest request =
(HttpWebRequest)asynchronousResult.AsyncState;
HttpWebResponse response =
(HttpWebResponse)request.EndGetResponse(asynchronousResult);
XDocument doc = XDocument.Load(response.GetResponseStream());
List<FeedItem> feedItems = (from question in doc.Descendants(m + "properties")
select new FeedItem()
{
Id = question.Descendants().ToList()[0].Value,
Text = question.Descendants().ToList()[1].Value
}).ToList();
myContext.Dispatcher.BeginInvoke(() => { myContext.ItemsSource = feedItems; });
}
}
What can be used to hold the data so it can go in a pivot?
How do i parse the response item-by-item, into a new pivot?
Yes, you can. You need to provide a datatemplate to the Pivot control. Give attention to the header template which is defined at Pivote level not on the PivotItem's one.
<Grid x:Name="LayoutRoot" Background="Transparent">
<controls:Pivot ItemsSource="{Binding MyPivots}">
<controls:Pivot.HeaderTemplate>
<DataTemplate>
<TextBlock Text="{Binding MyTitle}"/>
</DataTemplate>
</controls:Pivot.HeaderTemplate>
<controls:Pivot.ItemTemplate>
<DataTemplate>
<controls:PivotItem>
<TextBlock Text="{Binding YourRssText}" />
</controls:PivotItem>
</DataTemplate>
</controls:Pivot.ItemTemplate>
</controls:Pivot>
And the code-behind class:
public partial class MainPage : PhoneApplicationPage{
public List<RssFeed> MyPivots { get; set; }
// Constructor
public MainPage()
{
MyPivots = new List<RssFeed>
{
new RssFeed{ MyTitle = "Title1", YourRssText = "Body1"},
new RssFeed{ MyTitle = "Title2", YourRssText = "Body2"},
new RssFeed{ MyTitle = "Title3", YourRssText = "Body3"},
};
InitializeComponent();
this.DataContext = this;
}
}
public class RssFeed
{
public string MyTitle { get; set; }
public string YourRssText { get; set; }
}
Related
i want to display data in a CollectionView that has state = false using CommunityToolkit.Mvvm, but I don't quite understand how to do it. I wanted to use ICollectionView but didn't find it in maui. help me please
Model
public class Task
{
public string Title { get; set; }
public string Text { get; set; }
public bool State { get; set; }
public DateTime CreateDate { get; set; }
}
ViewModel
public partial class ToDoViewModel : ObservableObject
{
[ObservableProperty]
string title;
[ObservableProperty]
string text;
[ObservableProperty]
bool state = false;
[ObservableProperty]
DateTime createDate = DateTime.Now.Date;
[ObservableProperty]
ObservableCollection<Task> tasks;
int count = 1;
public ToDoViewModel()
{
tasks = new ObservableCollection<Task>();
}
[RelayCommand]
void Add()
{
if (string.IsNullOrEmpty(text))
return;
Task task = new Task
{
Title = $"Task #{count}",
Text = text,
State = state,
CreateDate = createDate
};
tasks.Add(task);
count++;
}
[RelayCommand]
void Remove(Task task)
{
if (tasks.Contains(task))
tasks.Remove(task);
}
[RelayCommand]
void StateDone(Task task)
{
task.State = true;
}
}
First, you need to set a CollectionView :
public ICollectionView TasksView { get; set; }
When you initiate your ViewModel, you may the source of CollectionView, eventually add some sorting. Then you need to update the CollectionView filter :
public ToDoViewModel()
{
tasks = new ObservableCollection<Task>();
TasksView = new CollectionViewSource { Source = tasks }.View;
TasksView.SortDescriptions.Add(new SortDescription("Title", ListSortDirection.Ascending));
this.updateTasksView();
}
private void updateTasksView()
{
if (ListStockBeamView != null)
{
TasksView.Filter = new Predicate<object>(getFilteredTasksView);
}
}
private bool getFilteredTasksView(object obj)
{
Task task = (Task)obj;
if (task.State==true) return false;//you could just write return !task.State
if(task.Title=="") return false // i.e you want to hide tasks without title
return true;
}
I always set the filter in a function, because in the future, if you want to add a CheckBox, to select state==true or state==false, or even filter the text title, or any other filter, it is easy to add additive filters, then launch the function everytime you edit your filters
I write a simple demo for you and CollectionView can be used in the MAUI.
I noticed that you wrote a model file and a ViewModel file. So I write the xaml file.
<CollectionView ItemsSource="{Binding tasks}" >
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout>
<Label Text="{Binding Title }" IsVisible="{Binding State}"></Label>
<Label Text="{Binding Text }"></Label>
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
In the xaml, I bind the state to the IsVisible, so you can filter the data which you want to see.
Another way to use the state is by using the LINQ to select the data which the state is true
var TasksQuery =
from task in tasks
where task.State == "true"
select task;
Then you can use the TaskQuery which the state is all true.
I have an issue with binding ComboBox ItemsSource with a list. I read workplaces from csv file. It can't see the Workplaces list. Please, tell me what is wrong.
xaml:
<ComboBox Grid.Column="1" Grid.Row="9" Height="25" Margin="0,18,0,0" ItemsSource="{Binding Workplaces}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding title}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
xaml.cs:
public partial class MainWindow : Window
{
private BindableCollection<WorkplaceInfo> Workplaces { get; set; }
public MainWindow()
{
InitializeComponent();
Workplaces = new BindableCollection<WorkplaceInfo>(GetWorkplaces());
}
private List<WorkplaceInfo> GetWorkplaces()
{
List<WorkplaceInfo> workplaces = new List<WorkplaceInfo>();
using (var streamReader = new StreamReader("Workplaces.csv"))
{
using (var csvReader = new CsvReader(streamReader, CultureInfo.InvariantCulture))
{
//csvReader.Context.RegisterClassMap<WorkplaceInfoClassMap>();
var workplaceInfoList = csvReader.GetRecords<dynamic>().ToList();
foreach (var wi in workplaceInfoList)
{
workplaces.Add(new WorkplaceInfo(wi.title, wi.member_of.Split(";")));
}
}
}
return workplaces;
}
}
WorkplaceInfo class:
class WorkplaceInfo
{
public String title { get; }
public String[] memberOfList { get; }
public WorkplaceInfo(string title, string[] memberOfList)
{
this.title = title;
this.memberOfList = memberOfList;
}
}
Here is your code optimized to work:
public ObservableCollection<WorkplaceInfo> Workplaces { get; set; }
public MainWindow()
{
this.DataContext = this;
Workplaces = new ObservableCollection<WorkplaceInfo>(GetWorkplaces());
InitializeComponent();
}
private List<WorkplaceInfo> GetWorkplaces()
{
List<WorkplaceInfo> workplaces = new List<WorkplaceInfo>();
try
{
using (var streamReader = new StreamReader("Workplaces.csv"))
{
using (var csvReader = new CsvReader(streamReader, CultureInfo.CurrentCulture))
{
//csvReader.Context.RegisterClassMap<WorkplaceInfoClassMap>();
var workplaceInfoList = csvReader.GetRecords<dynamic>().ToList();
foreach (var wi in workplaceInfoList)
{
var titl = wi.title;
workplaces.Add(new WorkplaceInfo(wi.title, new List<string>() { wi.member_of }.ToArray()));
}
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
return workplaces;
}
So the changes needed in your code are:
Set your Workplaces collection to public ObservableCollection
Add DataContext binding
Read in the Date and create the collection before the main window is initialized (other way round the UI will not detect the change in you object unless you implement INotifyPropertyChanged event)
p.s. I don't know the structure of your csv file so I made my small demo like (Workplaces.csv) and adopted the parser. You can keep your parser if it matches your csv file structrue.:
title;member_of
London;First
Amsterdam;Second
And my warm recommendation is to use try-catch block always when handling files and when working with anything what is external to your application.
Best regards.
I am trying to populate a collection view from a ViewModel, however when I try to bind the data to the collection view, the ViewModel is null.
xaml.cs file
ObservableCollection<ReportsClass> newKidList = new ObservableCollection<ReportsClass>();
public ReportsViewModel viewmodel { get; set; }
public ReportsPage()
{
InitializeComponent();
viewmodel = new ReportsViewModel();
this.BindingContext = viewmodel;
PreviousDateRange.CornerRadius = 20;
NextDateRange.CornerRadius = 20;
DateTime firstDate = currentDate.StartOfWeek(DayOfWeek.Sunday);
DateTime secondDate = currentDate.AddDays(7).StartOfWeek(DayOfWeek.Saturday);
DateRange.Text = firstDate.ToString("MMMM d") + " - " + secondDate.ToString("MMMM d");
Kids.SetBinding(ItemsView.ItemsSourceProperty, nameof(viewmodel.kids));
}
Here is my view model
public class ReportsViewModel
{
public ObservableCollection<ReportsClass> kids { get; set; }
FirebaseStorageHelper firebaseStorageHelper = new FirebaseStorageHelper();
WebServiceClass webServiceClass = new WebServiceClass();
DateTime currentDate = DateTime.Now;
public ReportsViewModel()
{
GetKids();
}
public async void GetKids()
{
var parentId = await SecureStorage.GetAsync("parentid");
kids = await webServiceClass.Reports(Convert.ToInt32(parentId), currentDate.StartOfWeek(DayOfWeek.Sunday), currentDate.AddDays(7).StartOfWeek(DayOfWeek.Saturday));
}
}
And here is the method that gets the data for the view model
public async Task<ObservableCollection<ReportsClass>> Reports(int parentid, DateTime startDate, DateTime endDate)
{
var content = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("parentid", parentid.ToString()),
new KeyValuePair<string, string>("startDate", startDate.ToString("yyyy-MM-dd H:mm:ss")),
new KeyValuePair<string, string>("endDate", endDate.ToString("yyyy-MM-dd"))
});
var response = await client.PostAsync(string.Format("https://example.com/api/index.php?action=reports"), content);
var responseString = await response.Content.ReadAsStringAsync();
ObservableCollection<ReportsClass> items = JsonConvert.DeserializeObject<ObservableCollection<ReportsClass>>(responseString);
return items;
}
What am I doing wrong? The purpose of me doing it this way is so I can update an item in the collectionview
Here is my ReportsClass
public class ReportsClass
{
public ReportsClass(string firstName)
{
first_name = firstName;
}
public string first_name { get; set; }
}
OPTION A:
Fix the syntax of Kids.SetBinding, to not get null. Refer to the CLASS ReportsViewModel, not to the INSTANCE viewmodel:
Kids.SetBinding(ItemsView.ItemsSourceProperty, nameof(ReportsViewModel.kids));
The kids still won't appear in list. To fix, kids needs OnPropertyChanged:
public ObservableCollection<ItemModel> kids {
get => _kids;
set {
_kids = value;
OnPropertyChanged();
}
}
private ObservableCollection<ItemModel> _kids;
See the other code in Option B. Adapt as desired.
When you need XAML to see a DYNAMIC change, you need OnPropertyChanged. This is an implementation of INotifyPropertyChanged. Add this call to properties (that XAML binds to) of ReportsClass:
// Inheriting from `BindableObject` is one way to obtain OnPropertyChanged method.
public class ReportsClass : Xamarin.Forms.BindableObject
{
public ReportsClass(string firstName)
{
first_name = firstName;
}
public string first_name {
get => _first_name;
set {
_first_name = value;
// This tells XAML there was a change.
// Makes "{Binding first_name}" work dynamically.
OnPropertyChanged();
}
}
private string _first_name;
}
OPTION B:
Didn't find an answer anywhere that does everything correctly, so here is a complete sample, for future reference:
Remove Kids.SetBinding(...). (It can be fixed as shown in OPTION A, but its easier to get it correct in XAML, so below I show it in XAML.)
Bindings from Page to VM. See xaml below.
Create ObservableCollection with setter that does OnPropertyChanged. This informs XAML when the list is ready, so page updates. (This is an implementation of INotifyPropertyChanged, as Jason mentioned.)
Use Device.BeginInvokeOnMainThread(async () to create an async context, that is queued to run after constructor returns. (This fixes the issue Jason mentioned, which is that a constructor isn't an async context, so should not DIRECTLY call an async method such as QueryItemsAsync, or your GetKids.) This is more reliable.
PageWithQueryData.xaml:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="TestXFUWP.PageWithQueryData">
<ContentPage.Content>
<StackLayout>
<CollectionView ItemsSource="{Binding Items}">
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout>
<Label Text="{Binding Name}" />
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
<CollectionView.EmptyView>
<Grid>
<Label Text="Loading ..." FontSize="24" TextColor="Blue" BackgroundColor="LightBlue" HorizontalTextAlignment="Center" VerticalTextAlignment="Center" />
</Grid>
</CollectionView.EmptyView>
</CollectionView>
</StackLayout>
</ContentPage.Content>
</ContentPage>
PageWithQueryData.xaml.cs:
public partial class PageWithQueryData : ContentPage
{
public PageWithQueryData()
{
InitializeComponent();
// ... other initialization work here ...
// BUT remove `Kids.Binding(...);` line. See XAML: `ItemsSource="{Binding Items}"`.
BindingContext = new VMWithQueryData();
}
}
VMWithQueryData.cs:
class VMWithQueryData : Xamarin.Forms.BindableObject
{
public VMWithQueryData()
{
// Start an async task to query.
Xamarin.Forms.Device.BeginInvokeOnMainThread(async () => {
await QueryItemsAsync();
});
// Alternative implementation: Start a background task to query.
//QueryItemsInBackground();
}
public ObservableCollection<ItemModel> Items {
get => _items;
set {
_items = value;
OnPropertyChanged();
}
}
private ObservableCollection<ItemModel> _items;
private async Task QueryItemsAsync()
{
var names = new List<string> { "One", "Two", "Three" };
bool queryOneAtATime = false;// true;
if (queryOneAtATime) {
// Show each item as it is available.
Items = new ObservableCollection<ItemModel>();
foreach (var name in names) {
// Simulate slow query - replace with query that returns one item.
await Task.Delay(1000);
Items.Add(new ItemModel(name));
}
} else {
// Load all the items, then show them.
// Simulate slow query - replace with query that returns all data.
await Task.Delay(3000);
var items = new ObservableCollection<ItemModel>();
foreach (var name in names) {
items.Add(new ItemModel(name));
}
Items = items;
}
}
// Alternative implementation, using a background thread.
private void QueryItemsInBackground()
{
Task.Run(() => {
var names = new List<string> { "One", "Two", "Three" };
bool queryOneAtATime = false;// true;
if (queryOneAtATime) {
// Show each item as it is available.
Items = new ObservableCollection<ItemModel>();
foreach (var name in names) {
// Simulate slow query - replace with query that returns one item.
System.Threading.Thread.Sleep(1000);
Items.Add(new ItemModel(name));
}
} else {
// Load all the items, then show them.
// Simulate slow query - replace with query that returns all data.
System.Threading.Thread.Sleep(3000);
var items = new ObservableCollection<ItemModel>();
foreach (var name in names) {
items.Add(new ItemModel(name));
}
Items = items;
}
});
}
}
ItemModel.cs:
public class ItemModel
{
public ItemModel(string name)
{
Name = name;
}
public string Name { get; set; }
}
This also demonstrates <CollectionView.EmptyView> to display a message to user, while the data is being queried.
For completeness, I've included an alternative QueryItemsInBackground, that uses a background thread instead of an async method. Either approach works well.
Notice inheritance from Xamarin.Forms.BindableObject. This is one way to get an implementation of INotifyPropertyChanged. You can use any other MVVM library or technique.
Move this line of code to the end of your constructor
this.BindingContext = viewmodel;
I've tried searching around here and on microsoft docs but I can't find a solution to my specific query, as mainly I've seen posts about how to do things on itemclick rather than retrieve data.
I'm currently using an API, which sends a JSON request that I deserialize into 2 partial classes, where I use a foreach loop to add new items to the ListView. You can see the classes here:
public partial class GameListObject
{
[JsonPropertyName("id")]
public long GameID { get; set; }
[JsonPropertyName("name")]
public string GameName { get; set; }
[JsonPropertyName("release_dates")]
public ObservableCollection<ReleaseDate> ReleaseDates { get; set; }
}
public partial class ReleaseDate
{
[JsonPropertyName("id")]
public long Id { get; set; }
[JsonPropertyName("human")]
public string Human { get; set; }
}
And the request, deserialization and adding to the ListView here:
//On search box content change
private async void gamehub_search_TextChanged(object sender, TextChangedEventArgs e)
{
var SearchQuery = gamehub_search.Text;
try
{
// Construct the HttpClient and Uri
HttpClient httpClient = new HttpClient();
Uri uri = new Uri("https://api.igdb.com/v4/games");
httpClient.DefaultRequestHeaders.Add("Client-ID", App.GlobalClientidIGDB);
httpClient.DefaultRequestHeaders.Add("Authorization", "Bearer " + App.GlobalAccessIGDB);
//Debug.WriteLine("Request Headers: ");
// Construct the JSON to post
HttpStringContent content = new HttpStringContent($"search \"{SearchQuery}\"; fields name,release_dates.human;");
Debug.WriteLine("Request Contents: " + content);
// Post the JSON and wait for a response
HttpResponseMessage httpResponseMessage = await httpClient.PostAsync(
uri,
content);
// Make sure the post succeeded, and write out the response
httpResponseMessage.EnsureSuccessStatusCode();
var httpResponseBody = await httpResponseMessage.Content.ReadAsStringAsync();
Debug.WriteLine("Request Response: " + httpResponseBody);
//Deserialise the return output into game id, game name and release date
List<GameListObject> gamelistobjects = JsonSerializer.Deserialize<List<GameListObject>>(httpResponseBody);
ObservableCollection<GameListObject> dataList = new ObservableCollection<GameListObject>(gamelistobjects);
ObservableCollection<GameListObject> GameList = new ObservableCollection<GameListObject>();
foreach (var item in dataList)
{
Debug.WriteLine($"id: {item.GameID}");
Debug.WriteLine($"name: {item.GameName}");
GameListObject add = new GameListObject() { GameID = item.GameID, GameName = item.GameName };
GameList.Add(add);
if (item.ReleaseDates != null)
{
foreach (var date in item.ReleaseDates)
{
Debug.WriteLine($"releaseDate: {date.Human}");
}
}
}
gamehub_list.ItemsSource = GameList;
}
catch (Exception ex)
{
Debug.WriteLine(ex);
}
}
Now I have set an gamehub_list_ItemClick method which runs when an item within the ListView is pressed. I would like to retrieve the GameID that's present in that item because I'll need that for another page which the user gets redirected to so that I know what game I must request data for. However, I've tried finding the index of the item and using the member names of the class to retrieve it but I can't seem to get it working.
The ItemClick method currently is:
private void gamehub_list_ItemClick(object sender, ItemClickEventArgs e) //When an item in List View is pressed
{
string clickedItemText = e.ClickedItem.ToString();
Debug.WriteLine("Click Item text: " + clickedItemText);
}
When I tried to get the index of the item, it always returned as -1 and for the current clickedItemText it returns "Click Item text: ReviewR.GameHubs+GameListObject".
My xaml with the ListView:
<ListView x:Name="gamehub_list" SelectionMode="None" IsItemClickEnabled="True" ItemClick="gamehub_list_ItemClick" Margin="30,140,44,30" BorderThickness="5" BorderBrush="Black" Background="Gray" RequestedTheme="Dark" Visibility="Collapsed">
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<StackPanel>
<TextBlock Text="{Binding GameID}"></TextBlock>
<TextBlock Text="{Binding GameName}"></TextBlock>
</StackPanel>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Click Item text: ReviewR.GameHubs+GameListObject
The problem is ClickedItem is object type, if you pass it to string directly, it will return text like you mentioned above.
For this scenario, you need unbox ClickedItem.
var clickedItem = e.ClickedItem as GameListObject
Debug.WriteLine("Click Item text: " + clickedItem.GameID );
I'm new to WPF and trying to wrap my head around the preferred way to handle data. I found this link that explains the databinding for a tree view. I have tried to create my code in a similar way, but I can't see why that code runs fine and mine doesn't.
Anyway, I've defined some class for artists/albums/songs
class LibArtist
{
public string Name { get { return mName; } }
string mName;
public ObservableCollection<LibAlbum> Albums;
public LibArtist(string name)
{
mName = name;
Albums = new ObservableCollection<LibAlbum>();
}
}
class LibAlbum
{
public string Name { get { return mName; } }
public string Artist { get { return mArtist.Name; } }
public uint Year { get { return mYear; } }
public ObservableCollection<LibSong> mSongs = new ObservableCollection<LibSong>();
uint mYear;
LibArtist mArtist;
string mName;
public LibAlbum(string pName, LibArtist pArtist, uint pYear)
{
mName = pName;
mArtist = pArtist;
mYear = pYear;
}
}
class LibSong
{
public string Title { get { return mName; } }
public string Artist { get { return mArtist; } }
public string Album { get { return mAlbum; } }
public string Location { get { return mLocation; } }
public uint Year { get { return mYear; } }
string mName;
uint mYear;
string mAlbum;
string mArtist;
string mLocation;
public LibSong(string pSongLocation)
{
mLocation = pSongLocation;
TagLib.File lFile = TagLib.File.Create(pSongLocation);
mAlbum = lFile.Tag.Album;
mName = lFile.Tag.Title;
mArtist = lFile.Tag.AlbumArtists.Length > 0 ? lFile.Tag.AlbumArtists[0] : "???";
//use tag lib to fill the data if this file exists
mYear = lFile.Tag.Year;
}
public override bool Equals(object obj)
{
LibSong temp = obj as LibSong;
if (temp == null)
return false;
if (temp.Location == this.Location)
return true;
if (temp.Artist == this.Artist && temp.Album == this.Album && temp.Year == this.Year)
return true;
return false;
}
}
And these sit in a library class:
class Library
{
public SortedDictionary<string, List<string>> mArtistsToAlbums;
SortedDictionary<string, List<LibSong>> mAlbumsToSongs;
public List<LibSong> mSongList;
public ObservableCollection<LibSong> mSongList2;
public ObservableCollection<LibAlbum> mAlbumList;
public ObservableCollection<LibArtist> mArtistList;
...
}
In my main window, I set the data context of my treeview to the library object:
public MainWindow()
{
mPlayer = new izPlayer(0);
InitializeComponent();
libraryTreeView.DataContext = mLibrary;
mLibrary = new Library();
mLibrary.CreateTestData();
In my xaml, I define the treeview like so:
<TreeView Name="libraryTreeView"
HorizontalAlignment="Left"
ItemsSource="{Binding mArtistList}"
Height="443" Margin="10,50,0,0" VerticalAlignment="Top" Width="344" MouseDoubleClick="libraryTreeView_MouseDoubleClick"
>
<TreeView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</TreeView.ItemTemplate>
</TreeView>
And when I run this, I don't get anything displayed in the treeview. As I said, I'm not sure why this is different from the example code, or why it isn't displaying the data inside mArtistList.
Any help would be appreciated!
Specifically for the TreeView Dennis' answer is a great resource. If you're not getting any items even in at the top level thought, it may be due to invalid binding sources. It looks like Library is declaring public fields
public ObservableCollection<LibArtist> mArtistList;
In order to use binding in the XAML these sources need to be public properties
public ObservableCollection<LibArtist> mArtistList { get; set; }
This is totally different from example code (I mean XAML difference).
The main concept for the data-bound TreeView in WPF is that you must describe hierarchical data templates for your nodes, because you want to display hierarchical data.
Your XAML should look like this:
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type yourNamespace:LibArtist}" ItemsSource="{Binding Albums}">
<!-- the template tree for displaying artist's data -->
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type yourNamespace:LibAlbum}" ItemsSource="{Binding Songs}">
<!-- the template tree for displaying song's data -->
</HierarchicalDataTemplate>
<!-- and so on -->
</TreeView.Resources>