How to populate GridView with buttons? - xaml

Want to populate the gridview with buttons that all share the same ICommand.
So far I have this:
<DataTemplate x:Key="DataTemplate1">
<Button Content="{Binding Name}"
Command="{Binding NumberCommand}">
</Button>
</DataTemplate>
<GridView SelectionMode="None"
ItemContainerStyle="{StaticResource GridViewItemStyle1}"
ItemTemplate="{StaticResource DataTemplate1}">
<GridView.ItemsPanel>
<ItemsPanelTemplate>
<WrapGrid Orientation="Horizontal"
MaximumRowsOrColumns="3">
</WrapGrid>
</ItemsPanelTemplate>
</GridView.ItemsPanel>
<GridViewItem Content="1"></GridViewItem>
<GridViewItem Content="2"></GridViewItem>
<GridViewItem Content="3"></GridViewItem>
<GridViewItem Content="4"></GridViewItem>
<GridViewItem Content="5"></GridViewItem>
<GridViewItem Content="6"></GridViewItem>
<GridViewItem Content="7"></GridViewItem>
<GridViewItem Content="8"></GridViewItem>
<GridViewItem Content="9"></GridViewItem>
</GridView>
The problem is the GridViewItems are just showing up as text and not buttons. What am I doing wrong?

Instead of setting the items in the Xaml bind them to a collection. With your DataTemplate's binding you'll need a class with Name and NumberCommand properties:
class MyData
{
public string Name { get; set;}
public ICommand MyCommand { get; set; }
}
Then create a collection of the data:
ObservableCollection<MyData> data;
And bind it to the GridView. For simplicity I gave the Grid a name here and set the ItemsSource directly. That's likely good enough for your scenario. You'll obviously want to hook up the Command too, and for more complicated scenarios you could bind via a CollectionViewSource.
public MainPage()
{
this.InitializeComponent();
data = new ObservableCollection<MyData>();
for (int i=1;i<10;i++)
{
data.Add(new MyData() { Name = i.ToString() });
}
myGrid.ItemsSource = data;
}

Related

Refresh SemanticZoom ObservableCollection in ViewModel

