Binding WPF DataTemplate to Dictionary <int, Page> - c#

So I'm trying to bind a XAML DataTemplate to a Dictionary.
Example:
pages: {
1: {
thumb: "https://www.sample.com",
image: "https://www.sample.com"
},
2: {
thumb: "https://www.sample.com",
image: "https://www.sample.com"
}
}
And so on.
Basically, I'm getting a variable number of pages for each request, and I solved the dynamic deserialization by creating a Dictionary with the key/values , but I can't seem to find a way to bind said Dictionary to a XAML DataTemplate.
In this particular view, I am trying to display a grid of the 'thumb' images from a given Content item. Any ideas on how I'm supposed to approach this? Preferably something that can be easily done with ItemsSource or Binding, or such, if there is such a way.

Alright, so I think I found a work-around.
The images are bound to an Image within a DataTemplate in the ListBox I have set up.
I was looking for a way to have one ListBox with a WrapPanel, which would display images from a dictionary - in this case, the thumbs of each page within the dictionary, of which has a variable number of <int> Keys.
While I would like to know if it is possible to bind the Image Source to a dictionary like I have described above, I found I can use this for the time being:
private static void GenerateThumbsAndImages()
{
foreach (Page i in CurrentContentInfo.Pages.Values)
{
PageImages.Add(new Uri(i.image));
PageThumbs.Add(new Uri(i.thumb));
}
}
Which I then bound with my XAML:
<ListBox x:Name="ThumbView" ItemsSource="{x:Static local:Posts.PageThumbs}" ... >
<Image x:Name="PageThumbnailImage" Source="{Binding OriginalString}" ... />
If anybody knows how I could bind directly to the dictionary, that'd be great, but this works fine for now.

Related

WinRT ListView UI Virtualization and DataTemplateSelector with a lot of templates (10-15)

I need to display ListView with different items (in total 10-15 item types). For this I use DataTemplateSelector. But this causes ListView odd behavior during scrolling: at some point it jumps to the top of list view. I've found this article for UWP: https://msdn.microsoft.com/en-us/windows/uwp/debug-test-perf/optimize-gridview-and-listview. It says that ItemTemplateSelector supports only 5 DataTemplates
Additionally, an item template selector only considers five possible candidates when evaluating whether a particular container can be reused for the current data item.
I think this is the reason. I've tried to reduce the number of DataTemplates returned by my DataTemplateSelector and it solved the issue: scrolling works as expected. But how can I solve this issue without reducing number of DataTemplates? I know that I can disable virtualization, but I would like to keep it enabled if possible.
For UWP there is an option to use ChoosingItemContainer event, but it isn't available for WinRT.
Is it possible to solve this issue without disabling UI virtualization in WinRT?
This is what I ended up doing in my project (I have a listview with infinite scrolling). Basically, I did a part of virtualization myself.
I removed DataTemplateSelector completely. Instead, I use one template for all the items:
<ListView
...
>
<ListView.ItemTemplate>
<DataTemplate>
<messages:MyCustomContainer />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Where MyCustomContainer is a simple UserControl:
<UserControl
x:Class="MyCustomContainer"
...
DataContextChanged="OnDataContextChanged"
>
<Grid x:Name="Container"/>
</UserControl>
I instantiate and select appropriate nested template in code behind of MyCustomContainer:
void OnDataContextChanged(FrameworkElement sender, DataContextChangedEventArgs args)
{
var context = DataContext as MyModelThatHelpsDecideOnAppropriateVisualTemplate;
if (context == null) {
// this means, item has been removed from the list and cached (we call this 'virtualization')
Container.Children.Remove(CurrentTemplate);
ReleaseTemplate(CurrentTemplate); // clear and cache our concrete template
CurrentTemplate = null;
} else {
// this means, we need to get a concrete template
// ... some logic to decide on the proper visual template type
Type templateType = GetTemplateTypeForData(context);
// ... some logic to get visual template from cache
CurrentTemplate = GetTemplateFromCache(templateType);
Container.Children.Add(CurrentTemplate);
}
}
On the bright side, this works fine (does for me, and I have around a dozen item templates).
On the other side, this way the UI framework only virtualizes MyCustomContainer list items, and you have to cache concrete visuals yourself. In my example, you have to store instances of your 10-15 templates in some cache, and implement GetTemplateTypeForData(), GetTemplateFromCache() and ReleaseTemplate()... But that should be really straightforward, took around 100 lines of code for me.

WPF and MVVM: bind UserControl to XAML dynamically

