Save the folder path for loading
I uploaded images from a folder selected by a FilePicker but, I would like (after the first time I chose the folder) that when I start the app, automatically loads the selected folder, without having to retrieve it from the picker file every time.
MainPage.xaml:
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Button x:Name="btnPickFolder"
Content="Pick Folder"
Click="btnPickFolder_Click"
HorizontalAlignment="Left"
Margin="10,10,0,0"
VerticalAlignment="Top"/>
<Grid x:Name="GridShowImages" HorizontalAlignment="Stretch" Margin="20,52,20,20">
<GridView x:Name="ListViewImage" ItemsSource="{x:Bind listImage}">
<GridView.ItemTemplate>
<DataTemplate x:DataType="local:SingleImage">
<Image Source="{x:Bind ImageToLoad}"
Margin="5"
Width="300"
Height="168.75"/>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
</Grid>
</Grid>
MainPage.xaml.cs:
public sealed partial class MainPage : Page
{
ObservableCollection<SingleImage> listImage = new ObservableCollection<SingleImage>();
public MainPage()
{
this.InitializeComponent();
}
private async void btnPickFolder_Click(object sender, RoutedEventArgs e)
{
FolderPicker folderPicker = new FolderPicker();
folderPicker.SuggestedStartLocation = PickerLocationId.PicturesLibrary;
folderPicker.FileTypeFilter.Add("*");
StorageFolder SelectFolderToLoad = await folderPicker.PickSingleFolderAsync();
StorageApplicationPermissions.FutureAccessList.AddOrReplace("PickedFolderToken", SelectFolderToLoad);
foreach (var file in await SelectFolderToLoad.GetFilesAsync())
{
BitmapImage bmp = new BitmapImage();
IRandomAccessStream stream = await file.OpenReadAsync();
bmp.SetSource(stream);
listImage.Add(new SingleImage() { ImageToLoad = bmp });
StorageFolder StorageParent = await file.GetParentAsync();
}
}
}
SingleImage class:
public class SingleImage
{
public BitmapImage ImageToLoad { get; set; }
}
you can use the FutureAccesList (https://learn.microsoft.com/en-us/uwp/api/Windows.Storage.AccessCache.StorageApplicationPermissions#Windows_Storage_AccessCache_StorageApplicationPermissions_FutureAccessList) to keep access to the selected folder / file after your app restarts
Related
The following code can add images from a database to a listview, however I would like to bind the image property to the pictures in XAML using Image Source rather than use listview1.Items.Add. Can this code be easily modified to do this or do I have to go about this another way. Hopefully this isn't a stupid question to ask and I would appreciate any help.
public async void showImage()
{
var query = GetAll();
foreach (var stuff in query)
{
string FileName;
FileName = stuff.RecipeImage;
var file = await
Windows.Storage.KnownFolders.PicturesLibrary.GetFileAsync(FileName);
var stream = await file.OpenReadAsync();
var bitmapImage = new BitmapImage();
bitmapImage.SetSource(stream);
Image ctrlImage = new Image();
ctrlImage.Source = bitmapImage;
ctrlImage.Height = 50;
ctrlImage.Width = 50;
ctrlImage.Stretch = Stretch.UniformToFill;
listView1.Items.Add(ctrlImage);
}
}
I need to add the image to the item source that is already used for my database which contains:
public class AddRecipe
{
[PrimaryKey,AutoIncrement]
public int ID { get; set; }
public string RecipeName { get; set; }
public string RecipeImage { get; set; }
}
<ListView x:Name="listView" HorizontalAlignment="Left" Height="493" Margin="725,60,0,0" VerticalAlignment="Top" Width="528" IsItemClickEnabled="True" SelectionMode="None" ItemClick="listView_SelectionChanged" FontSize="26.667">
<ListView.ItemTemplate>
<DataTemplate >
<StackPanel Orientation="Vertical" Margin="4">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding RecipeName}" Foreground="Black"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding MealType}" Foreground="Black"/>
</StackPanel>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
We can use ListView.ItemTemplate to set the DataTemplate used to display each item and put all the images into a ObservableCollection<BitmapImage> as ListView's ItemsSource. Then in DataTemplate, we can use Bind to set the Image.Source. Following is a simple sample:
In XAML, set DataTemplate with {x:Bind} to show image.
<ListView ItemsSource="{x:Bind ImgList}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="BitmapImage">
<Image Width="50"
Height="50"
Source="{x:Bind }"
Stretch="UniformToFill" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
ImgList is defined in code-behind, it's defined as ObservableCollection<BitmapImage>, so the DataType of DataTemplate is BitmapImage and as I just bind the whole BitmapImage object to Image.Source so here just use Source="{x:Bind }".
The code-behind may like following:
public sealed partial class MainPage : Page
{
public ObservableCollection<BitmapImage> ImgList = new ObservableCollection<BitmapImage>();
public MainPage()
{
this.InitializeComponent();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
showImage();
}
public async void showImage()
{
var query = GetAll();
foreach (var stuff in query)
{
string FileName = stuff.RecipeImage;
var file = await Windows.Storage.KnownFolders.PicturesLibrary.GetFileAsync(FileName);
var stream = await file.OpenReadAsync();
var bitmapImage = new BitmapImage();
bitmapImage.SetSource(stream);
ImgList.Add(bitmapImage);
}
}
}
Besides, I noticed that you are getting images form Pictures Library. If these images are stored by your app earlier, then storing them in app data would be better as this makes the binding easier and in Pictures Library, these images could be easily deleted by users.
To store images in app data, we can using ApplicationData.Current.LocalFolder to retrieve app's local data folder. For example, copy selected image into local data folder:
//This method copies selected image into local data folder and returns new file's name.
public async Task<string> CopySelectedImage()
{
FileOpenPicker openPicker = new FileOpenPicker();
openPicker.ViewMode = PickerViewMode.Thumbnail;
openPicker.SuggestedStartLocation = PickerLocationId.PicturesLibrary;
openPicker.FileTypeFilter.Clear();
openPicker.FileTypeFilter.Add(".bmp");
openPicker.FileTypeFilter.Add(".jpg");
openPicker.FileTypeFilter.Add(".jpeg");
openPicker.FileTypeFilter.Add(".png");
var file = await openPicker.PickSingleFileAsync();
if (file != null)
{
var localFolder = ApplicationData.Current.LocalFolder;
var newCopy = await file.CopyAsync(localFolder, file.Name, NameCollisionOption.GenerateUniqueName);
return newCopy.Name;
}
else
{
return null;
}
}
Then we can using code like following to retrieve the image and create the BitmapImage:
var path = await CopySelectedImage();
var bitmapImage = new BitmapImage(new Uri($"ms-appdata:///local/{path}"));
Update:
I suppose you have had RecipeImage in your item source, then you can add a Image control into your DataTemplate and bind RecipeImage to it's Source with a ImageConverter like following:
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Margin="4" Orientation="Vertical">
<StackPanel Orientation="Horizontal">
<TextBlock Foreground="Black" Text="{Binding RecipeName}" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Foreground="Black" Text="{Binding MealType}" />
</StackPanel>
<Image Width="50"
Height="50"
Source="{Binding RecipeImage,
Converter={StaticResource ImageConverter}}"
Stretch="UniformToFill" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
ImageConverter is used to convert string to BitmapImage as your RecipeImage is defined as string but Image.Source need a BitmapImage. Before using it in Binding, we need set it as a StaticResource firstly:
<Page.Resources>
<local:ImageConverter x:Key="ImageConverter" />
</Page.Resources>
The code of ImageConverter may like:
public class ImageConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
string FileName = value as string;
var file = Windows.Storage.KnownFolders.PicturesLibrary.GetFileAsync(FileName).AsTask().Result;
var stream = file.OpenReadAsync().AsTask().Result;
var bitmapImage = new BitmapImage();
bitmapImage.SetSource(stream);
return bitmapImage;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
Simple
xaml
<ListView Name="listView1">
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<Image Source="{Binding}" Height="50" Width="50" Stretch="UniformToFill" />
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
и в коде добавляем List<BitmapImage>
List<BitmapImage> data_list = new List<BitmapImage>();
foreach (var stuff in query)
{
string FileName;
FileName = stuff.RecipeImage;
var file = await
Windows.Storage.KnownFolders.PicturesLibrary.GetFileAsync(FileName);
BitmapImage bitmapImage;
using (var stream = await file.OpenAsync(Windows.Storage.FileAccessMode.Read))
{
bitmapImage = new BitmapImage();
bitmapImage.SetSource(stream);
}
data_list.Add(bitmapImage);
}
listView1.ItemsSource = data_list;
then we just fill in the data into our ListView
I'm building a Windows Phone 8.1 app (Windows Runtime, not Windows Phone Silverlight 8.1). In my app, I need to display all photos of CameraRoll in a GridView, but as Thumbnails, to reduce memory usage. When I try my app, everything works fine, but it's EXTREMELY slowly.
My code is as follows:
===================== MainPage.xaml.cs=============================
var files = await KnownFolders.CameraRoll.GetFilesAsync();
List<ImageSource> imageSources = new List<ImageSource>();
for(int i=0; i<files.Count; i++)
{
await ExecuteCode(i, files, KnownFolders.CameraRoll, imageSources);
}
photosGrid.DataContext = imageSources;
private async Task ExecuteCode(int index, IReadOnlyList<StorageFile> files, StorageFolder folder, List<ImageSource> imageSources)
{
uint requestedSize = 90;
using(StorageItemThumbnail itemThumbnail = await files[index].GetThumbnailAsync(ThumbnailMode.PicturesView, requestedSize))
{
using(IRandomAccessStream fStream = itemThumbnail.AsStreamForRead().AsRandomAccessStream())
{
BitmapImage bitmapImage = new BitmapImage();
await bitmapImage.SetSourceAsync(fStream);
imageSources.Add(bitmapImage);
bitmapImage = null;
GC.AddMemoryPressure((long)itemThumbnail.Size);
}
}
}
========================MainPage.xaml==========================
<GridView x:Name="photosGrid" Height="392" Width="400" ItemsSource="{Binding}" Margin="0,0,-0.333,0" SelectionMode="Multiple" Background="Black">
<GridView.ItemTemplate>
<DataTemplate>
<Image Width="90" Height="90" Margin="5" Source="{Binding}" Stretch="UniformToFill"/>
</DataTemplate>
</GridView.ItemTemplate>
<GridView.ItemsPanel>
<ItemsPanelTemplate>
<ItemsStackPanel />
</ItemsPanelTemplate>
</GridView.ItemsPanel>
</GridView>
StorageFile.GetThumbnailAsync(ThumbnailMode) | getThumbnailAsync(ThumbnailMode) method
https://msdn.microsoft.com/en-us/library/windows/apps/br227212.aspx
I am facing an issue while displaying an image from ApplicationData.Current.LocalFolder.
I am able to save an image and store it in LocalFolder, but displaying it in a Image control or LongListSelector, the image does not show up.
Here is the code:
private async void StoreToFile(string imageFileName)
{
StorageFile file = await ApplicationData.Current.LocalFolder.CreateFileAsync(imageFileName, CreationCollisionOption.GenerateUniqueName);
using (Stream current = await file.OpenStreamForWriteAsync())
{
await photoStream.CopyToAsync(current);
}
}
Here is the code to bind:
var folder = ApplicationData.Current.LocalFolder;
var images = await folder.GetFilesAsync();
Recent.ItemsSource = images.ToList();
XAML Code:
<phone:PivotItem Name="pivot1" Header="item2" Background="White">
<phone:LongListSelector x:Name="Recent" Margin="0,0,0,72" ItemsSource="{Binding lst}" >
<phone:LongListSelector.ItemTemplate>
<DataTemplate>
<StackPanel>
<Image Source="{Binding Path}" Width="60"/>
</StackPanel>
</DataTemplate>
</phone:LongListSelector.ItemTemplate>
</phone:LongListSelector>
</phone:PivotItem>
I am able to bind the path to a TextBlock and able to see the exact path like:
C:\Data\Users\DefApps\AppData{GUID}\Local\bucket.png
But if I bind to an image source, images are not displaying.
Can anyone see what I'm doing wrong?
You need to use the msappx uri scheme:
new Uri("ms-appx:///myimage.jpg");
As ToniPetrina has said in comment - you can't bind to Path, your property should return BitmapImage. There are two approaches that come to my mind:
provide a special getter of your property that will return BitmapImage (some code below)
provide a Converter when binding to IsolatedStorage image - here is very nice example
The sample code for the first solution can look like this:
In Xaml:
<ListBox Name="myList" Grid.Row="2">
<ListBox.ItemTemplate>
<DataTemplate>
<Image Source="{Binding GetImgSource}" Width="60"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
In code behind:
public class Images : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string imgSource = "";
public string SetImgSource // setter - string
{
set
{
imgSource = value;
RaiseProperty("GetImgSource");
}
}
public BitmapImage GetImgSource // getter - BitmapImage
{
get
{
if (String.IsNullOrEmpty(imgSource)) return null;
BitmapImage temp = new BitmapImage();
using (IsolatedStorageFile ISF = IsolatedStorageFile.GetUserStoreForApplication())
using (IsolatedStorageFileStream file = ISF.OpenFile(imgSource, FileMode.Open, FileAccess.Read))
temp.SetSource(file);
return temp;
}
}
public void RaiseProperty(string property = null)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
ObservableCollection<Images> myImg = new ObservableCollection<Images>();
myList.ItemsSource = myImg; // somewhere
Currently I am able to select multiple files but when I click open, the selected images are not being shown. Instead, "Windows.UI.XAML.Media.Imaging.BitmapImage" appears as a text. The FlipView functionality is still there though. What am I doing wrong?
XAML.
<FlipView x:Name="flpView" Grid.Row="1" Margin="10, 10, 10, 10">
<Image x:Name="images" Stretch="UniformToFill" />
</FlipView>
Behind code.
public async Task flipviewload()
{
// Add code to perform some action here.
Windows.Storage.Pickers.FileOpenPicker openPicker = new Windows.Storage.Pickers.FileOpenPicker();
openPicker.SuggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.PicturesLibrary;
openPicker.ViewMode = Windows.Storage.Pickers.PickerViewMode.Thumbnail;
// Filter to include a sample subset of file types.
openPicker.FileTypeFilter.Clear();
openPicker.FileTypeFilter.Add(".bmp");
openPicker.FileTypeFilter.Add(".png");
openPicker.FileTypeFilter.Add(".jpeg");
openPicker.FileTypeFilter.Add(".jpg");
var files = await openPicker.PickMultipleFilesAsync();
var images = new List<BitmapImage>();
if (files != null)
{
//foreach (StorageFile Images in files)
foreach (var file in files)
{
Windows.Storage.Streams.IRandomAccessStream fileStream = await file.OpenAsync(Windows.Storage.FileAccessMode.Read);
using (Windows.Storage.Streams.IRandomAccessStream filestream = await file.OpenAsync(Windows.Storage.FileAccessMode.Read))
{
BitmapImage bitmapImage = new BitmapImage();
await bitmapImage.SetSourceAsync(fileStream);
//Images.Source = bitmapImage;
images.Add(bitmapImage);
}
}
}
flpView.ItemsSource = images;
}
I also added Task foo = flipviewload(); in my public MainPage();
You get this result because default rendering calls ToString() on the item, which prints the name of the class. If you want to display the image you have to supply an ItemTemplate:
<FlipView x:Name="flpView" Grid.Row="1" Margin="10, 10, 10, 10">
<FlipView.ItemTemplate>
<DataTemplate>
<Image Stretch="UniformToFill" Source="{Binding}" />
</DataTemplate>
</FlipView.ItemTemplate>
</FlipView>
I am trying to write a Windows 8 app in C# in which I want to display a list of images that the user selects through FileOpenPicker. I wish to display these images in a GridView using XAML Data-binding. I have tried a few things but the data-binding doesn't seem to work. I am not sure at what location exactly do I need to set the itemssource of the GridView. If I do it in the MainPage constructor then the GridView doesn't get refreshed as the data-bound list gets populated later as the user selects the images.
How do I fix this?
UPDATE 1
If you want to bind GridView, then you need to add few things. See I have updated my answer with some comment lines. You need to add those lines to supply ItemsSource via XAML
Here you go.
C#
private async void btnBrowsePhotos_Click(object sender, RoutedEventArgs e)
{
//var objImageItem = new ImageItem();
FileOpenPicker openPicker = new FileOpenPicker();
openPicker.ViewMode = PickerViewMode.Thumbnail;
openPicker.SuggestedStartLocation = PickerLocationId.PicturesLibrary;
openPicker.FileTypeFilter.Add(".jpg");
openPicker.FileTypeFilter.Add(".jpeg");
openPicker.FileTypeFilter.Add(".png");
var files = await openPicker.PickMultipleFilesAsync();
List<ImageItem> ImageList = new List<ImageItem>();
foreach (var file in files)
{
using (var stream = await file.OpenAsync(FileAccessMode.Read))
{
//objImageItem.ImageList.Add(new ImageItem(stream, file.Name));
ImageList.Add(new ImageItem(stream, file.Name));
}
}
gv.ItemsSource = ImageList;
//gv.DataContext = objImageItem;
}
public class ImageItem //: INotifyPropertyChanged
{
/*private ObservableCollection<ImageItem> _ImageList = new ObservableCollection<ImageItem>();
public ObservableCollection<ImageItem> ImageList
{
get { return _ImageList; }
set { _ImageList = value; OnPropertyChanged("ImageList"); }
}*/
public BitmapImage Source { get; set; }
public string Name { get; set; }
public ImageItem()
{
}
public ImageItem(IRandomAccessStream stream, string name)
{
BitmapImage bmp = new BitmapImage();
bmp.SetSource(stream);
Source = bmp;
Name = name;
}
}
XAML
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<StackPanel>
<Button Click="btnBrowsePhotos_Click" Style="{StaticResource BrowsePhotosAppBarButtonStyle}" />
<!-- Add ItemsSource="{Binding ImageList}" to GridView -->
<GridView x:Name="gv">
<GridView.ItemTemplate>
<DataTemplate>
<Grid>
<Image Stretch="Fill" Source="{Binding Source}" Height="192" Width="342" />
<Border Opacity=".8" Background="Black" VerticalAlignment="Bottom" >
<TextBlock Text="{Binding Name}" FontSize="18"/>
</Border>
</Grid>
</DataTemplate>
</GridView.ItemTemplate>
<GridView.ItemsPanel>
<ItemsPanelTemplate>
<WrapGrid MaximumRowsOrColumns="3" />
</ItemsPanelTemplate>
</GridView.ItemsPanel>
</GridView>
</StackPanel>
</Grid>