Replace dynamically BitmapImage in Button (xaml) - c#

I have following code snippet in xaml:
<UserControl x:Class="Ournamespace.OurClassName"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
...
d:DesignHeight="250" d:DesignWidth="774" Loaded="UserControl_Loaded">
....
<Button Grid.Column="0" x:Name="Button_NewMarker" Style="{StaticResource ViewpointFlatButtonStyle}" Width="90" Height="65"
VerticalAlignment="Top" Click="Button_NewMarker_Click" x:FieldModifier="public" Margin="0,0,0,0" Grid.Row="1">
<BitmapImage UriSource="Icons/markers_add_disabled.png" />
</Button>
and I have tried to replace image on button dynamically, by loading new image
if( !imgCache.ContainsKey(path) )
{
BitmapImage bi = new BitmapImage();
bi.BeginInit();
bi.UriSource = new Uri(path, UriKind.Relative);
bi.DownloadFailed += bi_DownloadFailed;
bi.DecodeFailed += bi_DecodeFailed;
bi.EndInit();
imgCache[path] = bi;
}
Button_NewMarker.BeginInit();
Button_NewMarker.Content = imgCache[path];
//Button_NewMarker.Content = oldbmp;
Button_NewMarker.EndInit();
There is no error occurring (overridden events for this)
and BitmapImage seems to be replaced - but for some reason it's grey.
Images are added to project as resources, and first image loaded seems to be working - but not dynamically replaced one.
I have tried also to use <Image ... > and even <Image ... <ImageBitmap - but all of them are not working - I suspect it has something to do with our control being UserControl.
What I have seen in internet - there is huge amount of recommendations of what and how should be done - I want now this to be done with absolute minimum amount of code - and preferably without using binding and templating. Binding can be proposed as second step after everything is done manually.

If image files are managed as resource files (i.e. project files which have their Build Action set to Resource) you should load them by a Resource File Pack URI.
Assuming a project folder Images with an image file Test.png:
var bi = new BitmapImage(new Uri("pack://application:,,,/Images/Test.png"));
Using the file name as key, the abobe would probably look like:
var path = "Test.png";
var uri = "pack://application:,,,/Images/" + path;
imgCache[path] = new BitmapImage(new Uri(uri));

Related

How to show relative source image on WPF hosted by WinForm Project without modifying behind code?

I made WinForm project and add ElementHost for using WPF blend effect on some Images.
ElementHost m_host = new ElementHost();
WPF_TK4S wpfTK4S = new WPF_TK4S();
m_host.Child = wpfTK4S;
m_host.Size = new Size(1000, 1000);
m_host.Location = new Point(0, 0);
this.Controls.Add(m_host);
I added the image control on WPF user control like below.
<UserControl x:Class="Test.WPF_TK4S"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="1000" d:DesignWidth="1000">
<Grid>
<Grid.Background>
<ImageBrush/>
</Grid.Background>
<Image Source="/Resources/TK4S_254X253.png" HorizontalAlignment="Left" Height="344" Margin="48,44,0,0" VerticalAlignment="Top" Width="342" Stretch="None"/>
</Grid>
The image is shown well on control view but is now shown when I run the application.
Fortunately, after I changed the source uri into absolute uri like
"D://Visual Project/Test/Test/Resources/TK4S_254X253.png"
the image is shown well!!
I tried to use all kinds of relative uri like
".../Test/Resources/TK4S_254X253.png", "pack://application:,,,/TK4S_254X253.png"
but it is not work.
I need to use relative path because the project is operating on Server so every project paths are different. And I have to use this things on xaml because I have to use blend after the image is initialized.
Does anything I need to know other option?

Windows Phone 8.1 Loading Images from Remote URLs Placeholder

