How to check if an image is loaded in silverlight - silverlight

I want to take a snapshot once an image is loaded, and here is a code snippet:
imageBrush.ImageOpened += (sender, e)
=> {
doSomething();
};
One problem with this code snippet is that if the image brush is already loaded, the event will not be fired, so I need a way to verify that the image is already loaded but seems like there is no "IsLoaded" property in silverlight. Could you suggest an easy workaround?

You can check that an Image control's Width property is non-zero to test its loaded.
It may be therefore that you can also use the BitmapSource PixelWidth property to perform the same test:-
bool isLoaded = ((BitmapSource)ImageBrush.ImageSource).PixelWidth == 0;
I've not tested this myself but it seems logical.

Related

Force binding in WPF

I'm writing tests which will check correctness of Binding elements specified in XAML. They work so far, the only issue is that I do not know how to correctly force databinding to happen. Surprisingly it is not enough to simply set something in DataContext, binding won't happen until you show your control/window. Please not that I'm writing 'unit'-tests and I'd like to avoid showing any windows.
Take a look at following code:
// This is main class in console application where I have all WPF references added
public class Program
{
[STAThread]
public static void Main()
{
var view = new Window();
BindingOperations.SetBinding(view, Window.TitleProperty, new Binding("Length"));
view.DataContext = new int[5];
//view.Show(); view.Close(); // <-- this is the code I'm trying not to write
Console.WriteLine(view.Title);
}
}
Here I'm creating a Window and putting an array as DataContext to that window. I'm binding Window.Title to Array.Length so I expect to see number 5 printed in console. But until I Show window (commented line) I will get empty string. If I uncomment that line then I will receive desired 5 in console output.
Is there any way I can make binding happen without showing a window? It is pretty annoying to look at ~20 windows while launching tests.
P.S.: I know I can make windows more transparent and etc, but I'm looking for more elegant solution.
UPDATE Code above is simplified version of what I really have. In real code I receive a View (some UIElement with bindings) and object ViewModel. I do not know which exactly binding there were set on View, but I still want all of them to be initialized.
UPDATE 2: Answering to the questions regarding what I test and I why. I do not intend to test that classes like Binding, BindingBase, etc are working as expected, I assume they are working. I'm trying to test that in all my XAML files I have written bindings correctly. Because bindings are stringly typed things, they are not verified during compilation and by default they cause only errors in output window, which I'm missing occasionally. So if we take my example from above and if we will made a typo there in binding: {Binding Lengthhh} then my tests will notify you that there is no property with name Lengthhh available for binding. So I have around 100 XAML files and for each XAML I have a test (3-5 lines of code) and after launching my tests I know for sure that there are no binding errors in my solution.
The bindings are updated by the dispatcher with the DispatcherPriority.DataBind - so if you wait for a dummy task with SystemIdle priority you are sure that any pending databinding is done.
try
{
this.Dispatcher.Invoke(DispatcherPriority.SystemIdle, new Action(() => { }));
}
catch
{
// Cannot perfom this while Dispatcher in suspended mode
}
If you are trying to test correctness of your view, I suggest you test your view :-)
Why not run the UI from a unit test and write code that checks content of UI after changing data.
VS2010 does have GUI testing, or you could take a look at the code of tools such as Snoop.
Edit following comment:
If ALL you want to do is test a few simple bindings, try writing a static code test that runs as a post build event using reflection on view models and regular expressions on XAMLs. Add attributes on VM or use a config file so your test will know which view receives which View Model as DataContext. Compare property names and types in View Models with binding strings in View (automatically search XAML for these) and throw exception (thus failing build) if strings do not match.
If your bindings are more complex (converters, multibindings, ...) this may be a bit more complicated to implement.
I think you should first set the DataContext and then do the Binding, e.g.:
view.DataContext = new int[5];
BindingOperations.SetBinding(view, Window.TitleProperty, new Binding("Length"));
I'm not sure if this is real solution for your general problem, but it works in this case.
I don't believe the Window's bindings will run without calling Show or ShowDialog, because that is the only way it gets associated with the UI message loop/dispatcher.
Your best bet would be to set it to be as least visible as possible, potentially using an extension method to clean things up:
public static void PokeWindowDispatcher(this Window window)
{
window.WindowState = WindowState.Minimized;
window.ShowInTaskbar = false;
window.Visibility = Visibility.None;
using (var wait = new ManualResetEvent())
{
Action<object, RoutedEventArgs> loaded = (sender, e) => wait.Set();
window.Loaded += loaded;
try
{
window.Show();
wait.WaitOne();
}
finally
{
window.Loaded -= loaded;
window.Close();
}
}
}
I had the same problem, and from sixlettervariables gave me an idea. It's very simple.
I am using WPF in WinForms application, so I use ElementHost control to host Wpf controls on WinForms control. To enforce WinForms control initialization you can just read value of Handle (which is actually Windows HWND) and this will force control to fully initialize itself including child ElementHost and all Wpf binding work.
I didn`t try to perform the same thing for pure Wpf control. But you can easily use ElementHost to initialize your Wpf controls like this:
var el = new ElementHost();
var p = new TextBlock();
p.DataContext = new { Data = "1234" };
p.SetBinding(TextBlock.TextProperty, "Data");
el.Child = p;
var t = el.Handle;
Debug.Assert(p.Text == "1234");
PS: Found, that everything work better, if you first set DataContext and only then force a Handle to be created (just like my example). But, I think, this is already the case for you, so should not be a problem.
Have you tryed to use the IsDataBound
http://msdn.microsoft.com/en-us/library/system.windows.data.bindingoperations.isdatabound.aspx
Also check this out:
System.Windows.Interop.WindowInteropHelper helper = new System.Windows.Interop.WindowInteropHelper(view).EnsureHandle();
http://msdn.microsoft.com/en-us/library/system.windows.interop.windowinterophelper.ensurehandle.aspx
My other question is why you trying to do a UNIT test on something that has been technically tested already? By the way I am not critising, just want to understand a little better.
Not sure, but maybe something like this will work?
view.GetBindingExpression(Window.TitleProperty).UpdateTarget();

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

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.

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.

Add a Load event for Winforms Control just as Form class

is there a way I can get a Load event for System.Windows.Forms.Control just like System.Windows.Forms.Form.Load?
I want to run some initialize code before the control first shown.
Also, it would be nice to be able to do the same for System.Windows.Forms.ToolStripStatusLabel which is not actually a Control, but works like one.
Ideally, I can do this:
control.OnLoad(() => { dosomething here; });
in which OnLoad is a extension method that would run the argument Action when the "control" "Loads".
Thanks!
Form.Load event is called by the OnLoad method which is called from the OnCreateControl method which belongs to the Control class. So for the form the calling sequence would be following:
OnCreateControl start
OnLoad start
Form Load event call
OnLoad finish
OnCreateControl finish
I guess you can override OnCreateControl for your component and add your optimization code there.
Hope this helps, Regards.
For a control you can override either OnControlCreated or OnHandleCreated. The latter one can fire multiple times if it is necessary to recreate the control window. Be sure to use it if your code affects the window itself. In other words, if you do anything that requires the Handle property.
Few suitable choices for a ToolStripItem derived control. I'd recommend overriding SetVisibleCore() or OnAvailableChanged() or the AvailableChanged event. They run when the Visible property of the ToolStripItem changes. Beware that it may fire multiple times, keep a bool field that tracks that your initialization code has already run.
Last but not least, be sure to only do any of this if your code actually requires the control to be created. The vast majority of init code can go in the constructor. You only need a Load event if your code depends on the actual Location and Size of the control. Which might be different from the designer value if the form rescales itself due to a different system font or video DPI setting on the target machine.
I needed a solution like this for a TabPage within a TabControl.The only thing I came up with was using the paint event handler. I added the event handler for Paint and in the very first line I remove the event handler and then do more initialization code. This only works if you do nothave any custom painting. Alternatively, if you do need to do custom painting you could add a flag to check for each time Paint Executes.
//Paint only runs once
private void tabPage1_Paint(object sender, PaintEventArgs e)
{
tabPage1.Paint -= tabPage1_Paint;
//Do initialization here
}
/////////////////////////////////////////////////////////////////////////////////
//Paint always runs
private bool IsFirstPaint = true;
private void tabPage1_Paint(object sender, PaintEventArgs e)
{
if(IsFirstPaint)
{
IsFirstPaint = false;
//Do initialization here
}
//Do custom painting here
}

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.

Resources