Blank - Black Image control in WPF if image source is absolute Uri - wpf

I use on listbox control own datatemplate. Listbox item consist one image control and some textblock.
On image source I bind property type of Uri (absolute url - for example: http://u.aimg.sk/fotky/1730/71/17307141.jpg?v=2)
Listbox have about 50 - 300 items.
If I test app, I sometimes see blank - white or black image instead user images.
The problem you can see on this images:
I would like to know what cause this problem and how can I solve this problem.
Image sources are good, I check it in browser.
Thank for advice.

I think what's happening is a race condition. Some of your images haven't completed downloading by the time you are asking them to display. There is a pretty good example given here http://social.msdn.microsoft.com/Forums/en/wpf/thread/dc4d6aa9-299f-4ee8-8cd4-27a21ccfc4d0 which I'll sum up:
private ImageSource _Src;
public ImageSource Src
{
get { return _Src; }
set
{
_Src = value;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("Src"));
((BitmapImage)_Src).DownloadCompleted += new EventHandler(MainWindow_DownloadCompleted);
}
}
void MainWindow_DownloadCompleted(object sender, EventArgs e)
{
PropertyChanged(this, new PropertyChangedEventArgs("Src"));
((BitmapImage)_Src).DownloadCompleted -= MainWindow_DownloadCompleted;
}
With the above code, your images that are binding to your property will be told to update with the PropertyChanged call when the value is first assigned as well as AFTER the images have downloaded 100%. This is taken care of in the DownloadCompleted event handler that is utilized in the above example. This should make them not appear as a black image anymore, but as their fully-ready selves.
Also, if you are using a stream to as the source for your images, you need to make sure you use BitmapCacheOption.OnLoad. Such as:
BitmapImage source = new BitmapImage();
source.BeginInit();
source.CacheOption = BitmapCacheOption.OnLoad;
source.StreamSource = yourStream;
source.EndInit();
This is because by default the image using the source will lazy load it and by then your stream is probably closed, which could also be why you get blank/black images.
Good luck.

Related

Printing In Silverlight. Some pages missing

I am maintaining an old application that prints checks from silverlight.
The checks are in a grid and the user selects them and presses the print button.
I verified that all the checks selected in the grid do get sent to the printer but I noticed that sometimes some are missing in the actual printout. I check the EndPrint even for errors and there is none.
How can I make sure all the data gets actually printed?
Here is the code for the printpage event
StackPanel stackPanel = new StackPanel();
CheckInfo check = selectedChecks[printItemIndex];
PrintCheck printCheck = BuildPrintCheck(check);
stackPanel.Children.Add(printCheck);
stackPanel.Measure(new Size(args.PrintableArea.Width, double.PositiveInfinity));
if (++printItemIndex < selectedChecks.Count)
args.HasMorePages = true;
args.PageVisual = stackPanel;
I found a workaround to this issue posted here
http://www.thomasclaudiushuber.com/blog/2009/11/25/how-to-print-dynamically-created-images-in-silverlight-4-beta/
Basically instead of putting the Image straight into the page, put a Rectangle and and at run time load the image dynamically, set it as the image source for an image brush and then set the fill property of the rectangle to the image brush.
OK it turned out that SilverLight5 (runtime) has an issue with printing images. The problem does not exist on clients running SilverLight4.
Here is how I fixed it in my code
private void PlaceImages()
{
var logoStreamResourceInfo = Application.GetResourceStream(new Uri("myApp;/Images/logo.png", UriKind.Relative));
var logo = new BitmapImage();
logo.SetSource(logoStreamResourceInfo.Stream);
var logoImageBrush = new ImageBrush();
logoImageBrush.ImageSource = logo;
upperLogo.Fill = logoImageBrush;
lowerLogo.Fill = logoImageBrush;
}

Show default local image while url image is loading

