C# Windows 10 UWP Nested Listviews with Databinding - c#

I have a app that retrieves comments from a website. I can programmatically add them to a StackPanel, calulating their indentation for comment replies but I'd like to learn how to bind a list of comments to a ListView and have it display correctly there.
My Comment Class looks like this:
class Comment
{
public List<Comment> Replies { get; set; }
public string Body { get; }
public int Level { get; set; }
public Comment(string BodyText)
{
Body = BodyText;
}
public Comment(string BodyText, List<Comment> replies, int level)
{
Body = BodyText;
Replies = replies;
Level = level;
}
}
So each Comment can have a List<> of comments (replies) to it and the Level variable indicates the depth of the comment.
What would be the process to set up a ListView so that I can bind a list of comments to it and those comments replies to those and so on? Or is there a better way to do this?
Thank you.
This is how I currently have it implemented which is visually correct but I'd like to use data binding rather than doing it through code.

Create a ListView, bind it's ItemsSource property to the list of top level comments. Use an ItemTemplate that contains the comment and another ListView in a vertical StackPanel. That inner ListView needs to get the same ItemTemplate it is in. I'm not sure if {StaticResource} will handle that, but it should.
If you use ObservableCollections this will actually be dynamic.

I recommend you to use the third party package WinRTXamlToolkit. Which contains a TreeView control that can meet your hierarchy requirements well. You can just bind the comments collection to the TreeView control code behind. Code example as follows:
Xaml Code
<controls:TreeView Width="400" MaxHeight="400" x:Name="Treeviewcomment">
<controls:TreeView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Body}"/>
<data:DataTemplateExtensions.Hierarchy>
<data:HierarchicalDataTemplate ItemsSource="{Binding Replies}" />
</data:DataTemplateExtensions.Hierarchy>
</DataTemplate>
</controls:TreeView.ItemTemplate>
</controls:TreeView>
Binding code
this.InitializeComponent();
ObservableCollection<Comment> comments = new ObservableCollection<Comment>
{
new Comment ("By the way,I have noticed that ..."),
new Comment("Has this been metioned anywhere before..",
new List<Comment>
{
new Comment("Delta upgrade..."),
new Comment("When only stuff that...",
new List<Comment> {
new Comment("That's blloby...")},
3)},
2),
new Comment("Just had to turn off..")
};
And the result:
Special nuget package for uwp: WinRT XAML Toolkit. And I also upload the above code example to GitHub.

Related

C# WPF How can I have multiple DataContext

I am very new to WPF so despite finding other similar threads, my code is a little different so I don't know how to apply those solutions to mine.
<grid>
<CheckBox
x:Name="sortChk"
Content="Sort Variables"
IsChecked="{Binding SortVariables}"
/>
<b:FlatButton Command="{Binding ClickCommand}">
<TextBlock Text="Compare" FontSize="13"></TextBlock>
</b:FlatButton>
</grid>
Code behind looks something like this
string dataset1FullPath = null;
string dataset2FullPath = null;
string dataset1FilePath = null;
string dataset2FilePath = null;
public bool SortVariables { get; set; }
public SingleCompareWindow(string path1, string path2)
{
InitializeComponent();
dataset1FullPath = path1;
dataset2FullPath = path2;
dataset1FilePath = System.IO.Path.GetDirectoryName(path1);
dataset2FilePath = System.IO.Path.GetDirectoryName(path2);
//DataContext = this;
DataContext = new DataCompareVM(dataset1FullPath, dataset2FullPath, dataset1FilePath, dataset2FilePath, SortVariables);
}
You can see that I have commented out DataContext = this. If I dont comment it out and remove the other DataContext, then my SortVariables variable will take the value of the checkbox. If I have the other DataContext for the button click, then the checkbox will not bind. I need it to bind as I pass it as a parameter to DataCompareVM().
I'm very new to WPF and MVVM so forgive my ignorance.
You can't have more than one DataContext for a WPF control, you should move your properties to the ViewModel.
Yes, you can have different data contexts for different elements. But here you're trying to replace the same, window data context.
What you can do is use the named element. Replace your commented line with:
sortChk.DataContext = this;
Now, for the longest answer: you're not really doing MVVM here. As Fildor mentions, you should instead move the SortVariables property to the DataCompareVM ViewModel.
Since you seem to be struggling with MVVM, I believe reading my introductory book will help you get things straight: Learn WPF MVVM.

StackedBarChart from modern UI

