Why don't I see my programmatically generated image in WPF? - c#

I need to generate the image programmatically, so using an tag in the xaml isn't an option. I tried also to run at, when the window is loaded, but
Running the program just gives me a blank screen:
My .CS
namespace WpfApplication2
{
public partial class MainWindow : Window
{
public Image anImage;
public MainWindow()
{
InitializeComponent();
anImage = new Image();
anImage.Visibility = Visibility.Visible;
anImage.Width = 120;
anImage.Height = 310;
anImage.HorizontalAlignment = HorizontalAlignment.Left;
anImage.VerticalAlignment = VerticalAlignment.Top;
anImage.Margin = new Thickness(500, 500, 0, 0);
BitmapImage bmImage = new BitmapImage();
bmImage.BeginInit();
bmImage.UriSource = new Uri("balloonB.png", UriKind.Relative);
bmImage.EndInit();
anImage.Source = bmImage;
anImage.Stretch = Stretch.None;
this.AddVisualChild(anImage);
this.InvalidateVisual();
}
}
}
My XAML
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="1024" Width="1280" ResizeMode="NoResize"
WindowStartupLocation="CenterScreen" WindowState="Maximized">
<Grid Width="1280" Height="1024">
</Grid>
</Window>

There are at least two things wrong with your code.
First, you should make sure that the image file can be loaded at runtime. The easiest way is to add the file to your Visual Studio project, perhaps in a folder named "Images". The Build Action of the file has to be set to Resource, but this is the default for image files anyway. Now you can access this image resource file in code behind by means of a Pack URI (or a Resource Pack URI to be more precise). Given the folder name "Images", that URI would look like this:
pack://application:,,,/Images/balloonB.png
Second, you should not use AddVisualChild to add the Image control to your MainWindow. Instead, add it to the top-level Grid by setting the x:Name attribute on the Grid in XAML, and then using the auto-generated member variable with that name in code-behind.
<Window ...>
<Grid x:Name="rootGrid" Width="1280" Height="1024">
</Grid>
</Window>
Add the Image control to the Grid:
rootGrid.Children.Add(anImage);
Putting it all together and omitting unnecessary property settings, your code would look like this:
var anImage = new Image();
anImage.Width = 120;
anImage.Height = 310;
anImage.HorizontalAlignment = HorizontalAlignment.Left;
anImage.VerticalAlignment = VerticalAlignment.Top;
anImage.Margin = new Thickness(500, 500, 0, 0);
anImage.Source = new BitmapImage(new Uri("pack://application:,,,/Images/balloonB.png"));
rootGrid.Children.Add(anImage);
Or, a little shorter:
var anImage = new Image
{
Width = 120,
Height = 310,
HorizontalAlignment = HorizontalAlignment.Left,
VerticalAlignment = VerticalAlignment.Top,
Margin = new Thickness(500, 500, 0, 0),
Source = new BitmapImage(new Uri("pack://application:,,,/Images/balloonB.png"))
};
rootGrid.Children.Add(anImage);