I am a wpf newbie. In my wpf app, I have images that load from the web. To avoid gui blocking, I followed the approach as mentioned in How can I keep a WPF Image from blocking if the ImageSource references an unreachable Url?
Works great. Issue is, until the image loads, gui doesn't honor dimensions properties set in the Image node. End result is kind of a 'resize' affect - initially the gui(elements) are of one size and 'readjust' once image is loaded.
I wish to make the load 'smooth'. I want to be able to specify a 'initial default' image. Something like initial image in WPF Image Control
However I am not able to get it to work. May be something like below in ImageAsyncHelper(which is obviously wrong) :
public static readonly DependencyProperty SourceUriProperty = DependencyProperty.RegisterAttached("SourceUri", typeof(Uri), typeof(ImageAsyncHelper), new PropertyMetadata
{
PropertyChangedCallback = (obj,e) =>
{
((Image)obj).SetBinding(Image.SourceProperty,
new Binding("DefaultUri")
{
Source = new Uri("pack://application:,,,/blah/Images/Default.gif", UriKind.RelativeOrAbsolute)
});
}
PropertyChangedCallback += (obj, e) =>
{
((Image)obj).SetBinding(Image.SourceProperty,
new Binding("VerifiedUri")
{
Source = new ImageAsyncHelper { GivenUri = (Uri)e.NewValue },
IsAsync = true,
});
}
});
What are my options?
Try setting the Image elements MinWidth and MaxWidth to the desired size.

Silverlight Image Data Binding

I am new to Silverlight, and have an issue with binding.
I have a class ItemsManager, that has inside its scope another class Item.
class ItemsManager
{
...
class Item : INotifyPropertyChanged
{
...
private BitmapImage bitmapSource;
public BitmapImage BitmapSource
{
get { return bitmapSource; }
set
{
bitmapSource = value;
if(PropertyChanged != null )PropertyChanged("BitmapSource")
}
}
}
}
I do the following in code to test binding:
{
ItemsManager.Instance.AddItem("123");
//Items manager started downloading item visual
//part (in my case bitmap image png)
Binding b = new Binding("Source");
b.Source = ItemsManager.Instance.GetItem("123").BitmapSource;
b.BindsDirectlyToSource = true;
Image img = new Image();
img.SetBinding(Image.SourceProperty, b);
img.Width = (double)100.0;
img.Height = (double)100.0;
LayoutRoot.Children.Add(img);
}
Once image is loaded, image doesn't appear. Though, if I set directly after image has been loaded its source, it displays well.
I also noticed that PropertyChanged("BitmapSource") never fires, because PropertyChanged is null, like Image never binded to it.
I am looking forward to hearing from you!
PropertyChanged("BitmapSource") fires in case of two-way binding.
I.e. if you use two way binding and then manually change image source, like
img.Source = new BitmapImage(new Uri("http://...."));
the property changed event would fire.
As for the image appearance, it seems you bind data in wrong way.
Try declarative bindings.
There are several things wrong with this code:-
You've bound directly to the BitmapImage exposed by the BitmapSource property so you've taken your nested Item class out of the picture anyway.
Also for the property setter to be called you would need something to assign a value to the Image elements Source property and your binding would need to be in two way mode.
Your binding object creation is confused, it specifies a path (which is wrong anyway) but then binds direct to source.
Hence your code would need to look like this:-
Binding b = new Binding("BitmapSource");
b.Source = ItemsManager.Instance.GetItem("123");
b.Mode = BindingMode.TwoWay;
Now when a new BitmapImage is assigned the Image Source property your setter code should run. However it should be born in mind that the this property is of the more general type ImageSource. Hence this code will break if another derivative of ImageSource is assigned instead.
One other thing which may be a problem, I can't recall of the top of my head whether Silverlight supports binding to nested types. You might need to bring your Item class out of ItemsManager and give it a more specific name like ManagedItem.
First, do not bind to BitmapImage unless you have a good reason. Binding to a string is good enough. The implicit conversion will happen automatically. Second, use declaritive binding. Programatic creation and binding is a real mess. Third, only implement INotifyPropertyChanged if you need to send changes of that property to the UI.
You are likely over complicating your situation with all this extra code.
Thanks for explanations. However, I don't need a TwoWay binding. Just one way, once ItemsManager downloads Item image, it should be automatically updated in the Image control.
So, I changed my code to this:
ItemsManager.Instance.AddItem("123");
Binding b = new Binding("BitmapSource");
b.Source = ItemsManager.Instance.GetItem("123");
Image img = new Image();
img.SetBinding(Image.SourceProperty, b);
img.Width = (double)100.0;
img.Height = (double)100.0;
LayoutRoot.Children.Add(img);
I also took Item out of ItemsManager scope, so it is now in its own class file, but image still stays empty, even though bitmap image arrives, and changes in BitmapSource property of Item object.