I wanna add to my tool a statistics window that will contain stacked bars chart.
I used for a similar application Modern UI (Metro) charts to create a single graph and it worked fine.
This Time, I need to show an unknown number of StackedBarChart and I'm having problems doing so.
I'd like your thoughts for a solution or a work-around
this is what my code for a single chart looks like (which works fine):
<chart:StackedBarChart Width="400" Height="400" ChartTitle="Statistics" ChartSubTitle="A single stacked bar">
<chart:StackedBarChart.Series>
<chart:ChartSeries
SeriesTitle="Y axis name"
DisplayMember="Name"
ValueMember="Number"
ItemsSource="{Binding SomeObservableCollectionOfNamesAndNumbers}"/>
</chart:StackedBarChart.Series>
</chart:StackedBarChart>
and what i want to do is something like this (using itemsSource):
<chart:StackedBarChart Width="400" Height="400" ChartTitle="Statistics" ChartSubTitle="Multiple stacked bars">
<chart:StackedBarChart.Series ItemsSource="{SomeObservableCollectionOfSomeObservableCollectionOfNamesAndNumbers}">
<chart:ChartSeries
SeriesTitle="{binding SomeObservableCollectionOfNamesAndNumbers.Name}"
DisplayMember="Name"
ValueMember="Number"
ItemsSource="{Binding SomeObservableCollectionOfNamesAndNumbers}"/>
</chart:StackedBarChart.Series>
</chart:StackedBarChart>
but unfortunately it is not possible, can you think of any other way?
you have to bind Series to your Observable Collection like this:
chart:StackedColumnChart ChartTitle="Statistics" ChartSubTitle="Multiple stacked bars" Series="{Binding Bars}">
</chart:StackedColumnChart>
You have to define your series in the Observable Collection (Bars in this case) in your Code Behind/ViewModel
ObservableCollection<TestClass> blocks = new ObservableCollection<TestClass>();
ChartSeries chartSerie = new ChartSeries();
chartSerie.SeriesTitle = "Some Title"
chartSerie.DisplayMember = "Category";
chartSerie.ValueMember = "Number";
Bars.Add(chartSerie);
chartSerie.ItemsSource = blocks;
blocks = new ObservableCollection<TestClass>();
Blocks are holding the single stacks and a ChartSerie is one whole Bar.
The TestClass holds your values and looks like this:
public class TestClass
{
public string Category { get; set; }
public int Number { get; set; }
}
Feel free to ask, if you need any further information.

Simple Static Gridview

