XAML, Image control, local storage image - c#

The first is the first, I'm very newbie on .NET, I was developed un Visual Basic 6, but now I'm trying to make an application to Windows Phone 8.0.
At this time I'm trapped with a (maybe simple) problem.
I have a xaml page with control, and it is part of LongListSelector thats implements an Observable Collection of "Prenda" class.
...
var prendasData = from r in db.Prendas select r;
PrendasItems = new ObservableCollection<Prenda>(prendasData);
llsPrendas.ItemsSource = PrendasItems;
...
The XAML portion is the code bellow, please, i know that severals things maybe are wrong, but I'm learning alone, be patient with me :D
<phone:LongListSelector x:Name="llsPrendas" Margin="0,0,-12,0" ItemsSource="{Binding Prendas}" SelectionChanged="llsPrendasSelectionChanged">
<phone:LongListSelector.ItemTemplate>
<DataTemplate>
<StackPanel>
<Image Width="100" Height="100" Margin="5,0,0,0" Source="{Binding PrendaImageURI}" ImageFailed="errcargaimg"/>
<StackPanel Orientation="Vertical">
<TextBlock FontWeight="Normal" Text="{Binding Nombre}" Margin="10,0,0,0" />
<TextBlock FontWeight="Normal" Text="{Binding Precio}" Margin="10,0,0,0" />
</StackPanel>
</StackPanel>
</DataTemplate>
</phone:LongListSelector.ItemTemplate>
</phone:LongListSelector>
The problem is the Image Control. It doesn't show anything and if I debug it, the error the message is: "AG_E_NETWORK_ERROR", googled this error and I know that is (in this case) same as "File not found." But I'm sure thats file exists. because I seen it with IsoStoreSpy, at /Shared/Media/ShellContent/WP_XXX.jpg, i think that the root of the Isolated Storage is called isostore:/ and the complete URI must be: isostore:/Shared/Media/ShellContent/WP_XXX.jpg.
This string is saved as string column in the class, and I create a property thats use this string to make an Uri to use to bind the Source property of Image control at design time.
(portion of class declaration)
[Column]
public string Foto
{
get
{
return foto;
}
set
{
if (foto != value)
{
foto = value;
NotifyPropertyChanged("Foto");
}
}
}
public Uri PrendaImageURI
{
get
{
return new Uri(this.Foto, UriKind.Absolute);
}
}
I'm going crazy, because I cannot understand why it's doesn't work. Can somebody help me? (Sorry for my bad english)

You can not read from Isolated storage using URI, You have to read using IsolatedStorageFile class:
private static BitmapImage GetImageFromIsolatedStorage(string imageName)
{
var bimg = new BitmapImage();
using (var iso = IsolatedStorageFile.GetUserStoreForApplication())
{
using (var stream = iso.OpenFile(imageName, FileMode.Open, FileAccess.Read))
{
bimg.SetSource(stream);
}
}
return bimg;
}
You can find more details from below Posts:
How get image from isolated storage
How to load an image from isolated storage into image control on windows phone?

I solved it with Pratik Goyal help (thank you, very much!), making a BitmapImage Property in the class "Prendas", taking The Foto string data. Later I will take more careful with the exception control, but is a good start, I think.
public BitmapImage ImageFoto
{
get
{
return GetImageFromIsolatedStorage(Foto);
}
}
public BitmapImage GetImageFromIsolatedStorage(string imageName)
{
var bimg = new BitmapImage();
using (var iso = IsolatedStorageFile.GetUserStoreForApplication())
{
using (var stream = iso.OpenFile(imageName, FileMode.Open, FileAccess.Read))
{
bimg.SetSource(stream);
}
}
return bimg;
}

Just for the children:
Silverlight notifies this with an AG_E_NETWORK_ERROR / HRESULT=0x80131500. Which of course can't be found anywhere on the web and of course means jack and shit to me and not only to me.

Is the UI being notified of the PrendaImageURI property changing?
Try adding
NotifyPropertyChanged("PrendaImageURI");
when your Foto property is set.

Related

WPF XAML serialize/deserialize