How to fetch attributes of image in silverlight?

I am loading an image to my silverlight application. This image will fit in a 3d model as a texture map. I need to fetch image attributes. For that I am using ImageOpened event, like this:
public MainPage()
{
BitmapImage img = new BitmapImage(new Uri("imagens/textura.jpg", UriKind.Relative));
img.ImageOpened += new EventHandler<RoutedEventArgs>(img_ImageOpened);
img.ImageFailed += new EventHandler<ExceptionRoutedEventArgs>(img_ImageFailed);
imageBrush.ImageSource = img;
InitializeComponent();
this.Loaded += new RoutedEventHandler(MainPage_Loaded);
this.MouseLeftButtonUp += new MouseButtonEventHandler(MainPage_MouseLeftButtonUp);
(...)
and then:
private void img_ImageOpened(object sender, RoutedEventArgs e)
{
BitmapImage i = sender as BitmapImage;
ImgSize.Width = i.PixelWidth;
ImgSize.Height = i.PixelHeight;
MessageBox.Show("LOADED IMAGE SIZE\n W:" + ImgSize.Width.ToString() + " H:" + ImgSize.Height.ToString());
}
The messagebox is showing the correct values for the loaded picture. But this is running after the scene is loaded, so the size is always default (0,0) ... I don't know how to fix this. I've runned the debugger, i've noticed that the scene and the model is rendered and the picture width and height is zero. After this, the event is fired ... I can't figure it out.
Thanks in advance,
Jose'
First off this is a little confusing:-
imageBrush.ImageSource = img;
InitializeComponent();
Unless you have something quite unusual happening the imageBrush object will be null until after the InitializeComponent has run.
As to your question at a guess you load the 3D model in MainPage_Loaded. The problem then is that his happens asynchronously with the arrival of the bitmap. Hence you do not want to actually load the 3D Model until both Loaded and ImageOpened events have both occured. Note it would be dangerous to assume that ImageOpened would always happen last.
The simplest solution I can think of would be to move all your existing code in the MainPage_Loaded event to ImageOpened then move the code that fetches the image to the MainPage_Loaded. This serialises the sequence, when execution of ImageOpened you are guaranteed that the page has loaded.
Not the most sophisticated solution and doesn't make use of benefits of the asynchronous nature of SL. However it should get you going and you can assess whether there is any benefit in having Page loading and bitmap downloading operating in parallel.

using BitmapSource as Image source in WPF

I'm trying to update an Image (_browserScreenshot below) object in XAML by changing the source image every time an event determines the source needs updating. Right now I have this:
public BitmapSource GetScreen()
{
Bitmap bitmap = new Bitmap(app.Browser.ClientRectangle.Width, app.Browser.ClientRectangle.Height);
app.Browser.DrawToBitmap(bitmap, app.Browser.Bounds);
BitmapSource bitmapSource = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(bitmap.GetHbitmap(), IntPtr.Zero, Int32Rect.Empty, System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions());
bitmapSource.Freeze();
bitmap.Dispose();
bitmap = null;
return bitmapSource;
}
Then I have an event handler as shown:
app.BitmapSource.Changed += new EventHandler(BitmapSource_Changed);
void BitmapSource_Changed(object sender, EventArgs e)
{
Window1._browserScreenshot.Source = app.GetScreen();
}
Now whenever this event fires a new screenshot is taken and the source of the Image (called _browserScreenshot here) control should be updated. I keep getting an error about changing the IsFrozen propery, but I can't figure out how to change this correctly and have this work the way I want it to. Thanks in advance everyone.
In all likelyhood you want to Freeze the object. The problem you are having is that you want to create a completely new BitmapSource every time and let the garbage collector dispose of the old image.
The following line turned out to be my problem:
bitmapSource.Freeze();

Resources