When using a SemanticZoom control, is there a way to update the ObservableCollection in the ViewModel after a table change? After making changes to the table in SQLite, within the same page (categories.xaml.cs), the SemanticZoom control does not update. Reloading the page from menu navigation does reload the page with the correct data. If the control just took an ObservableCollection as it's items source, the ObservableCollection could just be refreshed. Using a ViewModel was the only code example I could find for the SemanticZoom control. Thanks in advance!
categories.xaml
<Page.DataContext>
<vm:CategoriesViewModel></vm:CategoriesViewModel>
</Page.DataContext>
<Page.Resources>
<CollectionViewSource x:Name="Collection" IsSourceGrouped="true" ItemsPath="Items" Source="{Binding CategoryGroups}" />
</Page.Resources>
<SemanticZoom Name="szCategories" ScrollViewer.ZoomMode="Enabled">
<SemanticZoom.ZoomedOutView>
<GridView ScrollViewer.IsHorizontalScrollChainingEnabled="False">
<GridView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Group.Name }" Foreground="Gray" Margin="5" FontSize="25" />
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
</SemanticZoom.ZoomedOutView>
<SemanticZoom.ZoomedInView>
<ListView Name="lvCategories" ItemsSource="{Binding Source={StaticResource Collection}}" Tapped="lvCategories_Tapped">
<ListView.ItemTemplate>
<DataTemplate x:DataType="data:Category">
<StackPanel>
<TextBlock Text="{Binding Title}" Margin="5" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<TextBlock Text='{Binding Name}' Foreground="Gray" FontSize="25" Margin="5,5,5,0" />
</StackPanel>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</ListView.GroupStyle>
</ListView>
</SemanticZoom.ZoomedInView>
</SemanticZoom>
categories.xaml.cs
public Categories()
{
this.InitializeComponent();
var collectionGroups = Collection.View.CollectionGroups;
((ListViewBase)this.szCategories.ZoomedOutView).ItemsSource = collectionGroups;
}
CategoriesViewModel.cs
internal class CategoriesViewModel : BindableBase
{
public CategoriesViewModel()
{
CategoryGroups = new ObservableCollection<CategoryDataGroup>(CategoryDataGenerator.GetGroupedData());
}
private ObservableCollection<CategoryDataGroup> _groups;
public ObservableCollection<CategoryDataGroup> CategoryGroups
{
get { return _groups; }
set { SetProperty(ref _groups, value); }
}
}
public abstract class BindableBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
{
if (object.Equals(storage, value)) return false;
storage = value;
this.OnPropertyChanged(propertyName);
return true;
}
protected void OnPropertyChanged(string propertyName)
{
var eventHandler = this.PropertyChanged;
if (eventHandler != null)
{
eventHandler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
SymanticZoom.cs
internal class CategoryDataGroup
{
public string Name { get; set; }
public List<CategoryData> Items { get; set; }
}
internal class CategoryData
{
public CategoryData(string grp, string title)
{
Grp = grp;
Title = title;
}
public string Grp { get; private set; }
public string Title { get; private set; }
}
internal class CategoryDataGenerator
{
private static List<CategoryData> _data;
public static List<CategoryDataGroup> GetGroupedData()
{
if (_data != null)
_data.Clear();
GenerateData();
return _data.GroupBy(d => d.Grp[0],
(key, items) => new CategoryDataGroup() { Name = key.ToString(), Items = items.ToList() }).ToList();
}
private static void GenerateData()
{
ObservableCollection<Category> ocCategories = new ObservableCollection<Category>();
SQLiteManager.Categories.Select(ocCategories);
_data = new List<CategoryData>();
foreach (var temp in ocCategories)
{
_data.Add(new CategoryData(temp.Name.Substring(0,1), temp.Name));
}
}
}
The zoomed-in view and zoomed-out view should be synchronized, so if a user selects a group in the zoomed-out view, the details of that same group are shown in the zoomed-in view. You can use a CollectionViewSource or add code to synchronize the views.
For more info, see Semantic zoom.
We can use CollectionViewSource control in our page, it provides a data source that adds grouping and current-item support to collection classes. Then we can bind the GridView.ItemSource and ListView.ItemSource to the CollectionViewSource. When we set new data to the CollectionViewSource, the GridView in SemanticZoom.ZoomedOutView and ListView in SemanticZoom.ZoomedInView will be updated.
xmlns:wuxdata="using:Windows.UI.Xaml.Data">
<Page.Resources>
<CollectionViewSource x:Name="ContactsCVS" IsSourceGrouped="True" />
<DataTemplate x:Key="ZoomedInTemplate" x:DataType="data:Contact">
<StackPanel Margin="20,0,0,0">
<TextBlock Text="{x:Bind Name}" />
<TextBlock Text="{x:Bind Position}" TextWrapping="Wrap" HorizontalAlignment="Left" Width="300" />
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="ZoomedInGroupHeaderTemplate" x:DataType="data:GroupInfoList">
<TextBlock Text="{x:Bind Key}"/>
</DataTemplate>
<DataTemplate x:Key="ZoomedOutTemplate" x:DataType="wuxdata:ICollectionViewGroup">
<TextBlock Text="{x:Bind Group.(data:GroupInfoList.Key)}" TextWrapping="Wrap"/>
</DataTemplate>
</Page.Resources>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<StackPanel>
<SemanticZoom x:Name="Control1" Height="500">
<SemanticZoom.ZoomedInView>
<GridView ItemsSource="{x:Bind ContactsCVS.View,Mode=OneWay}" ScrollViewer.IsHorizontalScrollChainingEnabled="False" SelectionMode="None"
ItemTemplate="{StaticResource ZoomedInTemplate}">
<GridView.GroupStyle>
<GroupStyle HeaderTemplate="{StaticResource ZoomedInGroupHeaderTemplate}" />
</GridView.GroupStyle>
</GridView>
</SemanticZoom.ZoomedInView>
<SemanticZoom.ZoomedOutView>
<ListView ItemsSource="{x:Bind ContactsCVS.View.CollectionGroups}" SelectionMode="None" ItemTemplate="{StaticResource ZoomedOutTemplate}" />
</SemanticZoom.ZoomedOutView>
</SemanticZoom>
</StackPanel>
</Grid>

How to make the textblock text centered in a button with image and text if no image is available in WPF

I have a button with the image and textblock.
Buttons are created dynamically based on the values from the database.
Now for a particular value text is present and no image is there I want to show that text in the center of the button (horizontally and vertically), but it is not working.
Please find the xaml below:
<ItemsControl ItemsSource="{Binding CategoriesList}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Width="100" Margin="5" HorizontalContentAlignment="Center" VerticalContentAlignment="Center">
<Button.Template>
<ControlTemplate>
<Border CornerRadius="10" Background="Maroon">
<StackPanel Orientation="Vertical">
<Image Source="{Binding CategoryImagePath}" Height="50"></Image>
<TextBlock Text="{Binding CategoryName}" Height="20" HorizontalAlignment="Center" VerticalAlignment="Center"></TextBlock>
</StackPanel>
</Border>
</ControlTemplate>
</Button.Template>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
If no image is available I want to show only the text on the button but it should be centered.
If there is Image then I make the Image and text both displayed but when the Image is not available the text is getting displayed but it is not in the center It moves to the top portion of the button.
You can use a DataTemplateSelector to select different templates depending on whether you have an image. Such a selector might look like this:
public sealed class ButtonTemplateSelector : DataTemplateSelector
{
/// <summary>
/// Gets or sets the <see cref="DataTemplate"/> to use when we have an image.
/// The value is set in XAML.
/// </summary>
public DataTemplate ImageTemplate { get; set; }
/// <summary>
/// Gets or sets the <see cref="DataTemplate"/> to use when we don't have an image.
/// The value is set in XAML.
/// </summary>
public DataTemplate NoImageTemplate { get; set; }
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
Category category = item as Category;
if (category != null)
{
return category.CategoryImagePath == null ? NoImageTemplate : ImageTemplate;
}
return base.SelectTemplate(item, container);
}
}
I'm assuming a model object something like this:
public class Category
{
public string CategoryImagePath { get; set; }
public string CategoryName { get; set; }
}
Create and initialize a ButtonTemplateSelector resource in your XAML, then reference it from your ItemsControl:
<Window
x:Class="WPF.MainWindow"
x:Name="self"
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:wpf="clr-namespace:WPF"
mc:Ignorable="d"
Title="MainWindow"
Height="350"
Width="525">
<Grid>
<Grid.Resources>
<wpf:ButtonTemplateSelector x:Key="ButtonTemplateSelector">
<wpf:ButtonTemplateSelector.ImageTemplate>
<DataTemplate DataType="wpf:Category">
<Button
Width="100"
Margin="5"
HorizontalContentAlignment="Center"
VerticalContentAlignment="Center">
<Button.Template>
<ControlTemplate>
<Border CornerRadius="10" Background="Maroon">
<StackPanel Orientation="Vertical">
<Image
Source="{Binding CategoryImagePath}"
Height="50" />
<TextBlock
Foreground="White"
Text="{Binding CategoryName}"
Height="20"
HorizontalAlignment="Center"
VerticalAlignment="Center" />
</StackPanel>
</Border>
</ControlTemplate>
</Button.Template>
</Button>
</DataTemplate>
</wpf:ButtonTemplateSelector.ImageTemplate>
<wpf:ButtonTemplateSelector.NoImageTemplate>
<DataTemplate DataType="wpf:Category">
<Button
Width="100"
Margin="5"
HorizontalContentAlignment="Center"
VerticalContentAlignment="Center">
<Button.Template>
<ControlTemplate>
<Border
CornerRadius="10"
Background="Maroon"
Height="70">
<TextBlock
Foreground="White"
Text="{Binding CategoryName}"
Height="20"
HorizontalAlignment="Center"
VerticalAlignment="Center" />
</Border>
</ControlTemplate>
</Button.Template>
</Button>
</DataTemplate>
</wpf:ButtonTemplateSelector.NoImageTemplate>
</wpf:ButtonTemplateSelector>
</Grid.Resources>
<ItemsControl
DataContext="{Binding ElementName=self}"
ItemsSource="{Binding CategoriesList}"
ItemTemplateSelector="{StaticResource ButtonTemplateSelector}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</Grid>
</Window>
For completeness, the code-behind for the window:
public partial class MainWindow
{
public MainWindow()
{
InitializeComponent();
}
public IEnumerable<Category> CategoriesList { get; } = new List<Category>
{
new Category { CategoryName = "First", CategoryImagePath = "/Assets/Square.bmp" },
new Category { CategoryName = "Second", CategoryImagePath = null },
};
}
This shows up as follows, which I think is what you're asking:

How to Bind IsSelected Property in DataTemplate of ListViewItem

Well, I want to make the ListViewItem in UWP ,that'll be changing his view on selecting. So I need to change the Visibility property of some elements of ListViewItem on selecting.
I found some way to do this with making custom Style of ListViewItem and Binding IsSelected property like this:
<Style x:Key="VehicleListViewItemStyle" TargetType="ListViewItem" >
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListViewItem">
<Grid Background="Gray" Margin="1">
<Border Margin="2" Padding="10" Background="Gray" >
<StackPanel>
<ContentPresenter x:Name="Presenter1" />
<StackPanel Orientation="Horizontal" Background="Transparent" Margin="-10,0,-9,-9" VerticalAlignment="Center" x:Name="infoPanel"
Visibility="{Binding IsSelected, RelativeSource={RelativeSource Mode=TemplatedParent}, Converter={StaticResource BooleanToVisibilityConverter}}">
<TextBlock Text="{Binding DeviceID}/> </StackPanel>
</StackPanel>
</Border>
<Border BorderThickness="1" BorderBrush="Orange" Visibility="{Binding IsSelected, RelativeSource={RelativeSource Mode=TemplatedParent}, Converter={StaticResource BooleanToVisibilityConverter}}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Its working good, but with this way I cant bind the DeviceID text.
Another one way is creating DataTemplate like this:
<DataTemplate x:Key="monitoringListViewItem" x:Name="item">
<Grid Background="Gray" Margin="1" Width="300" >
<StackPanel>
<ContentPresenter x:Name="Presenter"/>
<StackPanel Orientation="Horizontal">
<Image Source="/Assets/14th_crane_stop.png" Height="50" Width="50" Stretch="Uniform"/>
<StackPanel Orientation="Vertical" Margin="25,0,0,0 "
Visibility="{Binding IsSelected, RelativeSource={RelativeSource Mode=TemplatedParent}, Converter={StaticResource BooleanToVisibilityConverter}}"
>
<TextBlock Text="{Binding DeviceID}" Style="{StaticResource VehicleTextStyle}"/>
<TextBlock Text="{Binding Mark}" Style="{StaticResource VehicleTextStyle}"/>
</StackPanel>
</StackPanel>
</StackPanel >
</Grid>
</DataTemplate>
Now I can bind the text correctly, but cant bind the IsSelected property. I've tried to do this with different Modes, but it still doesn't work because I cant use the TemplatedParent key inside the DataTemplate.
So I need some answers:
-can I bind the text in first way and how can I do that?
-how can I bind the IsSelected property in the second way?
I don't recommend changing the ListViewItem template since you lose all the bells and whistles it provides (selection appearance, ability to be checked, etc).
Using Mode=TemplatedParent in the second snippet won't work because the templated parent from that context is a ListViewItemPresenter, not the ListViewItem (which is the presenter's parent).
It looks like what you're trying to do is to show additional information in the list item when it is selected.
Single selection
C# classes
public class NotifyPropertyChangedBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
protected bool SetProperty<T>(ref T property, T value, [CallerMemberName] string propertyName = "")
{
if (EqualityComparer<T>.Default.Equals(property, value))
{
return false;
}
property = value;
OnPropertyChanged(propertyName);
return true;
}
}
public class Item : NotifyPropertyChangedBase
{
private string text;
public string Text
{
get { return text; }
set { SetProperty(ref text, value); }
}
private bool isSelected;
public bool IsSelected
{
get { return isSelected; }
set { SetProperty(ref isSelected, value); }
}
}
public class MainPageViewModel : NotifyPropertyChangedBase
{
public List<Item> Items { get; set; }
private Item selectedItem;
public Item SelectedItem
{
get { return selectedItem; }
set
{
if (selectedItem != value)
{
if (selectedItem != null)
{
selectedItem.IsSelected = false;
}
SetProperty(ref selectedItem, value);
if (selectedItem != null)
{
selectedItem.IsSelected = true;
}
}
}
}
public MainPageViewModel()
{
Items = new List<Item>()
{
new Item() { Text = "Apple" },
new Item() { Text = "Banana" },
};
}
}
MainPage.xaml
<ListView ItemsSource="{Binding Items}" SelectedItem="{Binding SelectedItem, Mode=TwoWay}">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate x:DataType="local:Item">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Text}"/>
<!-- x:Bind doesn't require visibility converter if min SDK is targeting Anniversary update -->
<TextBlock Text="I'm selected!" Grid.Column="1" Visibility="{x:Bind IsSelected, Mode=OneWay}"/>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
MainPage.xaml.cs
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
DataContext = new MainPageViewModel();
}
}
Multiple selection
Same as single selection but with the following changes:
MainPageViewModel
public class MainPageViewModel : NotifyPropertyChangedBase
{
public List<Item> Items { get; set; }
public MainPageViewModel()
{
Items = new List<Item>()
{
new Item() { Text = "Apple" },
new Item() { Text = "Banana" },
};
}
public void SelectionChanged(object sender, SelectionChangedEventArgs e)
{
foreach (Item item in e.RemovedItems)
{
item.IsSelected = false;
}
foreach (Item item in e.AddedItems)
{
item.IsSelected = true;
}
}
}
MainPage.xaml
<ListView ItemsSource="{Binding Items}" SelectionChanged="{x:Bind ViewModel.SelectionChanged}" SelectionMode="Extended">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate x:DataType="local:Item">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Text}"/>
<TextBlock Text="I'm selected!" Grid.Column="1" Visibility="{x:Bind IsSelected, Mode=OneWay}"/>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
MainPage.xaml.cs
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
DataContext = new MainPageViewModel();
}
public MainPageViewModel ViewModel => (MainPageViewModel)DataContext;
}
To answer the actual question in the topic: Yes you can bind ListViewItem.IsSelected quite easily. Make sure your DataTemplate content is wrapped in a ListViewItem.
<ListView.ItemTemplate>
<DataTemplate x:DataType="local:MyItemViewModel">
<ListViewItem IsSelected="{Binding IsSelected, Mode=TwoWay}">
<Grid>
// template content
</Grid>
</ListViewItem>
</DataTemplate>
</ListView.ItemTemplate>
Note that there are numerous Q&As here for WPF saying this does not work. It does work with UWP (at least as of SDK 10.0.19041). The WPF answers suggest binding it in the ItemsPanelTemplate or in a ResourceDictionary. For some reason this does not work in UWP.
I did the following and it worked fine
<ListView.ItemTemplate>
<DataTemplate x:DataType="local:PersonalProfile">
<ListViewItem x:Name="root">
<Grid >
<!-- .... -->
<CheckBox IsChecked="{Binding ElementName=root, Path=IsSelected, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</Grid>
</ListViewItem>
</DataTemplate>
</ListView.ItemTemplate>

