I am making a video game and have problem with screen resolutions.
Earlier, a few month ago, I asked very similar question.
Need images with lower resolution "stretched" to screen size
Back then, it seemed the answer I got (using ViewBox) was perfect, but now I am getting problems.
I want every and every image and control in my game to have a few different possible variations fit for resolutions.
For example, if user sets resolution 800x600, all images and buttons are reduced to correct sizes. But I do want my game to be fullscreen only, not windowed.
So, if the resolution is lower then end-user monitor has, all images must be stretched and look "fuzzy". And if higher, part of it must be outside of screen.
For now, my code just sets resolution to end-user monitor, whatever it is. That's absolutely not what I want.
What I get:
A very crude example of what I need:
I'll show xaml as its now. Of course it isn't full, but I'll show the beginning and a few elements, so you'll know how it's built.
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" x:Name="wdwMain" x:Class="RealityIncognita.MainWindow"
Height="900" Width="1600" ResizeMode="NoResize" WindowState="Maximized" Cursor="Cross" WindowStyle="None" Loaded="wdwMain_Loaded">
<Viewbox x:Name="viewMain" Stretch="Fill">
<Grid x:Name="areaContainer" HorizontalAlignment="Left" Height="900" VerticalAlignment="Top" Width="1600">
<Grid x:Name="areaMain">
<Grid.Background>
<ImageBrush ImageSource="Resources/Images/Interface/main_interface.jpg"/>
</Grid.Background>
<Label x:Name="lblTextOutput" Content="Label" HorizontalAlignment="Center" Height="52" Margin="55,726,31,0" VerticalAlignment="Top" Width="1514" FontFamily="Arial" FontSize="22" FontWeight="Bold" HorizontalContentAlignment="Center"/>
<Button x:Name="btnExit" HorizontalAlignment="Left" Height="106" Margin="1464,771,0,0" VerticalAlignment="Top" Width="126" Click="btnExit_Click" Background="{x:Null}" BorderBrush="{x:Null}" Foreground="{x:Null}">
<Button.Template>
<ControlTemplate>
<Image Source="/Resources/Images/Interface/Blank.png" Stretch="Fill" Margin="12,0,6,0"/>
</ControlTemplate>
</Button.Template>
</Button>
<Grid x:Name="areaShowers" HorizontalAlignment="Left" Height="700" Margin="1653,790,-1561,-590" VerticalAlignment="Top" Width="1508" IsVisibleChanged="areaShowers_IsVisibleChanged">
<Grid.Background>
<ImageBrush ImageSource="Resources/Images/Rooms/Showers/shower_room.jpg" />
</Grid.Background>
<Button x:Name="objShowersSoap" HorizontalAlignment="Left" Height="29" Margin="613,423,0,0" VerticalAlignment="Top" Width="17" MouseLeave="MouseLeaveAnyObject" RenderTransformOrigin="11.706,1.897" Click="objShowersSoap_Click" MouseEnter="objShowersSoap_MouseEnter">
<Button.Template>
<ControlTemplate>
<Image Source="Resources/Images/Rooms/Showers/soap.png" Stretch="Fill" Margin="0,0,0,0"/>
</ControlTemplate>
</Button.Template>
</Button>
<Image x:Name="imgShowersOpenMachine" HorizontalAlignment="Left" Margin="0,0,0,0" VerticalAlignment="Top" Source="Resources/Images/Rooms/Showers/drying_machine_open.png" RenderTransformOrigin="0.808,0.471" Stretch="Fill"/>
</Button>
</Grid>
<Grid x:Name="areaLockerRoom" Height="700" VerticalAlignment="Top" Width="1508" IsVisibleChanged="areaLockerRoom_IsVisibleChanged" Margin="1653,17,-1561,0" MouseDown="areaLockerRoom_MouseDown" MouseEnter="areaLockerRoom_MouseEnter" MouseMove="areaLockerRoom_MouseMove">
<Grid.Background>
<ImageBrush ImageSource="Resources/Images/Rooms/LockerRoom/locker_room_ready.png"/>
</Grid.Background>
<Button x:Name="objLockerRoomCrowbar" Content="" HorizontalAlignment="Left" Height="243" Margin="604,328,0,0" VerticalAlignment="Top" Width="51" MouseEnter="objCrowbar_MouseEnter" Panel.ZIndex="1" Click="objCrowbar_Click" MouseLeave="MouseLeaveAnyObject">
<Button.Template>
<ControlTemplate>
<Image Source="Resources/Images/Rooms/LockerRoom/crowbar_only.png" Stretch="Fill" Margin="0,0,0,0"/>
</ControlTemplate>
</Button.Template>
</Button>
<Button x:Name="objLockerRoomOdyssey" HorizontalAlignment="Left" Height="53" Margin="797,638,0,0" VerticalAlignment="Top" Width="61" Click="objBookOdyssey_Click" MouseLeave="MouseLeaveAnyObject" MouseEnter="objBookOdyssey_MouseEnter">
<Button.Template>
<ControlTemplate>
<Image Source="Resources/Images/Rooms/LockerRoom/img_book_odyssey.png" Stretch="Fill" Margin="0,0,0,0"/>
</ControlTemplate>
</Button.Template>
</Button>
<Button x:Name="objLockerRoomEdda" HorizontalAlignment="Left" Height="53" Margin="1335,549,0,0" VerticalAlignment="Top" Width="61" MouseLeave="MouseLeaveAnyObject" Click="objBookEdda_Click" MouseEnter="objBookEdda_MouseEnter">
<Button.Template>
<ControlTemplate>
<Image Source="Resources/Images/Rooms/LockerRoom/img_book_edda.png" Stretch="Fill" Margin="0,0,0,0"/>
</ControlTemplate>
</Button.Template>
</Button>
To summarize the structure is like that:
Main Window - Viewbox - areaContainer (main grid) - game areas (grids) - images and buttons for each grid
To summarize:
What I get: game screens are set for end-user monitor resolution.
What I need: game sets all images to specific resolution, makes it "fuzzy" if resolution is LOWER then end-user's, and "cuts" it if it is HIGHER.
Thank you in advance,
Evgenie
ADDED:
If I understand correctly how it works - the container grid that includes all other items should be resized, images should become smaller (by default they are 1600x900), if needed. Then, this container grid must fit user's screen resolution, staying with small images quality.
And to simplify it even more: Can I make large images smaller, then change them to be big again (and lose quality) directly in Visual Studio?
Actually simply adjusting the viewbox did the trick.
<Viewbox x:Name="viewMain" VerticalAlignment="Center" HorizontalAlignment="Center">
And in code, I had to change MaxWidth and MaxHeight, instead of regular ones.
viewMain.MaxWidth = 1024;
viewMain.MaxHeight = 768;
Here is my xaml:
<StackPanel Height="333">
<Canvas x:Name="imageCanvas"
RenderOptions.BitmapScalingMode="NearestNeighbor"
RenderOptions.EdgeMode="Aliased">
<Canvas.Background>
<ImageBrush x:Name="image1"
Stretch="None"
AlignmentX="Left"
AlignmentY="Top">
</ImageBrush>
</Canvas.Background>
</Canvas>
<Canvas x:Name="overlayCanvas">
<Rectangle Name="dummyRectangle" Width="1" Height="2" Fill="Transparent" />
</Canvas>
</StackPanel>
Here is my C# code behind:
void PlayImages()
{
string testImageFolder = "C:\\TestImages";
DirectoryInfo d = new DirectoryInfo(testImageFolder);//Assuming Test is your Folder
FileInfo[] Files = d.GetFiles("*.tif"); //Getting Text files
image1.ImageSource = new BitmapImage(new Uri("C:\\TestImages\\ChanA_0001_0001_0001_0001.tif"));
}
However, when the C# code above executed, nothing happened on UI. I am wondering where should I change to make the image show up? Thanks.
It was the stack panel that causes the problem. I am not sure why, but if some of them are removed, then the image shows up. The xaml with some stack penal removed is like following:
<StackPanel HorizontalAlignment="Left">
<!--Controls:MenuControl/-->
<Controls:ToggleButtonControl Margin="0,0,0,0" Height="43" RenderTransformOrigin="0.5,-0.233" />
</StackPanel>
<Canvas x:Name="imageCanvas"
RenderOptions.BitmapScalingMode="NearestNeighbor"
RenderOptions.EdgeMode="Aliased" Margin="0,52,0,0">
<Canvas.Background>
<ImageBrush x:Name="image1"
Stretch="None"
AlignmentX="Left"
AlignmentY="Top"
ImageSource="{Binding Path=Bitmap, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}">
</ImageBrush>
</Canvas.Background>
</Canvas>
Your imageCanvas always has width and height zero, because you neither add any children, not set its Width or Height explicitly.
Change your XAML to use only one Canvas instead, and optionally (depending on the outer container and the children to be added) set its Width and Height properties:
<Canvas x:Name="overlayCanvas" Height="333" Width="500">
<Canvas.Background>
<ImageBrush x:Name="image1" ... />
</Canvas.Background>
<Rectangle ... />
</Canvas>
For resolution independence we want scaling art. Ok, so a common source for that mentioned on stack is Syncfusion Metro Studio.
Metro Studio 2 produces this for XAML:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Viewbox x:Key="error">
<Grid Width="64" Height="64" Visibility="Visible">
<Grid Visibility="Visible">
<Rectangle Fill="#FFD21818" Visibility="Visible" />
<Ellipse Fill="#FFD21818" Visibility="Collapsed" />
<Path Data="M50.5,4.7500001C25.232973,4.75 4.75,25.232973 4.7500001,50.5 4.75,75.767029 25.232973,96.25 50.5,96.25 75.767029,96.25 96.25,75.767029 96.25,50.5 96.25,25.232973 75.767029,4.75 50.5,4.7500001z M50.5,0C78.390381,0 101,22.609621 101,50.5 101,78.390381 78.390381,101 50.5,101 22.609621,101 0,78.390381 0,50.5 0,22.609621 22.609621,0 50.5,0z" Stretch="Fill" Fill="#FFD21818" Visibility="Collapsed" />
</Grid>
<Path Data="F1M54.0573,47.8776L38.1771,31.9974 54.0547,16.1198C55.7604,14.4141 55.7604,11.6511 54.0573,9.94531 52.3516,8.23962 49.5859,8.23962 47.8802,9.94531L32.0026,25.8229 16.1224,9.94531C14.4167,8.23962 11.6511,8.23962 9.94794,9.94531 8.24219,11.6511 8.24219,14.4141 9.94794,16.1198L25.8255,32 9.94794,47.8776C8.24219,49.5834 8.24219,52.3477 9.94794,54.0534 11.6511,55.7572 14.4167,55.7585 16.1224,54.0534L32.0026,38.1745 47.8802,54.0534C49.5859,55.7585 52.3516,55.7572 54.0573,54.0534 55.7604,52.3477 55.763,49.5834 54.0573,47.8776z" Stretch="Uniform" Fill="#FFFFFFFF" Width="36" Height="36" Margin="0,0,0,0" RenderTransformOrigin="0.5,0.5">
<Path.RenderTransform>
<TransformGroup>
<TransformGroup.Children>
<RotateTransform Angle="0" />
<ScaleTransform ScaleX="1" ScaleY="1" />
</TransformGroup.Children>
</TransformGroup>
</Path.RenderTransform>
</Path>
</Grid>
</Viewbox>
</ResourceDictionary>
So far so good, just merge this into the project resources. But how to consume this?
using viewbox in ResourceDictionary file has an answer that lets you change the ViewBox to DataTemplate in the ResourceDictionary and then use a converter to display it as a button's ContentTemplate. That is ok for Button based stuff, but what if I just need the icon itself. How do I go from ViewBox in a resource dictionary to somehow including it in lets say a grid in XAML?
You can use it as Content for any ContentControl Directly,in case if you just want the icon in Grid,try like below,
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Viewbox x:Key="error">
<Grid Width="64" Height="64" Visibility="Visible">
<Grid Visibility="Visible">
<Rectangle Fill="#FFD21818" Visibility="Visible" />
<Ellipse Fill="#FFD21818" Visibility="Collapsed" />
<Path Data="M50.5,4.7500001C25.232973,4.75 4.75,25.232973 4.7500001,50.5 4.75,75.767029 25.232973,96.25 50.5,96.25 75.767029,96.25 96.25,75.767029 96.25,50.5 96.25,25.232973 75.767029,4.75 50.5,4.7500001z M50.5,0C78.390381,0 101,22.609621 101,50.5 101,78.390381 78.390381,101 50.5,101 22.609621,101 0,78.390381 0,50.5 0,22.609621 22.609621,0 50.5,0z" Stretch="Fill" Fill="#FFD21818" Visibility="Collapsed" />
</Grid>
<Path Data="F1M54.0573,47.8776L38.1771,31.9974 54.0547,16.1198C55.7604,14.4141 55.7604,11.6511 54.0573,9.94531 52.3516,8.23962 49.5859,8.23962 47.8802,9.94531L32.0026,25.8229 16.1224,9.94531C14.4167,8.23962 11.6511,8.23962 9.94794,9.94531 8.24219,11.6511 8.24219,14.4141 9.94794,16.1198L25.8255,32 9.94794,47.8776C8.24219,49.5834 8.24219,52.3477 9.94794,54.0534 11.6511,55.7572 14.4167,55.7585 16.1224,54.0534L32.0026,38.1745 47.8802,54.0534C49.5859,55.7585 52.3516,55.7572 54.0573,54.0534 55.7604,52.3477 55.763,49.5834 54.0573,47.8776z" Stretch="Uniform" Fill="#FFFFFFFF" Width="36" Height="36" Margin="0,0,0,0" RenderTransformOrigin="0.5,0.5">
<Path.RenderTransform>
<TransformGroup>
<TransformGroup.Children>
<RotateTransform Angle="0" />
<ScaleTransform ScaleX="1" ScaleY="1" />
</TransformGroup.Children>
</TransformGroup>
</Path.RenderTransform>
</Path>
</Grid>
</Viewbox>
</ResourceDictionary>
<Grid>
<ContentControl Content="{StaticResource error}"/>
</Grid>
if you are intend to use the same resource in multiple location,please set x:Shared attribute as false as below,
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Viewbox x:shared="false" x:Key="error">
.......
</Viewbox>
</ResourceDictionary>
I did a similar thing in one of my projects using Syncfusion Metro Studio. I found wrapping the image in a ViewBox caused a number of annoyances. I ended up using the image alone, and then embedded it into a Path where I wanted to use the image.
I found it a lot more flexible than trying to squeeze a ViewBox into xaml.
How I did it:
There is a type Geometry, which allows you to define a Bezier path as a resource:
<Geometry x:Key="Keyboard">M48.537998,24.254L57.365002,24.254 57.365002,30.875 48.537998,30.875z M17.642,24.254L46.332001,24.254 46.332001,30.875 17.642,30.875z M6.6760006,24.254L15.504,24.254 15.504,30.875 6.6760006,30.875z M50.744999,15.426L57.365002,15.426 57.365002,22.047001 50.744999,22.047001z M41.986,15.426L48.606998,15.426 48.606998,22.047001 41.986,22.047001z M33.09,15.426L39.709999,15.426 39.709999,22.047001 33.09,22.047001z M24.261999,15.426L30.882999,15.426 30.882999,22.047001 24.261999,22.047001z M15.435,15.426L22.056,15.426 22.056,22.047001 15.435,22.047001z M6.6070004,15.426L13.229,15.426 13.229,22.047001 6.6070004,22.047001z M50.744999,6.599L57.365002,6.599 57.365002,13.219 50.744999,13.219z M41.986,6.599L48.606998,6.599 48.606998,13.219 41.986,13.219z M33.09,6.599L39.709999,6.599 39.709999,13.219 33.09,13.219z M24.261999,6.599L30.882999,6.599 30.882999,13.219 24.261999,13.219z M15.435,6.599L22.056,6.599 22.056,13.219 15.435,13.219z M6.6070004,6.599L13.229,6.599 13.229,13.219 6.6070004,13.219z M4.47015,4.4635506L4.47015,33.242199 59.6413,33.242199 59.6413,4.4635506z M1.3333101,0L62.666698,0C63.403,0,64,0.59634399,64,1.3333397L64,36.166698C64,36.903702,63.403,37.5,62.666698,37.5L1.3333101,37.5C0.59704602,37.5,0,36.903702,0,36.166698L0,1.3333397C0,0.59634399,0.59704602,0,1.3333101,0z</Geometry>
Once you have a geometry resource you can use it in Path.Data. In this example the border is the bounds of the image 32x32 pixels. Then you can use the Border and use it in a Grid as you would with any other control.
<Border Width="32" Height="32">
<Path Data="{StaticResource Keyboard}" Fill="White" Stretch="Uniform" RenderTransformOrigin="0.5, 0.5">
</Path>
</Border>
This technique also allows you to bind the properties as needed. I.e. Fill to a color, and have it change dynamically.
Simply import the output from Metro Studio into Expression Blend
-> Design View
Select icon and click 'Tools' -> Make Brush Resource -> MakeDrawingBrush
Then Blend will convert the icon for use anywhere in your app
I'm binding following XAML to RotateAngle property and it works great with one "but". Image displays cropped. Image control doesn't seem to be refreshing/resizing after rotation. Is there any way to force resize on image and scrollviewer?
<ScrollViewer Grid.Row="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" VerticalScrollBarVisibility="Auto" BorderThickness="0" HorizontalScrollBarVisibility="Auto">
<Image
VerticalAlignment="Stretch" HorizontalAlignment="Stretch"
Source="{Binding Input, Converter={StaticResource ByteArrayToBitmapConverter}}"
RenderTransformOrigin="0.5,0.5">
<Image.RenderTransform>
<RotateTransform Angle="{Binding RotateAngle}"></RotateTransform>
</Image.RenderTransform>
</Image>
</ScrollViewer>
http://www.silverlight.net/content/samples/sl3/toolkitcontrolsamples/run/default.html
Go to this page, there is a control called LayoutTransformer. See the sample of that control. It handles rotation, scaling and skewing of images, textbox, listbox, etc.
You will get the code there.
Hope that helps.!
You can try:
<Image x:name="ctrl"
VerticalAlignment="Stretch" HorizontalAlignment="Stretch"
Source="{Binding Input, Converter={StaticResource ByteArrayToBitmapConverter}}"
RenderTransformOrigin="0.5,0.5">
<Image.RenderTransform>
<RotateTransform Angle="{Binding DataContext.RotateAngle, ElementName=ctrl}"></RotateTransform>
</Image.RenderTransform>
</Image>
Or you can use:
<Image
VerticalAlignment="Stretch" HorizontalAlignment="Stretch"
Source="{Binding Input, Converter={StaticResource ByteArrayToBitmapConverter}}"
RenderTransformOrigin="0.5,0.5">
<Image.RenderTransform>
<RotateTransform Angle="{Binding DataContext.RotateAngle, RelativeSource={RelativeSource Self}}"></RotateTransform>
</Image.RenderTransform>
</Image>
Assuming you want to scale your image down to fit the original image space, you could use my CalculateConstraintScale method from here:
Silverlight Rotate & Scale a bitmap image to fit within rectangle without cropping to scale the image down based on the rotation.
Click here for a working testbed app created for that answer (looks like the image below):
I am using Silverlight 3.0 + .Net 3.5 + VSTS 2008 + C# to silverlight application.
I want to learn TranslateTransform and RenderTransformOrigin, could anyone recommend me some tutorials? I am a newbie of this area. And I did not find anything which is good to learn for a newbie from MSDN (correct me if there are some good stuff). :-)
BTW: I am headache about the coordination transformation matrix, it is great if the tutorial could cover this topic.
EDIT: here is the code which I am confused.
<Grid Margin="-1,0,100,0" x:Name="controlsContainer" Height="35" RenderTransformOrigin="0.5,0.5" VerticalAlignment="Bottom">
<Grid.RenderTransform>
<TransformGroup>
<ScaleTransform/>
<SkewTransform/>
<RotateTransform/>
<TranslateTransform Y="0"/>
</TransformGroup>
</Grid.RenderTransform>
<Rectangle Margin="0,0,0,0" Height="35" VerticalAlignment="Top" Fill="#97000000" Stroke="#00000000" RenderTransformOrigin="0.5,0.5"/>
<VideoPlayer:mediaControl Height="35" Margin="1,0,0,0" HorizontalAlignment="Stretch" VerticalAlignment="Top" x:Name="mediaControls" Visibility="Visible"/>
</Grid>
First of all translation does not use an origin so the RenderTransformOrigin does not apply to a TranslateTransform.
To learn about transforms why not try them out? Place a shape two times in a grid, and let the top one be transparent. Then transform the top shap and view the effect. Here I have rotated a rectangle by 45 degrees around the center of the rectangle.
<Grid Background="White">
<Rectangle Width="50" Height="50" Fill="Black"/>
<Rectangle Width="50" Height="50" Fill="Red" Opacity="0.5"
RenderTransformOrigin="0.5, 0.5">
<Rectangle.RenderTransform>
<RotateTransform Angle="45"/>
</Rectangle.RenderTransform>
</Rectangle>
</Grid>
Translate is specifically referred to by MSDN as Move. Refer to section to get a visual understanding of Transformations and Coordinate Systems.
Moves (translates) an element by the specified X and Y amounts.
alt text http://i.msdn.microsoft.com/dynimg/IC212086.png