How do I refresh a BitmapCache in WPF? When I apply a BitmapCache, it stretches the window content I cached when I change the window size.
I need to know in either C# or VB .NET.
Depending on how much the window size is growing by, you could try using the "RenderAtScale" attribute, e.g. (xaml):
<Path ...>
<Path.CacheMode>
<BitmapCache RenderAtScale="2"/>
</Path.CacheMode>
</Path>
(C#):
Path path = new Path();
BitmapCache bitmapCache = new BitmapCache();
bitmapCache.RenderAtScale = 2;
path.CacheMode = bitmapCache;
This would cache the path at twice its original size, meaning that it shouldn't look fuzzy when scaled up.
I know this isn't what you asked to do but it might solve your problem.
http://blogs.msdn.com/b/llobo/archive/2009/11/10/new-wpf-features-cached-composition.aspx
In the above link it says:
"Note that changing the UIElement subtree or these properties (EnableClearType\RenderAtScale) will cause the cache to be regenerated."
So everytime the window is stretched, do one of the above to cause the cache to be regenerated/refreshed - I would imagine flipping the EnableClearType= property would be the simplest to try first.
Related
I'm trying to make a game which uses a pixel art style. I've created my assets and used them as resources, which are added into the window as System.Controls.Image objects.
In the designer, they always turn out too small. As a result, when I resize them, they become somewhat blurred due to some sort of bicubic interpolation being applied onto them.
I have managed to avoid this by avoiding resizing; I resize in the designer to find out what size is appropriate, and then use nearest neighbour resizing on the original image (using an external program) to get the source file to that size. I then update the image in the project and remove any resizing, therefore leaving it at original size, i.e. interpolation-free.
As you can imagine, this is a rather tedious process. I looked into interpolation choices during resizing, but most answers I can find relate to System.Drawing.Image, not System.Controls.Image. I feel like any such solution (if adapted) would be horribly messy and involve multiple (and perhaps unecessary) conversions/casts.
Is there any way to get nearest neighbour resizing on System.Controls.Image?
To set the resize mode, you need to set the RenderOptions.BitmapScalingMode="NearestNeighbor" option for the visual tree. You can set this at the window level.
To address the larger issue, it seems that something is causing your images to be scaled in the first place:
Ensure you are setting the Stretch="None" option on the Image control,
Ensure that you are using SnapsToDeveicePixels or Layout rounding
Lastly if all else fails, explicitly set the width and height of the image.
I have also run into instances where the image file's DPI not being set to (I believe) 90, causes the renderer to apply scaling.
I'm resizing a canvas with touch events as follows:
e.Handled = true;
var transformation = MyCanvas.RenderTransform as MatrixTransform;
var matrix = transformation == null ? Matrix.Identity :transformation.Matrix;
matrix.ScaleAt(e.DeltaManipulation.Scale.X,
e.DeltaManipulation.Scale.Y,
e.ManipulationOrigin.X,
e.ManipulationOrigin.Y);
MyCanvas.RenderTransform = new MatrixTransform(matrix);
The canvas has several child canvasses. I don't want to resize them and in fact need them to go smaller. So looked at RenderTransform.Inverse but am not having any joy.
You can create a custom canvas by inheriting from Panel with
A new dependency property: NonInheritableScale
A binding between bind the transform's scale to the NonInhertiableScale property
overrides of the MeasureOverride() and ArrangeOverride() methods,
so that the take 1.0/NonInhertiableScale.X and 1.0/NonInhertiableScale.Y into account during the layout.
Here is an article on creating custom WPF panels that might help you (a search result, haven't read it).
EDIT after reading the comment below
In that case of a chart you might want to redraw the chart with different axis ranges. A RenderTransform might be not accurate enough and indeed you will have to scale back everything else (axis, labels, gridlines,...)
previous answer, still valid
You will have to iterate through the child canvases and scale them individually. As far as I know there is no build in support for what you want.
You will have to apply both the inverse scale transformation to negate the parent's resize and a scale transformation that will make them smaller.
Post the code you are using to get more detailed help and or feedback.
I have a window that has a menu, a toolbar at the top, and various other controls. I then have my own control that derives from ContentControl that I want to have use up all remaining space. I can't leave it to its own devices unfortunately, because the control is a Win32 control that's sort of... put inside this WPF control, and I need to use SetWindowPos.
At the moment what I am doing is using ArrangeOverride, getting the MainWindow.Content control and looking at the Height and Width. I then use Size I get in as a parameter and call the SetWindowPos function. It's written in C++/CLI, and here's the code:
Size WebView::ArrangeOverride(Size finalSize)
{
Application::Current->MainWindow->Measure(finalSize);
UIElement^ obj = dynamic_cast<UIElement^>(Application::Current->MainWindow->Content);
double objHei = obj->RenderSize.Height;
double objWid = obj->RenderSize.Width;
SetWindowPos(hWnd, NULL, objWid-finalSize.Width, objHei-finalSize.Height, finalSize.Width, finalSize.Height, NULL);
So in my head I thought this would then set the position of the control to within the remaining available space. And it does sort of work, but it seems as if the MainWindow.Content control is not being measured until afterwards? What am I doing wrong here?
edit: most of the problems seem to be when full-screening the window and then un-fullscreening it.
I have managed to fix this by using the answer to this question here
I simply put my control into a Frame, so it'd be the parent.
Then using the Point I set the window position to that, along with the size that is passed through as a parameter to the ArrangeOverride method.
How do you get the REAL position of objects in silverlight?
I have a header image centered on the screen. When I make the browser window smaller, obviously, the header's left side goes off the screen. Finding out the actual position is good to know if you want to position objects on top of the image.
I capture the Content_Resized and I run a little test:
if (App.Current.Host.Content.ActualWidth > header.Width)
{
TEST = Canvas.GetLeft(header);
}
else
{
TEST = Canvas.GetLeft(header);
}
TEST always returns zero.
EDIT: header sits on a grid instead of a canvas. "Well, there is your problem..." So a better question might be this. How would I get the margins of an image sitting on a grid?
I probably should just answer the question but how to find the position of an element relative to another is probably something that has been answered before (by myself and others) here and elsewhere on the tinternet.
However if your goal is to place an item over an image then place the image in a Grid and then add the item as child of the Grid. That way you assign the relative position over the image as the margin of the item and let Silverlight's layout system do the rest.
As a general rule if you feel that you need to write code to move stuff about when the size of things change then unless you are writing a custom panel or something you're probably not using Silverlight layout system properly.
Edit:
Try this experiment:-
<Grid x:Name="LayoutRoot">
<Grid x:Name="headerContainer" Margin="50, 60, 0, 0" HorizontalAlignment="Left" VerticalAlignment="Top">
<Image Source="YourLargeImage" />
<Image Source="YourSmallerImage" HorizontalAlignment="Center" VerticalAlignment="Top" />
</Grid>
</Grid>
Now try changing the inner grid's Margin to move its position around the screen. Note the smaller image always remains at the top center of the large image.
I got it working.
First of all, these images are on a grid, not a canvas. But switching the grid to a canvas caused lots of other problems one of which is that I could not have the header image centered like before.
The solution was to change the margin of the smaller image sitting on top of the larger header image when the content resized like this:
blankbarimage.Margin = new Thickness((App.Current.Host.Content.ActualWidth - header.Width) / 2, 0, 0, 0);
and, by the way, you create a content resized method like this:
App.Current.Host.Content.Resized += new EventHandler(Content_Resized);
So, to answer my own question, the way you get the REAL position of object in silverlight is (if they are on a grid) by looking at their margin settings.
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_).