Here's my requirement: Using DirectX11 (via SlimDX) I have to download a series of verteces and use them to create a Texture2D of a map of county borders. Then I need to do the same thing with state borders, and draw them over the county borders. Then, I need to take that texture and create 2 different textures from it, each containing unique radar data. Then I want to take those textures and display them so that the user can look at, for example, base reflectivity and base velocity side by side. The user should be able to zoom in and out of particular areas of the map.
Here's what I've got working: I'm creating my Texture2D without multisampling or depth on a billboard which is displaying in 2 separate views. But it looks blocky, and if you zoom too far out, some of the borders start to disappear.
Here are my issues:
1) I can't for the life of me get any multisampling quality. I'm using an ATI Radeon HD 5750, so I know it must be able to do it, but no formats I've tried support a quality greater than 0.
2) I'm uncertain whether I need to use a depth stencil since I'm drawing all these textures on top of each other. I hope not because when I try, the ShaderResourceView says, "Puny Human! You cannot use a depth stencil format in a ShaderResourceView! Bwa ha ha!" (I'm embellishing)
I'm willing to bet that a lot of these issues would be solved if I just drew the primitives directly into the world space, but when I do that rendering takes way too long because there are so many lines to render. Is there perhaps a way I can cut down on the time it takes?
And here's the code of my last working version:
using SlimDX;
using SlimDX.D3DCompiler;
using SlimDX.Direct3D11;
using SlimDX.DXGI;
using SlimDX.Windows;
using System;
using System.Windows.Forms;
using System.Collections.Generic;
using Device = SlimDX.Direct3D11.Device;
using Buffer = SlimDX.Direct3D11.Buffer;
using Resource = SlimDX.Direct3D11.Resource;
using Format = SlimDX.DXGI.Format;
using MapFlags = SlimDX.Direct3D11.MapFlags;
namespace Radar
{
abstract public class Renderer
{
protected static Device mDevice = null;
protected SwapChain mSwapChain = null;
protected RenderTargetView RenderTarget { get; set; }
public static Device Device { get { return mDevice; } protected set { mDevice = value; } }
public static DeviceContext Context { get { return Device.ImmediateContext; } }
protected SwapChain SwapChain { get { return mSwapChain; } set { mSwapChain = value; } }
public Texture2D Texture { get; protected set; }
protected int RenderTargetIndex { get; set; }
protected VertexShader VertexShader { get; set; }
protected PixelShader PixelShader { get; set; }
protected Buffer VertexBuffer { get; set; }
protected Buffer MatrixBuffer { get; set; }
protected InputLayout Layout { get; set; }
protected ShaderSignature InputSignature { get; set; }
protected SamplerState SamplerState { get; set; }
protected Color4 mClearColor = new Color4(0.117f, 0.117f, 0.117f);
protected Color4 ClearColor { get { return mClearColor; } }
protected void CreateDevice(IntPtr inHandle)
{
if (Device == null)
Device = new Device(DriverType.Hardware, DeviceCreationFlags.Debug);
SwapChainDescription chainDescription = new SwapChainDescription()
{
BufferCount = 2,
Usage = Usage.RenderTargetOutput,
OutputHandle = inHandle,
IsWindowed = true,
ModeDescription = new ModeDescription(0, 0, new Rational(60, 1), Format.R8G8B8A8_UNorm),
SampleDescription = new SampleDescription(8, 0),
Flags = SwapChainFlags.AllowModeSwitch,
SwapEffect = SwapEffect.Discard
};
SwapChain = new SwapChain(Device.Factory, Device, chainDescription);
}
protected void SetupViewport(int inWidth, int inHeight)
{
Viewport viewport = new Viewport(0.0f, 0.0f, inWidth, inHeight);
Context.OutputMerger.SetTargets(RenderTarget);
Context.Rasterizer.SetViewports(viewport);
}
public void Clear()
{
Context.ClearRenderTargetView(RenderTarget, ClearColor);
}
public void Present()
{
SwapChain.Present(0, PresentFlags.None);
}
// I do this to ensure the texture is correct
public void Save()
{
Texture2D.ToFile(Context, Texture, ImageFileFormat.Png, "test.png");
}
public virtual void Dispose()
{
Texture.Dispose();
SamplerState.Dispose();
VertexBuffer.Dispose();
Layout.Dispose();
InputSignature.Dispose();
VertexShader.Dispose();
PixelShader.Dispose();
RenderTarget.Dispose();
SwapChain.Dispose();
Device.Dispose();
}
public class RenderTargetParameters
{
public int Width { get; set; }
public int Height { get; set; }
public IntPtr Handle { get; set; }
public RenderTargetParameters()
{
Width = 0;
Height = 0;
Handle = new IntPtr(0);
}
}
public abstract void Render(int inWidth, int inHeight, int inCount = -1);
public abstract void Prepare(string inShaderName = null);
}
public class TextureRenderer : Renderer
{
public TextureRenderer(RenderTargetParameters inParms)
{
CreateDevice(inParms.Handle);
Texture2DDescription description = new Texture2DDescription()
{
Width = inParms.Width,
Height = inParms.Height,
MipLevels = 1,
ArraySize = 1,
Format = Format.R8G8B8A8_UNorm,
SampleDescription = new SampleDescription(8, 0),
Usage = ResourceUsage.Default,
BindFlags = BindFlags.RenderTarget | BindFlags.ShaderResource,
CpuAccessFlags = CpuAccessFlags.None,
OptionFlags = ResourceOptionFlags.None
};
Texture = new Texture2D(Device, description);
RenderTarget = new RenderTargetView(Device, Texture);
SetupViewport(inParms.Width, inParms.Height);
using (ShaderBytecode bytecode = ShaderBytecode.CompileFromFile("ShaderFX.fx", "VShader", "vs_5_0", ShaderFlags.Debug, EffectFlags.None))
{
InputSignature = ShaderSignature.GetInputSignature(bytecode);
VertexShader = new VertexShader(Device, bytecode);
}
// load and compile the pixel shader
InputElement[] elements = new[] { new InputElement("POSITION", 0, Format.R32G32B32_Float, 0) };
Layout = new InputLayout(Device, InputSignature, elements);
Context.InputAssembler.InputLayout = Layout;
Context.InputAssembler.PrimitiveTopology = PrimitiveTopology.LineStrip;
Context.VertexShader.Set(VertexShader);
}
public override void Prepare(string inShaderName)
{
using (ShaderBytecode bytecode = ShaderBytecode.CompileFromFile("ShaderFX.fx", inShaderName, "ps_4_0", ShaderFlags.Debug, EffectFlags.None))
PixelShader = new PixelShader(Device, bytecode);
Context.PixelShader.Set(PixelShader);
}
public void SetVertices(DataStream inShape)
{
VertexBuffer = new Buffer(Device, inShape, (int)inShape.Length, ResourceUsage.Default, BindFlags.VertexBuffer, CpuAccessFlags.None, ResourceOptionFlags.None, 0);
Context.InputAssembler.SetVertexBuffers(0, new VertexBufferBinding(VertexBuffer, 12, 0));
}
public override void Render(int inWidth, int inHeight, int inCount = -1)
{
Context.Draw(inCount, 0);
}
}
public class RuntimeRenderer : Renderer
{
private ShaderResourceView ResourceView { get; set; }
public RuntimeRenderer(RenderTargetParameters inParms, ref TextureRenderer inTextureRenderer)
{
CreateDevice(inParms.Handle);
Texture = inTextureRenderer.Texture;
using (Resource resource = Resource.FromSwapChain<Texture2D>(SwapChain, 0))
RenderTarget = new RenderTargetView(Device, resource);
//using (var factory = SwapChain.GetParent<Factory>())
//factory.SetWindowAssociation(inParms.Handle, WindowAssociationFlags.IgnoreAltEnter);
}
public void Resize()
{
RenderTarget.Dispose();
SwapChain.ResizeBuffers(2, 0, 0, Format.R8G8B8A8_UNorm, SwapChainFlags.AllowModeSwitch);
using (SlimDX.Direct3D11.Resource resource = Resource.FromSwapChain<Texture2D>(SwapChain, 0))
RenderTarget = new RenderTargetView(Device, resource);
}
public override void Prepare(string inShaderName)
{
using (ShaderBytecode bytecode = ShaderBytecode.CompileFromFile("ShaderFX.fx", "TextureVertexShader", "vs_4_0", ShaderFlags.EnableStrictness, EffectFlags.None))
{
InputSignature = ShaderSignature.GetInputSignature(bytecode);
VertexShader = new VertexShader(Device, bytecode);
}
using (ShaderBytecode bytecode = ShaderBytecode.CompileFromFile("ShaderFX.fx", "TexturePixelShader", "ps_4_0", ShaderFlags.EnableStrictness, EffectFlags.None))
PixelShader = new PixelShader(Device, bytecode);
InputElement[] elements = new InputElement[2];
elements[0].SemanticName = "POSITION";
elements[0].SemanticIndex = 0;
elements[0].Format = Format.R32G32B32_Float;
elements[0].Slot = 0;
elements[0].AlignedByteOffset = 0;
elements[0].Classification = InputClassification.PerVertexData;
elements[0].InstanceDataStepRate = 0;
elements[1].SemanticName = "TEXCOORD";
elements[1].SemanticIndex = 0;
elements[1].Format = Format.R32G32_Float;
elements[1].Slot = 0;
elements[1].AlignedByteOffset = InputElement.AppendAligned;
elements[1].Classification = InputClassification.PerVertexData;
elements[1].InstanceDataStepRate = 0;
Layout = new InputLayout(Device, InputSignature, elements);
BufferDescription matrixDescription = new BufferDescription()
{
Usage = ResourceUsage.Dynamic,
SizeInBytes = sizeof(float) * 16 * 4,
BindFlags = BindFlags.ConstantBuffer,
CpuAccessFlags = CpuAccessFlags.Write,
OptionFlags = ResourceOptionFlags.None,
StructureByteStride = 0
};
MatrixBuffer = new Buffer(Device, matrixDescription);
ShaderResourceViewDescription resourceViewDescription = new ShaderResourceViewDescription()
{
Format = Texture.Description.Format,
Dimension = ShaderResourceViewDimension.Texture2DMultisampled,
MipLevels = Texture.Description.MipLevels,
MostDetailedMip = 0,
};
//Texture2D.ToFile(Context, Texture, ImageFileFormat.Png, "test.png");
ResourceView = new ShaderResourceView(Device, Texture, resourceViewDescription);
SamplerDescription samplerDescription = new SamplerDescription()
{
Filter = Filter.MinMagMipLinear,
AddressU = TextureAddressMode.Wrap,
AddressV = TextureAddressMode.Wrap,
AddressW = TextureAddressMode.Wrap,
MipLodBias = 0.0f,
MaximumAnisotropy = 1,
ComparisonFunction = Comparison.Always,
BorderColor = ClearColor,
MinimumLod = 0,
MaximumLod = 99999
};
SamplerState = SamplerState.FromDescription(Device, samplerDescription);
}
public override void Render(int inWidth, int inHeight, int inCount = -1)
{
Clear();
Billboard.SetVerteces(Device, Texture.Description.Width, Texture.Description.Height, inWidth, inHeight);
SetupViewport(inWidth, inHeight);
Context.InputAssembler.SetVertexBuffers(0, new VertexBufferBinding(Billboard.Verteces, 20, 0));
Context.InputAssembler.SetIndexBuffer(Billboard.Indeces, Format.R32_UInt, 0);
Context.InputAssembler.PrimitiveTopology = PrimitiveTopology.TriangleStrip;
Context.InputAssembler.InputLayout = Layout;
Context.VertexShader.Set(VertexShader);
Context.PixelShader.Set(PixelShader);
Context.PixelShader.SetSampler(SamplerState, 0);
Context.VertexShader.SetConstantBuffer(MatrixBuffer, 0);
Context.PixelShader.SetConstantBuffer(MatrixBuffer, 0);
Context.PixelShader.SetShaderResource(ResourceView, 0);
Context.DrawIndexed(4, 0, 0);
Present();
}
}
}
Image 1 is what it looks like if I save the texture to a file (I scaled this down a LOT so it would fit in my post).
Image 2 is what it looks like in runtime when viewed at about a medium distance (not ideal, but not so bad)
Image 3 is what it looks like zoomed in to a county (Eww! Blocky and fuzzy!)
Image 4 is what it looks like zoomed out (where did all the borders go?)
About multisampling, generally you can keep quality to 0, quality setting generally are different "subpixels" (aka : samples) patterns. 0 generally does fine.
In case you render to texture with multisampling, you also need to resolve your resource, multi sampled textures are bound as Texture2DMS (instead of Texture2D) in shaders.
To do so, you need to create a second texture (with same format/size), but with only one sample.
Then once you're done rendering your multisampled texture, you need to do the following call:
deviceContext.ResolveSubresource(multisampledtexture, 0, nonmultisampledtexture,
0, format);
You can then use the ShaderView of the non multisampled texture in subsequent passes.
From what I see you should not need to use a depth stencil, just make sure you draw your elements in the correct order.
About formats, this is normal since depth is a bit "special", you need to pass different formats for resource/views. If you want to use D24_UNorm_S8_UInt (most common format i'd say), you need to setup the following:
In the texture description, format needs to be Format.R24_UNorm_X8_Typeless
In the Depth Stencil view description, Format.D24_UNorm_S8_UInt
In the shader view description, Format.R24_UNorm_X8_Typeless
That will allow you to build a depth stencil that you can read (if you don't need to read your depth buffer, just ignore shader view and use depth format directly).
Also you can increase quality by using mipmaps (which would help a lot, specially when zooming out).
To do so, in your texture description, set the following options (make sure that this texture is not multisampled)
texBufferDesc.OptionFlags |= ResourceOptionFlags.GenerateMipMaps;
texBufferDesc.MipLevels = 0; //0 means "all"
once you're done with your rendering, call:
context.GenerateMips
using the shader resource view of the texture that just got rendered.
About drawing the lines directly behind that's definitely possible, and for certain will give you the best quality.
Not sure how many lines you render, but it doesn't look like something a reasonably modern card would struggle with. And a bit of culling can easily help discard lines that are out of the screen so they don't get drawn.
You could also do some "hybrid" (use texture when zoomed out, render a subset of the lines when zoomed in), that's not too hard to setup either.
Related
I am facing some issues when I am trying to bind photo taken from camera through my application and binding it to the image view and binding image from gallery.
I use Micromax Phone for testing our application, it works fine and binds the image as it is from that phone, but when we change our phone for example Samsung the original orientation is different than the hardware orientation so what is happening is that it is binding as per the orientation set by the hardware.
I tried to correct the orientation but was not working at all.
Can any one please try to solve my problem......
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Java.IO;
using Android.Graphics;
using Android.Provider;
using Android.Content.PM;
using MyApplication.Droid;
using Android.Media;
namespace MyApplication.Droid
{
[Activity(Label = "CameraActivity")]
public class CameraActivity : Activity
{
File _file;
File _dir;
Bitmap bitmap;
ImageView _imageView, _imageView1;
int PickImageId = 1;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.Camera);
if (IsThereAnAppToTakePictures())
{
CreateDirectoryForPictures();
Button button = FindViewById<Button>(Resource.Id.TakePik);
_imageView = FindViewById<ImageView>(Resource.Id.imageView2);
_imageView1= FindViewById<ImageView>(Resource.Id.imageView1);
button.Click += TakeAPicture;
}
Button button1 = FindViewById<Button>(Resource.Id.Galary);
button1.Click += delegate
{
Intent intent = new Intent();
intent.SetType("image/*");
intent.SetAction(Intent.ActionGetContent);
StartActivityForResult(Intent.CreateChooser(intent, "Select Picture"), PickImageId);
};
// Create your application here
}
private void CreateDirectoryForPictures()
{
_dir = new File(
Android.OS.Environment.GetExternalStoragePublicDirectory(
Android.OS.Environment.DirectoryPictures), "MRohit Task");
if (!_dir.Exists())
{
_dir.Mkdirs();
}
}
private bool IsThereAnAppToTakePictures()
{
Intent intent = new Intent(MediaStore.ActionImageCapture);
IList<ResolveInfo> availableActivities =
PackageManager.QueryIntentActivities(intent, PackageInfoFlags.MatchDefaultOnly);
return availableActivities != null && availableActivities.Count > 0;
}
private void TakeAPicture(object sender, EventArgs eventArgs)
{
Intent intent = new Intent(MediaStore.ActionImageCapture);
_file = new File(_dir, String.Format("MRohit_Task_{0}.jpg", Guid.NewGuid()));
intent.PutExtra(MediaStore.ExtraOutput, Android.Net.Uri.FromFile(_file));
StartActivityForResult(intent, 0);
}
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{if (requestCode == 1)
{
Android.Net.Uri uri = data.Data;
_imageView1.SetImageURI(uri);
}
else
{
base.OnActivityResult(requestCode, resultCode, data);
// Make it available in the gallery
Intent mediaScanIntent = new Intent(Intent.ActionMediaScannerScanFile);
Android.Net.Uri contentUri = Android.Net.Uri.FromFile(_file);
mediaScanIntent.SetData(contentUri);
SendBroadcast(mediaScanIntent);
// string str_file_path = Android.Net.Uri.Parse(_file.Path.ToString()).ToString();
// Display in ImageView. We will resize the bitmap to fit the display.
// Loading the full sized image will consume to much memory
// and cause the application to crash.
int height = Resources.DisplayMetrics.HeightPixels;
int width = _imageView.Height;
bitmap = _file.Path.LoadAndResizeBitmap(width, height);
ExifInterface ei = new ExifInterface(_file.Path);
int orientation = ei.GetAttributeInt(ExifInterface.TagOrientation,
(int)Android.Media.Orientation.Undefined);
switch (orientation)
{
case (int)Android.Media.Orientation.Rotate90:
rotateImage(bitmap, 90);
break;
case (int)Android.Media.Orientation.Rotate180:
rotateImage(bitmap, 180);
break;
case (int)Android.Media.Orientation.Rotate270:
rotateImage(bitmap, 270);
break;
case (int)Android.Media.Orientation.Normal:
default:
break;
}
if (bitmap != null)
{
_imageView.SetImageBitmap(bitmap);
bitmap = null;
}
// Dispose of the Java side bitmap.
GC.Collect();
}
}
public static Bitmap rotateImage(Bitmap source, float angle)
{
Matrix matrix = new Matrix();
matrix.PostRotate(angle);
return Bitmap.CreateBitmap(source, 0, 0, source.Width, source.Height,
matrix, true);
}
}
//To crop the image size
public static class BitmapHelpers
{
public static Bitmap LoadAndResizeBitmap(this string fileName, int width, int height)
{
////// First we get the the dimensions of the file on disk
BitmapFactory.Options options = new BitmapFactory.Options { InJustDecodeBounds = true };
BitmapFactory.DecodeFile(fileName, options);
////// Next we calculate the ratio that we need to resize the image by
////// in order to fit the requested dimensions.
int outHeight = options.OutHeight;
int outWidth = options.OutWidth;
int inSampleSize = 1;
if (outHeight > height || outWidth > width)
{
inSampleSize = outWidth > outHeight
? outHeight / height
: outWidth / width;
}
// Now we will load the image and have BitmapFactory resize it for us.
options.InSampleSize = inSampleSize;
options.InJustDecodeBounds = false;
Bitmap resizedBitmap = BitmapFactory.DecodeFile(fileName, options);
return resizedBitmap;
}
}
}
I'm pulling my hair out trying the figure this out. I have a simple button, that checks for the mouse to be over, and then changes the texture if it is. It works fine. However, when I add a camera into the mix, it breaks everything. I've tried transforming both the mouse and rectangle I use for bounding-box collision, and it won't work. Here's my code for the button:
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace x.Graphics.UI
{
public enum ButtonStates
{
Normal,
Hover,
Pressed
}
public delegate void ButtonPress();
public class Button
{
public Texture2D Texture
{
get
{
Texture2D result = null;
switch (ButtonState)
{
case ButtonStates.Normal:
result = NormalTexture;
break;
case ButtonStates.Hover:
result = HoverTexture;
break;
case ButtonStates.Pressed:
result = DownTexture;
break;
}
return result;
}
}
public Vector2 Position { get; set; }
public event ButtonPress ButtonPressed;
public ButtonStates ButtonState { get; set; }
public Rectangle CollisionRect { get; set; }
private Texture2D NormalTexture;
private Texture2D HoverTexture;
private Texture2D DownTexture;
private MouseState mouseState;
private MouseState previousMouseState;
public Button(Texture2D normalTexture, Texture2D hoverTexture, Texture2D downTexture,
Vector2 position)
{
NormalTexture = normalTexture;
HoverTexture = hoverTexture;
DownTexture = downTexture;
Position = position;
mouseState = Mouse.GetState();
previousMouseState = mouseState;
CollisionRect = new Rectangle((int)Position.X, (int)Position.Y,
Texture.Width,
Texture.Height);
}
public void Update (MouseState currentState)
{
mouseState = currentState;
if (CollisionRect.Contains(new Point(mouseState.X, mouseState.Y)))
{
if (mouseState.LeftButton == Microsoft.Xna.Framework.Input.ButtonState.Pressed)
{
ButtonState = ButtonStates.Pressed;
ButtonPressed();
}
else
ButtonState = ButtonStates.Hover;
}
else
ButtonState = ButtonStates.Normal;
}
public void Update(MouseState currentState, Camera camera)
{
Vector2 mouse = new Vector2(mouseState.X, mouseState.Y);
mouse = Vector2.Transform(mouse, camera.InverseTransform);
CollisionRect = CalculateTransformedBoundingBox(CollisionRect, c.InverseTransform);
Console.WriteLine("Rectangle[X: {0}, y: {1}], Mouse:[X: {2}, Y: {3}]", CollisionRect.X, CollisionRect.Y, mouse.X, mouse.Y);
if (CollisionRect.Contains(new Point((int)mouse.X, (int)mouse.Y)))
{
if (mouseState.LeftButton == Microsoft.Xna.Framework.Input.ButtonState.Pressed)
{
ButtonState = ButtonStates.Pressed;
ButtonPressed();
}
else
ButtonState = ButtonStates.Hover;
}
else
ButtonState = ButtonStates.Normal;
}
public void Draw(SpriteBatch spriteBatch)
{
spriteBatch.Draw(Texture, Position, null, Color.White, 0.0f,
Vector2.Zero, 1.0f, SpriteEffects.None, 1.0f);
}
private Rectangle CalculateTransformedBoundingBox(Rectangle local, Matrix toWorldSpace)
{
Vector2 leftTop = new Vector2(local.Left, local.Top);
Vector2 rightTop = new Vector2(local.Right, local.Top);
Vector2 leftBottom = new Vector2(local.Left, local.Bottom);
Vector2 rightBottom = new Vector2(local.Right, local.Bottom);
Vector2.Transform(ref leftTop, ref toWorldSpace,
out leftTop);
Vector2.Transform(ref rightTop, ref toWorldSpace,
out rightTop);
Vector2.Transform(ref leftBottom, ref toWorldSpace,
out leftBottom);
Vector2.Transform(ref rightBottom, ref toWorldSpace,
out rightBottom);
// Find the minimum and maximum extents of the
// rectangle in world space
Vector2 min = Vector2.Min(Vector2.Min(leftTop, rightTop),
Vector2.Min(leftBottom, rightBottom));
Vector2 max = Vector2.Max(Vector2.Max(leftTop, rightTop),
Vector2.Max(leftBottom, rightBottom));
// Return that as a rectangle
return new Rectangle((int)min.X, (int)min.Y,
(int)(max.X - min.X), (int)(max.Y - min.Y));
}
}
}
And my code for the camera:
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace x.Graphics
{
public class Camera
{
protected float _zoom;
protected Matrix _transform;
protected Matrix _inverseTransform;
protected Vector2 _pos;
protected float _rotation;
protected Viewport _viewport;
protected MouseState _mState;
protected KeyboardState _keyState;
protected Int32 _scroll;
public float Zoom
{
get { return _zoom; }
set { _zoom = value; }
}
public Matrix Transform
{
get { return _transform; }
set { _transform = value; }
}
public Matrix InverseTransform
{
get { return _inverseTransform; }
}
public Vector2 Pos
{
get { return _pos; }
set { _pos = value; }
}
public float Rotation
{
get { return _rotation; }
set { _rotation = value; }
}
public Camera(Viewport viewport)
{
_zoom = 1.0f;
_scroll = 1;
_rotation = 0.0f;
_pos = Vector2.Zero;
_viewport = viewport;
}
public void Update()
{
Input();
MathHelper.Clamp(_zoom, 0.01f, 10.0f);
_rotation = ClampAngle(_rotation);
_transform = Matrix.CreateRotationZ(_rotation) *
Matrix.CreateScale(new Vector3(_zoom, _zoom, 1)) *
Matrix.CreateTranslation(_pos.X, _pos.Y, 0);
_inverseTransform = Matrix.Invert(_transform);
}
protected virtual void Input()
{
_mState = Mouse.GetState();
_keyState = Keyboard.GetState();
//Check Move
if (_keyState.IsKeyDown(Keys.A))
{
_pos.X += 10f;
}
if (_keyState.IsKeyDown(Keys.D))
{
_pos.X -= 10f;
}
if (_keyState.IsKeyDown(Keys.W))
{
_pos.Y -= 10f;
}
if (_keyState.IsKeyDown(Keys.S))
{
_pos.Y += 10f;
}
}
protected float ClampAngle(float radians)
{
while (radians < -MathHelper.Pi)
{
radians += MathHelper.TwoPi;
}
while (radians > MathHelper.Pi)
{
radians -= MathHelper.TwoPi;
}
return radians;
}
}
}
I'm not 100% sure what's wrong, but the mouse position only changes when I press a button. I'm really confused, I've never worked with cameras before. Any help would be really appreciated. Thanks!
UPDATE:
It detects the mouse as being over the button before I try and move the camera. After that, the coorindates of the rectangle are continually incremented.
Don't transform bounding box... is easy to transform mouse coordinates ... ;)
Use the inverse transform of your camera matrix to transform mouse coords to the same space of your bounding box.
I have a screen which call a listfield.
public class Main_AllLatestNews extends MainScreen {
private Database_Webservice webservice;
private String[] title, category, date, imagepath = {"no picture", "no picture", "no picture", "no picture","no picture","no picture","no picture","no picture","no picture", "no picture"};
private int[] newsid;
private List_News newslist;
public Main_AllLatestNews(final boolean needdownload) {
super(USE_ALL_WIDTH);
webservice = new Database_Webservice();
add(new Custom_TopField(this, 0, -1, "", 1, 1));
add(new Custom_BottomField(this, 0));
add(new Custom_HeaderField(Config_GlobalFunction.latest));
if (needdownload){
Main.getUiApplication().pushScreen(
new Custom_LoadingScreen(30));
webservice.UpdateAllCatNews();
}else {
webservice.LoadtodayNews();
newsid = new int[webservice.news.size()];
title = new String[webservice.news.size()];
category = new String[webservice.news.size()];
date = new String[webservice.news.size()];
//imagepath = new String[webservice.news.size()];
for (int i = 0; i < webservice.news.size(); i++) {
newslist = (List_News) webservice.news.elementAt(i);
newsid[i] = newslist.getID();
title[i] = newslist.getNtitle();
category[i] = newslist.getNewCatName();
date[i] = newslist.getNArticalD();
//imagepath[i] = newslist.getImagePath();
}
add(new Custom_ListField(newsid, title, date, category, imagepath, true));
}
}
}
When I add custom_listfield then I get:
Failed to allocate timer 0: no slots left
Here is my listfield
public Custom_ListField(int newsid[], String title[], String date[],
String category[], String imagepath[], boolean islatest) {
super(0, ListField.MULTI_SELECT);
this.newsid = newsid;
setCallback(this);
setBackground(Config_GlobalFunction.loadbackground("background.png"));
this.islatest = islatest;
rows = new Vector();
for (int x = 0; x < title.length; x++) {
TableRowManager row = new TableRowManager();
titlelabel = new Custom_LabelField(title[x],
LabelField.USE_ALL_WIDTH | DrawStyle.LEFT);
titlelabel.setFont(Font.getDefault().derive(Font.BOLD, 23));
row.add(titlelabel);
datelabel = new Custom_LabelField(date[x], DrawStyle.ELLIPSIS
| LabelField.USE_ALL_WIDTH | DrawStyle.LEFT);
datelabel.setFont(Font.getDefault().derive(Font.BOLD, 18));
datelabel.setFontColor(Color.GRAY);
row.add(datelabel);
categorylabel = new Custom_LabelField(category[x],
DrawStyle.ELLIPSIS | LabelField.USE_ALL_WIDTH
| DrawStyle.LEFT);
categorylabel.setFont(Font.getDefault().derive(Font.BOLD, 18));
categorylabel.setFontColor(Color.RED);
row.add(categorylabel);
/*Bitmap imagebitmap = null;
if (!imagepath[x].toString().equals("no picture")) {
imagebitmap = Util_ImageLoader.loadImage(imagepath[x]);
} else {
imagepath[x] = "image_base.png";
imagebitmap = Bitmap.getBitmapResource(imagepath[x]);
}
image = new BitmapField(imagebitmap, Field.FIELD_HCENTER
| Field.FIELD_VCENTER);
row.add(image);*/
//setRowHeight(image.getBitmapHeight() + 10);
setRowHeight(70);
rows.addElement(row);
}
setSize(rows.size());
}
In this list, it will call 10 images or more. First I will check got link send to it else load local images. So the row height must be not same, however, it does not auto set row height for each row but set a same height to all row. I think out of memory because i call too many images? but I call in android also no problem.
This is my imageloader.
public class Util_ImageLoader {
public static Bitmap loadImage(String url) {
HttpConnection connection = null;
InputStream inputStream = null;
EncodedImage bitmap;
byte[] dataArray = null;
try {
// can use this for BlackBerry 5.0+ :
// connection = (HttpConnection) (new
// ConnectionFactory()).getConnection(url).getConnection();
connection = (HttpConnection) Connector
.open(url + Util_GetInternet.getConnParam(),
Connector.READ, true);
int responseCode = connection.getResponseCode();
if (responseCode == HttpConnection.HTTP_OK) {
inputStream = connection.openDataInputStream();
dataArray = IOUtilities.streamToBytes(inputStream);
}
} catch (Exception ex) {
} finally {
try {
inputStream.close();
connection.close();
} catch (Exception e) {
}
}
if (dataArray != null) {
bitmap = EncodedImage.createEncodedImage(dataArray, 0,
dataArray.length);
return bitmap.getBitmap();
} else {
return null;
}
}
}
1) What can I do to reduce the use of memory?
2) How to set different row height? I am set bitmap.getbitmapheight() but different bitmap will have different height.
//Updated//
I am running on simulator 9930 OS 7.0 and 8520 OS 5.0. Both also same result. Real Device cannot run because after signing the key also prompt the warning message try to Secure APi. I am completely commented all the images also same. I did not call neither online nor local image. I think is the data problem?
#AlanLai, can you tell us which device this is being run on, and which OS? Is it a simulator, or real hardware? Why don't you try commenting out the image completely. Don't show any images (network images, or local images). See if you still get the problem. Let's try to narrow down where exactly the code is that's causing your problem. Note: please post the information about which device you're testing on above, in the question, not as a comment response here. Thanks
How about to have only one TableRowManager and every drawRow set values with layout with specific values?
There's a lot of things you can do to reduce memory usage. For one, try to avoid keeping objects in memory longer than you really need them. One way this happens is if you keep member variables in your class, that could really be local variables in a method. Keeping member variables may lead to objects living longer than they need to, preventing the release of the memory they occupy.
Util_ImageLoader
For example, in Util_ImageLoader, you do almost all the work in the constructor. But then, you keep the result around (the Bitmap) in a static member variable (_bmap), which keeps it in memory. I know you do this so that you can call getBitmap(). But, you could change the class to be like this:
public class Util_ImageLoader {
public static Bitmap loadImage(String url) {
HttpConnection connection = null;
InputStream inputStream = null;
EncodedImage bitmap;
byte[] dataArray = null;
try {
// can use this for BlackBerry 5.0+ :
// connection = (HttpConnection) (new ConnectionFactory()).getConnection(url).getConnection();
connection = (HttpConnection) Connector.open(url + Util_GetInternet.getConnParam(), Connector.READ,
true);
int responseCode = connection.getResponseCode();
if (responseCode == HttpConnection.HTTP_OK) {
inputStream = connection.openDataInputStream();
dataArray = IOUtilities.streamToBytes(inputStream);
}
} catch (Exception ex) {
}
finally {
try {
inputStream.close();
connection.close();
} catch (Exception e) {
}
}
if (dataArray != null) {
bitmap = EncodedImage.createEncodedImage(dataArray, 0, dataArray.length);
return bitmap.getBitmap();
} else {
return null;
}
}
}
Because your Util_ImageLoader class doesn't really have any state associated with it, you can probably make it a class with just one static method. The static method does not require you to create an instance of Util_ImageLoader to use it. Just do this:
Bitmap img = Util_ImageLoader.loadImage("http://domain.com/path/image.png");
This allows the image that's loaded to be released as soon as the UI is done with it. The existing code keeps that image in memory for the life of the program.
Also, I replaced your custom code that uses a byte[] buffer, with the useful IOUtilities.streamtoBytes() method. Let the built-in libraries do the work of optimizing for you. Most of the time, they will do a pretty good job of that.
You also had some fixed point scaling code in your Util_ImageLoader class that wasn't doing anything. It was creating a scaled image of the same size as the original. So, I just removed that code. That can only help your memory usage. Image manipulation can be expensive.
Finally, I checked the web server return code (HTTP_OK) before I created any of the large objects needed for this method. If the network request fails, you certainly don't want to waste memory for no reason.
Custom_ListField
Again, you are keeping some objects around, possibly longer than needed. Let's go through your member variables:
private Bitmap bg = Bitmap.getBitmapResource("background.png"),
imagebitmap;
I don't know how many instances of Custom_ListField you will have in your app, but if you are going to assign bg to a constant app resource image, you should at least make it a static member variable, so that if there are 10 instances of Custom_ListField, you will only be keeping one bg variable in memory:
private static Bitmap bg = Bitmap.getBitmapResource("background.png"),
imagebitmap;
But, in your case, I don't think you need to keep that member variable at all. You can simply replace it where it's used, like this:
Background background = BackgroundFactory.createBitmapBackground(Bitmap.getBitmapResource("background.png"));
Then, the imagebitmap member can also be replaced with a local variable:
Bitmap imageBitmap = null;
if (!imagepath[x].toString().equals("no picture")) {
imageBitmap = Util_ImageLoader.loadImage(imagepath[x]);
imageBitmap = loader.getbitmap();
} else {
imagepath[x] = "image_base.png";
imageBitmap = Bitmap.getBitmapResource(imagepath[x]);
}
image = new BitmapField(imageBitmap, Field.FIELD_HCENTER | Field.FIELD_VCENTER);
imageBitmap only needs to be a local variable, not a member variable.
Debugging memory usage usually requires having the whole program, running, and profiling it. With only some of your code, I can't see all the other code that uses it. How many of each class is created is important? Which images are the large ones, and which are small? These are all questions you need to ask yourself to get your memory usage down.
But, hopefully, the general techniques I showed example of above can help you get started.
The problem was the Custom_ListField. This should extends listfield
instead of custom extends manager
public class Custom_ListField extends ListField {
private String[] title, category, date, imagepath;
private int[] newsid, catsid;
private List_News newslist;
private Bitmap imagebitmap[], localimage = Bitmap
.getBitmapResource("image_base.png");
private BrowserField webpage;
private Custom_BrowserFieldListener listener;
private boolean islatest;
private Vector content = null;
private ListCallback callback = null;
private int currentPosition = 0;
public Custom_ListField(Vector content, boolean islatest) {
this.content = content;
this.islatest = islatest;
newsid = new int[content.size()];
title = new String[content.size()];
category = new String[content.size()];
date = new String[content.size()];
imagepath = new String[content.size()];
catsid = new int[content.size()];
imagebitmap = new Bitmap[content.size()];
for (int i = 0; i < content.size(); i++) {
newslist = (List_News) content.elementAt(i);
newsid[i] = newslist.getID();
title[i] = newslist.getNtitle();
category[i] = newslist.getNewCatName();
date[i] = newslist.getNArticalD();
imagepath[i] = newslist.getImagePath();
if (!imagepath[i].toString().equals("no picture")) {
imagebitmap[i] = Util_ImageLoader.loadImage(imagepath[i]);
} else {
imagebitmap[i] = localimage;
}
catsid[i] = newslist.getCatID();
}
initCallbackListening();
this.setRowHeight(localimage.getHeight() + 10);
}
private void initCallbackListening() {
callback = new ListCallback();
this.setCallback(callback);
}
private class ListCallback implements ListFieldCallback {
public ListCallback() {
setBackground(Config_GlobalFunction
.loadbackground("background.png"));
}
public void drawListRow(ListField listField, Graphics graphics,
int index, int y, int width) {
currentPosition = index;
graphics.drawBitmap(
Display.getWidth() - imagebitmap[index].getWidth() - 5,
y + 3, imagebitmap[index].getWidth(),
imagebitmap[index].getHeight(), imagebitmap[index], 0, 0);
graphics.setColor(Color.WHITE);
graphics.drawRect(0, y, width, imagebitmap[index].getHeight() + 10);
graphics.setColor(Color.BLACK);
graphics.setFont(Font.getDefault().derive(Font.BOLD, 20));
graphics.drawText(title[index], 5, y + 3, 0, Display.getWidth()
- imagebitmap[index].getWidth() - 10);
System.out.println(Display.getWidth()
- imagebitmap[index].getWidth() - 10);
graphics.setColor(Color.GRAY);
graphics.setFont(Font.getDefault().derive(Font.BOLD, 15));
graphics.drawText(date[index], 5, y + 6
+ Font.getDefault().getHeight() + 3);
if (islatest) {
graphics.setColor(Color.RED);
graphics.setFont(Font.getDefault().derive(Font.BOLD, 15));
graphics.drawText(category[index], Font.getDefault()
.getAdvance(date[index]) + 3, y + 6
+ Font.getDefault().getHeight() + 3);
}
}
public Object get(ListField listField, int index) {
return content.elementAt(index);
}
public int getPreferredWidth(ListField listField) {
return Display.getWidth();
}
public int indexOfList(ListField listField, String prefix, int start) {
return content.indexOf(prefix, start);
}
}
public int getCurrentPosition() {
return currentPosition;
}
protected boolean navigationClick(int status, int time) {
int index = getCurrentPosition();
if (catsid[index] == 9) {
if (Config_GlobalFunction.isConnected()) {
webpage = new BrowserField();
listener = new Custom_BrowserFieldListener();
webpage.addListener(listener);
MainScreen aboutus = new Menu_Aboutus();
aboutus.add(webpage);
Main.getUiApplication().pushScreen(aboutus);
webpage.requestContent("http://www.orientaldaily.com.my/index.php?option=com_k2&view=item&id="
+ newsid[index] + ":&Itemid=223");
} else
Config_GlobalFunction.Message(Config_GlobalFunction.nowifi, 1);
} else
Main.getUiApplication().pushScreen(
new Main_NewsDetail(newsid[index]));
return true;
}
}
I am working on a WP7 game. I am using Game state managment (http://create.msdn.com/en-US/education/catalog/sample/game_state_management , but I think its not important ) I have problem with saving data into
Microsoft.Phone.Shell.PhoneApplicationService.Current.State
if I put there sprite in this method
public override void Deactivate()
{
#if WINDOWS_PHONE
Microsoft.Phone.Shell.PhoneApplicationService.Current.State["Score"] = Score;
Microsoft.Phone.Shell.PhoneApplicationService.Current.State["cloudSprite"] = cloudSprite;
#endif
base.Deactivate();
}
there is nothing in
Microsoft.Phone.Shell.PhoneApplicationService.Current.State
in activate method. However if I remove cloudSprite and put there only Score which is int it works fine. I dont know whats wrong maybe it cant handle more complex objects. I tried also float doubel, it all works. But if I put there something more complex it doesnt work. What do you think ?
EDIT
This is my sprite class. I dont know how to make it serializable. I have added there [DataContractAttribute()] and [DataMember] but its not working
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;
using System.Runtime.Serialization;
using System.IO;
namespace GameStateManagementSample.GameObjects
{
[DataContractAttribute()]
public class Sprite
{
[DataMember]
public Vector2 Position;
[DataMember]
public Vector2 Size;
[DataMember]
public Texture2D Texture;
[DataMember]
public Rectangle Rect
{
get
{
return new Rectangle((int)Position.X, (int)Position.Y, (int)Size.X, (int)Size.Y);
}
}
public Sprite(Vector2 position)
{
Position = position;
}
public Sprite(Vector2 position, Vector2 size)
{
Position = position;
Size = size;
}
public Sprite(Vector2 position, Texture2D texture)
{
Position = position;
Texture = texture;
Size = new Vector2(Texture.Width, Texture.Height);
}
public void LoadContent(string assetName, ContentManager content)
{
Texture = content.Load<Texture2D>(assetName);
if (Size == Vector2.Zero)
Size = new Vector2(Texture.Width, Texture.Height);
}
public virtual void Draw(SpriteBatch spriteBatch)
{
//spriteBatch.Draw(Texture, Rect, Color.White);
spriteBatch.Draw(Texture, Position, Color.White);
}
public virtual void Draw(SpriteBatch spriteBatch, Rectangle TexturePositionInSpriteSheet, Color color)
{
spriteBatch.Draw(Texture, Position, TexturePositionInSpriteSheet, color);
}
public bool Intersects(Vector2 point)
{
if (point.X >= Position.X && point.Y >= Position.Y && point.X <= Position.X + Size.X && point.Y <= Position.Y + Size.Y)
return true;
else return false;
}
public bool Intersects(Rectangle rect)
{
return Rect.Intersects(rect);
}
public static void Serialize(Stream streamObject, object objForSerialization)
{
if (objForSerialization == null || streamObject == null)
return;
DataContractSerializer ser = new DataContractSerializer(objForSerialization.GetType());
ser.WriteObject(streamObject, objForSerialization);
}
public static object Deserialize(Stream streamObject, Type serializedObjectType)
{
if (serializedObjectType == null || streamObject == null)
return null;
DataContractSerializer ser = new DataContractSerializer(serializedObjectType);
return ser.ReadObject(streamObject);
}
}
}
Objects added to the State collection are serialized with the DataContractSerializer. Ensure that anything you're saving there can be serialized that way.
Any serialization errors are silently ignored.
Update
Here's a simplified version of your Sprite object:
[DataContract]
public class Sprite
{
[DataMember]
public Vector2 Position;
[DataMember]
public Vector2 Size;
[DataMember]
public Texture2D Texture;
public Sprite()
{
}
public Sprite(Vector2 position)
{
Position = position;
}
public Sprite(Vector2 position, Vector2 size)
{
Position = position;
Size = size;
}
public Sprite(Vector2 position, Texture2D texture)
{
Position = position;
Texture = texture;
Size = new Vector2(Texture.Width, Texture.Height);
}
}
And here's an example of it being serialized and deserialized:
// Sprite serialization test
var sprite1 = new Sprite(new Vector2(12.34f, 56.78f));
Sprite sprite2;
using (var memStr = new MemoryStream())
{
var serializer = new DataContractSerializer(typeof(Sprite));
serializer.WriteObject(memStr, sprite1);
memStr.Position = 0;
var sr = new StreamReader(memStr);
var serialized = sr.ReadToEnd();
// serialized now looks like
// <Sprite xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/MiscExperiments"><Position xmlns:d2p1="http://schemas.datacontract.org/2004/07/Microsoft.Xna.Framework"><d2p1:X>12.34</d2p1:X><d2p1:Y>56.78</d2p1:Y></Position><Size xmlns:d2p1="http://schemas.datacontract.org/2004/07/Microsoft.Xna.Framework"><d2p1:X>0</d2p1:X><d2p1:Y>0</d2p1:Y></Size><Texture xmlns:d2p1="http://schemas.datacontract.org/2004/07/Microsoft.Xna.Framework.Graphics" i:nil="true" /></Sprite>
memStr.Position = 0;
sprite2 = (Sprite)serializer.ReadObject(memStr);
// sprite2 now contains the same as
// sprite2.Position = { X:12.34, Y:56.78 }
}
I believe that you get the ArgumentOutOfRangeException which is silently ignored if you aren't debugging (in which case the exception is thrown). The items stored in the State dictionary need to be serializable. Try storing that sprite somehow and saving only a link (source) to the sprite in the state dictionary.
I am creating a ListField. in each row of I am adding a image and 3 labelfield.
Can any one tell me how to create a keywordfilterField for this...
Thanks in advance
I am new to blackberry.
Little code will help me alot
This is my code for creating a custom list
class CustomListField extends ListField implements ListFieldCallback
{
String type;
int DISPLAY_WIDTH = Display.getWidth();
int DISPLAY_HEIGHT = Display.getHeight();
Vector mItems = new Vector();
Vector mine = new Vector();
Vector three= new Vector();
// SizedVFM mListManager = new SizedVFM(DISPLAY_WIDTH, DISPLAY_HEIGHT - 40);
Bitmap searchresult = Bitmap.getBitmapResource("res/searchresult.png");
HorizontalFieldManager hfManager;
Bitmap image ,image1;
int z = this.getRowHeight();
CustomListField(String text1,String text2,String type)
{
for (int i = 1; i < 31; i++)
{
mItems.addElement(text1 +String.valueOf(i));
mine.addElement(" "+text2);
three.addElement("31");
}
this.type=type;
this.setRowHeight((2*z));
this.setCallback(this);
this.setSize(20);
//mListManager.add(mListField);
//add(mListManager);
}
public void drawListRow(ListField field, Graphics g, int i, int y, int w)
{
// Draw the text.
image = Bitmap.getBitmapResource("res/searchresult.png");
String text = (String) get(field, i);
String mytext = (String)mine.elementAt(i);
String urtext=(String)three.elementAt(i);
g.drawBitmap(0, y, image.getWidth(),image.getHeight(), image, 0, 0);
g.drawText(text, image.getWidth(), y, 0, w);
g.setColor(Color.GRAY);
g.drawText(mytext, image.getWidth(), y+getFont().getHeight(), 0, w);
g.drawText(urtext,Graphics.getScreenWidth()*7/8,y,0,w);
if (i != 0)
{
g.drawLine(0, y, w, y);
}
}
public Object get(ListField listField, int index)
{
return mItems.elementAt(index);
}
public int getPreferredWidth(ListField listField)
{
return DISPLAY_WIDTH;
}
public int indexOfList(ListField listField, String prefix, int start)
{
return 0;
}
protected boolean touchEvent(TouchEvent message)
{
// If click, process Field changed
if ( message.getEvent() == TouchEvent.CLICK )
{
if(type.equals("Stops"))
UiApplication.getUiApplication().pushScreen(new SearchScreen("Services"));
else if(type.equals("Services"))
UiApplication.getUiApplication().pushScreen(new SearchScreen("Stops"));
return true;
}
return super.touchEvent(message);
}
}
The problem with KeywordFilterField is that it uses internally its own ListField, so I think it is going to be difficult to customize. If you wanted to use it as it is provided, you'll have to use it as follows:
//KeywordFilterField contains a ListField to display and a search edit field to type in the words
KeywordFilterField keywordFilterField = new KeywordFilterField();
//Instantiate the sorted collection:
CustomList cl = new CustomList(mItems);
//Pass the custom collection
keywordFilterField.setSourceList(cl, cl);
//Now you have to add two fields: first the list itself
myManager.add(keywordFilterField);
//And the search field, probably you'd want it at top:
myScreen.setTitle(keywordFilterField.getKeywordField());
You'll have to implement a custom sortable collection to hold the items you wan't to display:
class CustomList extends SortedReadableList implements KeywordProvider {
//In constructor, call super constructor with a comparator of <yourClass>
public CustomList(Vector elements)
{
super(new <yourClass>Comparator()); //pass comparator to sort
loadFrom(elements.elements());
}
//Interface implementation
public String[] getKeywords( Object element )
{
if(element instanceof <yourClass> )
{
return StringUtilities.stringToWords(element.toString());
}
return null;
}
void addElement(Object element)
{
doAdd(element);
}
//...
}
You have a full demo available inside the JDE samples folder. It is called keywordfilterdemo.
To use a custom list like the one you posted, you'll probably have to code a lot of stuff, like a custom EditField to type in the keywords receiving events on every typed character, linked to a search on a sortered collection (maybe you could use a SortedReadableList for this) which will select in your ListField the first search result returned by this collection.