Odd behaviour of Image control in WPF - c#

I'm trying to get started with WPF in c#.
I set a Canvas as the content of a window, then I create another Canvas and put it as a child of the first Canvas (together with other elements, such as buttons and labels). Everything runs fine until I create an Image object and add it dynamically to the inner Canvas:
Image m_Img = new Image
{
Name = "img1",
Width = cvWindow.Width,
Height = dUsefulHeight,
Stretch = Stretch.Uniform
};
m_Img.Source = new BitmapImage(
new Uri(xMap.SelectSingleNode("#image").FirstChild.Value,
UriKind.RelativeOrAbsolute));
Canvas.SetLeft(m_Img, 0);
Canvas.SetTop(m_Img, 0);
double d = m_Img.Source.Width;
cvWindow.Children.Add(m_Img);
Here m_Img is the image I create, cvWindow is the inner Canvas. The source of the image is a PNG file, extracted from an XML file (the string returned is correct).
The odd behaviour is here: if I comment out the line
double d = m_Img.Source.Width;
the Image is not displayed anymore, although other controls in the Canvas (such as labels and buttons) are correctly displayed.
I don't need the width of the source image, so the compiler tells me that variable is never used.
I updated Visual Studio 2010 to the last SP1, but the behaviour remained. Google doesn't help either. I came to think that the Width property may have a getter method that triggers some action, but cannot solve the puzzle.
Edit: Same thing happens using another property of Source (e.g. Height). If I access at least one property, the image displays ok.

I finally discovered what happens: the Image control needs that the properties DecodePixelWidth and DecodePixelHeight of the Source are set to the correct values.
Once the Source is created, those values are not set, and the Image is not drawn. Upon first access to any property of the BitmapImage that serves as source the image is actually decoded and those properties are set to the final width and height of the decoded image (so the Image can be drawn).
I can solve this by setting those values by hand (with a cast to int) like this
BitmapImage bs1 = new BitmapImage();
bs1.BeginInit();
bs1.UriSource = new Uri(
xMap.SelectSingleNode("#image").FirstChild.Value,
UriKind.RelativeOrAbsolute);
bs1.EndInit();
bs1.DecodePixelHeight = (int)bs1.Height;
bs1.DecodePixelWidth = (int)bs1.Width;
m_Img.Source = bs1;
but I think I will re-design my views with a better separation (views in XAML, model and viewmodel via code).
This bit is also mentioned in the second note in http://msdn.microsoft.com/en-us/library/ms747027.aspx
When you specify the size of an image with either Width or Height, you should also set either DecodePixelWidth or DecodePixelHeight to the same respective size.

Don't define your views via code - use XAML,
even if you are trying to creating dynamic views, using XAML is much more clean
and using a good designer app (e.g. Blend), you'll notice things that you didn't consider.
Some things that you didn't consider are that .Width is not necessary equal to .ActalWidth.
Default widths are usually double.NaN (which means auto-width).
Use bindings, bind width of A to width of B, use margin, padding or value converters to make width A binded to width of B - const.
Use layout panels (e.g. Grid, StackPanel, DockPanel) to make alignment of multiple controls simple (and binding free).
Also, prefer using standard naming conventions (e.g. no m_).

Related

Replace an image when clicked