my application is basically taking one WPF Canvas containing other controls and serialize it to an XML file and then deserialize it to display the serialized data or a previously saved one. The serialization/deserialization is working fine everything is saved and restored back. The issue is that after deserialization if I try to change an image source with the bellow code it doesn't work:
testLogo.Source = new BitmapImage(new Uri(file.FileName));
The Image is referrenced in the XAML as bellow:
<Canvas Name="mainCanva" Margin="0,0,12,0" Width="1729" Height="150" VerticalAlignment="Top">
<Border BorderThickness="3" BorderBrush="#FF009B80" FlowDirection="LeftToRight" VerticalAlignment="Top" HorizontalAlignment="Left" Width="1729" Height="150">
<Grid Margin="0" Background="White" Height="Auto" Width="Auto" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Uid="">
<Grid HorizontalAlignment="Left" Margin="3,28,0,57">
<Image HorizontalAlignment="Left" Margin="0,0,0,0" x:Name="testLogo" Stretch="UniformToFill" Width="317" Source="file:///C:/Users/logo.jpg" />
</Grid>
</Grid>
</Border>
</Canvas>
The Deserialization code is as bellow:
Canvas canvas = DeSerializeXAML(appPath + "\\tmp\\mainCanva.xml") as Canvas;
mainCanva.Children.Clear();
while (canvas.Children.Count > 0)
{
UIElement obj = canvas.Children[0];
canvas.Children.Remove(obj);
mainCanva.Children.Add(obj); // Add to canvas
}
Another point to note is that I tried to find out what was happening using Snoop, after Deserialization Snoop is also unable to change the image source although if I reconnect Snoop to the app by drag and dropping the crosshair Snoop is now able to change the Image source. The 'old' Snoop window can see the image source being updated from the testLogo.Source = command. WPF inspector doesn't have this issue it is immediately updating itself when the deserialization is happening. My guess is that there is something wrong with the visual tree ... and as WPF can do it I think it can be sorted.
Thanks for the help guys.
As requested the Serialize/Deserialize functions:
public static void SerializeToXAML(UIElement element, string filename)
{
string strXAML = System.Windows.Markup.XamlWriter.Save(element);
using (System.IO.FileStream fs = System.IO.File.Create(filename))
{
using (System.IO.StreamWriter streamwriter = new System.IO.StreamWriter(fs))
{
streamwriter.Write(strXAML);
}
}
}
public static UIElement DeSerializeXAML(string filename)
{
using (System.IO.FileStream fs = System.IO.File.Open(filename, System.IO.FileMode.Open, System.IO.FileAccess.Read))
{
return System.Windows.Markup.XamlReader.Load(fs) as UIElement;
}
}
You need to update your variable reference.
When you call mainCanva.Children.Clear(), it removes all the children including testLogo. Your variable testLogo will still be pointing at the original testLogo object even though it's not part of the UI anymore.
Try this:
Canvas canvas = DeSerializeXAML(appPath + "\\tmp\\mainCanva.xml") as Canvas;
mainCanva.Children.Clear();
while (canvas.Children.Count > 0)
{
UIElement obj = canvas.Children[0];
canvas.Children.Remove(obj);
mainCanva.Children.Add(obj); // Add to canvas
}
testLogo = mainCanva.FindName("testLogo") as Image;
I ended up using Application resources:
<Application.Resources>
<BitmapImage x:Key="testLogo" >file:///C:/Users/test_B&W.png</BitmapImage>
</Application.Resources>
then in the code:
Resources["AVItestLogoic"] = ...
This way no matter what happens to the Visual Tree, the application resource always point to the right item

Out of Memory Exception when Populating ListView with Images (Windows Phone 8.1)

So I am able to display images from my custom folder in the Pictures Library to my ListView in the app. However, when that custom folder has 3 or more images, it's either Out of Memory Exception occurs or the app just crashes and Visual Studio doesn't even realize that the app have crashed. My question is how can I make this work?
Here are my codes...
In the xaml.cs file:
List<StorageFile> FileList = (await temp.GetFilesAsync()).ToList();
List<ImageItem> ImageList = new List<ImageItem>();
for (int i = 0; i < FileList.Count; i++)
{
using (IRandomAccessStream FileStream = await FileList[i].OpenAsync(FileAccessMode.Read))
{
using(StorageItemThumbnail thumbnail = await file.GetThumbnailAsync(ThumbnailMode.PicturesView))
{
if (thumbnail != null && thumbnail.Type == ThumbnailType.Image)
{
BitmapImage bitmap = new BitmapImage();
await bitmap.SetSourceAsync(FileStream);
ImageList.Add(new ImageItem() { ImageData = bitmap });
}
}
}
}
this.PhotoListView.DataContext = ImageList;
Here is my Helper Class:
public class ImageItem
{
public BitmapImage ImageData { get; set; }
}
Here is my xaml ListView code:
<ListView Grid.Column="1"
Grid.Row="0"
x:Name="PhotoListView"
Grid.RowSpan="1"
ItemsSource="{Binding}">
<ListView.ItemTemplate>
<DataTemplate>
<Image Source="{Binding ImageData}"
Margin="10"/>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
</ListView>
The problem with your code is that when you use BitmapImage you didn't specify the DecodePixelHeight and DecodePixelWidth, you can solve the issue in 2 ways:
the first is to specify the DecodePixelHeight and DecodePixelWidth, the second is to pass the path of the image to the list view using this code:
List<StorageFile> FileList = (await temp.GetFilesAsync()).ToList();
List<string> ImageList = new List<string>();
foreach(var file in FileList)
{
ImageList.Add(file.Path);
}
this.PhotoListView.DataContext = ImageList;
the Image control is able to do all the stuff for you, and takes care of the memory management as well.
I think your main problem is settings the ItemsPanelTemplate to Stackpanel. This kills virtualization. There is no reason for you to override the default item panel.
Also as frenk91 mentions, adding DecodePixelHeight and DecodePixelWidth to your XAML may be useful.