I find many times that a full pack Uri is a bit much and that a simplified Uri works just as well if not better, just be sure that your image build property is configured as MSBuild Resource:
Image anImage = new Image();
anImage.Visibility = Visibility.Visible;
anImage.Width = 120;
anImage.Height = 310;
anImage.HorizontalAlignment = HorizontalAlignment.Left;
anImage.VerticalAlignment = VerticalAlignment.Top;
BitmapImage bmImage = new BitmapImage(new Uri(#"pack://application:,,,/yournameSpace;component/Subfolder/balloonB.png"));
bmImage.BeginInit();
bmImage.EndInit();
anImage.Source = bmImage;
anImage.Stretch = Stretch.None;
gr.Children.Add(anImage);
Xaml
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="1024" Width="1280" ResizeMode="NoResize"
WindowStartupLocation="CenterScreen" WindowState="Maximized">
<Grid Name="gr" Width="1280" Height="1024">
</Grid>
</Window>

There might be a problem with this line:
anImage.Margin = new Thickness(500, 500, 0, 0);
I renamed my Grid to gr and tried the following code and it works, I used the ImageBrush to draw the image:
Image anImage = new Image();
anImage.Visibility = Visibility.Visible;
anImage.Width = 120;
anImage.Height = 310;
anImage.HorizontalAlignment = HorizontalAlignment.Left;
anImage.VerticalAlignment = VerticalAlignment.Top;
//anImage.Margin = new Thickness(500, 500, 0, 0);
BitmapImage bmImage = new BitmapImage();
bmImage.BeginInit();
bmImage.UriSource = new Uri("balloonB.png", UriKind.Relative);
bmImage.EndInit();
ImageBrush ib = new ImageBrush(bmImage);
anImage.Source = ib.ImageSource;
gr.Children.Add(anImage);
Xaml
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="1024" Width="1280" ResizeMode="NoResize"
WindowStartupLocation="CenterScreen" WindowState="Maximized">
<Grid Name="gr" Width="1280" Height="1024">
</Grid>
</Window>

Related

UWP equivalent of Android's SpannableString with image inside

Is there a way to implement an image within a TextBlock rather than outside of it? Android has a feature called SpaanableStringBuilder which allows the user to create text with different appearances. Does UWP have something similar?
MyTextBlock.Text = "Google" + ? + "icon";
As Raymond said, in UWP, RichTextBlock is needed for image and text mixing.
With Xaml
<RichTextBlock>
<Paragraph>
<Run Text="Something..."/>
<InlineUIContainer>
<Image Source="your_image_url" Width="20" Height="20"/>
</InlineUIContainer>
<Run Text="Something..."/>
</Paragraph>
</RichTextBlock>
With Code
var richTextBlock = new RichTextBlock();
var para = new Paragraph();
para.Inlines.Add(new Run() { Text = "Something..." });
var imgContainer = new InlineUIContainer();
var img = new Image();
img.Source = new BitmapImage(new Uri("your_image_url"));
imgContainer.Child = img;
para.Inlines.Add(imgContainer);
para.Inlines.Add(new Run() { Text = "Something..." });
richTextBlock.Blocks.Add(para);
Or you could write it this way, which more closely mirrors the XAML.
var richTextBlock = new RichTextBlock()
{
Blocks = {
new Paragraph {
Inlines = {
new Run { Text = "Something" },
new InlineUIContainer {
Child = new Image {
Source = new BitmapImage(new Uri("your_image_url"))
}
},
new Run { Text = "Something..."}
}
}
}
};

Binding array of objects using ItemsControl

I need to bind array of object Images to WrapPanel.
I have declared objects in main class constructor:
public MainWindow()
{
InitializeComponent();
private Masina[] _masina = new Masina[12];
DataContext = new
{
data1 = _masina
};
}
My Class Masina haves few variables inside it, but I want to bind just Image:
public class Masina
{
public Image masina_pav = new Image();
public bool r_mas;
public string s_mas;
public Masina()
{
byte[] buffer = File.ReadAllBytes("teksturos/masinos/red/top.png");
MemoryStream memoryStream = new MemoryStream(buffer);
BitmapImage bitmap = new BitmapImage();
bitmap.BeginInit();
bitmap.DecodePixelWidth = 100;
bitmap.DecodePixelHeight = 200;
bitmap.StreamSource = memoryStream;
bitmap.EndInit();
bitmap.Freeze();
masina_pav.Source = bitmap;
Canvas.SetLeft(masina_pav, 100);
Canvas.SetTop(masina_pav, 200);
}
}
I have tried this XAML code:
<WrapPanel Name="zem" Height="1000" Width="1108" >
<ItemsControl ItemsSource="{Binding data1}" DisplayMemberPath="masina_pav">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Name="masinu_sarasas" HorizontalAlignment="Center" IsItemsHost="True" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</WrapPanel >
For now program starts but don't show me any Image (should be 12 of them). Can someone help me to figure it out ?
Image is a view class that should not be used in view models. Instead, your class should provide a public property of type ImageSource. Note that it's a property, not a field as you had declared it. This is necessary because WPF data binding only works with public properties.
public class Masina
{
public ImageSource MasinaPav { get; private set; }
...
public Masina()
{
using (var fileStream = new FileStream(
"teksturos/masinos/red/top.png",
FileMode.Open, FileAccess.Read, FileShare.Read))
{
var bitmap = new BitmapImage();
bitmap.BeginInit();
bitmap.DecodePixelWidth = 100;
bitmap.DecodePixelHeight = 200;
bitmap.StreamSource = fileStream;
bitmap.EndInit();
bitmap.Freeze();
MasinaPav = bitmap;
}
}
}
Now your ItemsControl would have an ItemTemplate with an Image control that is bound to the view model property:
<ItemsControl ItemsSource="{Binding data1}">
...
<ItemsControl.ItemTemplate>
<DataTemplate>
<Image Source="{Binding MasinaPav}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Besides that, you should be careful with setting a BitmapImage's DecodePixelWidth and DecodePixelHeight at the same time, because it may spoil the bitmap's aspect ratio.

How do I update an Image Source without losing the ability to refresh the image?

If I execute the following C#/WPF code, tempImage (System.Windows.Controls.Image) will show an image as expected.
Image tempImage = new Image();
tempImage.Source = layers[layerIndex].LayerImageSource;
// LayerImageSource is of type "ImageSource"
However, if I update LayerImageSource with a new ImageSource object of the same type, tempImage doesn't refresh itself (i.e., the original image is still shown instead of the updated image).
I have tried setting the binding as shown below, but all I get is a black rectangle (before I even try to update LayerImageSource).
Image tempImage = new Image();
Binding b = new Binding();
b.Path = new PropertyPath("BitmapSource"); // Also tried "Source" and "ImageSource"
b.Source = layers[layerIndex].LayerImageSource;
b.Mode = BindingMode.TwoWay; // Also tried BindingMode.Default
tempImage.SetBinding(Image.SourceProperty, b);
Here is my code to update LayerImageSource:
layerToUpdate.LayerImageSource = updatedMasterImage.ColoredImageSource;
Image curImage = (Image)curGrid.Children[0]; // Get the image from the grid
BindingExpression be = curImage.GetBindingExpression(Image.SourceProperty);
if (be != null)
be.UpdateSource();
Try this
Image tempImage = new Image();
BitmapImage img = new BitmapImage();
img.BeginInit();
img.UriSource = new Uri(layers[layerIndex].LayerImageSource.ToString(), UriKind.Relative);
img.EndInit();
tempImage.Source = img;
Reference link
I figured out the problem. The source must refer to the object and the path must refer to the property of the source object to which the binding is being bound. The complete code is below.
Binding tempSourceBinding = new Binding();
tempSourceBinding.Source = layers[layerIndex].layerImage;
tempSourceBinding.Path = new PropertyPath("Source");
tempSourceBinding.Mode = BindingMode.TwoWay;
Image tempImage = new Image();
tempImage.SetBinding(Image.SourceProperty, tempSourceBinding);
curGrid.Children.Insert(0, tempImage);
The GetBindingExpression and UpdateSource code is not necessary.

Add images to ListBox (c#, windows phone 7)

I am developing the Windows Phone 7, I need to add images to ListBox.
I want do it with C# code, not XAML.
I read about it, but everyone uses BitmapImage, which I can't get to work on Windows Phone 7.
I have XAML code:
<StackPanel Margin="0,0,0,17">
<local:MatchDates Adress="http://soccernet.espn.go.com/results/_/league/esp.1/spanish-la-liga?cc=57393" />
</StackPanel>
and C# function in my class MatchDates:
void add_items(List<List<string>> code)
{
if (code.Count == 0)
this.Items.Add("no mathes");
else
{
foreach (List<string> temp1 in code)
{
foreach (string temp2 in temp1)
{
this.Items.Add(temp2);
}
this.Items.Add("---------------------------------");
}
}
}
How can I add images in function add_items?
This code:
Uri uri = new Uri("/eng.png", UriKind.Relative);
BitmapImage bitmap = new BitmapImage(uri);
Image img = new Image();
img.Height = 30;
img.Width = 30;
img.Source = bitmap;
this.Items.Add(img);
this.Items.Add(temp2);
Only empty space is presented, How do I add images to ListBox?
One of the ways I used:
XAML:
<ListBox Name="lstView" ItemsSource="{Binding}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Source="{Binding ImagePath}"></Image>
<TextBlock Text="{Binding Name}"></TextBlock>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
C#:
public class Article
{
public string Name { get; set; }
public string ImagePath { get; set; }
}
Article article1 = new Article() { Name = "name1", ImagePath = "path of image 1" };
Article article2 = new Article() { Name = "name2", ImagePath = "path of image 2" };
var articles = new List<Article>();
articles.Add(article1);
articles.Add(article2);
lstView.DataContext = articles;
For retrieving articles I use WCF service.
BitmapImage is supported. You have to declare the BitmapImage as ImageSource.

How can I add multiple FlowDocumentReaders to a StackPanel?

Thanks to Leom's answer I was able to add a FlowDocument to a StackPanel by wrapping it in a FlowDocumentReader.
But now I have two problems:
it seems only the first FlowDocumentReader is added and the rest ignored
there is an unwanted margin that I can't get rid of
How can I add multiple FlowDocumentReaders to a StackPanel without the unwanted margin?
alt text http://www.deviantsart.com/upload/1ndiqqe.png
XAML:
<Window x:Class="TestFlowdoc23432.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="200" Width="300">
<StackPanel Margin="10">
<ContentControl x:Name="MainArea"/>
</StackPanel>
</Window>
Code Behind:
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
namespace TestFlowdoc23432
{
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
StackPanel sp = new StackPanel();
TextBlock tb1 = new TextBlock();
tb1.Text = "first text block";
sp.Children.Add(tb1);
TextBlock tb2 = new TextBlock();
tb2.Text = "second text block";
sp.Children.Add(tb2);
sp.Children.Add(GetFlowDocumentReader("first flow document reader"));
sp.Children.Add(GetFlowDocumentReader("second flow document reader"));
MainArea.Content = sp;
}
FlowDocumentReader GetFlowDocumentReader(string text)
{
FlowDocumentReader fdr = new FlowDocumentReader();
FlowDocument fd = new FlowDocument();
fdr.Document = fd;
fdr.Margin = new Thickness(0);
Paragraph par = new Paragraph();
par.Margin = new Thickness(0);
fd.Blocks.Add(par);
Run r = new Run(text);
par.Inlines.Add(r);
return fdr;
}
}
}
To make the text appear to the left you need to set the pagepadding property on your flowdocument as follows:
fd.PagePadding = new Thickness(0);
the reason that you only seem to get the first reader is becuase it fills the space available (move it to be the first object and you will not see the textblocks).
If you change the FlowDocumentReader to be a FlowDocumentScrollViewer and use the VerticalScrollBarVisibility property then you can get the desired effect. The following is your GetFlowDocumentReader method with the changes applied:
FlowDocumentScrollViewer GetFlowDocumentReader(string text)
{
FlowDocumentScrollViewer fdr = new FlowDocumentScrollViewer();
FlowDocument fd = new FlowDocument();
fdr.Document = fd;
fdr.Margin = new Thickness(0);
Paragraph par = new Paragraph();
par.Margin = new Thickness(0);
fd.Blocks.Add(par);
Run r = new Run(text);
par.Inlines.Add(r);
fd.PagePadding = new Thickness(0);
fdr.VerticalScrollBarVisibility = ScrollBarVisibility.Hidden;
return fdr;
}

Categories