I've started to learn about GridView (XAML) for Windows 8 Store apps. I've used the ItemsPage example to try and have a look at the code.
My aim is use it as a horizontal menu, on the click event it will simply open another xaml page.
I've edited the SampleDataSource.cs file and filled up with the wanted content, i thought that there would be an easier, cleaner way of doing this. This Menu won't be dynamic and won't change so i'm looking for a way to statically add the menu items.
I can do the following to add a simple text entry;
<x:String>Item 1</x:String>
But i have i'm not sure how to bind to certain parts, such as the image and title elements
Right, i think i'm understanding this a little bit more. The following code would try and bind to itemsViewSource
<CollectionViewSource
x:Name="itemsViewSource"
Source="{Binding Items}"
d:Source="{Binding AllGroups, Source={d:DesignInstance Type=data:SampleDataSource, IsDesignTimeCreatable=True}}"/>
So would i need to create a new list, such as;
List<String> itemsList = new List<string>();
itemsList.Add("Item 1");
itemsList.Add("Item 2");
Only issue is that each item (line) would need a Title and Image text field and how to bind those to the correct items.
If your ItemsControl (GridView) needs to display more than one bound item for each list item - you need to bind its Source to a list of something more than a string unless you want to parse the strings. In your case you would have something like
List<TitleAndImageText> itemsList = new List<TitleAndImageText>();
itemsList.Add(new TitleAndImageText { Title = "Title 1", ImageText = "Image Text 1");
itemsList.Add(new TitleAndImageText { Title = "Title 2", ImageText = "Image Text 2");
Then in your ItemTemplate/DataTemplate you would have two TextBlocks - that bind to the properties of your item view model like this: Text="{Binding Title}", Text="{Binding ImageText}".
Of course you need to define your TitleAndImageText, e.g.
public class TitleAndImageText
{
public string Title { get; set; }
public string ImageText { get; set; }
}

SelectedListIndex property of a databound ListBox cannot be set, why?

I avoided to ask this question, but the ListBox's selected index can no be set. I have read the other threads and applied the settings, but it doesn't work.
<ListBox ItemsSource="{Binding}"
HorizontalAlignment="Right"
Name="lstReading" Height="Auto"
SelectedIndex="{Binding BookmarkSelectedIndex}">
In the something.xaml.cs, I am settings
lstReading.DataContext = IQText;
Where, IQText is an IEnumerable<dictIQ> and includes the BookmarkSelectedIndex as data element. Other data elements from IQText can be used but the listindex can't be set. Could someone please let me know why?
Are you have BookmarkSelectedIndex inside of dictIQ class? So, you have one BookmarkSelectedIndex per item, not per collection!
You can create separate property BookmarkSelectedIndex outside of dictIQ or create class that inherited from ObservalbeCollection<dictIQ> and have additional property BookmarkSelectedIndex:
public class CollectionWithIndex: ObservalbeCollection<dictIQ>
{
public int BookmarkSelectedIndex { get; set; }
}
I hope you choose best solution suitable for you
use this code for select item at runtime...
List<Audio> lst = Audio.GetAudioFiles();
Audio aufile = new Audio { FileDisplayName = "Select Audio File" };
lst.Insert(0, aufile);
lstPickAudio.ItemsSource = lst;
string FileDisplayName="your condition"
lstPickAudio.SelectedItem = lst.Where(p => p.FileName == FileDisplayName).First();

Can't seem to get WPF DataBinding in my head

Ok, I'm no newbie at programming or C# as such, I just can't seem to get WPF's databinding straight in my head. My colleagues are raving about it (and yes, I will ask them as well) but right now I'm stumped.
Here's what I'd like to do for starters:
As an example I've got a list of things like this:
List<Thing> thingList = Source.getList();
Now normally I'd go
foreach(Thing t in thingList)
{
//add thing to combobox
}
But from what I can gather is that I can somehow not do this but use a databinding to populate the combobox for me.
What I can't seem to get is where do I put the 'thingList'? Do I make it a separate property somewhere? Where do I put that property?
I feel very stupid at the moment, as I've been struggling with this for a while now and I can't find any examples out there that make me understand this - probably very simple - concept.
Anyone out there willing to help me or point me at some step-by-step guide I might have missed?
Use ObservableCollection<T> for databinding in WPF. Where T is your class. See example below
public class NameList : ObservableCollection<PersonName>
{
public NameList() : base()
{
Add(new PersonName("A", "E"));
Add(new PersonName("B", "F"));
Add(new PersonName("C", "G"));
Add(new PersonName("D", "H"));
}
}
public class PersonName
{
private string firstName;
private string lastName;
public PersonName(string first, string last)
{
this.firstName = first;
this.lastName = last;
}
public string FirstName
{
get { return firstName; }
set { firstName = value; }
}
public string LastName
{
get { return lastName; }
set { lastName = value; }
}
}
Now in XAML. Go to resource tag
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:c="clr-namespace:SDKSample"
x:Class="Sample.Window1"
Width="400"
Height="280"
Title="MultiBinding Sample">
<Window.Resources>
<c:NameList x:Key="NameListData"/>
</Window.Resources>
<ListBox Width="200"
ItemsSource="{Binding Source={StaticResource NameListData}}" // Name list data is declared in resource
ItemTemplate="{StaticResource NameItemTemplate}"
IsSynchronizedWithCurrentItem="True"/>
xmnls:c will give you option to choose the class. Here you can choose c,d ,e x whatever but be sure it should be used earlier
When it comes to data-binding i've found that this page if read thoroughly answers most of the questions beginners have about it.
To answer the question: The alernative to adding all the items is to tell the ComboBox where to get its items from, this is done with the ItemsSource property.
You can either set this in XAML or in code, while you would need a binding expression in XAML a normal assignment should do in code:
comboBox.ItemsSource = thingList;
If you do not specify further how those objects in the list are to be displayed the ToString method will be called, unless overridden you will usually end up with the class-path of your object. There are two main ways of telling the application how to display the object:
The fist and more heavy option is Data Templates which is used for displaying complex data using controls (which in turn can have items and templates etc), the second way is using lightweight properties like DisplayMemberPath, which should be set to the name of the property which should be displayed (usually just a string).
If your list changes and the combo box should be updated on its own the source should implement INotifyCollectionChanged, in most cases you will use the standard implementation ObersableCollection<T>.
Most people would use WPF Databinding to populate the combobox but you don't have to.
You can add the items via code if you want to.
It's easy to get trapped into doing everything as it "should" be done without have a good reason for doing it that way (BTW, I'm not recommending you manually add the items, I'm just saying you can).
List<Thing> thingList = Source.getList();
foreach(Thing t in thingList)
{
combobox.Items.Add( t );
}

Categories