I'm trying to display an animated GIF on a form from an embedded resource, yet nothing is displayed, which makes me think loading from a stream doesn't work this way.
If I ditch this idea and load from file, the GIF is displayed correctly.
Is this something that just won't work, or have I made a mistake along the way? Also, this is being done from within a DLL.
My Code:
private void Window_Loaded(object sender, RoutedEventArgs e)
{
// Set the image
this.pictureBox.Image = GetImageFromManifest("TempNamespace.Wait.gif");
// Remove the close button
var hwnd = new WindowInteropHelper(this).Handle;
SetWindowLong(hwnd, GWL_STYLE, GetWindowLong(hwnd, GWL_STYLE) & ~WS_SYSMENU);
}
public System.Drawing.Image GetImageFromManifest(string sPath)
{
// Ready the return
System.Drawing.Image oImage = null;
try
{
// Get the assembly
Assembly oAssembly = Assembly.GetAssembly(this.GetType());
string[] names = oAssembly.GetManifestResourceNames();
// Get the stream
Stream oStream = oAssembly.GetManifestResourceStream(sPath);
// Read from the stream
oImage = System.Drawing.Image.FromStream(oStream);
}
catch (Exception ex)
{
// Missing image?
}
//Return the image
return oImage;
}
My XAML:
<wfi:WindowsFormsHost HorizontalAlignment="Center" VerticalAlignment="Center" Height="50" Width="50">
<winForms:PictureBox x:Name="pictureBox" Height="50" Width="50" SizeMode="StretchImage" BackgroundImageLayout="None"/>
</wfi:WindowsFormsHost>
In summary, the problem was caused by setting the form's 'SizeToContent' value in either the XAML or the constructor (in my case, it was set to 'SizeToContent.WidthAndHeight').
Removing this property, and setting it in the 'Loaded' event rectified the issue. I assume that the Windows Form Host doesn't render correctly with animated GIF files when the form itself is not painted.
Related
I'm working on a game level editor in WPF using C#.
I have a series of image controls for choosing textures, and I want each image to be clickable, with some visible feedback to show which one is selected.
Here's one of the image controls, along with a green highlight border that shows up when it's clicked:
<Image x:Name="tile_image1" Source="as asphalt_test.png" Stretch="Fill" HorizontalAlignment="Right" VerticalAlignment="Top" Width="50" Height="50" MouseDown="texture_click" Margin="0,93,69,0" RenderTransformOrigin="0.16,2.04"/>
<Border x:Name="tile_border" BorderBrush="Lime" BorderThickness="3" HorizontalAlignment="Right" Height="54" Margin="0,91,65,0" VerticalAlignment="Top" Width="54" Visibility="Hidden" />
My question involves the "texture_click" function.
I want to re-use the same function for each image control, which I can easily assign using the MouseDown attribute in XAML. However, what I don't know is how to tell from within the function which control called it, or how to access that control's property's, such as ".Source". I want to be able to grab the file name of the image, as well as move the coordinates of the green border behind the new selection.
Right now, I just have it hard-coded to the first image control. Clicks on the other images will call the function, but the function will only select the first image (not the one that was actually clicked).
// click on tile 1
private void texture_click (object sender, MouseButtonEventArgs e)
{
tile_border.Visibility = Visibility.Visible;
current_tilefile = tile_image1.Source;
string source_string = Convert.ToString (tile_image1.Source);
int last_slash = source_string.LastIndexOf ('/');
current_tile = source_string.Substring (last_slash + 1, 3);
}
I tried using "sender", since I thought that might be the object that called the function, but that returned an error. I also tried calling the function with "texture_click (this)", but that was also no good. These were, admittedly, complete shots in the dark, so I wasn't surprised.
I'm still pretty new to this software, so any insight you guys can give would be great.
You just have to cast the sender parameter to the control type (Image in this case):
private void texture_click (object sender, MouseButtonEventArgs e)
{
//tile_border.Visibility = Visibility.Visible;
var image = sender as Image;
if (image != null)
{
current_tilefile = image.Source;
string source_string = image.Source.ToString();
int last_slash = source_string.LastIndexOf ('/');
current_tile = source_string.Substring (last_slash + 1, 3);
}
}
Of course, this doesn't give you access to the associated border. One thing you can do is to just dump the border into the Image's Tag property:
<Image x:Name="tile_image1" ... Tag="{Binding ElementName=tile_border}" />
<Border x:Name="tile_border" ... />
Then you can retrieve it, again by casting:
private void texture_click (object sender, MouseButtonEventArgs e)
{
var image = sender as Image;
if (image != null)
{
var border = image.Tag as Border;
if (border != null)
{
border.Visibility = Visibility.Visible;
}
// ...
}
}
Note that this (manipulating UI elements from code-behind) is not the ideal way to write a WPF application. Typically you would do something like this by using an existing control (like a ToggleButton), and re-writing its ControlTemplate so that its IsChecked visual state shows a border. But I realize that is a mouthful ...
I am trying to add a background image to a textbox but on textChange event, the image goes away as expected, however if I backspace or delete text in the textbox so that it is empty, I get the DirectoryNotFoundException was handeled.
And the directory:
Could not find a part of the path 'C:\myProjectFolder\bin\Debug..img\txtBackground.png'.
The XAML:
<TextBox Name="myTextBox" Width="200" TextChanged="myTextBox_TextChanged">
<TextBox.Background>
<ImageBrush ImageSource="img/txtBackground.png" />
</TextBox.Background>
C# Code:
private void myTextBox_TextChanged(object sender, TextChangedEventArgs e)
{
if (myTextBox.Text == "")
{
ImageBrush textImageBrush = new ImageBrush();
textImageBrush.ImageSource =
new BitmapImage(
new Uri(#"..img/txtBackground.png", UriKind.Relative)
);
myTextBox.Background = textImageBrush;
}
else
{
myTextBox.Background = null;
}
}
Deleted the references, re-added them, build/clean solution and rebuild but nothing.
These errors only occur when I try to add the background to the textbox.
Assuming that the img folder that has image is under the Project (not under the debug folder, ideally it should not be in Debug folder) and image's BuildAction is set to Resource, you can try this:
new BitmapImage(new Uri("pack://application:,,,/img/txtBackground.png", UriKind.Absolute));
If you have img in Debug folder then you have to reach upto that
new Uri(#"bin/Debug/img/txtBackground.png", UriKind.Relative)
Your Uri should read
new Uri(#"/img/txtBackground.png", UriKind.Relative)
At least that's what the error message says.
You can also try this:
textImageBrush.ImageSource =new BitmapImage("/Projectname;Component/img/txtBackground.png");
The image's Build Action must be set to Resource.
im trying to update an image in an image control that is bound to a class which implements INotifyPropertyChanged. i've tried most of the methods which relate to refreshing bitmap cache so that the image can refresh but none seems to work for my case. the image contorl is defined in the xaml file as: <Image Source="{Binding Chart}" Margin="0 0 0 0"/>
and in the code behind the class is:
private ImageSource imagechart = null;
public ImageSource Chart
{
get
{
return imagechart;
}
set
{
if (value != imagechart)
{
imagechart = value;
NotifyPropertyChanged("Chart");
}
}
}
after an event i now set the image using the following code:
c.Chart = image;
when i now run my application this will display the image but during the running of the application i update the image but calling this c.Chart = image; displays the initial image. i came to understand that WPF caches the image but all methods claiming to solve this dint work for me. one of the solutions that did not work for me is Problems overwriting (re-saving) image when it was set as image source
Try to change the return Type of your Image property to Uri. The TypeConverter on the Source Property should do the rest. If this doesnt work, verify that the resource has actually changed.
You can read the resource from your assembly using Assembly.GetManifestResourceStreams and resolve the bytes. Than manually save them with File.WriteAllBytes to your output directory an see if it has the expected image.
As far as i know Application Ressources (which are embedded into the assembly) can not be changed during runtime (?). You are referencing the assembly ressource and not an output ressource with your pack uri.
thank you all for your input coz through them i finally figure a way around this. so my xaml still remains bound as <Image Source="{Binding Chart}" Margin="0 0 0 0"/> but in the code behind i changed the class property chart to return a bitmap as shown below:
private BitmapImage image = null;
public BitmapImage Chart
{
get
{
return image;
}
set
{
if (value != image)
{
image = value;
NotifyPropertyChanged("Chart");
}
}
}
this class mind you implements INotifyPropertyChanged . at the point where i set the image i am now using this code:
BitmapImage img = new BitmapImage();
img.BeginInit();
img.CacheOption = BitmapCacheOption.OnLoad;
img.CreateOptions = BitmapCreateOptions.IgnoreImageCache;
//in the following code path is a string where i have defined the path to file
img.UriSource = new Uri(string.Format("file://{0}",path));
img.EndInit();
c.Chart = img;
this works well for me and refreshes the image upon update.
I am trying to add images that I get from my webcam to gridview however each time I get exception. I have separate page to make a picture from webcam and later, I pass image full path to other page. Here are the code snippets:
C#
protected override void OnNavigatedTo(NavigationEventArgs e)
{
//If navigated from PhotoCapturePage
if (m_ToBeNavigatedPageType == typeof(PhotoCapturePage))
{
if (e.Parameter == null)
{
return;
}
Image img = new Image();
img.Width = 64;
img.Height = 64;
img.Source = new BitmapImage(new Uri(e.Parameter.ToString()));
IncidentPictures.Add(img);
IncidentPictureGrid.Items.Add(img);
}
}
XAML:
<GridView ItemsSource="{Binding IncidentPictures}" Name="IncidentPictureGrid" Width="350"></GridView>
Each time IncidentPictureGrid.Items.Add(img) is performed, I receive Catastrophic failure (Exception from HRESULT: 0x8000FFFF (E_UNEXPECTED)) exception.
Does anyone has suggestions?
Thanks in advance,
Miroslawas
Why do you add the image to the Grid.Items collection when you add it to IncidentPictures collection too?
You already bind the GridView's ItemsSource to the collection. Maybe this cause the error ... Remove the IncidentPictureGrid.Items.Add and make sure, that IncidentPictures is an ObservableCollection.
I want to display all images stored in the Windows Phone 8 photo folder in my custom gallery which uses a ListBox for displaying the images.
The ListBox code is as follows:
<phone:PhoneApplicationPage.Resources>
<MyApp:PreviewPictureConverter x:Key="PreviewPictureConverter" />
</phone:PhoneApplicationPage.Resources>
<ListBox Name="previewImageListbox" VirtualizingStackPanel.VirtualizationMode="Recycling">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel CleanUpVirtualizedItemEvent="VirtualizingStackPanel_CleanUpVirtualizedItemEvent_1">
</VirtualizingStackPanel>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Image Source="{Binding Converter={StaticResource PreviewPictureConverter}}" HorizontalAlignment="Center" VerticalAlignment="Center" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
With the following converter:
public class PreviewPictureConverter : System.Windows.Data.IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
PreviewImageItem c = value as PreviewImageItem;
if (c == null)
return null;
return c.ImageData;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
Images are stored in a custom class:
class PreviewImageItem
{
public Picture _picture = null;
public BitmapImage _bitmap = null;
public PreviewImageItem(Picture pic)
{
_picture = pic;
}
public BitmapImage ImageData
{
get
{
System.Diagnostics.Debug.WriteLine("Get picture " + _picture.ToString());
_bitmap = new BitmapImage();
Stream data = _picture.GetImage();
try
{
_bitmap.SetSource(data); // Out-of memory exception (see text)
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine("Exception : " + ex.ToString());
}
finally
{
data.Close();
data.Dispose();
data = null;
}
return _bitmap;
}
}
}
The following code is used to set the ListBox data source:
private List<PreviewImageItem> _galleryImages = new List<PreviewImageItem>();
using (MediaLibrary library = new MediaLibrary())
{
PictureCollection galleryPics = library.Pictures;
foreach (Picture pic in galleryPics)
{
_galleryImages.Add(new PreviewImageItem(pic));
}
previewImageListbox.ItemsSource = _galleryImages;
};
Finally here is the "cleanup" code:
private void VirtualizingStackPanel_CleanUpVirtualizedItemEvent_1(object sender, CleanUpVirtualizedItemEventArgs e)
{
PreviewImageItem item = e.Value as PreviewImageItem;
if (item != null)
{
System.Diagnostics.Debug.WriteLine("Cleanup");
item._bitmap = null;
}
}
All this works fine but the code crashes with an OutOfMemoryException after a few images (especially when scrolling fast). The method VirtualizingStackPanel_CleanUpVirtualizedItemEvent_1 is called regulary (e.g. every 2 or 3 listbox entries) when the ListBox is scrolled.
What's wrong with this sample code?
Why is memory not freed (fast enough)?
Oh, I recently killed whole day to make this working!
So the solution is:
Make your Image control free resources. So set the
BitmapImage bitmapImage = image.Source as BitmapImage;
bitmapImage.UriSource = null;
image.Source = null;
as it was mentioned before.
Make sure you virtualize _bitmap on every item of the list. You should load it on demand (LongListSelector.Realized method) and you have to destroy it! It won't going to collect automatically and GC.Collect doesn't work either.
Null reference is not working too :(
But here is the method:
Make 1x1 pixel file. Copy it into assembly and make resource stream from it to dispose your images with 1x1 pixel blank. Bind custom dispose method to LongListSelector.UnRealized event (e.Container handles your list item).
public static void DisposeImage(BitmapImage image)
{
Uri uri= new Uri("oneXone.png", UriKind.Relative);
StreamResourceInfo sr=Application.GetResourceStream(uri);
try
{
using (Stream stream=sr.Stream)
{
image.DecodePixelWidth=1; //This is essential!
image.SetSource(stream);
}
}
catch { }
}
Working for me in LongListSelector with 1000 images 400 width each.
If you miss the 2 step with the data collection you can see the the good results but the memory overflows after 100-200 items scrolled.
You just had Windows Phone with show all the picture's in a user's media library "pictures" folder on screen. That's unbelievably memory intensive and considering the 150MB limit on WP8 apps it's no wonder you're getting OOM exceptions.
A few things you should consider adding:
1) Set Source and SourceUri properties to null when scrolling the listboxitem out of view. See "Caching Images" in Stefan's article here # http://blogs.msdn.com/b/swick/archive/2011/04/07/image-tips-for-windows-phone-7.aspx
BitmapImage bitmapImage = image.Source as BitmapImage;
bitmapImage.UriSource = null;
image.Source = null;
2) If you're on WP8 make sure to set DecodePixelWidth and/or DecodePixelHeight. That way an image will be loaded into memory, resized permanently and only the resized copy is stored in memory. The images loaded into memory can be much bigger then the screen size of the phone itself. So cropping those down to the right size and only storing the resized images is vital. Set BitmapImage.DecodePixelWidth=480 (at maximum) to help with that.
var bmp = new BitmapImage();
// no matter the actual size,
// this bitmap is decoded to 480 pixels width (aspect ratio preserved)
// and only takes up the memory needed for this size
bmp.DecodePixelWidth = 480;
bmp.UriSource = new Uri(#"Assets\Demo.png", UriKind.Relative);
ImageControl.Source = bmp;
(code sample from here)
3) Why are you using Picture.GetImage() instead of Picture.GetThumbnail()? Do you really need the image to take up the whole screen?
4) Consider moving from ListBox to LongListSelector if this is a WP8 exclusive app. LLS has much, much better virtualization then ListBox. Looking at your code sample it might be enough for you to just change your XAML ListBox element to LongListSelector element.
Try this approach: Image downloader with auto memory cleaning. Sample project here: https://simca.codeplex.com/