Hola!
Ya he acabado los examenes, asi que ya me he puesto a enredar con la programacion.
Hoy me dio por echarle un ojo a C# y windows forms, que si boton para aqui , que si textbox para alla. Y me he puesto a "intentar hacer" un editor de mapas de tiles por echar el rato.
Pues bien, me he quedado atascado en este punto:
Ya tengo todo lo facil hecho (botones y cosillas wapas) y empece a programar las funciones y demas. Como era la primera vez q trabajaba con windows forms y este tipo de programacion, pues ando algo liado.
El problema es el siguiente: Me he creado una clase que va a contener un array dinamico a PictureBox's que contendran los tiles del mapa (no se si lo normal es hacerlo con picturebix, o si hay alguna forma mas curiosa). Entonces lo que hago es pasarle el Form como parametro al metodo de esa clase, y en el se supone que lo dibuja. Como no me entero de lo que pongo ni yo, os pongo codigo.
public void assignForm(System.Windows.Forms.Form mainForm)
{
mainForm.SuspendLayout();
tiles = new System.Windows.Forms.PictureBox();
this.tiles.BackColor = System.Drawing.Color.Black;
this.tiles = new System.Windows.Forms.PictureBox();
this.tiles.Location = new System.Drawing.Point(25,25);
this.tiles.Name = "pictureBox";
this.tiles.Size = new System.Drawing.Size(128, 64);
this.tiles.TabIndex = 0;
this.tiles.TabStop = false;
mainForm.Controls.Add(this.tiles);
mainForm.ResumeLayout(false);
}
En este ejemplo lo hago solo para un picturebox y sin array como os dije, pero era pa probar a ver si iba 1, y luego pasar a varios. He estado mirando la ayuda de msdn para lo de SuspendLayout y ResumeLayout, y creo que lo estoy usando bien.
msdnPasarle el form como parametro funciona, porq probe a hacer un mainForm.text y cambiarlo y lo cambia. Asi q no se me ocurre que puede ser.
Si no se entendio bien la duda decirlo , y lo esplico mejor :P
Saludos y gracias!
Hola,
la verdad que no tengo muy claro porque no sale, como apunte quizás solo que primero lo asignes al form y luego le des la posición, a ver si es eso (que lo posiciones respecto a nada de nada porque aun no tiene un padre). No se me ocurre otra cosa.
Respecto a si eso se hace así o no, yo cuando he hecho alguna cosa de estas he utilizado un panel y me he cargado el evento OnPaint sobreescribiéndolo con el código que a mi me interesaba (el panel tenía una matriz de objetos Tile y eso era lo que pintaba en el panel a mano).
Espero te sirva de algo. Un saludo!
Vicente
jajaja
sabes donde estaba el fallo?
Si te fijas hago 2 instancias (tanto copiar y pegar :P), una justo despues del ajustarle el color. Entonces solo tenia en cuenta la segunda, que se creaba de color gris, por eso no la veia y yo pensando que no salia :P
Gracias por tu tiempo ;)
Saludos!
El método de Vicente creo que tiene más futuro. (ole)
Estoy haciendo un hibrido ahora :P
Estoy metiendo los pictureBox dentro del panel, que no sabia que tb podia contener controles como un Form.
Por cierto, los objetos Tile a los que te refieres Vicente, son un tipo creado por ti, o ya te vienen....
Mas cosillas ahora q me vienen a la cabeza. Lo que voy a intentar hacer es un array (o matriz) con pictureboxes, que al darles click cambien su imagen al tile de pintado actual. Esto ya lo habia intentado otra vez, pero no sake la solucion a como discenir sobre que picturebox estoy clickando. Lo que hacia era usar para cada pictBox la misma funcion OnClick, que ponia su color background a negro. Pero claro, de cual de ellos ha de poner en negro si todos utilizaban la misma funcion. Me hacia falta una funcion que me devuelva sobre que controlador esta el puntero o algo similar, o calcular mediante coordenadas del raton que pict es, pero eso es mas engorroso.
gracias de antemano ;)
Saludos!
Hola,
jejeje, la verdad que vaya fallo más tonto, no lo había visto yo tampoco.
Respecto al Tile: sip, era una clase propia mía (que tenía la imagen que representaba esa Tile y más cosas). Respecto a lo de sacar que PictureBox, lo de las coordenadas te lo vas a encontrar con las Tiles también, pero al final no es tan engorroso saber en que tile te han pulsado dentro de un mapa de tiles cuadradas (si son hexagonales ya es más pesado, pero cuadradas no es tan difícil).
Un saludo!
Vicente
P.D.: Jare, me desespera la IA de tu jueguecito, jugando en plan "casual" siempre me da pal pelo :P
Hombre, lo de las coordenadas es relativamente facil. Lo q pasa que el Panel tiene scroll para cuando se metan mas pcitBox del tamaño. Entonces no se si las coordenadas de cada tile son respecto al Panel, o al Form. De ser este ultimo caso veo muy dificil calcular a que pictBox estoy apuntando. Mañana le echo un ojo y ya os comento ;)
Gracias de nuevo.
Saludos!
Hola,
yo diría que las coordenadas te las da en base al contenedor donde has pinchado. Por cierto, creo que zupervaca hizo un editor de tiles en C#, lo mismo le puedes echar un ojo. Un saludo!
Vicente
Si, es lo mas logico, porq cuando agregas un controlador al contenedor y le indicas coordenadas las hace respecto a las del contenedor, asi q me parece q va a ser mas facil de lo que parece (ole)
Lo de zupervaca ya lo habia pensado, le tendre q echar un ojo a ver q tal. Y a ver si esta el codigo pa bajar, y no solo el editor, sino estoy igual :P
Saludos!
Si lo hice cuando estaba aprendiendo c-sharp, el metodo que usaba era tener imagenes por separado y luego las pintaba sobre un control panel, tambien tenia una imagen con todos los tiles para mostrarla en el panel izquierdo, como el codigo que habia hecho era tan cutre me dio verguenza enseñarlo :lol:, no obstante no ofusque el codigo con lo que podras hecharle un ojo con algun programa, mi idea era tener algo hecho para luego reescribir todo el codigo y dejarlo limpio, pero no tuve nunca mas ni ganas ni tiempo :)
Editado: He mirado otra vez el proyecto y almaceno las imagenes en clases Bitmap y luego las pinto usando la funcion DrawImageUnscaled de Graphics, si no creo recordar mal el tema de pintar en un graphics era lentisimo y tuve que hacer que no borrara el fondo del control y hacerlo yo solo en las zonas que pinto los bitmaps, es decir, dibujar solo lo necesario mediante el rectangle que nos pasan a la funcion de dibujar el control.
Me he bajado tu pack de herramientas, y la verdad, no se como estaran en cuanto a codigo, pero tienen wena pinta :D
He estado mirando en la ayuda de msdn sobre DrawImageUnscaled y el PaintEventArgs y parece un wen metodo. Probare varias formas y vere a ver cual se adapta mejor :P. Lo que tu explicabas de los rectangulos te refieres a el metodo de Dirty Rectangles o algo asi no? en plan actualizar el rectangulo que vas a pintar y nada mas...
Gracias por la info y la ayuda!
Saludos!
Madre mia que caos :D
He estado probando mil cosas, y he hecho y rehecho codigo pa'lante y pa'tras. He probado estas opciones:
1. La idea inicial de tener un array de pictureboxes, donde meter cada tile, todos ellos dentro de un panel. Este metodo me funciona de momento, salvo unos eventos de raton. Por ejemplo, hago que cuando el puntero pase por encima de 1 picturebox, este ponga su borderStyle a bordersimple o algo asi, pa que parezca q esta recuadrado. Esto funciona bien, pero se van quedando todos los pictbox con el borde, asi que le meti otro evento para q cuando el puntero salga del pictbox se quite el borde. Este evento funciona cuando quiere. Si el puntero va muy rapido se los deja con borde... en fin una chapuza.
2. He probado lo que decia vicente de pintar directamente sobre el panel. Y si, funciona, lo que pasa es que si pinto algo fuera de los limites visibles del panel, pues no me sale la barra de Autoscroll, ya q solo sale cuando un Control esta fuera de los limites. Y no estoy usando ningun control , solo pinto el fondo del panel.
3. Tb he probado lo "que creo" que decia zupervaca. Me cargo cada imagen en una clase Image (en bitmap no puedo ya que aun heredando de la clase image, el metodo para cargar los ficheros (FromFile) me devuelve un Image y no un bitmap). He intentado usar como fondo del panel un objeto de la clase image, para ver si salian las barras de autoscroll y asi pintar sobre esa "plantilla". Pero resulta q no es un control, y no se puede hacer.
En fin, las ideas creo q las tengo, pero me falta destrza con los controles y demas cosas de Windows Forms y C# para hacer el maldito editor :P
Alguna idea mas clarividente sobre que tengo q hacer exactamente? Respuestas en plan: create un panel que tenga un bla bla bla y, ahi pintas no se ke :P Diciendome exactamente q deberia usar :D
Saludos y mil gracias de antemano!
Hola,
el problema del autoscroll es un poco pesado. Esto no te aseguro que sea así, pero creo que es lo que te pasa: tu estás pintando fuera de pantalla, y además, fuera del panel. Tienes que conseguir que el panel crezca para que aparezca el scroll. Si estás usando docking, tu puedes ponerle un tamaño al panel (el que te de la gana), pero el manejador del layout va a saltarse tu decisión para cumplir la propiedad de docking (lo mismo si usaras layouts de C# 2.0). Puede que lo que te ocurra sea esto.
Si no te funciona dilo, que si tengo un rato hago un ejemplo ;) Un saludo!
Vicente
Edit: en
Winforms Quickstarts a lo mejor encuentras algo de info.
Este es el procedimiento que uso:
Citar
public void assignForm(Panel mainPanel)
{
imagen = System.Drawing.Bitmap.FromFile("hola.jpg");
mainPanel.Paint += new PaintEventHandler(panelPaint);
mainPanel.Invalidate();
}
Le meto el evento de pintado y invalido el panel para q lo repinte.
El procedimiento de pintado es este:
public void panelPaint (object sender,PaintEventArgs e)
{
Point arribaIz = new Point(300, 0);
Point arribaDe = new Point(400, 0);
Point abajoIz = new Point(300, 100);
Point [] destPara = {arribaIz, arribaDe, abajoIz};
e.Graphics.DrawImage(imagen, destPara);
}
Y lo que ma sale es esto:
(http://usuarios.lycos.es/rage182/imagenforo1.jpg)
Pero si dibujo mitad fuera del panel y mitad dentro no sale el autoscroll pero si lo dibuja:
public void panelPaint (object sender,PaintEventArgs e)
{
Point arribaIz = new Point(500, 0);
Point arribaDe = new Point(600, 0);
Point abajoIz = new Point(500, 100);
Point [] destPara = {arribaIz, arribaDe, abajoIz};
e.Graphics.DrawImage(imagen, destPara);
}
(http://usuarios.lycos.es/rage182/imagenforo2.jpg)
Segun lei en msdn la funcion de autoscroll del panel sale cuando esta fuera del rango un control. Parece ser que el pintado no le afecta. Estaria bien poder forzarle a crear autoscroll, aunq no se si se podra. Tb se me paso por la cabeza crear un control invisible. Por ejemplo, si pinto el panel por la derecha 100 pixeles mas alla del rango de vision, creo un control invisible a esa distancia y lo mismo para abajo. Y solo usando 2 solucionaria (creo) el problema. Pero probablemente sea la cosa mas chapuza que haya programado nunca xDDD
Por cierto, eso de docking que se supone q es? es qu viendo la definicion que dan a la propiedad en el VS no me queda muy claro aun :S
Saludos y (gracias) elevado a mil!
Para leer un archivo en un bitmap haz esto:
Bitmap imagen = new Bitmap("imagen.png");
Un truco para redimensionar los paneles (es un truco guarro) es crear un control panel hijo del control que quieras tener barras de scroll desde el editor de formularios y posicionarlo desde codigo, algo asi:
determinatamanio.Bounds = new Rectangle( anchomapa, altomapa, 0, 0);
Asi obligas al panel a mostrar las barras de scroll, asegurate que la propiedad AutoScroll del panel padre de este este control esta true, ademas por suerte (mas milagro que otra cosa) los controles panel no crean un nuevo dispositivo de contexto para su contenido solventando el problema de que pudiera chupar mucha memoria segun fuera mas grande, he de decirte que en windows vista pintar con drawimage, o usar controles picutrebox hace que la aplicacion vaya muy, pero que muy lenta, asi como a los 1000 picturebox la aplicacion genera un error, creo que la solucion perfecta es usar una llamada a bitblt del gdi de win32 mediante una dllimport.
Editado: He hecho el codigo del bitblt por si quieres probarlo:
using System.Runtime.InteropServices;
[DllImport("gdi32.dll")]
private static extern bool BitBlt(IntPtr obj, int xDest, int yDest, int widthDest,
int heightDest, IntPtr objSource, int xSource, int ySource, int rop);
[DllImport("gdi32.dll")]
public static extern IntPtr CreateCompatibleDC(IntPtr dc);
[DllImport("gdi32.dll")]
public static extern bool DeleteDC(IntPtr dc);
[DllImport("gdi32.dll")]
public static extern IntPtr SelectObject(IntPtr dc, IntPtr obj);
[DllImport("gdi32.dll")]
public static extern bool DeleteObject(IntPtr obj);
// Pintar una imagen usando el GDI de Win32
private void DrawImage(Graphics g, Bitmap image, int x, int y)
{
IntPtr target = g.GetHdc();
IntPtr source = CreateCompatibleDC(target);
IntPtr oldObj = SelectObject(source, image.GetHbitmap());
BitBlt(target, x, y, image.Width, image.Height, source, 0, 0, 0x00CC0020);
IntPtr newObj = SelectObject(source, oldObj);
DeleteObject(newObj);
DeleteDC(source);
g.ReleaseHdc(target);
}
En el vista me ha acelerado un poco, pero no lo suficiente, puede que en el xp vaya mucho mas rapido.
Acabo de probar el bitblt que me acabas de pasar y funciona perfecto :P . No te puedo decir en cuanto a rendimiento, pues estoy haciendo pruebas con un solo bitmap y no se nota la diferencia de momento.
Respecto a lo del panel hijo. Ya he hecho pruebas y funciona perfecto para que salgan las barras de scroll. De todas formas, ¿como puedo hacer para que no se vea? Si lo pongo invisible deja de haber scroll, y si le pongo color transparente, al mover las barras de scroll se deforma la imagen. Ha de haber alguna forma, o bien de pintar encima de el, o de hacer q no se pinte, pero que este ahi.
Saludos y muchas gracias ;)
Para que no se vea debes de posicionarlo como te indique, ademas con tamaño 0, 0
Puede que esto vaya mas rapido que la version anterior, es lo mismo salvo que no creas el dc del graphics constantemente:
// Preparar un graphics para su renderizado mediante DrawBlk
private void PrepareGraphics(Graphics g, ref IntPtr target, ref IntPtr source)
{
target = g.GetHdc();
source = CreateCompatibleDC(target);
}
// Pintar un bloque indicando los minimos atributos
private void DrawBlk(IntPtr target, IntPtr source, IntPtr bitmap, int x, int y, int width, int height)
{
IntPtr oldObj = SelectObject(source, bitmap);
BitBlt(target, x, y, width, height, source, 0, 0, 0x00CC0020);
IntPtr newObj = SelectObject(source, oldObj);
DeleteObject(newObj);
}
// Liberar el graphics preparado
private void ReleasePrepareGraphics(Graphics g, IntPtr target, IntPtr source)
{
DeleteDC(source);
g.ReleaseHdc(target);
}
Un ejemplo de como utilizarlo:
IntPtr target = (IntPtr)0, source = (IntPtr)0;
PrepareGraphics(g, ref target, ref source);
for( int y = 0; y < 16; y++ )
{
for( int x = 0; x < 16; x++ )
{
this.DrawBlk(target, source, tile.GetHbitmap(), x*tile.Width, y*tile.Height, tile.Width, tile.Height);
}
}
ReleasePrepareGraphics(g, target, source);
PD: Escribir codigo en este foro es un infierno :angry:
Hola,
lo que te pasa es que aunque parezca que tu panel es más grande, no lo es, y por eso no fuerza que aparezca el autoscroll (pintar fuera del panel no cambia su tamaño). Si tu imagen es más grande que tu panel (o quieres pintar fuera), tienes que redimensionar tu a mano el tamaño del mismo como te ha dicho Zupervaca (el mismo problema lo tienes en java).
Respecto al docking, se parece al BorderLayout de Java. Indica donde quieres que esté un control respecto a su contenedor. Puede situarlo arriba, abajo, derecha, izquierda o fill (que coja todo el hueco libre). El problema es que si por ejemplo tu formulario es de 500x500, pones un panel en fill (que ocuparía por ejemplo 498x498) y tu vas y lo redimensionas a mano a 1000x1000 (para que aparezcan barras de scroll), pues el docking te lo vuelve a poner de 498x498 (lo mismo con layout).
Pero vamos, que el truco es ese, si quieres pintar algo fuera de tu panel, tu panel tiene que ser más grande (y lo tienes que hacer más grande tu a mano desgraciadamente). Un saludo!
Vicente
Ok ok, ya me va quedando todo mas claro :D
Voy a probar a ver estas cosas. Es que cuando me pusiste:
determinatamanio.Bounds = new Rectangle( anchomapa, altomapa, 0, 0);
Pensaba que lo estabas dibujando de la esquina inferior derecha hasta la superior izquierda, de ahi el 0,0, pero resulta q era el tamaño :P
Voy a hacer pruebas a ver q sale.
Gracias a los 2 por vuestro tiempo ;)
Cita de: "Vicente"Pero vamos, que el truco es ese, si quieres pintar algo fuera de tu panel, tu panel tiene que ser más grande (y lo tienes que hacer más grande tu a mano desgraciadamente). Un saludo!
Con lo de fuera de tu panel me refería a "fuera del área visible" de tu panel. Un saludo!
Vicente
No problem si estoy aburrido jeje
Ya que estamos te pongo el codigo para pintar imagenes con el mismo sistema pero indicando un alfa
private struct AlphaBlendOp
{
public byte BlendOp;
public byte BlendFlags;
public byte SourceConstantAlpha;
public byte AlphaFormat;
}
[DllImport("Msimg32.dll")]
private static extern bool AlphaBlend(IntPtr obj, int xDest, int yDest, int widthDest, int heightDest, IntPtr objSource, int xSource, int ySource, int widthSource, int heightSource, AlphaBlendOp blend);
// Pintar un bloque con mascaraa
private void DrawBlkMask(IntPtr target, IntPtr source, IntPtr bitmap, int x, int y, int width, int height, AlphaBlendOp blend)
{
IntPtr oldObj = SelectObject(source, bitmap);
AlphaBlend(target, x, y, width, height, source, 0, 0, width, height, blend);
IntPtr newObj = SelectObject(source, oldObj);
DeleteObject(newObj);
}
Y el ejemplo de uso:
AlphaBlendOp blend;
blend.BlendOp = 0;// AC_SRC_OVER;
blend.BlendFlags = 0;
blend.SourceConstantAlpha = 100; // Intensidad del alfa
blend.AlphaFormat = 1;// AC_SRC_ALPHA;
IntPtr target = (IntPtr)0, source = (IntPtr)0;
PrepareGraphics(g, ref target, ref source);
this.DrawBlkMask(target, source, tile.GetHbitmap(), 0, 0, tile.Width, tile.Height, blend);
ReleasePrepareGraphics(g, target, source);
La forma correcta seria hacer una clase GDI32 para no tener los dllimport por ahi perdidos ;), he buscado algo de info por internet y ya hay gente usando este sistema por la lentitud de drawimage.
Otro tema si vas a usar el gdi de win32, creo recordar que la funcion transparentblt del gdi32 tiene un fallo y es que cada vez que se llama no libera bien los recursos del sistema con lo que en vez de usar transparentblt usaria en ese caso la drawimage del .net o haria una atraves de varios bitblt.
Suerte en el proyecto ;)
PD: Pues el tema del panel pense que lo habia hecho en plan guarro, pero esta claro que no soy el unico que encuentra soluciones artesanales (ole)
Jejeje, la verdad que el metodo tiene tela :P
He estado probando cosillas y mi nueva funcion es esta:
public void assignForm(Panel mainPanel)
{
panelHijo = new Panel();
panelHijo.Location = new System.Drawing.Point(540, 440);
panelHijo.Name = "panelHijo";
panelHijo.Size = new System.Drawing.Size(0,0);
panelHijo.TabIndex = 0;
mainPanel.Controls.Add(panelHijo);
imagen = new Bitmap("hola.jpg");
mainPanel.Paint += new PaintEventHandler(panelPaint);
Rectangle rect = new Rectangle();
x=0;
y=0;
rect.X=x;
rect.Y=y;
rect.Height = 100;
rect.Width = 100;
mainPanel.Invalidate(rect);
x=300;
y=300;
rect.X=x;
rect.Y=y;
mainPanel.Invalidate(rect);
}
Estoy haciendo la prueba para 2 imagenes. Pero solo me muestra la segunda. No se si es que uso mal el invalidate o que pasa.
Esto sigue siendo igual, a salvedad de que uso tu optima funcion ;):
public void panelPaint (object sender,PaintEventArgs e)
{
this.DrawImage(e.Graphics,imagen, x,y);
}
x e y son variables private de la clase. Solo se me ocurre q use mal el invalidate con rectangulos. Si lo uso para todo el panel, es decir, mainPanel.invalidate(); tb me muestra solo la segunda imagen :P
Saludos!
PD: En cuanto consiga ordenar todo esto un poco, pruebo tus nuevas funciones de GDI :P
Prueba a poner dos variables X, Y para dibujar
El problema puede estar en que el evento donde lo haces puede que no vaya bien el invalidate, es decir, despues de hacer tus invalidates hace el otro entero sobre el control.
Es que me modifica el valor de las variables antes de entrar a la funcion de pintado. Si pongo esto:
public void assignForm(Panel mainPanel)
{
panelHijo = new Panel();
panelHijo.Location = new System.Drawing.Point(540, 440);
panelHijo.Name = "panelHijo";
panelHijo.Size = new System.Drawing.Size(0,0);
panelHijo.TabIndex = 0;
mainPanel.Controls.Add(panelHijo);
imagen = new Bitmap("hola.jpg");
mainPanel.Paint += new PaintEventHandler(panelPaint);
x=0;
y=0;
mainPanel.Invalidate();
x=200;
y=200;
// mainPanel.Invalidate();
}
Solo repinto despues del primero. Y aun asi me pinta la imagen en la (200,200). Yo no se como estara programado internamente los eventos, pero parece q estuviesen con Threads :P y sigue ejecutando el procedimiento antes de llamar a la rutina del evento, en este caso de pintado.
Saludos!
Es lo que te comento en el anterior post, despues de ejecutarse la funcion assignForm el dialogo se vuelve a invalidar, ¿podrias poner la funcion desde donde llamas a la funcion assignForm?
private void menuItem12_Click(object sender, System.EventArgs e)
{
Mapa.assignForm(panel1);
}
Es del menu Archivo del Form principal, del apartado Nuevo.
Claro, seguramente tengas razon. Al ser un menu desplegable que se pinta sobre el panel y demas controles, provocara un invalidate sobre los que se dibuje.
Editado: Acabo de llamar la funcion desde un botn, en vez de un menu y me pasa lo mismo :o
Editado2: Acabo de agregar otro boton, modifique assigForm pa solo pintar una vez y hice una funcion assignForm2 que pinta la segunda imagen. Cada uno de ellos con un invalidate(rect). y va bien. Entoces la pregunta del millon es: ¿Que debo llamar a assignForm varias veces para pintar todas las cosas, en vez de pintar todas las cosas dentro del assigForm? que cosas mas raras :D
Saludos!
Fijate que en la funcion assignForm estas creando un nuevo panel y esto implica que repinte todo el dialogo ya que deben de recalcularse todos los controles.
Aunq ponga en comentarios la creacion de ese panel sucede lo mismo, sigue dibujando en la ultima posicion. Voy a hacer el bucle de pintado fuera del assigForm en vez de dentro, a ver si asi se soluciona el problema. Y si veo que funciona, hago otro metodo de interfaz para el bucle de llamadas pa quede mas curioso y fuera.
En un rato comento a ver qtal :P
Saludos!
Na de na, esto es lo que hago ahora y pasa lo mismo...
public void panelPaint (object sender,PaintEventArgs e)
{
this.DrawImage(e.Graphics,imagen, x,y);
}
public void configMap(Panel mainPanel)
{
panelHijo = new Panel();
panelHijo.Location = new System.Drawing.Point(540, 440);
panelHijo.Name = "panelHijo";
panelHijo.Size = new System.Drawing.Size(0,0);
panelHijo.TabIndex = 0;
mainPanel.Controls.Add(panelHijo);
imagen = new Bitmap("hola.jpg");
mainPanel.Paint += new PaintEventHandler(panelPaint);
}
public void drawTile(Panel mainPanel,int posX, int posY)
{
Rectangle rect = new Rectangle();
x=posX;
y=posY;
rect.X=x;
rect.Y=y;
rect.Height=100;
rect.Width=100;
mainPanel.Invalidate(rect);
}
private void button1_Click(object sender, System.EventArgs e)
{
Mapa.configMap(panel1);
Mapa.drawTile(panel1,0,0);
Mapa.drawTile(panel1,200,200);
}
Y pasa lo mismo. Solo funciona cuando hago las llamas a DrawTile (q es el que hace el invalidate) desde sitios distintos. Si estan en la misma funcion, en este caso la funcion de evento de click al boton, pues solo funciona la ultima. SOCORROOOOO jejeje
Saludos!
Date cuenta que en la funcion button1_Click estas llamando Mapa.configMap que crea el el panel y este hace un invalidate de todo el control o dialogo.
Debes saber que la funcion invalidate no repinta al momento, es decir, deja en la cola de mensajes del dialogo un mensaje indicando que debe de repintarse esa zona, esa cola de mensajes solo es tratada despues de salir de la funcion button1_Click, con lo que pasa esto:
- Al crear el panel se agrega a la cola de mensajes un invalidate de todo el control
- Al dibujar creas un invalidate de la zona que quieres
- Al dibujar otra vez creas un invalidate de la zona que quieres
- Se tratan los mensajes
- Primer invalidate que repinta todo, pinta la imagen en las coordenadas X e Y, las ultimas que indicaste
- Segundo invalidate, repinta la parte del control, como las coordenas X e Y son las del ultimo que indicaste no pinta nada
- Tercer invalidate, repinta la parte del control, las coordenadas X e Y son correctas y pinta otra vez la imagen en esa posicion
Como ves el problema esta que en el invalidate no hace que pinte de forma inmediata.
Ya veo.... joer como sabes todo eso jejejej, yo mire cosillas de msdn y clases y demas, pero vamos, lo de la cola no lo sakaria ni en mil años :P
Bueno, entos una vez resuelto la duda del porque, queda el como. Si se van metiendo en la cola los repintados que ha de hacer, y cada uno de ellos respecto a un par (x,y), no puedo ir modificando esas variables, ya que entonces pasa lo de ahora, q repinta N veces en la ultima coordenada.....
Es que si me fijo, este problema solo lo tendre al cargar inicialmente el mapa vacio, ya que luego voy dibujando tile a tile, y solo se hace un invalide por evento, asi q no hay mas en la cola.
Tu como has hecho para solucionar esto? :P, para pintar todos al principio. Se me ocurre pintar todos sin hacer invalidates y hacer un invalidate final a todo el panel. Pero no se como acceder a la estructura Graphics desde algun sitio q no sea una funcion de eventos, para que me proporcione el PaintEventArgs.
Saludos! y gracias de nuevo :P
Para pintar todos los bloques debes de tener un array con todos los bloques e ir con un for pintandolos, es decir:
int [,] bloques = new int[100,100];
... (inicializar los bloques con valores)
// Esto en el evento de pintar
for( int x = 0; x < 100; x++ )
{
for( int y = 0; y < 100; y++ )
{
int indice = bloques[x,y];
Bitmap imagen = imagenes[indice];
graphics.drawimage(imagen, x*32, y*32)
}
}
Fijate que el codigo que te he puesto es algo muy sencillo y muy concreto, pero sirve para que te hagas una idea.
Bueno, despues de tomarme un descansito, que llevaba todo el dia mirando cosas :P he vuelto a la carga. Por un lado ya va. Funciona para pintar todas las imagenes, ademas si adapto el panel Hijo me salen las barras de scroll y todo perfecto (ole) . El problema es cuando las uso, q se me distorsiona la imagen. He probado varios eventos del panel, pero todos afectan a el panel en si, no a sus barras. Seguramente haya alguno que se ejecute cuando use las barras o similar. Te pongo una imagen de como queda distorsionada la imagen:
(http://usuarios.lycos.es/rage182/imagenforo3.jpg)
Saludos!
Por lo que veo el problema es que pintas a partir de el Rectangle que te pasa la funcion, cada vez que desplazas la barra de scroll el Rectangle solo tiene la zona que se va a actualizar, para que te hagas una idea de lo que digo:
(http://img141.imageshack.us/img141/436/imagenforo30rc.th.jpg)La zona roja es la que se va a actualizar al hacer el scroll horizontal, si le dices que pinte a partir de la X del rectangulo estaras pintando el inicio de la imagen en esa posicion, por eso ves ese efecto, para evitar esto te hacen falta las matematicas :D, debes de calcular la posicion de la imagen respecto al ancho de la imagen, es decir, X = (X / anchoimagen) * anchoimagen, con eso haras que la imagen comience a pintarse en el sitio correcto, lo haces multiplo, tambien puedes poner esta otra formula X -= X % anchoimagen;, aqui tal vez alguien metido en arquitecturas nos podria decir que formula es mas rapida.
jajajaj y asi es como lo has hecho tu para tu editor?
Madre mia, el que me dijo que hacer esto era facil debe estar revolviendose en su tumba :P
Lo bueno de todo es que estoy aprendiendo mas de lo que esperaba sobre windows forms y demas (ole)
Voy a probarlo a ver si sako algo en claro.
Saludos!
Dios mio, que paranoia, veo ya la maldita cara hasta cuando cierro los ojos....
Vamos a ver. Lo ulltimo que he conseguido es esto (por pasos):
1. Dibujo las caras en filas y columnas. Por ejemplo: Dibujo 7x7 caras. Con una variable bool controlo que es el primer pintado, y no el de scroll.
2. Para no hacerme lios uso un panel siempre igual en tamaño. En este caso es 550x450. Y cada cara es 100x100.
3. Segun las voy pintando la primera vez (cuando le doy al boton) voy calculando el numero de caras que pinto horizontalmente en una variable X. Es variable la multiplico x 100 para saber donde va a acabar la cara.
4. Si en una iteracion del bucle de pintado resulta que 550 (ancho del panel) - X (numero de caras pintadas)*100 < 100 , es decir, que la diferencia del ancho del panel, con la posicion de la ultima cara es menor que 100, la siguiente cara se va a partir y no se pinta entera. Entonces me quedo con esa distancia (por ejemplo variable Actual) y sigo pintando todo.
5. Ya he acabado de pintar todo y pongo la bool de primer pintado a false;
6. Dentro de la rutina de pintado (la qu me diste de GDI) hago un if para ver si es el primer pintado, en ese caso pintar la cara entera con BitBlt, y en caso contrario indicarle a BitBlt que pinte la cara a partir de la distancia guardada antes (Actual).
7. Hago scroll y empieza a pintarse el resto de la cara (ole) (mas o menos, porq no considero el tamaño de la barra de Scroll, luego el tamaño ancho del panel ya no es 550, es algo menos. Pero eso de momento me da igual) pero se acaba la alegria cuando veo que se pinta rayada tb :P
Obviamente se pinta rayada porq a partir de ahora estoy pintando caras a partir de esa posicion (Actual). Lo que deberia hacer ahora es ir actualizando esa variable Actual y cada vez ir pintando con BitBlt a partir de esa posicion. Pero aqui ya me pierdo y no se seguir ;)
Si a todo esto le añades que tb hay q controlar scroll a la izquierda, arriba y abajo pues.... ami me dan ganas de pegarme un tiro jajaja
Por otro lado he estado pensando en el parametro BackgroundImage del panel. Seguramente esa imagen de fondo se autorepinte sola con el scroll. Si consigo hacer un Blit a esa imagen igual ya no tengo que preocuparme de controlar estas cosas. ¿Que opinas?
Por hoy creo que ya valio, voy a jugar a algo ;)
Saludos!
Weno, despues de un dia de tirones de pelo ya he conseguido solucionar el problema (ole)
Para ello tuve que bajarme un libro en pdf sobre c# que casualmente tenia un apartado sobre Drawing and Scrolling, y casualmente pintaba una imagen en un panel con scroll :P
Al final el problema no era tan complejo como pensabamos. Se resumia a dibujar con esta sentencia:
e.Graphics.DrawImage(imagen,main.AutoScrollPosition.X,main.AutoScrollPosition.Y,imagen.Width,imagen.Height);
Siendo imagen el Bitmap y main el Panel. esta era la funcion para una imagen. Si la ejecutabas N veces en el bucle te repintaba todas las imagenes encima de la anterior. Asi que habia que añadir esto:
Citar
e.Graphics.DrawImage(imagen,main.AutoScrollPosition.X+x,main.AutoScrollPosition.Y+y,imagen.Width,imagen.Height);
x e y se van incrementando en el bucle y son las que dicen donde va cada imagen. Asi ya funciona y salen todas las imagenes.
Ahi va una captura:
(http://usuarios.lycos.es/rage182/imagenforo4.jpg)
Asi que al hacer scroll se ven todas las imagenes a la perfeccion ;) (cambie la imagen, q la cara ya me saturaba :P)
Ahora queda todo lo demas, pero sin duda es un gran paso B)
Por cierto, al final no uso tu funcion de GDI, pero vamos, dudo que sea muy dificl adaptarla para estos nuevos parametros :P
Os mantendre informados!
Saludos!
Cita de: "zupervaca"te hacen falta las matematicas :D, debes de calcular la posicion de la imagen respecto al ancho de la imagen, es decir, X = (X / anchoimagen) * anchoimagen, con eso haras que la imagen comience a pintarse en el sitio correcto
X = (X / anchoimagen) * anchoimagen , qué matemáticas son esas? :blink:
Eso sirve para hacer multiplo del ancho de la image el valor de X, ejemplo:
X = 48
AnchoImage = 32
48/32=1.5
1.5=1 por que es Int
1*32=32
Se queda en la posicion 32, para la posicion X=105 se quedaria en la posicion 96 haciendolo multiplo de 32 otra vez
Cita de: "zupervaca"por que es Int
:rolleyes: ví la formula sin considerar el tipo de x... demasiadas matemáticas en la uni :wacko:
Te aviso que el valor de AutoScrollPosition.X hace que pintes todos los tiles aunque no sean visibles, es decir, si estas en la posicion 100 de la barra de scroll el valor de AutoScrollPosition.X es -100, con lo que estas pintando desde -100 que es fuera de la parte visible del control.
Citar
Te aviso que el valor de AutoScrollPosition.X hace que pintes todos los tiles aunque no sean visibles, es decir, si estas en la posicion 100 de la barra de scroll el valor de AutoScrollPosition.X es -100, con lo que estas pintando desde -100 que es fuera de la parte visible del control.
Ya ya, eso presuponia :P
De todas formas ese tipo de pintado solo lo hare al principio y cuando haga scroll. Para cuando pase el puntero por encima tengo pensado hacer lo que me dijiste de actualizar por cuadrados solamente lo que se modifican.
De todas maneras, el scroll va bastante fluido. Si bien es verdad que tampoco uso muchos tiles de momento, supongo que a media que haya grandes mapas hacer scroll sera mas costoso.
¿Para evitar eso que habria que volver a nuestro querido problema de pintar trozos? :P
PD: Ptra cosa q se me acaba de ocurrir ahora mismo es la sigiuente: Como las coordenadas X y Y definen las coordenadas del panel visibles (sin sumarle autoscroll.x ni nada), podria mediante algunos calculos pintar solo los tiles que vayan a tener algnua parte visible en el panel. Se me acaba de ocurrir ahora, pero no creo que suponga muchos calculos y optimizaria bastante para mapa grandes. ¿Que opinas?
Gracias por la ayuda ;)
Ese sistema es el que te comentaba antes, siempre se debe de pintar lo justo ya que no todo el mundo tiene una supermaquina ;)
Hola,
resucito esto: para el doble buffer usais esto?
"To fully enable double-buffering, you must set the UserPaint, AllPaintingInWmPaint, and DoubleBuffer bits to true."
Es de
esta página.
Un saludo!
Vicente
Exacto :P
yo pongo esto:
this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.DoubleBuffer,true);
Lo sake de
aqui.
La verdad es que se nota una burrada la mejoria :)
Saludos!
Yo actualmente prefiero no usar el doble bufer para evitar pintar lo que sea y luego pintar una imagen contra la vista del formulario, ademas asi no modifico el sistema de mensajes estandar, lo que hago para evitar repintados extraños y parpadeos es redifinir la funcion OnPaintBackground de esta manera
/// <summary>Renderizar el fondo del control</summary>
protected override void OnPaintBackground(PaintEventArgs e)
{
// En modo edicion obligamos pintar el fondo del control
if (this.DesignMode)
{
base.OnPaintBackground(e);
}
}
Despues en la función paint pinto a mano lo que haga falta mirando el rectangulo de actualizacion que me pasa
Creo que lo que haces con el background es lo mismo que se consigue usando los flags que he puesto y que usa nsl... Pero no sabría asegurarlo al 100%
Un saludo!
Vicente
Los flags ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint hacen eso exactamente (por lo menos eso creo), pero el flag del doble bufer crea un dispositivo de contexto oculto que es donde se renderiza todo y luego actualiza el dispositivo de contexto del formulario con el oculto, lo cual no me convencio, casi que prefiero optimizar el renderizado de las ventanas pintando lo justo evitando los parpadeos y ganar velocidad en el dibujado de los formularios.
Aunque esto que digo es una contradiccion por que en el sistema de skins que monte hace años en c++ precisamente hice un doble bufer para curarme en salud de los parpadeos, pero bueno tambien es que el grafista ponia montones de dibujos unos sobre otros :D