I am trying to bind thumbnail of my video StorageFile to Image XAML element through data template with x:Bind. I am using MVVM approach and I've used same method to achieve this in past but I don't know why it isn't working now.
I use live property explorer and source of Image is 0. other properties like video title are just working fine but image is not working. But problem occurs even with duration, sometimes duration shows up and sometimes it doesn't, which is odd.
I am providing my code below.
Model
public class VideoItem : LibraryItem
{
#region Props
public string Views { get; set; }
public string Duration { get; set; }
public BitmapImage Display { get; set; }
public VideoProperties MyVideoProperties { get; set; }
public StorageFile MyVideoFile { get; set; }
#endregion
public VideoItem(StorageFile File)
{
MyVideoFile = File;
Initialize();
}
#region PrivateMethods
private async void Initialize()
{
Title = MyVideoFile.DisplayName;
MyVideoProperties = await MyVideoFile.Properties.GetVideoPropertiesAsync();
var dur = MyVideoProperties.Duration;
Duration = $"{dur.Hours.ToString()} : {dur.Minutes.ToString()} : {dur.Seconds.ToString()}";
Display = await GetDisplay();
Views = MyVideoProperties.Rating.ToString();
}
private async Task<BitmapImage> GetDisplay()
{
var bitm = new BitmapImage();
using (var imgSource = await MyVideoFile.GetScaledImageAsThumbnailAsync(ThumbnailMode.VideosView))
{
if (imgSource != null) { bitm.SetSource(imgSource); }
else
{
var storelogoFolder = await Windows.ApplicationModel.Package.Current.InstalledLocation.GetFolderAsync("Assets");
var storageLogoFile = await storelogoFolder.GetFileAsync("StoreLogo.png");
bitm.UriSource = new Uri(storageLogoFile.Path);
}
}
return bitm;
}
#endregion
}
public class LibraryItem
{
public string Title { get; set; }
}
ViewModel
public class VideoLibraryViewModel
{
#region Constructor
public VideoLibraryViewModel(StorageFolder mainFolder)
{
VideoItems = new ObservableCollection<VideoItem>();
MainFolder = mainFolder;
Initialize();
}
#endregion
#region Props
public ObservableCollection<VideoItem> VideoItems { get; set; }
#endregion
#region PrivateFields
private StorageFolder MainFolder;
private IEnumerable<StorageFile> Videos;
private char[] sep = new char[] { '/' };
#endregion
#region PrivateMethods
private async void Initialize()
{
Videos = await MainFolder.GetFilesAsync();
Videos = Videos.Where(a => a.ContentType.Split(sep)[0] == "video");
FillUp();
}
private void FillUp()
{
foreach (var file in Videos)
{
VideoItems.Add(new VideoItem(file));
}
}
#endregion
}
View
<controls:AdaptiveGridView Name="VideosLibraryGridView" Grid.Row="1"
Header="Videos"
Style="{StaticResource MainGridView}"
ItemClick="VideosLibraryGridView_ItemClicked"
ItemsSource="{x:Bind VideoLibraryVM.VideoItems, Mode=OneWay}">
<controls:AdaptiveGridView.ItemTemplate>
<DataTemplate x:DataType="data:VideoItem">
<StackPanel Margin="4" >
<Grid>
<Image Source="{x:Bind Display, Mode=OneWay}" Style="{StaticResource GridViewImage}"/>
<Border Style="{StaticResource TimeBorder}">
<TextBlock Text="{x:Bind Duration, Mode=OneWay}" Foreground="White"/>
</Border>
</Grid>
<TextBlock Text="{x:Bind Title,Mode=OneWay}" Style="{StaticResource GridViewVideoName}"/>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Stretch">
<TextBlock Text="{x:Bind Views,Mode=OneWay}" Style="{StaticResource GridViewViews}"/>
<TextBlock Text="Views" HorizontalAlignment="Right"/>
</StackPanel>
</StackPanel>
</DataTemplate>
</controls:AdaptiveGridView.ItemTemplate>
</controls:AdaptiveGridView>
Style For Image
<Style TargetType="Image" x:Key="GridViewImage">
<Setter Property="Stretch" Value="UniformToFill"/>
<Setter Property="HorizontalAlignment" Value="Center"/>
<Setter Property="VerticalAlignment" Value="Center"/>
</Style>
Output in the app, you can see gridview items show no image and no duration, sometimes duration show and image never shows up:
Why no image?
Why no duration?
UPDATE
I have checked with breakpoints, all properties of items are appearing null, apart from title, all properties apart from title are retrieved asyncronosly, maybe that is the reason?
all properties of items are appearing null, apart from title, all properties apart from title are retrieved asyncronosly
It's because the UI just render grid items with all available properties without waiting any results from asynchronous methods, hence why sometimes you'll get some items with a proper duration text shows up and sometimes It doesn't.
So, the logical solution is to run the asynchronous method after the gridview load those items, right?
But how? placing it under loaded event inside datatemplate doesn't change anything since It'll just do the same problem again.
well, you can do it by abusing ContainerContentChanging event inside Gridview control due to how that event itself works.
Page.xaml
<controls:AdaptiveGridView
Name="VideosLibraryGridView" Grid.Row="1
ContainerContentChanging="VideosLibraryGridView_ContainerContentChanging"
Header="Videos"
Style="{StaticResource MainGridView}"
ItemClick="VideosLibraryGridView_ItemClicked"
ItemsSource="{x:Bind VideoLibraryVM.VideoItems, Mode=OneWay}">
<!--something something-->
</controls:AdaptiveGridView>
Page.xaml.cs
private void VideosLibraryGridView_ContainerContentChanging(ListViewBase sender, ContainerContentChangingEventArgs args
{
args.RegisterUpdateCallback(LoadImage);
}
private async void LoadImage(ListViewBase sender, ContainerContentChangingEventArgs args)
{
var templateRoot = args.ItemContainer.ContentTemplateRoot as Grid;
var imageurl = (args.Item as model).ThumbnailUri;
var cache = await getimagefromfileasync(imageurl);
//check your image location based on your template first.
var image = templateRoot.Children[0] as Image;
image.Source = new BitmapImage()
{
UriSource = new Uri(cache.Path)
};
image.Opacity = 1;
}
The code above is what I did in order to load the cached thumbnail asynchronously.
source:
Dramatically Increase Performance when Users Interact with Large Amounts of Data in GridView and ListView
ContainerContentChanging Event
Update ListView and GridView items progressively
Related
i am having an issue while trying to bind an image to an ImageSource. I have tried some of the other fix on stackoverflow but none of them works.
I seem to get an error on this line saying that collection "Items" must be empty.
ImageList.ItemsSource = List;
The bind works well while using the "url" member of the FlickrData class.
MainWindow.xaml
<ScrollViewer>
<ListView x:Name="ImageList" ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<Rectangle Margin="5" Width="100" Height="100">
<Rectangle.Fill>
<ImageBrush ImageSource="{Binding imageBinding}"/>
</Rectangle.Fill>
</Rectangle>
</ListView>
</ScrollViewer>
FlickrData Class:
public class FlickrData
{
public String url { get; set;}
public FlickrData(Photo photo)
{
url = photo.SmallUrl;
}
public ImageBrush imageBinding
{
get
{
ImageBrush brush = new ImageBrush();
brush.ImageSource = new BitmapImage(new Uri(url));
return brush;
}
}
}
MainWindow class:
public partial class MainWindow : Window
{
public ObservableCollection<FlickrData> List = new ObservableCollection<FlickrData>();
public static Flickr flickr = new Flickr("XXXXXXXXXXXXXX");
public MainWindow()
{
InitializeComponent();
}
public void SearchWithInput(object sender, RoutedEventArgs e)
{
var options = new PhotoSearchOptions { Tags = SearchInput.Text, PerPage = 20, Page = 1 };
PhotoCollection photos = flickr.PhotosSearch(options);
List.Clear();
foreach (Photo photo in photos)
{
String flickrUrl = photo.WebUrl;
Console.WriteLine("Photo {0} has title {1} with url {2}", photo.PhotoId, photo.Title, photo.WebUrl);
List.Add(new FlickrData(photo));
}
ImageList.ItemsSource = List;
}
}
Do this to clean up the process
Change your XAML's ListView to ItemsSource="{Binding List}" which only needs to be done once.
Remove the now redundant ImageList.ItemsSource = List;
The list control will update itself accordingly because an ObservableCollection sends notifications of the change which are subscribed to by the list control.
Ad[a,o]pting Brundritt's example here, which he referenced in responding to my earlier BingMaps question, I have been able to display certain data in an "infobox" like so:
...but am not getting the BitmapImage that is part of the data I intend to display.
My question is why are the images not displaying, and what do I need to go in order to display them?
It's not the data - I've saved the thumbnail image to a SQLite database, where it is stored as an array of bytes, but which can be seen as images in SQLiteToolbox:
:
So as you can see, the String data is displaying fine, but not the image. Here is the pertinent code I've got for retrieving the data and displaying it:
// The class used to create the SQLite table
public class PhotraxBaseData
{
[SQLite.PrimaryKey]
[SQLite.AutoIncrement]
public int Id { get; set; }
. . .
public DateTime dateTimeTaken { get; set; }
public String filePath { get; set; }
public Byte[] thumbnail { get; set; }
}
private async void Gener8MapMarkers(List<PhotraxBaseData> _pbd)
{
foreach (PhotraxBaseData pbd in _pbd)
{
String descAndFilename = Path.GetFileName(pbd.filePath);
if (null != pbd.imgDescription)
{
descAndFilename = String.Format("{0} - {1}", pbd.imgDescription, descAndFilename);
}
BitmapImage bmi = await PhotraxUtils.ByteArrayToBitmapImage(pbd.thumbnail);
if (PhotraxUtils.ValidLatAndLong(pbd.latitude, pbd.longitude))
{
Location loc = new Location(pbd.latitude, pbd.longitude);
AddPushpin(loc, descAndFilename, pbd.dateTimeTaken, null, DataLayer);
}
}
}
public static async Task<BitmapImage> ByteArrayToBitmapImage(this byte[] byteArray)
{
// from http://dotnetspeak.com/2013/04/convert-byte-array-to-an-image-in-winrt
if (byteArray != null)
{
using (var stream = new InMemoryRandomAccessStream())
{
await stream.WriteAsync(byteArray.AsBuffer());
var image = new BitmapImage();
stream.Seek(0);
image.SetSource(stream);
return image;
}
}
return null;
}
public class PushpinMetadata
{
public string DescAndFileName { get; set; }
public DateTime DateTimeTaken { get; set; }
public BitmapImage Thumbnail { get; set; }
}
public void AddPushpin(Location latlong, string descAndFileName, DateTime dateTimeTaken, BitmapImage thumbnail, MapLayer layer)
{
Pushpin p = new Pushpin()
{
Tag = new PushpinMetadata()
{
DescAndFileName = descAndFileName,
DateTimeTaken = dateTimeTaken,
Thumbnail = thumbnail
}
};
MapLayer.SetPosition(p, latlong);
p.Tapped += PinTapped;
layer.Children.Add(p);
}
private void PinTapped(object sender, Windows.UI.Xaml.Input.TappedRoutedEventArgs e)
{
Pushpin p = sender as Pushpin;
PushpinMetadata m = (PushpinMetadata)p.Tag;
//Ensure there is content to be displayed
if (!String.IsNullOrEmpty(m.DescAndFileName))
{
Infobox.DataContext = m;
Infobox.Visibility = Visibility.Visible;
MapLayer.SetPosition(Infobox, MapLayer.GetPosition(p));
}
else
{
Infobox.Visibility = Visibility.Collapsed;
}
}
The XAML:
<bm:Map.Children>
<!-- Data Layer-->
<bm:MapLayer Name="DataLayer"/>
<!--Common Infobox-->
<bm:MapLayer>
<Grid x:Name="Infobox" Visibility="Collapsed" Margin="0,-115,-15,0">
<Border Width="300" Height="110" Background="Black" Opacity="0.8" BorderBrush="White" BorderThickness="2" CornerRadius="5"/>
<StackPanel Height="100" Margin="5">
<Grid Height="40">
<TextBlock Text="{Binding DescAndFileName}" FontSize="20" Width="250" TextWrapping="Wrap" HorizontalAlignment="Left" />
<Button Content="X" Tapped="CloseInfobox_Tapped" HorizontalAlignment="Right" VerticalAlignment="Top"/>
</Grid>
<Grid Height="40">
<Viewbox Height="200" Stretch="Uniform" StretchDirection="Both">
<Image Source="{Binding Thumbnail}" Width="Auto" Height="Auto" Margin="2"/>
</Viewbox>
</Grid>
<Grid Height="40">
<TextBlock Text="{Binding DateTimeTaken}" FontSize="16" Width="290" TextWrapping="Wrap" Height="Auto"/>
</Grid>
</StackPanel>
</Grid>
</bm:MapLayer>
<callisto:CustomDialog Content="CustomDialog" Height="100" Width="100"/>
</bm:Map.Children>
My guess is that the Image's Source property doesn't quite know what to do with the BitmapImage it's bound to via "Thumbnail"; but I don't know how to unbabelize the mismatch (presuming that's where the problem lies).
UPDATE
To answer Faye's brother Chris, here is the "database" (SQLite class) declaration:
public Byte[] thumbnail { get; set; }
Here is where I was calling the converter method, passing the appropriate member of the class instance, and then adding a pushpin:
BitmapImage bmi = await PhotraxUtils.ByteArrayToBitmapImage(pbd.thumbnail);
if (PhotraxUtils.ValidLatAndLong(pbd.latitude, pbd.longitude))
{
Location loc = new Location(pbd.latitude, pbd.longitude);
//AddPushpin(loc, descAndFilename, pbd.dateTimeTaken, null, DataLayer);
AddPushpin(loc, descAndFilename, pbd.dateTimeTaken, bmi, DataLayer);
}
However: MY BAD, PEOPLES!!!
I had neglected to replace my placeholder "null" arg with "bmi" (you can see my previous code commented out above, and my new working call to AddPushpin() below it). The image's size/scale is all wrong, but that can be fixed easily enough, I reckon.
I don't know whether to jump for joy or kick myself in the keister, so I think I'll do both simultaneously.
Thanks to Mr. Dunaway for making me look closer at my code - something I obviously should have done before posting.
Hello in my WP8 app I get data from a web API and then set the Itemsource of my longlistselector to the instance of the Class.
The problem is that the API sometimes sends out image links before the Images are actually created, so when look at the longlist selectors Picture that is bound to a url in the Class it will turn out black and not load...
Now my question is, is there some way to filter those posts out so they don't show, the Images are not loaded in code behind but only in the XAML when the app runs..
EDIT:
Will add some code and better explain:
I use a webclient to download a Json file from the server and then use Json.net to deserialize it into my class like this:
string jsonstring = e.Result.ToString();
Latest lastdownloaded = JsonConvert.DeserializeObject<Latest>(jsonstring);
my class looks something like this:
public class Latest
{
public string ThumbLink{ get; set; }
public int Id { get; set; }
public string Title { get; set;
}
I then set the the item source of my longlist selector to the instance
latestlonglist.ItemsSource = lastdownloaded;
And then my xaml simply looks like this:
<phone:LongListSelector x:Name="latestlonglist" Margin="0,0,-12,0" ItemsSource="{Binding Items}" SelectionChanged="latestlonglist_SelectionChanged">
<phone:LongListSelector.ItemTemplate>
<DataTemplate>
<StackPanel Margin="0,0,0,17">
<Grid Height="160">
<TextBlock x:Name="titleBlock" Text="{Binding Title}" TextWrapping="Wrap" Margin="145,0,0,0" Style="{StaticResource PhoneTextExtraLargeStyle}" FontSize="26" Height="105" VerticalAlignment="Top"/>
<Image x:Name="latestListImage" Source="{Binding ThumbLink}" VerticalAlignment="Top" HorizontalAlignment="Left" Width="140" />
</Grid>
</StackPanel>
</DataTemplate>
</phone:LongListSelector.ItemTemplate>
</phone:LongListSelector>
the json containas 40 image links
Thanks
OK, there is no code so I can only guess. I guess that you have ObservableCollection<> of objects which contains picture objects which is in ViewModel.
You should make and async method for downloading pictures and storing it in BitmapImage.
In ViewModel you make a method to load data where you for looping through you image links and launch download with await and add them to your ObservableCollection<>. That way items to your ObservableCollection<> will be added only after downloading them and you solve problem with black squares.
Here is your class:
class Latest
{
public string ThumbLink { get; set; }
public BitmapImage Thumb { get; set; }
public int Id { get; set; }
public string Title { get; set; }
public async Task<bool> LoadThumbAsync()
{
WebClient client = new WebClient();
client.OpenReadAsync(new Uri(this.ThumbLink));
client.OpenReadCompleted += (s, e) => //Wait for completion
{
var tempBitmap = new BitmapImage(); //Create temp bitmap container
tempBitmap.SetSource(e.Result); //Copy stream to bitmap container
this.Thumb = tempBitmap;
e.Result.Close();
return;
};
return true; // return bool only to be able to await this method.
}
}
class ViewModel
{
ObservableCollection<Latest> lastdownloaded = new ObservableCollection<Latest>();
ObservableCollection<Latest> allItems = new ObservableCollection<Latest>();
public async void LoadData()
{
//Here you load all your thumbs in list allItems. This is only temporary container.
for (var i = 0; i < allItems.Count; i++) // Now here for every item in that container you load thumb and add it to lastdownloaded list which you bind to Long List Selector.
{
await allItems[i].LoadThumbAsync();
lastdownloaded.Add(allItems[i]);
}
}
}
Maybe this not the best code but should give you idea.
I am building an application in windows phone 7 where i need to retrieve multiple images from the web service in a single image view and the images should changes when the user swipes it. I tried it in the following way:
My xaml:
<Image Source="{Binding ImageBind }" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" Margin="79,61,72,503" Height="187" />
This is my image view where i want to display the images.
The cs code:
public class Rest
{
public string restaurant_image { get; set; }
public BitmapImage ImageBind { get; set; }
}
public const string RestXml = "Rest.xml";
public Restaura()
{
InitializeComponent();
LoadData();
}
private void LoadData()
{
bool isSuccess;
//try to load data from iso store
var doc = ReadXml(out isSuccess);
if (isSuccess) PopulateList(doc);
//if failed (data doesn't exists in iso store), download data from web service
else
{
RahmService.RahmSoapClient client = new RahmService.RahmSoapClient();
client.getRestaurantLocationAllCompleted += new EventHandler<RahmService.getRestaurantLocationAllCompletedEventArgs>(client_getRestaurantLocationAllCompleted);
client.getRestaurantLocationAllAsync();
}
}
void client_getRestaurantLocationAllCompleted(object sender, RahmService.getRestaurantLocationAllCompletedEventArgs e)
{
var doc = XDocument.Parse(e.Result);
PopulateList(doc);
WriteXml(doc);
}
Here i am not getting any result. Please help me with code
Your xaml should be this.
<ListBox Name="ListBoxProduct" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<Image Source="{Binding ImageBind }" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" Height="187" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
In code behind
private void PopulateList(XDocument doc)
{
List<Rest> restList = new List<Rest>();
foreach (var location in doc.Descendants("UserDetails"))
{
Rest data = new Rest();
data.restaurant_image = location.Element("restaurant_image").Value;
data.ImageBind = new BitmapImage(new Uri(#" http://........"
+ data.restaurant_image, UriKind.Absolute));
restList.Add(data);
}
ListBoxProduct.ItemsSource= restList;
}
Try this.
<FlipView Name="flipView"
AutomationProperties.AutomationId="ItemsFlipView"
AutomationProperties.Name="Item Details"
TabIndex="1"
Grid.Row="1"
ItemsSource="{Binding}" Style="{StaticResource FlipViewStyle1}">
<TextBlock Name="answerT" Text="{Binding question}"/>
<FlipView.ItemContainerStyle>
<Style TargetType="FlipViewItem">
<Setter Property="Margin" Value="0,137,0,0"/>
</Style>
</FlipView.ItemContainerStyle>
<FlipView.ItemTemplate>
<DataTemplate>
<TextBlock x:Name="flipTxt" Text="{Binding question}"/>
</DataTemplate>
</FlipView.ItemTemplate>
</FlipView>
I have the above FlipView in XAML defined. I want to get the information that i have in the "flipTxt" TextBlock in a string in C#.
Tried with VisualTreeHelper but i can't seem to understand exactly how it works.
As well, tried to create another textblock (answerT) that would read the same info and get the text from that one. Didn't work either.
Thanks
LE:
This is how i did the binding, i get the data from MobileService.
private IMobileServiceTable<myObj> obj_tb = App.MobileService.GetTable<myObj>();
private ObservableCollection<myObj> obj_it;
var res= await obj_tb.ToListAsync();
obj_it = new ObservableCollection<myObj(res);
flipView.ItemsSource = obj_it;
Providing your FlipView is binding to its collection correctly you can access the SelectedItem in code on different events eg SelectionChanged event
private void flipView_SelectionChanged(object sender, Windows.UI.Xaml.Controls.SelectionChangedEventArgs e)
{
if (flipView != null)
{
var item = flipView.SelectedItem as MyObj;
string question = item.Question;
string answer = item.Answer;
}
}
Possible MyObj
public class MyObj
{
public string Question { get; set; }
public string Answer { get; set; }
}
or as youre using MobileServices you would probably have this if using Json.NET
public class MyObj
{
[JsonProperty(PropertyName = "Question")
public string Question { get; set; }
[JsonProperty(PropertyName = "Answer")]
public string Answer { get; set; }
}