I'm messing around with some windows phone development as I'm new to it coming from an Android background.
I am using "theCatAPI" to load a random picture of a cat and show it, then when the picture is clicked, or the button at the bottom of the screen, the image refreshes to a new one.
I have the following so far:
XAML:
<Page
x:Class="CatFactsPics.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:CatFactsPics"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid x:Name="LayoutRoot">
<Grid.ChildrenTransitions>
<TransitionCollection>
<EntranceThemeTransition/>
</TransitionCollection>
</Grid.ChildrenTransitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!-- TitlePanel -->
<StackPanel Grid.Row="0" Margin="24,17,0,28">
<TextBlock Text="My Application" Style="{ThemeResource TitleTextBlockStyle}" Typography.Capitals="SmallCaps"/>
<TextBlock Text="page title" Margin="0,12,0,0" Style="{ThemeResource HeaderTextBlockStyle}"/>
</StackPanel>
<!--TODO: Content should be placed within the following grid-->
<Grid Grid.Row="1" x:Name="ContentRoot">
<Image HorizontalAlignment="Center" Stretch="UniformToFill" VerticalAlignment="Center" x:Name="KittyPic" Tapped="KittyPic_Tapped"/>
<Button HorizontalAlignment="Center" VerticalAlignment="Bottom" x:Name="newPic" Click="newPic_Click" >New Kitty</Button>
</Grid>
</Grid>
</Page>
and in the page.cs:
...
protected override void OnNavigatedTo(NavigationEventArgs e)
{
this.navigationHelper.OnNavigatedTo(e);
Uri myUri = new Uri("http://thecatapi.com/api/images/get?format=src&type=jpg", UriKind.Absolute);
KittyPic.Source = new BitmapImage(myUri);
}
...
private void newPic_Click(object sender, RoutedEventArgs e)
{
Uri myUri = new Uri("http://thecatapi.com/api/images/get?format=src&type=jpg", UriKind.Absolute);
BitmapImage bmi = new BitmapImage();
bmi.CreateOptions = BitmapCreateOptions.IgnoreImageCache;
bmi.UriSource = myUri;
KittyPic.Source = bmi;
}
I have a couple of questions:
1) is this the correct way of doing things? In Android I'd try and do things asynchronously to avoid stalling the UI thread. That being said, I don't appear to be having any issues with things the way they are now. I'm not familiar with the Windows way of doing things, and haven't found any resources giving any explanation or advice on doing so.
2) There is a delay in displaying the new picture causing a short (couple of second) period where the image view turns black, before the new image reappears. Is there a way of setting it up so either the old picture remains until the new one is physically ready to be displayed, or alternatively display a placeholder "loading" image until the new one can replace it.
Any other advice or tips on how to do things would be great, thanks.
1) With your current code you do not block the UI thread as yes you are setting the URI on the UI thread, but the actually loading of the image is done on another thread automatically. (For doing manually downloading of images, strings etc, you will probably use async/await to avoid locking the UI thread).
2) The image goes black because you change the ImageSource before the new image has loaded. There are as you mention several ways to deal with this. Common for most of them though is that you will want to use the ImageOpened and ImageFailed events on the Image control, which triggers whenever the image is done loading (or an error occurred, for example no internet connection). Here is an example of displaying a loading bar while it is loading, which just hides/shows the loading progress:
In the page.xaml file:
<Grid Grid.Row="1" x:Name="ContentRoot">
<Image x:Name="KittyPic" Tapped="KittyPic_Tapped" ImageOpened="KittyPic_ImageOpened" ImageFailed="KittyPic_ImageFailed" />
<StackPanel x:Name="LoadingPanel" VerticalAlignment="Center">
<ProgressBar IsIndeterminate="True" IsEnabled="True" />
<TextBlock Text="Loading image..." HorizontalAlignment="Center" />
</StackPanel>
</Grid>
And in the page.xaml.cs file
private void KittyPic_Tapped(object sender, TappedRoutedEventArgs e)
{
LoadingPanel.Visibility = Visibility.Visible;
Uri myUri = new Uri("http://thecatapi.com/api/images/get?format=src&type=jpg", UriKind.Absolute);
BitmapImage bmi = new BitmapImage();
bmi.CreateOptions = BitmapCreateOptions.IgnoreImageCache;
bmi.UriSource = myUri;
KittyPic.Source = bmi;
}
private void KittyPic_ImageOpened(object sender, RoutedEventArgs e)
{
LoadingPanel.Visibility = Visibility.Collapsed;
}
private async void KittyPic_ImageFailed(object sender, ExceptionRoutedEventArgs e)
{
LoadingPanel.Visibility = Visibility.Collapsed;
await new MessageDialog("Failed to load the image").ShowAsync();
}
Instead of the TextBlock and ProgressBar you could of course show whatever you want, or for example swapping between two images to keep showing the old one.
For other advice I think when you got used to the basics is to take a look at data bindings which is very helpful and powerful. Also take a look at this article about MVVM pattern.
1) is this the correct way of doing things?
No. It's not. You should definitely adopt the MVVM pattern and detach your business logic from your view - meaning you shouldn't create bitmap images or request/assign such remote image URL's in your view's code-behind. You should be doing such stuff in your ViewModel and bind them to your View.
So in your case there will be an Uri (or a string where you will assign the remote URL) property in your ViewModel (which implements INotifyPropertyChanged) then in your View you will be binding it like this:
<!--TODO: Content should be placed within the following grid-->
<Grid Grid.Row="1" x:Name="ContentRoot">
<BitmapImage UriSource="{Binding BackGroundImage}" CreateOptions="BackgroundCreation" />
<Button HorizontalAlignment="Center" VerticalAlignment="Bottom" x:Name="newPic" Click="newPic_Click" >New Kitty</Button>
</Grid>
Whenever you set the BackGroundImage property you will be raising an event called;
RaisePropertyChanged("BackGroundImage") -> This is the classical MVVM approach.
So that your view will be aware of the fact that the BackGroundImage is changed and it will load it automatically. (But note that if you just provide a string for this BackGroundImage - you will have to use a converter, a string to Uri converter, since it only accepts Uri's for remote images)
2) "...Is there a way of setting it up so either the old picture remains until the new one is physically ready to be displayed, or alternatively display a placeholder "loading" image until the new one can replace it."
I suggest going with displaying a 'loading' image. I experienced the exact same problem as you do here and my workaround for this was inserting a loading image and setting it's opacity value to 0.1 - along with the actual image. While you are switching between remote URL's, when the previous image disappears the opaque loading image appears and when the next actual image is loaded the loading image is not displayed because new image is overwriting it.