System.Drawing alternative in wpf?

Hi I am running into a small problem with loading a Bitmap image from a wcf rest service:
public Image GetImage(int width, int height)
{
string uri = string.Format("http://localhost:8000/Service/picture/{0}/{1}", width, height);
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
using (Stream stream = response.GetResponseStream())
{
return new Bitmap(stream); //no System.Drawing.Bitmap class in wpf?
}
}
}
Seems there is no System.Drawing class for wpf so how can I fix this? Another problem related to this is how do I set the source:
image1.Source = GetImage(image1.Height, image1.Width); //best overload for this line
// also not sure if source would be correct?
In windows forms you can do this:
pictureBox1.Image = GetImage(pictureBox1.Height, pictureBox1.Width);
Which works fine but wpf obviously has to annoy me to no end!
Im really hoping there is something simple that can be done here?
<GroupBox Height="141" HorizontalAlignment="Left" Name="groupBox1" VerticalAlignment="Top" Width="141" BorderBrush="#FFA3A3A3" Background="#37000000" Margin="1,21,0,0">
<Image Name="image1" Stretch="Fill"/>
</GroupBox>
WPF shouldn't annoy you. It's even easier.
<GroupBox Height={Binding Height}" Width="{Binding Width"}>
<Image Source="{Binding MyImageUrl}" />
</GroupBox>
Your view model could be something like
public class ImageViewModel : INotifyPropertyChanged
{
public string ImageUrl
{
get
{
return "your url here";
}
}
public double Width
{
get { return "required width"; }
}
public double Height
{
get { return "required height"; }
}
}
and of course you would need to implement INotifyPropertyChanged.

Add Items to ComboBox Programmatically

I have been working with WPF all of 2 days, coming from ASP.NET so bear with me!
I am populating a ComboBox with xml filenames from a directory and adding a icon to each item. I have everything working just fine but I am wondering if there is a "better", more "efficient" way of doing this. As I stated, I am just getting started with WPF and I want to go about things the "right" way. My working code is below, can or should I be going about this a different way? Thanks in advance for any pointers!
<ComboBox Height="24" HorizontalAlignment="Left" Margin="153,138,0,0" Name="cmbFiles" VerticalAlignment="Top" Width="200" //>
private void FillSrFileCombo()
{
string[] dirFiles = Directory.GetFiles(#"D:\TestFiles", "*.xml");
foreach (string datei in dirFiles)
{
string fileName = System.IO.Path.GetFileName(datei);
System.Windows.Controls.StackPanel stkPanel = new StackPanel();
stkPanel.Orientation = Orientation.Horizontal;
cmbFiles.Items.Add(stkPanel);
System.Windows.Controls.Image cboIcon = new Image();
BitmapImage bitMap = new BitmapImage();
bitMap.BeginInit();
bitMap.UriSource = new Uri(#"tag.jpg", UriKind.Relative);
bitMap.EndInit();
cboIcon.Source = bitMap;
cboIcon.Height = 15;
stkPanel.Children.Add(cboIcon);
System.Windows.Controls.TextBlock cboText = new TextBlock();
cboText.Text = " - " + fileName;
stkPanel.Children.Add(cboText);
}
}
I have answered a similar question an hour ago see here :http://stackoverflow.com/questions/9637514/add-usercontrol-to-listbox-wpf.
I will recap the most important parts here based on your example
In the XAML you need to create a "DataTemplate", that is the XAML representation of your file object - in your case an image + file name. You can create this Datatemplate as a resource and assign it to your ComboBox or simply create it in the combobox if you don't plan to reuse it
<ComboBox ItemsSource="{Binding Files}">
<ComboBox.ItemTemplate>
<StackPanel>
<Image Source="{Binding FileImage}" Height="16" Width="16"/>
<TextBlock Margin="5" Text="{Binding FileName}" />
</StackPanel>
</ComboBox.ItemTemplate>
</ComboBox>
In your Codebehind, you need to create a structure that represets the data you want to present in your combobox - let's say a "FileInfo" class. The FileInfo class needs to expose the "FileImage" and "FileName" as properties so you can bind to them (as seen above).
Next, you need to create a collection of such objects in the code-behind of the xaml you put your ComboBox in. The collection needs to be an ObservableCollection.
So you would have smth like this:
public class FileInfo
{
public ImageSource FileImage { get; set; }
public string FileName { get; set; }
}
and then in the MainWindow.xaml.cs
public ObservableCollection Files { get; private set; }
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
Files = new ObservableCollection();
foreach (string datei in dirFiles)
{
var fName = System.IO.Path.GetFileName(datei);
BitmapImage bitMap = new BitmapImage();
bitMap.BeginInit();
bitMap.UriSource = new Uri(#"tag.jpg", UriKind.Relative);
bitMap.EndInit();
Files.Add(new FileInfo(){FileName=fName, FileImage = bitMap});
}
}
You will still need to read a lot about why this will work. I recomend reading about DataTemplates DataBinding, ObservableCollection and in the end, read about MVVM, a pattern that ties all this stuff nicely and allows you to harness all the WPF power and decouple yor logic from the UI.
One way that you should consider for WPF/Silverlight/WP7 apps is the MVVM design pattern.
In this instance you would have a view model containing the collection of items for your ComboBox, and you would use a binding expression to set the ItemsSource of the ComboBox. You would then template the ComboBox to display your item images.
Look into data binding and data templating, the only C# code you should need here is to get the files (even though you could also do that in XAML using something like an ObjectDataProvider)

XamlParseException when binding to listbox

In my app I am allowing users to save photos from camera and photo library to isolated storage. I then get the name of each file and read the photo and add to my list. Once the list is built, I bind it to the list box.
I can get about 5 displayed without a problem. After I scroll I get the exception:
System.Windows.Markup.XamlParseException occurred
Message= [Line: 0 Position: 0]
--- Inner Exception ---
KeyNotFoundException
This is my XAML:
<ListBox x:Name="userPhotosListBox">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel x:Name="DataTemplateStackPanel" Orientation="Horizontal">
<ContentControl Content="{Binding Image}" Width="400" />
<Image Name="{Binding FileName}" Source="/Images/appbar.delete.rest.png" Width="48" Height="48"
MouseLeftButtonUp="Image_MouseLeftButtonUp" VerticalAlignment="Center" HorizontalAlignment="Center" MaxWidth="48" MaxHeight="48" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
This is the code:
using (var store = IsolatedStorageFile.GetUserStoreForApplication())
{
var userFiles = store.GetFileNames();
foreach (var userFile in userFiles)
{
if (userFile.Contains(PhotoInIsolatedStoragePrefix))
{
var currentBitmap = ReadBitmapImageFromIso(userFile);
var userPhotoImage = new Image { Source = currentBitmap };
var userImg = new Img(userPhotoImage, userFile);
userPhotosListBox.Items.Add(userImg);
}
}
}
public class Img
{
public Img(Image img, string fileName)
{
this.Image = img;
this.FileName = fileName;
}
public Image Image { get; set; }
public string FileName { get; set; }
}
Very new to WP7 development and confused as to why my code partially works.
I think you made a mistake in: Name="{Binding FileName}"
Name must start with a letter or the underscore character (_), and must contain only letters, digits, or underscores: look here
I think some of your file names are not math with these principles
Use another property like Tag instead.
Check out this post: XAMLParseException driving me CRAZY!
The bottom line is that often a XmlParseException is actually a TargetInvocationException which can be determined in the InnerException. That could be an anchor for further investigation.
Use a:
try
{
}
catch(Exception ex)
{
}
construct and set a breakpoint at the catch. Then inspect the ex variable in greater detail to see if it contains a InnerException that may give you more insight.

Categories