seems like a trivial task: i am building a wpf application, using MVVM pattern. what i want is dynamically change part of a view, using different UserControls, dependent on user input.
let's say, i have got 2 UserControls, one with a button, and another with a label.
in main view i have a container for that. following XAML "works":
<GroupBox Header="container" >
<local:UserControlButton />
</GroupBox>
and a UserControl element with buttons pops up. if i change it to another one, it works too.
question is how to feed that groupbox dynamically. if i put something like that in my model view:
private UserControl _myControl;
public UserControl MyControl
{
get
{
return _myControl;
}
set
{
_myControl= value;
InvokePropertyChanged("MyControl");
}
}
and change my view XAML to something like:
<GroupBox Header="container" >
<ItemsControl ItemsSource="{Binding MyControl}" />
</GroupBox>
and feed it from command with usercontrol for button or for label: nothing happens, although "MyControl" variable is set and is "invoke property changed"..
Obviously there are many ways to skin this particular cat - but to answer the question of why it doesn't work you need to look into the ItemsSource property of ItemsControl on MSDN.
The items control is designed to show multiple items, provided through an IEnumerable passed to the ItemsSource property. You are passing a UserControl, so the binding will fail.
For your example, I would change the ItemsControl to a ContentControl and bind the content to your MyControl property. This should then work.
<GroupBox Header="container" >
<ContentControl Content="{Binding MyControl}" />
</GroupBox>
However, I would strongly recommend looking into other ways of doing this - having a control in your VM breaks MVVM to my mind. Depending on what you are doing look at data templates - #Sheridan's link in the comments provides an great description of a way to do it.
Couldn't post this as a comment so adding as answer..
Have a look at this:
Implementing an own "Factory" for reusing Views in WPF
It uses DataTemplates but doesn't require the DataTemplate section for each view. If you potentially have a lot of user controls/views you wish to display or you are reusing through multiple views or you are intending to actually dynamically generate a view (versus just loading an existing user control) then this might suite your needs.

How to populate LongListSelector

I'm starting out with C# Windows Phone 8 development and I am trying to write an app which uses a LongListSelector. The app will show a long list of train station names.
I have been looking at some of the samples online, including the MSDN PeopleHub, and PhotoHub samples but they seem very complex. I took the PeopleHub sample and hacked at it a bit until I got it to display a list of stations in a LongListSelector but I wasn't able to find out which item had been selected in the list. The SelectedItem property was just returning the app name when passed to another page and not which item had been picked in the list.
I think I need a basic example of how to add some items to a LongListSelector and then find and pass the selected item to another page. I don't fully understand how the control works, if you have to use some sort of DataBinding with the LongListSelector to populate it or whether it's something simpler like:
LongListSelectorThing.add("trainstationA");
LongListSelectorThing.add("trainstationB");
LongListSelectorThing.add("trainstationC");
etc
Can someone give me some simple basic pointers as to how to populate this control and find out which item the user selects? When I say which item they select, when the LongListSelector grid appears, they click on A for example, and it then shows a list of things beginning with A and they then click on trainstationA, I'd want to be able to detect they've picked trainstationA and pass that info to another page so I can display further information about it.
Sorry for if this seems basic, I'm quite new to this.
Thanks!
Here is a basic example which should help you understand:
First in your Page (xaml file) you define the control LongListSelector (LLS):
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<phone:LongListSelector Name="myLLS" Margin="0">
<phone:LongListSelector.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</phone:LongListSelector.ItemTemplate>
</phone:LongListSelector>
</Grid>
You also declare how its Items will look like. It can be any UIElement - a button, Image, Grid and so on. In the code above I declared that my Item would be a TextBlock which content (text) I've bound to a property 'Name'. I've also given the LLS a name, that I can refer to it later.
In Page.cs code you populate the LLS. Lets create the simple Station class, and populate LLS:
public class Station
{
private string _stationName;
public string Name
{
get { return _stationName; }
set { _stationName = value; }
}
public Station(string station)
{
this.Name = station;
}
}
public partial class MainPage : PhoneApplicationPage
{
ObservableCollection<Station> trainStations = new ObservableCollection<Station>();
public MainPage()
{
InitializeComponent();
myLLS.ItemsSource = trainStations;
trainStations.Add(new Station("Germany"));
trainStations.Add(new Station("France"));
trainStations.Add(new Station("Italy"));
}
}
What is important:
look that in my Station class there is property called 'Name' - that's the one to which content of the TextBlock is bound.
I've created ObservableCollection which is a collection of my Stations - it's similar to a List but when the new item is added or removed the PropertyChanged event is raised and therefore your LongListSelector can be automatically updated when you add a new station.
I've assigned created collection to myLLS.ItemsSource - it means that created LLS will be populated with Items (described in xaml as DataTemplate) and a Source of this items is that collection.
Hope this helps. Happy coding.