How to bind to a list of images

I have changed my code to this:
the view:
<phone:Panorama.ItemTemplate>
<DataTemplate>
<ScrollViewer Width="800" HorizontalContentAlignment="Left" Margin="0,50,0,0">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ListBox x:Name="list_of_images" ItemsSource="{Binding ImagesUrls}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<Image Width="300" Height="300" Source="{Binding}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ListBox>
<TextBlock Text="{Binding Title}"
Grid.Row="1"
Loaded="TextBlock_Loaded_1"
Margin="50,0,0,0"
FontSize="23"
TextWrapping="Wrap"
Width="360"
HorizontalAlignment="Left"
Foreground="Black"/>
<TextBox Text="{Binding ContactEmail}"
Grid.Row="2"
BorderBrush="Black"
Width="340"
HorizontalAlignment="Left"
BorderThickness="1"
Margin="40,0,0,0"
Foreground="Black" />
<TextBlock Text="{Binding Body}"
Grid.Row="3"
TextWrapping="Wrap"
Foreground="Black"
Margin="50,5,0,0"
Width="360"
HorizontalAlignment="Left"
FontSize="20" />
</Grid>
and I build a new object with different properties, with one of the properties being a list of strings which represents the imageurls, but I cannot get the images to show?
I have attached screenshots, what in my xaml must I change so that I can display the images, cause at the moment it doesn't show any images but it shows all the other details
code for populating collection:
ObservableCollection<ClassifiedAds> klasifiseerd_source = new ObservableCollection<ClassifiedAds>();
ImagesClassifieds new_Classifieds = new ImagesClassifieds();
ObservableCollection<string> gallery_images = new ObservableCollection<string>();
new_Classifieds.Title = klasifiseerd_source[0].Title;
new_Classifieds.ContactEmail = klasifiseerd_source[0].ContactEmail;
new_Classifieds.Body = klasifiseerd_source[0].Body;
foreach (var item in klasifiseerd_source[0].Gallery.Images)
{
var deserialized = JsonConvert.DeserializeObject<GalleryImages>(item.ToString());
gallery_images.Add(deserialized.ImageUrl);
//new_Classifieds.ImageUrls.Add(deserialized.ImageUrl);
}
new_Classifieds.ImageUrls = gallery_images;
// classifiedPanorama.ItemsSource = new_list;
new_Classifieds_list.Add(new_Classifieds);
classifiedPanorama.ItemsSource = new_Classifieds_list;
public class ImagesClassifieds
{
public string Title { get; set; }
public string ContactEmail { get; set; }
public string Body { get; set; }
public ObservableCollection<string> ImageUrls { get; set; }
}
here is the imageurl format, this works (in another par tof my app I simply bind to 1 image in this format and it works perfectly)
Depending on whether you want to just display a list of images or if you also want to be able to select them, you may either choose an ItemsControl or a ListBox. In both case you have to define a DataTemplate that controls how each item is displayed.
<ItemsControl ItemsSource="{Binding Images}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Image Source="{Binding}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
or
<ListBox ItemsSource="{Binding Images}">
<ListBox.ItemTemplate>
<DataTemplate>
<Image Source="{Binding}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Then you should think about how you define your item class. Just in case you want to be able to dynamically add or remove images from the list and let the UI be automatically updated, you should use an ObservableCollection as container type. As there is a built-in type conversion from string to ImageSource (the type of the Image control's Source property), you may simply use an ObservableCollection<string>.
public class Gal
{
public ObservableCollection<string> Images { get; set; }
}
You may create an instance of that class like so:
var gallery = new Gal();
gallery.Images = new ObservableCollection<string>(
Directory.EnumerateFiles(#"C:\Users\Public\Pictures\Sample Pictures", "*.jpg"));
Now you may directly bind to that instance by simply setting the DataContext of your Window (or wherever the image list should be shown) to this instance:
DataContext = gallery;
Note the binding declaration in the ItemsControl or ListBox above was {Binding Images}. If you really have to have a property Gallery (which I assume is in MainWindow) and bind like {Binding Gallery.Images} you may set the DataContext like this:
DataContext = this;
so I basically created a loaded event on the listbox and added this code
private void list_of_images_Loaded_1(object sender, RoutedEventArgs e)
{
ListBox lst = (ListBox)sender;
lst.ItemsSource = new_Classifieds_list[0].ImageUrls;
}
which binds the itemssource of the listbox to the list of imageurls. since I cannot access the listbox from codebehind due to it being inside the panorama's itemstemplate

XAML: Bind to a single child in a collection

I have a class like this:
public class Contest {
List<ContestTeam> Teams { get; set; }
}
public class ContestTeam {
int TeamId { get; set; }
int FinalScore { get; set; }
}
And my view model looks like this:
public class ScheduleViewModel {
int CurrentTeamId { get; }
List<Contest> Schedule { get; }
}
My XAML looks something like this:
<ListBox ItemsSource="{Binding Path=Schedule}">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" Orientation="Horizontal">
<!-- TODO: DataContext is currently hard coded to 425 - this needs to be fixed -->
<StackPanel Orientation="Horizontal"
DataContext="{Binding Path=Teams, Converter={StaticResource CurrentTeam}, ConverterParameter=425}">
<TextBlock Text="{Binding SomeProperty}" />
</StackPanel>
<Button Content="Delete" />
</StackPanel>
<ListBox Grid.Row="1" ItemsSource="{Binding Teams}">
<!-- a list of all the teams -->
</ListBox>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Essentially, so I can continue developing I created a value converter (CurrentTeam) and hard-coded the TeamId as the ConverterParameter so I can continue developing the view. But I'm at a bit of an impasse. Is there a way (using XAML) to bind the DataContext of the StackPanel to the ContestTeam in the Teams collection that matches the value of ScheduleViewModel.TeamId?
My last recourse will be to just use the Loaded event of the StackPanel to set it's DataContext in the code-behind, but I'd like to avoid that. Are there any XAML Ninjas out there who can help me figure this out?
There's no way to make queries in XAML bindings. In this case, since you really have two input values (Teams and CurrentTeamId), just use a MultiBinding and a IMultiValueConverter:
public class FindContestByIdConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType,
object parameter, CultureInfo culture)
{
var teams = (IEnumerable<ContestTeam>)values[0];
var teamId = (int)values[1];
return teams.Single(t => t.TeamId == teamId);
}
// ConvertBack that throws NotSupportedException
...
}
and then XAML:
<StackPanel>
<StackPanel.DataContext>
<MultiBinding Converter="{StaticResource FindContestByIdConverter}">
<Binding Path="Teams"/>
<Binding Path="CurrentTeamId" />
</MultiBinding>
</StackPanel.DataContext>
...
</StackPanel>