i want to bind a list of images to a stackpanel which is inside a DataGrid.RowDetailsTemplate.
My class structure is as follows:
public class A
{
private List<MyImage> _images = new List<MyImage>();
public List<MyImage> Images { get; set; }
public string Name { get; set; }
public void AddImage(byte[] src) { ... }
}
public class MyImage
{
public BitmapImage Image { get; set; }
public byte[] RawData { get; set; }
}
In my main class i have a list of A:
public List<A> AList { get; set; }
dataGrid1.ItemsSource = AList;
dataGrid1.DataContext = AList;
All i want to do is to display the Name property of an element in a DataGridTextColumn and all images stored in the Images property in the RowDetails.
My xaml is:
<DataGrid name="dataGrid1">
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Path=Name}"/>
</DataGrid.Columns>
<DataGrid.RowDetailsTemplate>
<DataTemplate>
<StackPanel DataContext="{Binding Path=Images}">
<Image Source="{Binding Path=RawData}"/>
</StackPanel>
</DataTemplate>
</DataGrid.RowDetailsTemplate>
All i get to see is just one image although there are some more stored in Images. Any ideas?
Ok, so the solution of this problem was the use of ContentPresenter combined with a converter.
Now my XAML looks like this:
<DataGrid name="dataGrid1">
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Path=Name}"/>
</DataGrid.Columns>
<DataGrid.RowDetailsTemplate>
<DataTemplate>
<ContentPresenter Content="{Binding Images, Converter={StaticResource ImageCollectionConverter}}"/>
</DataTemplate>
</DataGrid.RowDetailsTemplate>
</DataGrid>
And the corresponding converter class:
public class ImageCollectionConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
List<MyImage> images = value as List<MyImage>;
if (images != null)
{
StackPanel stack = new StackPanel();
stack.Orientation = Orientation.Horizontal;
foreach (DesignImage img in images)
{
Image image = new Image();
image.Source = img.Image;
stack.Children.Add(image);
}
return stack;
}
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Related
I have a carouselview in my Xam.Forms project. I have also created 3 ContentViews (one for each DataTemplate). My template selector class looks like this
public class DashboardTemplateSelector : DataTemplateSelector
{
public DataTemplate QuickMessageTemplate { get; set; }
public DataTemplate DataViewTemplate { get; set; }
public DataTemplate LastUsedTemplate { get; set; }
public DashboardTemplateSelector()
{
QuickMessageTemplate = new DataTemplate(typeof(QuickMessage));
DataViewTemplate = new DataTemplate(typeof(DataView));
LastUsedTemplate = new DataTemplate(typeof(LastusedView));
}
protected override DataTemplate OnSelectTemplate(object item, BindableObject container)
{
var cv = (Frame)item;
DataTemplate rv = null;
switch(cv.ClassId)
{
case "data":
rv = DataViewTemplate;
break;
case "quick":
rv= QuickMessageTemplate;
break;
case "last":
rv = LastUsedTemplate;
break;
}
return rv;
}
Nothing out of the ordinary and I have the ClassId on each frame within the ContentView set to match the name in the switch.
When I build the app and run it, it looks fine but there is nothing in the CarouselView and a break point set in the OnSelectTemplate method (the first line) is never hit.
My XAML for the carouselview is this
<ContentPage.Resources>
<ResourceDictionary>
<local:DashboardTemplateSelector x:Key="templateSelector" />
</ResourceDictionary>
</ContentPage.Resources>
<CarouselView Grid.Row="2" PeekAreaInsets="12" Margin="8" ItemTemplate="{StaticResource templateSelector}" HeightRequest="200" BackgroundColor="BlueViolet" />
The view shows (can see the background colour) but nothing in the view itself.
I've only checked this on a physical android device and not on iOS, but I'm guessing the same retult. My guess is that I can't cast to a Frame for the object, but I'm not sure.
To populate data, you have to set an ItemsSource via DataBinding or Code-Behind.
Then your DataTemplateSelector will be hit with each item of the ItemsSource as object item. Please see the documentation here: https://learn.microsoft.com/fr-fr/xamarin/xamarin-forms/user-interface/carouselview/layout
<CarouselView Grid.Row="2" PeekAreaInsets="12" Margin="8" ItemsSource="{Binding ViewsViewModels} ItemTemplate="{StaticResource templateSelector}" HeightRequest="200" BackgroundColor="BlueViolet" />
The item in OnSelectTemplate is itemsource data. Change the item to container and use CarouselView instead of Frame. Do not forget to set the ClassId of your CarouselView.
The whold project for your reference.
Xaml:
<ContentPage.Resources>
<ResourceDictionary>
<local:DashboardTemplateSelector x:Key="templateSelector" />
</ResourceDictionary>
</ContentPage.Resources>
<CarouselView
Grid.Row="2"
Margin="8"
ClassId="data"
HeightRequest="200"
ItemTemplate="{StaticResource templateSelector}"
ItemsSource="{Binding infos}"
PeekAreaInsets="12" />
Code behind:
public partial class MainPage : ContentPage
{
public ObservableCollection<Info> infos { get; set; }
public MainPage()
{
InitializeComponent();
infos = new ObservableCollection<Info>()
{
new Info{ DataViewText="DataViewText1", LastusedViewText="LastusedViewText1", QuickMessageText="QuickMessageText1"},
new Info{ DataViewText="DataViewText2", LastusedViewText="LastusedViewText2", QuickMessageText="QuickMessageText2"},
new Info{ DataViewText="DataViewText3", LastusedViewText="LastusedViewText3", QuickMessageText="QuickMessageText3"},
new Info{ DataViewText="DataViewText4", LastusedViewText="LastusedViewText4", QuickMessageText="QuickMessageText4"},
};
this.BindingContext = this;
}
}
public class Info
{
public string QuickMessageText { get; set; }
public string DataViewText { get; set; }
public string LastusedViewText { get; set; }
}
QuickMessage, DataView and LastusedView is a contentview with label which binding a text.
QuickMessage:
<Label Text="{Binding QuickMessageText}" />
DataView:
<Label Text="{Binding DataViewText}" />
LastusedView:
<Label Text="{Binding LastusedViewText}" />
DashboardTemplateSelector:
public class DashboardTemplateSelector : DataTemplateSelector
{
public DataTemplate QuickMessageTemplate { get; set; }
public DataTemplate DataViewTemplate { get; set; }
public DataTemplate LastUsedTemplate { get; set; }
public DashboardTemplateSelector()
{
QuickMessageTemplate = new DataTemplate(typeof(QuickMessage));
DataViewTemplate = new DataTemplate(typeof(DataView));
LastUsedTemplate = new DataTemplate(typeof(LastusedView));
}
protected override DataTemplate OnSelectTemplate(object item, BindableObject container)
{
var cv = (CarouselView)container;
DataTemplate rv = null;
switch (cv.ClassId)
{
case "data":
rv = DataViewTemplate;
break;
case "quick":
rv = QuickMessageTemplate;
break;
case "last":
rv = LastUsedTemplate;
break;
}
return rv;
}
}
I have an api URL here which provides the response below.
Json
{"status":200,"message":"Operation done successfully","data":{"enableNext":false,"products":[{"image":"http://bresa.lazyhost.in/upload/product/1/Tjtqr8.jpg","id":1,"code":"PROi6v8X5261","name":"Spandex Stretch Lounge Sofa with Couch Seat Cover SlipCover","description":"nice sofa , very comfortable for sitting in Living room","tags":"chairs, sofa, ","price":"1000.00","quantity":100,"images":["http://bresa.lazyhost.in/upload/product/1/Tjtqr8.jpg"]}]}}
The model class for above response is below.
CarouselModel
namespace CameliaMaison.Models
{
public partial class CarouselModel
{
[JsonProperty("status")]
public long Status { get; set; }
[JsonProperty("message")]
public string Message { get; set; }
[JsonProperty("data")]
public List<CarouselData> Carouseldata { get; set; }
}
public partial class CarouselData
{
[JsonProperty("id")]
public long Id { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("path")]
public string Path { get; set; }
}
}
The ViewModel
namespace CameliaMaison.ViewModels
{
public class CarouselImagesViewModel
{
private List<CarouselData> items;
public List<CarouselData> Items
{
get { return items; }
set
{
items = value;
}
}
public CarouselImagesViewModel()
{
var responseObj = MyHTTP.GetApiData().Result;
foreach(CarouselData item in responseObj){
Items.Add(item);
}
}
}
public class MyHTTP
{
public static async Task<List<CarouselData>> GetApiData()
{
HttpClient httpClient = new HttpClient();
HttpResponseMessage response = await httpClient.GetAsync("http://bresa.lazyhost.in/api/banners");
CarouselModel categoriesData = new CarouselModel();
var content = await response.Content.ReadAsStringAsync();
if (response.StatusCode == System.Net.HttpStatusCode.OK)
{
categoriesData = JsonConvert.DeserializeObject<CarouselModel>(await response.Content.ReadAsStringAsync());
}
return categoriesData.Carouseldata;
}
}
}
I need to parse the JSON and store in Model Object and populate the data in the listview via MVVM.However, there seems to be something wrong with the implementation. I am unable to figure out and the content page class is below.
ProductsListPage.xaml.cs
public partial class ProductsListPage : ContentPage
{
public ProductsListPage()
{
InitializeComponent();
}
}
ProductsListPage.xaml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:constants="clr-namespace:CameliaMaison;assembly=CameliaMaison"
x:Class="CameliaMaison.Views.ProductsListPage"
Title="Employee List">
<ListView x:Name="ListView">
<ListView.ItemTemplate>
<DataTemplate>
<TextCell Text="ABC" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ContentPage>
First you must have a button on your view page.
<Button Command = "{Binding GetInfoCommand}" />
Than you must create a view-model. Something like this :
public class CarouselModel: INotifyPropertyChanged
{
//get a reference from the another class where you make the call to get the json
Tasks ts = new Tasks();
List<CarouselData> _carouseldata ;
public List<CarouselData> CarouselData
{
get { return _carouseldata ; }
set
{
if (value == _carouseldata ) return;
_carouseldata = value;
OnPropertyChanged();
}
}
public ICommand GetIInfoComand
{
get
{
return new Command(async()=>
{
CarouselData= await _apiServices.GetInfo();
});
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Than in your tasks.cs class you must consume the call where you get the json. This simple. :)
==edit==
In your view you must replace your textcell.Something like this :
<ListView x:Name="ListView">
<ListView.ItemTemplate>
<DataTemplate>
<TextCell Text="{Binding Name }" />
</DataTemplate>
</ListView.ItemTemplate>
Also you get a reference of your view model and place it before the listview :
<ContentPage.BindingContext>
<CarouselModel/>
</ContentPage.BindingContext>
I'm working with Xamarin Forms and I want to load a listview with imagecells, also I'm binding the data with XAML.
My webservice provider returns me the binary code of the images, ¿someone knows how I can convert this to show the image?
This is my XAML listview template:
<ListView x:Name="lv_products">
<ListView.ItemTemplate>
<DataTemplate>
<ImageCell
Text="{Binding Name}"
Detail="{Binding Description}"
ImageSource="{Binding Image, Converter={StaticResource cnvImage}}">
</ImageCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
And the converter:
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value != null && value is byte[])
{
byte[] binary = (byte[])value;
Image image = new Image();
image.Source = ImageSource.FromStream(() => new MemoryStream(binary));
return image.Source;
}
return null;
}
But picture appears empty (transparent).
Here is working converter. I use MemoryStream and ImageSource.FromStream.
public class ByteImageConverter : IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
var image = value as byte[];
if (image == null)
return null;
return ImageSource.FromStream(() => new MemoryStream(image));
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Just as sample here is my page
public partial class Page : ContentPage
{
readonly ViewModel _bindingContext = new ViewModel();
public Page()
{
InitializeComponent();
BindingContext = _bindingContext;
LoadImage();
}
private async void LoadImage()
{
var assembly = typeof (ByteImageConverter).GetTypeInfo().Assembly;
var stream = assembly
.GetManifestResourceStream("TestImage.c5qdlJqrb04.jpg");
using (var ms = new MemoryStream())
{
await stream.CopyToAsync(ms);
_bindingContext.Image = ms.ToArray();
}
}
}
public class ViewModel : INotifyPropertyChanged
{
private byte[] _image;
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(
[CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
public byte[] Image
{
get { return _image; }
set
{
_image = value;
OnPropertyChanged();
}
}
}
If you have a URL which returns the image file, why aren't you just use the URL as ImageSource ?
<ImageCell Text="{Binding Name}"
Detail="{Binding Description}"
ImageSource="{Binding ImageURL}">
</ImageCell>
You can convert byte array to Bitmap image, and assign that bitmap to the ImageView. I did this in Xamarin.Android, dnt know will it work with forms or not.
bitmap = BitmapFactory.DecodeByteArray(byte, 0, byte.Length);
Then use imageView.FromBitmap() to display this image.
Db class propertys
[Serializable]
[EnableClientAccess()]
public class DbPersonelJobDetail
{
public DbPersonelJobDetail()
{
}
[Key]
public Guid PersonelID { get; set; }
public Guid JobID { get; set; }
public string JobName { get; set; }
public string Adi { get; set; }
}
DomainServices Linq Query
public IQueryable<DTO.DbPersonelJobDetail> GetPersonelJobTreeList()
{
IQueryable<DTO.DbPersonelJobDetail> result = from p in ObjectContext.SPA_PersonelJobDetail
join c in ObjectContext.SPA_PersonelJob on p.PersonelJobID equals c.ID
select new DTO.DbPersonelJobDetail()
{
JobID=p.PersonelJobID,
JobName = c.JobName,
PersonelID=p.ID,
Adi=p.Adi
};
return result.AsQueryable();
}
BindTreeList methot
public void BindTreeList()
{
loadOP = context.Load(context.GetPersonelJobTreeListQuery(), false);
loadOP.Completed += loadOP_Completed;
}
void loadOP_Completed(object sender, EventArgs e)
{
treeListPersonel.ItemsSource = loadOP.Entities;
}
I'm Treeview of binding BindTreeList() methot.
The following, as in the picture. HierarchicalDataTemplate Itemsource binding howto?
Could you make an example?
I could not :(
Waiting for your ideas...
Pucture
Load first lavel nodes.
In HierarchicalDataTemplate bind ItemsSource to LoadChildsConverter
<riaControls:DomainDataSource x:Name="MyData" QueryName="GetFirstLavel"
AutoLoad="True" LoadSize="50">
<riaControls:DomainDataSource.DomainContext>
<web:AdvDomainContext />
</riaControls:DomainDataSource.DomainContext>
</riaControls:DomainDataSource>
<sdk:TreeView ItemsSource="{Binding}" DataContext="{Binding ElementName=MyData, Path=Data}">
<sdk:TreeView.ItemTemplate>
<sdk:HierarchicalDataTemplate
ItemsSource="{Binding Converter={StaticResource TreeViewCollectionConverter}}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding CODE}" />
<TextBlock Text="{Binding DESC}" />
</StackPanel>
</sdk:HierarchicalDataTemplate>
</sdk:TreeView.ItemTemplate>
</sdk:TreeView>
TreeViewCollectionConverter.cs
public class TreeViewR5OBJECTCollectionConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
ObservableCollection<Node> nodeList = new ObservableCollection<Node>();
if (value != null)
{
AdvDomainContext ctx = new AdvDomainContext();
Node parentNode = (Node)value;
ctx.Load(ctx.GetChildsQuery(parentNode), iop =>
{
foreach (var o in iop.Entities)
nodeList.Add(o);
}, null);
}
return nodeList;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return null;
}
}
In AdvDomainService.cs
must have
public IQueryable<Node> GetFirstLavel()
to return first level nodes
and
public IQueryable<Node> GetChilds(Node ParentNode)
to return childs of ParentNode
So I have a simple RSS-reader, that has a feed that gets updated when the app is started. How can I add functionality that keeps the new unread items in a different color? I would like to make it visible for the user which posts are new since last time he/she opened the app.
Presuming you have a model something like;
public class RSSItem {
public bool IsUnread { get; set; }
public string Title { get; set; }
}
You'll want to bind the ForegroundColor of a TextBlock to your IsUnread property using a IValueConverter that takes a bool and returns a Color. So your XAML might look like;
<phone:PhoneApplicationPage.Resources>
<converters:UnreadForegroundConverter x:Key="UnreadForegroundConverter" />
</phone:PhoneApplicationPage.Resources>
<ListBox x:Name="RSSItems">
<DataTemplate>
<TextBlock Text="{Binding Title}" Foreground="{Binding IsUnread, Converter={StaticResource UnreadForegroundConverter}}" />
</DataTemplate>
</ListBox>
Don't forget to add the xmlns:converters attribute to your Page's tag.
You'll then want to implement your IValueConverter to do the boolean to colour conversion;
public class UnreadForegroundConverter : IValueConverter {
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
if ((bool)value == true) {
return Application.Current.Resources["PhoneAccentColor"];
}
return Application.Current.Resources["PhoneForegroundColor"];
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
throw new NotImplementedException();
}
}
And obviously you'll need to bind the listbox, RSSItems, to a collection of RSSItem. Eg.
ObservableCollection<RSSItem> items = new ObservableCollection<RSSItem>();
// populate items somehow
RSSItems.ItemsSource = items;
Hope that helps.