I want an hourglass while my IsAsync binding is busy; possible?

I've currently got XAML code like this:
<ListView Name="fileLV" SelectionMode="Extended" ItemsSource="{Binding path=DataContext.SelectedAsset.Files,ElementName=selectionView,IsAsync=True}"/>
That "Files" property takes fifteen seconds to return. The whole time the user is wondering what's going on. I've seen some other code to show the fallback value or use multiple bindings, but those don't rely imply "leave this control alone" like an hourglass over that control would imply.
What I want is to be able name a binding and then bind some other properties to that binding's IsBusy property. I want a trigger to change the cursor on that listview while his binding is busy. Is there any existing WPF framework help for this?
I don't know of any built-in, out-of-the-box solution but there sure are ways to make a nice experience out of it.
I will give you the quick idea of how I would build this and if you need I can come up with the code as well:
Create a "LoadingItem" DataTemplate that would show an progress bar of some kind as an item of your list
Create a "DataTemplateSelector" to switch between the LoadingItem
and the RegularItem of your list.
In your Files property, clear the collection and add an item that
will be shown as LoadingItem (depends on how you built your
DataTemplateSelector logic. Start another thread to scan for files
and fill a return the results in a temporary collection
(BackgroundWorker). When the method returns, you are on the UI
thread again, clear your ItemsSource collection again and fill it
with the results.
For this do not use IsAsync. On the Property use a BackGroundWorker. First return a source with a "working message", start BackGroundWorker, then on the callback supply the real source and call NotifyPropertyChanged. You can even have a progess bar.
I was able to make the DataTemplateSelector work. One caveat was that all the bindings for the ListView need to be enumerable. In my control I added a resource like this:
<UserControl.Resources>
<x:Array x:Key="LoadingTemplate" Type="DataTemplate">
<DataTemplate>...my daisy code...</DataTemplate></x:Array>...
Then I changed my binding to look like this:
<ListView.ItemsSource>
<PriorityBinding>
<Binding Path="DataContext.SelectedAsset.Files" ElementName="selectionView" IsAsync="True"/>
<Binding Source="{StaticResource LoadingTemplate}" />
</PriorityBinding>
</ListView.ItemsSource>
Then I installed this template selector:
public class OverridableDataTemplateSelector: DataTemplateSelector
{
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
return item as DataTemplate ?? base.SelectTemplate(item, container);
}
}

Silverlight, how to make List of a UserControl (In ContentControl?)

i've been banging my head on this for the last hours...
I have a User Control called "DayItem", and i want to show it 48 times in another UserControl called "DayPanel".
Let me mention this is done in MVVM style, but i'm only experiencing, and a straight way would by fine for an answer.
I have an ObservableCollection<DayItem> in the DayPanel model, and in the Xaml there's an <ItemsPresenter />.
if i do
this.ItemsSource = DayItems;
everything show up fine.
but, i wanna be able to use those DayItems in the UI like a list... to support multi-select etc.
so i tried using a ContentControl, and set it's content to the ObservableCollection.
but it just shows the ObservableCollection object's ToString text.
so i guess i need a DataTemplete there...
but why do i need a DataTemple to show a Control?
it's already styled in it's own Xaml, i don't wanna repeat it's styling again.
or maybe i'm totally wrong, anyway i need help :x
Edit:
I got this to work, saying what DataType wasn't necessary or even possible.
and in the code behind i told the listbox, that it's ItemSource was the ObservableCollection.
now i've ran into other problems... ListBox related...
There are Gaps between each control in the ListBox, which messes up the layout
and also i need to figure out a way to select multiple items by dragging...
thanks for the help so fat
First, you need a view model for you DayItem user control. Lets call it DayItemViewModel. Also I suppose you DayPanel also has a view model called something like DayPanelViewModel. Then, you DayPanelViewModel would expose a collection of DayItemViewModel instances:
public class DayPanelViewModel
{
public ObservableCollection<DayItemViewModel> DayItems { get; set; }
}
Then, in your DayPanel.xaml:
<UserControl x:Class="DayPanel"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<UserControl.Resources>
<DataTemplate x:Key="DayItemTemplate"
DataType="{x:Type my:DayItemViewModel}">
<my:DayItem />
</DataTemplate>
</UserControl.Resources>
<Grid>
<ListBox ItemsSource="{Binding DayItems}"
ItemTemplate="{StaticResource DayItemTemplate}" />
</Grid>
</UserControl>
Try using ListBox, since that implements multiselect...
Also it might be wise (for MVVM) if you do not contain DayItems, but DayItemModel's in your DayPanelModel, and set the ListBox's ItemTemplate to present each DayItemModel with a DayItem.

Categories