WPF - Load Image in ListView

i have a little problem with my WPF-Application.
Well, i have a ListView which shows at the first column a image. Each element can or cannot have a image, so from time to time i load a "default" image in this column.
Here is the problem.
When i load the default image, there is no image shown in the listview.
Here is the codepart, which adds the default image:
Uri src = new Uri(#"/myapp;component/Images/Picture.png", UriKind.RelativeOrAbsolute);
BitmapImage small_image_bmp = new BitmapImage();
small_image_bmp.BeginInit();
small_image_bmp.CacheOption = BitmapCacheOption.OnLoad;
small_image_bmp.UriSource = src;
small_image_bmp.EndInit();
small_image_bmp.Freeze();
dto.Bild1_Bitmap = small_image_bmp;
this.liste.Add(dto);
This code is not working.
But this part works very well:
Uri src = new Uri(#"C:\Users\me\Documents\Visual Studio 2010\Projects\myapp\myapp\Images\Picture.png", UriKind.RelativeOrAbsolute);
BitmapImage small_image_bmp = new BitmapImage();
small_image_bmp.BeginInit();
small_image_bmp.CacheOption = BitmapCacheOption.OnLoad;
small_image_bmp.UriSource = src;
small_image_bmp.EndInit();
small_image_bmp.Freeze();
dto.Bild1_Bitmap = small_image_bmp;
this.liste.Add(dto);
As you can see, the part who isnt working has a relative path and the code, who is working, has a absolute path.
But of course i cant use the absolute path...
Here is the XAML of the ListViewColumn:
<GridViewColumn Header="Bild">
<GridViewColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Width="100" Height="100" Source="{Binding Bild1_Bitmap}"/>
</StackPanel>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
If code you posted for loading an image is in same assembly as of image. You can ignore component path and use like this - Images\Picture.png.
Also make sure image build action is set to Resource.

Loading images from a relative path

I'm making a Blackjack game for a project in college (semester-long) and I've hit a road block that I can't seem to get over.
I'm trying to load the card images so they can be displayed onscreen, but I've been having very little luck in doing so. I've got absolute references down; I can load from those, but any attempt to load from a relative path fails. The project must be standalone; I can't instruct my professor to copy these images onto the root.
#if FROMDISK
Uri myUri = new Uri(#"C:\cards\" + getFilename(r, s, "png"), UriKind.Absolute);
PngBitmapDecoder decoder2 = new PngBitmapDecoder(myUri, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default);
BitmapSource bmp = decoder2.Frames[0];
#else
System.Reflection.Assembly myAssembly = System.Reflection.Assembly.GetExecutingAssembly();
Stream myStream = myAssembly.GetManifestResourceStream("Blackjack." + getFilename(r, s, "png"));
BitmapImage bmp = new BitmapImage();
bmp.StreamSource = myStream;
#endif
// Draw the Image
im.Source = bmp;
im.Stretch = Stretch.Uniform;
im.Width = CARD_WIDTH;
(Context)
You can use:
Uri uri = new Uri(#"FolderName\FileName.png",UriKind.Relative);
PngBitmapDecoder decoder2 = new PngBitmapDecoder(uri, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default);
Remember to set the properties for the image file to (otherwise the compiler will skip them) you can look the bin/debug folder after build and verify that the file is where it should be:
Build action: content
Copy to output directory: Copy always
Another option is to make the file an embedded resource then you can access it like this:
Bitmap bitmap = Properties.Resources.FileName;
I like to declare images as resources in my XAML. I will assume that you can still play around with the code structure at this point, and hopefully there aren't too many new concepts for you. Here's how I do it:
Start y creating a folder in your project called "Images". Add the images of your cards by drag and dropping them onto the folder in Visual Studio. Make sure their "Build Action" is set to Resource.
Create a new "Resource Dictionary" by pressing CTRL-SHIFT-A. Name it CardImages.xaml.
Link this file in App.xaml like this (showing how to link multiple XAML files at once, but for this of course remove the "AnyDictionary" line!)
App.XAML:
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Controls/CardImages.xaml" />
<ResourceDictionary Source="Controls/AnyDictionaryYouWant.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
Add this code to CardImages.xaml. Rename "MyBlackJackAssembly" to the assembly name of your proejct.
<Style TargetType="Image">
<Setter Property="RenderOptions.BitmapScalingMode" Value="HighQuality" />
</Style>
<DataTemplate x:Key="Spade1">
<Image Source="MyBlackJackAssembly;component/Images/Spade1.png" />
</DataTemplate>
<DataTemplate x:Key="Spade2">
<Image Source="MyBlackJackAssembly;component/Images/Spade2.png" />
</DataTemplate>
Then, you can locate them in your code like this:
Label label = new Label();
label.ContentTemplate = (DataTemplate)label.FindResource("Spade1");
This will get you a WPF Label object that should show your card. This technique works with anything that supports ContentTemplate. You can then add your Label to a grid on your UI, I'm not sure how you display your cards on screen.
For a blackjack application, I would probably create a UserControl called "Card" in order to be able to generate it kind of like this. This encapsulates the "card generation" logic into its own Control so the rest of the code can just focus on dealing the cards.
XAML:
<myControls:CardImage Kind="Spades" Digit="1" />
Or like this in c#:
CardImage aCard = new CardImage();
aCard.Kind = CardImage.Kinds.Spades; //enum
aCard.Digit = 1;
The way I'm loading some background images that are imported in the project's resources:
this.staticBg.Source = new BitmapImage(
new Uri(#"/Project;component/Images/" + ((App)App.Current).bgImage
+ ".jpg", UriKind.Relative));
Where "Project" is project name.
So basically you should add images to resources and call them with relative uri.

C# WPF add control to main window at run time

My goal is to attach a new image control while the application is running.
img = new System.Windows.Controls.Image();
img.Margin = new Thickness(200, 10, 0, 0);
img.Width = 32;
img.Height = 32;
img.Source = etc;
I've tried
this.AddChild(img);// says must be a single element
this.AddLogicalChild(img);// does nothing
this.AddVisualChild(img);// does nothing
It was never this difficult to add a element with forms.
How can I simply attach this new element to the main window (not another control) so that it will show up.
Solved it, I named the grid main, and from there I was able to access the children attribute and the add function
main.children.add(img);
<Window x:Class="Crysis_Menu.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525" Loaded="Window_Loaded" AllowsTransparency="False" Background="White" Foreground="{x:Null}" WindowStyle="SingleBorderWindow">
<Grid Name="main">
<Button Content="Run" Height="23" HorizontalAlignment="Left" Margin="12,12,0,0" Name="btnRun" VerticalAlignment="Top" Width="151" Click="btnRun_Click" />
<TextBox Height="259" HorizontalAlignment="Left" Margin="12,40,0,0" Name="tbStatus" VerticalAlignment="Top" Width="151" />
</Grid>
</Window>
You should have only one root element under window. Adding the image using this.AddChilda adds the image as child of window, but you probably have some other child defined(Grid for example). Give a name to this child (Grid in the example) and then in the code behind add the image to the Grid
Example :
<Window>
<Grid x:Name="RootGrid">
</Grid>
</Window>
Then in the code behind use
RootGrid.Children.Add(img);
What is this in your case? You can try this.Content = image; or this.Children.Add(image);
If your this is indeed a Window, you should know that Window can have only a single child, which you put into Content. If you want several items in Window, usually you put some appropriate container (for example, Grid or StackPanel) as Window's content, and add children to it.
Vlad got the solution. I used it :
var grid = this.Content as Grid;
// or any controls
Label lblMessage = new Label
{
Content = "I am a label",
Margin = new Thickness(86, 269, 0, 0)
};
grid.Children.Add(lblMessage);

Categories

Resources