Not sure if the title makes any sense.
In my WPF application I would like the window to contain a small image, icon size. When the user clicks on the image another one simply replaces it, that holds the same dimensions.
I have all the images loaded into my project for C#/WPF. (By the way there are 3 images)
What I have been trying:
I tried changing the opacity in the code-behind to make one image
have full opacity and the others have no opacity. Didn't work as the
first toggle would strangely make all images disappear.
I also tried dynamically changing the image source in the code-behind. I
used if statements and a field to determine what image to switch
the source to. Code being:
if (toggle == 1)
{
thebutton.Source = new BitmapImage(new Uri(#"/images/icon2.png", UriKind.Relative));
toggle = 2;
}
Also did not work (made it blank, rather than switching to another image) but I feel like there is an obvious way I'm not seeing.
What I initially wanted to do was simply (like in graphics programs) raise and lower the images to the top and bottom to determine which ones the user should see. All that matters is the visual. The user clicks image 1, image 1 disappears and image 2 appears in its place, and so on for image 3.
Set the Build Action of the image files to Resource, and load them by Resource File Pack URIs:
thebutton.Source = new BitmapImage(new Uri("pack://application:,,,/images/icon2.png"));

show 1 million locations with markers on a map based on OpenStreetMap from C# VS2013 WPF

I would like to show 1 million locations on a map based on OpenStreetMap.
I work on C# VS2013 and GMAP.NET WPF. But, when I added markers for each location, the map cannot be shown up because the marker is a bitmap image.
And 1 million markers consume too much memory on my laptop (with 8 GB mem).
The code is:
public void add_marker(List<Tuple<double, double>> latLongList, ref GMapControl myMap)
{
System.Windows.Media.Imaging.BitmapImage bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.UriSource = new Uri(#"C:\djx_2014_6_3\my_projects\test_gmap_dot_net\GMap_WPF\try1\try_gmap_wpf\try_gmap_wpf\images\map_marker.png", UriKind.Absolute);
bitmapImage.DecodePixelHeight = 5;
bitmapImage.DecodePixelWidth = 5;
bitmapImage.EndInit();
foreach(var v in latLongList)
{
GMap.NET.PointLatLng point = new GMap.NET.PointLatLng(v.Item1, v.Item2);
GMapMarker marker = new GMapMarker(point);
System.Windows.Controls.Image image = new System.Windows.Controls.Image();
image.Source = bitmapImage;
marker.Shape = image;
marker.ZIndex = 5;
myMap.Markers.Add(marker);
}
}
I do not want to use the image as markers but I cannot find out how to use default marker in openStreetMap.
Any help would be appreciated.
WPF wasn't designed to be used for things like this. First of all you're creating a Bitmap for each tag, which is a user control and comes with some pretty heavy overhead for GUI hit-testing and binding etc. Secondly, WPF renders with DirectX, which means at some point all that data has to be set up with vertex buffers and uploaded into the graphics card. If you use data binding or try to create separate UI elements then this is going to take a lot of initial set-up time and memory, as you have already discovered. And if you try to draw them yourself (e.g. by creating your own user control and overriding OnRender) then it can be even worse, since all that work is now being done every frame (apart from buffered stuff which still incurs the initial setup anyway so you're back to square one).
If I had to do this myself I would start by organizing the data set with an appropriate 2D structure such as a k-d tree, an R*-tree or even a basic quad-tree. This will allow you to quickly determine at any given moment which markers are in the view frustum.
Next, I would add the appropriate bindings to scrollbars etc so that I could monitor exactly where the current view frustum was, and then each frame I would update the list of visible tags based on that. So long as you don't have more than a few thousand objects comes into view at once you should be ok, otherwise you'll have to stagger the updates over multiple frames with a queue.
If that doesn't suit your needs then you really have only two options left: 1) generate the raw bitmap data yourself manually, or 2) use a more suitable technology.

Adding image to rtf doc being resized

We're using migradoc api to create an rtf document.. intermittently when we add an image it;s being resized and coming out absolutely tiny.
Code sample as follows:
MigraDoc.DocumentObjectModel.Shapes.Image image = section.AddImage(imagePath);
image.WrapFormat.Style = MigraDoc.DocumentObjectModel.Shapes.WrapStyle.Through;
If I set LockAspectRatio to true and set a width it does stop is from rendering very small but ideally would like to be able to set a MaxWidth.
Has anyone experiences similar issue?
You have a choice of either changing the image itself by storing a different DPI value in it.
The .Net command to do so with a Bitmap bmp is:
bmp.SetResolution(newHRes , newVRes);
This should not involve reencoding, but I'm not sure.
However you also can simply set the desired DPI value in the Migradoc Image by using its Image.Resolution Property, which
Gets or sets a user defined resolution for the image in dots per inch.
I am not familiar with it at all but I found this code that might help you :
image.RelativeVertical = RelativeVertical.Page;
image.RelativeHorizontal = RelativeHorizontal.Page;

RenderTargetBitmap does not respect ColumnDefinitions width

I need to create snapshot of Grid with some hidden columns (by setting it's ColumnDefinition.Width = 0).
On screen it looks fine, but the created image has all columns visible (does not respect the ColumnDefinitions). I red somewhere that it is because the RenderTargetBitmap is looking at different layer where these changes aren't present (Visual layer vs. Layout layer). Is there any chance to get realistic snapshot of the grid with correct ColumnDefinitions? I cannot simply use Rectagnel.Fill = VisualBrush, because I need to store these images in cycle (every iteration = new image).
I tried ways like this snippet
It was needed to force UpdateLayout() before each snapshot. I changed the sizes in cycle and layout was updated too late.
Call this method before you create a snapshot of an UIElement:
public static UIElement GetMeasuredAndArrangedVisual(UIElement visual)
{
visual.Measure(new Size
{
Height = double.PositiveInfinity,
Width = double.PositiveInfinity
});
visual.Arrange(new Rect(0, 0, visual.DesiredSize.Width, visual.DesiredSize.Height));
visual.UpdateLayout();
return visual;
}

How do you draw a string to a Bitmap in Silverlight?

In normal C# it is easy to draw to a bitmap using the Grpahics.DrawString() method. Silverlight seems to have done away with Bitmap objects and Graphics is no longer available either. So...How am I meant to manipulate/create a bitmap when using Silverlight? If it helps, I am using Silverlight 3.
Let me tell you what I am doing. I am being given a template, basically a pre-rendered image. The user is then able to select from multiple images and enter the deisred text. I then render it to the image, adjusting size etc... within bounds and centering it in the pre-defined area of the image. If I can calculate the size (as in the MeasureString method) and then draw the string (as in the Graphics.DrawString method) that would be fine. The real question, no matter why I want to be able to do this, is can it be done?
The question is: why do you want to? Why not just use a TextBlock?
If you are trying to dynamically generate an image, use standard Silverlight/WPF controls (including TextBlock) and render them to a WritableBitmap.
Edit:
Ok, you've updated and expanded, which gives me more to go on. Unfortunately, you're not going to like the answer. First, keep in mind that Silverlight and WPF in general are vector based, and intended to be used as such. Although the Canvas allows you to do pseudo-pixel manipulations, you cannot be nearly as pixel-accurate as old-school GDI. This is a factor of your medium. If you absolutely have to measure things the way you want to measure them, I suggest you build your images on a remote server and transmit them to your Silverlight app.
You can calculate the size on-screen of the text rendered via a TextBlock using the ActualWidth and ActualHeight properties. But it only works on an already rendered control. Something like MeasureString is simply not available in Silverlight. Based on your description of your app, some user interaction could accomplish what you want. The user selects the image, enters the text, and is shown a preview. The user can then adjust the width and height of the various text areas until satisfied, at which point you can take a snapshot using the render method I liked to above.
The following may work, its a bit nebulous because I haven't tried yet myself.
The object you are looking for is the WritableBitmap.
You create a Visual tree, for example create your self a Grid or Canvas (you're not adding this to the UI). Add to it the selected image and a TextBlock positioned and sized as you prefer.
Create a new WritableBitmap either of a specific size or using the selected image to initialize it.
Use the WritableBitmap Render method passing the above root Grid or Canvas to it.
Now you have a bitmap which you should able to use to do whatever its you needed to do that required all this hoop jumping in the first place.

Categories