Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   C++ Builder (https://www.clubdelphi.com/foros/forumdisplay.php?f=13)
-   -   Centrar texto StringGrid (https://www.clubdelphi.com/foros/showthread.php?t=73630)

mizzard 05-05-2011 23:40:33

Centrar texto StringGrid
 
Hola a todos, antes de nada decir q he mirado en este foro pero no consigo aclararme con la información que busco, en concreto en este thread:
http://www.clubdelphi.com/foros/show...ght=stringgrid

Mi intención es a partir de un evento en un boton, recoger una serie de información y mostrarla en mi StringGrid. Parace que la información la recojo bien y se muestra correctamente, pero me sale el texto justificado a la izquierda.

Tras mucho pelearme he conseguido que el texto del nombre de cada columna salga en negrita, pero ahora me gustaría que todo el texto del grid apareciese centrado.

Alguien me podría explicar (si es en codigo se agradece), como deberia hacerlo en el evento drawcell?

Muchas gracias por anticipado, cualquier respuesta se agradece debido a los cabezazos que llevo ya... jeje

Un saludo!

escafandra 06-05-2011 00:10:04

Precisamente en ese hilo respondía a la misma pregunta que tu haces. El evento OnDrawCell expuesto se encarga de centrar los textos, sólo tienes que colocar ese código y rellenar las celdas con tus datos sin preocuparte de nada mas.

No termino de entender bien cual es tu duda.

Saludos.

mizzard 06-05-2011 00:40:57

pues que he probado con muchos codigos y no consigo nada. Con ese en concreto copiando y pegando tal cual me salta el siguiente error en la línea:

Código:

  char *Cadena = grid->Cells[ACol][ARow].c_str();
[BCC32 Error] MainForm.cpp(1013): E2034 Cannot convert 'wchar_t *' to 'char *'

Uso C++ Builder 2010 y en concreto el codigo compreto es:

Código:

  TStringGrid *grid = dynamic_cast<TStringGrid*>(Sender);
  HDC dc = grid->Canvas->Handle;
  char *Cadena = grid->Cells[ACol][ARow].c_str();


    StrGridTique->Canvas->Font = StrGridTique->Font;

    // Ponemos el título en negrita
    if (ACol>=0 && State.Contains(gdFixed)){
        StrGridTique->Canvas->Font->Style = TFontStyles()<< fsBold;
    }

    const AnsiString Conceptos[3]= {"Cantidad","Precio ud","Precio"};
    if(ARow==0 && ACol==0){
        StrGridTique->Canvas->TextOutA(Rect.Left, Rect.Top,"Producto");
        }
    else if(ARow==0){
        StrGridTique->Canvas->TextOutA(Rect.Left, Rect.Top, Conceptos[ACol-1] );
    }

  grid->Canvas->Brush->Style = bsClear;
  DrawText(dc, Cadena, strlen(Cadena), &Rect, DT_CENTER | DT_VCENTER | DT_SINGLELINE );


Un saludo!

escafandra 06-05-2011 02:07:27

El problema es que la versión 2010 usa Unicode.

Debes usar la versión unicode de la API DrawText: DrawTextW (el copmpilador lo hace automáticamente) y cambiar la línea
Código:

char *Cadena = grid->Cells[ACol][ARow].c_str();
a
Código:

wchar_t *Cadena = grid->Cells[ACol][ARow].c_str();
Saludos.

mizzard 07-05-2011 12:42:07

Hola, perdona x no contestar antes pero he estado muy liado, gracias por la respuesta.

Tras hacer ese cambio ahora me pasa lo siguiente:


Error 1:Cannot convert wchar_t *' to const char *
Error 2: Type mismatch in parameter '__s' (wanted 'const char *', got 'wchar_t *')

en la 2ª línea:

Código:

grid->Canvas->Brush->Style = bsClear;
  DrawText(dc, Cadena, strlen(Cadena), &Rect, DT_CENTER | DT_VCENTER | DT_SINGLELINE );


Un saludo!

escafandra 07-05-2011 16:45:15

Te explico:

Las APIs de Windows Que usan cadenas, tienen dos versiones, la terminada en A y la terminada en W. Para DrawText tenemos internamente DrawTextA y DrawTextW. Lo normal es que el compilador escoja ya apropiada según si usa Unicode (W) o no (A).

Usa la versión DrawTextW;
Código:

DrawTextW(dc, Cadena, wcslen(Cadena), &Rect, DT_CENTER | DT_VCENTER | DT_SINGLELINE );
o bien:

Código:

DrawTextW(grid->Canvas->Handle, grid->Cells[ACol][ARow].c_str(), grid->Cells[ACol][ARow].Length(), &Rect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);

Saludos.

mizzard 07-05-2011 21:04:12

Muchisimas gracias por la respuesta, pero... sigo teneniendo un problema ... :(

Resulta q el texto se centra bien, pero me sale dos veces, a la izquierda y centrado :(

Lo que me gustaría es que se viera todo centrado tanto en la fila 0 que es fija (y en negrita) como en todos los campos.

Yo lo que hago es a partir de una serie de botones, recojo unos campos y hago que se muestren en el string grid usando:
Código:

StrGridTique->Cells[0][posFila]=DBText->Caption;
y controlando posFila voy añadiendo registros y diferentes nombres en el Caption del DBText.

Y luego en el evento hago:

Código:

void __fastcall TfrmMain::StrGridTiqueDrawCell(TObject *Sender, int ACol, int ARow,
          TRect &Rect, TGridDrawState State)
{
  TStringGrid *grid = dynamic_cast<TStringGrid*>(Sender);
  HDC dc = grid->Canvas->Handle;
  wchar_t *Cadena = grid->Cells[ACol][ARow].c_str();

  grid->Canvas->Brush->Style = bsClear;
  DrawTextW(grid->Canvas->Handle, grid->Cells[ACol][ARow].c_str(), grid->Cells[ACol][ARow].Length(), &Rect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);

  StrGridTique->Canvas->Font = StrGridTique->Font;

    // Ponemos el título en negrita
    if (ACol>=0 && State.Contains(gdFixed)){
        StrGridTique->Canvas->Font->Style = TFontStyles()<< fsBold;
    }

    // Preparamos la rejilla con el tique
    const AnsiString Conceptos[3]= {"Precio unitario","Cantidad","Precio"};
    if(ARow==0 && ACol==0){
        StrGridTique->Canvas->TextOutA(Rect.Left, Rect.Top,"Producto");
    }
    else if(ARow==0){
        StrGridTique->Canvas->TextOutA(Rect.Left, Rect.Top, Conceptos[ACol-1] );
    }
    else if (ARow>0){
        tposFila=ARow;
        bEditarCantidad=true;
    }
}


Uff perdona por mi insistencia, pero es que voy dando tumbos constantemente, veo que la solución está cerca pero no consigo llegar, jeje

Muchísimas gracias por adelantado.

Un saludo!

aams01 07-05-2011 22:55:34

Hola soy re-novato espero comprension si es que me equivoco.
Por que no desde que se active la forma pones los titulos de la rejilla
en cuanto se active la forma.
Código:

void __fastcall TTfrmMain::FormActivate(TObject *Sender)
{
        StrGridTique->Cells[0][0]="Precio unitario";
        StrGridTique->Cells[1][0]="Cantidad";
        StrGridTique->Cells[2][0]="Precio";

}

Para poner en negritas y centrar con lo siguiente recorte tu codigo asi que le hace falta el ultimo if por lo demas yo creo si lo hace
Código:

void __fastcall TTfrmMain::StrGridTiqueDrawCell(TObject *Sender, int ACol, int ARow, TRect &Rect,
                  TGridDrawState State)
{

  StrGridTique->Canvas->Brush->Color = clBtnFace; // Color por default,
//  cambialo al que gustes, Esto es para que no te empalme la informacion
  StrGridTique->Canvas->Rectangle(Rect);
  InflateRect(&Rect, -2, -2);
  if (ARow==0) {
          StrGridTique->Canvas->Font->Style = TFontStyles()<< fsBold;
  }

  DrawTextW(StrGridTique->Canvas->Handle, StrGridTique->Cells[ACol][ARow].c_str(), StrGridTique->Cells[ACol][ARow].Length(), &Rect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);

  if (ARow>0){
        tposFila=ARow;
        bEditarCantidad=true;
    }
}

Todo este codigo fue tomado de escafandra en uno de los temas, ojala y no enoje por tomar su codigo


Buno yo tengo unas preguntas para que utilizas
Código:

StrGridTique->Canvas->Brush->Style = bsClear;

escafandra 08-05-2011 00:47:50

aams01 te está dando parte de la solución.

Piensa que estás escribiendo textos en las rejillas dos veces, una centrado con DrawTextW y la otra a la izquierda con TextOutA. Lo propio es añadir los nuevos textos en otro procedimiento, de carga o en todo caso, con DrawTextW para poder centrarlos.

Pero aún te falta otra cosa. La VCL escribe por su cuenta las celdas a la izquierda. Si quieres redibujarlas debes borrarlas previamente y luego reescribirlas.

Código:

void __fastcall TForm1::StringGrid1DrawCell(TObject *Sender, int ACol,
      int ARow, TRect &Rect, TGridDrawState State)
{
  TStringGrid *grid = dynamic_cast<TStringGrid*>(Sender);
 
  InflateRect(&Rect, -1, -1);
 
  // Ajusto los colores de las celdas a las preestablecidos 
  if(State.Contains(gdFixed))
      grid->Canvas->Brush->Color = grid->FixedColor;
  else
      grid->Canvas->Brush->Color = grid->Color;

  // Borro las celdas
  grid->Canvas->FillRect(Rect);


  DrawText(grid->Canvas->Handle, grid->Cells[ACol][ARow].c_str(), grid->Cells[ACol][ARow].Length(), &Rect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);

  // Restauro el Rect que se pasó por referencia, no por valor...
  InflateRect(&Rect, 1, 1);
}

Saludos.

mizzard 08-05-2011 13:48:17

Hola a ambos, muchisimas gracias por las respuestas.

aams01, esa parte del código es porque en la propiedad del StringGrid Drawing Style uso gdsGradient

Cita:


Buno yo tengo unas preguntas para que utilizas
Código:
StrGridTique->Canvas->Brush->Style = bsClear;
Y al aplicar el código que has aportado con los cambios de escafranda veo que las celdas fijas se ponen del color que hay puesto en la propiedad fixedColor...

He probado cambiando el Style e incluso poniendo clNone, pero no consigo que salga con efecto degradado..., se podría aplicar al Canvas el estilo gdsGradient? o al menos que sea transparente?

Muchisimas gracias! :), Un saludo!

mizzard 08-05-2011 19:52:43

Hola de nuevo, ¿sabeis si existe alguna forma de detectar cuando se entra 2 veces en la misma celda?, me explico.

Resulta que cuando entro una celda la detecto con la propiedad OnSelectCell, recojo la fila en donde se ha hecho click y así actualizo los datos cuando se introduce un nuevo valor.

Si pulso un boton actualizo de nuevo el StringGrid y si me da por hacer click otra vez en la misma celda de antes, como no la detecta no modifico los datos que corresponden.

He probado con eventos OnMouseEnter/Leave, OnGetEditText, etc..., pero no consigo que me funcionen

¿Existe alguna manera de coger y mover en ejecución la celda seleccionada?

Se me ocurre que de esta forma si siempre tras pulsar el boton selecciono la Cells[0][0], siempre detectare cuando hago click en la misma celda.


Alguna idea??, muchas gracias

escafandra 08-05-2011 23:26:10

Cita:

Empezado por mizzard (Mensaje 399346)
...se podría aplicar al Canvas el estilo gdsGradient?...

Puedes escribir una función que pinte un rectángulo con un degradado para sustituir a grid->Canvas->FillRect(Rect);

Cita:

Empezado por mizzard (Mensaje 399353)
...¿sabeis si existe alguna forma de detectar cuando se entra 2 veces en la misma celda?...

Yo usaría algo como esto:
Código:

void __fastcall TForm1::StringGrid1MouseDown(TObject *Sender,
      TMouseButton Button, TShiftState Shift, int X, int Y)
{
  int ACol, ARow;
  StringGrid1->MouseToCell(X, Y, ACol, ARow);
  //..............
  // Tu código....
}

Saludos.

aams01 08-05-2011 23:55:50

Cita:

Empezado por mizzard (Mensaje 399353)
Se me ocurre que de esta forma si siempre tras pulsar el boton selecciono la Cells[0][0], siempre detectare cuando hago click en la misma celda.

Para seleccionar un selda es con lo siguiente reucuerda que un StringGrid o DBGrid contiene renglones y columnas

Código:

StrGridTique->Col=0;
StrGridTique->Row=0;

Aunq me agrada mas el codigo de escafandra.
No obstante toy seguro de que este codigo te podra servir para otra cosa.
:cool:

mizzard 10-05-2011 01:09:03

Gracias a ambos por las respuestas ya que me han sido de gran ayuda.

Ahora estoy mirando bugs e intentar corregirlos para mejorar la estabilidad del programa y uno que me surge como consecuencia directa de ir incorporando estos cambios es que me he dado cuenta que cuando se queda marcada una de las celdas el texto aparece dos veces una a la izquierda y otra vez centrado y cuando hago clic en otra celda todo vuelve a la normalidad.

Tras diversas pruebas, me he dado cuenta que si no hay ninguna celda marcada parece q todo va bien.

Comento este problema pq mi código de repente se vuelve inestable cuando pasa esto, lo que pasa es que tendré que mirarlo con más detenimiento para sacar una conclusión clara.

Como siempre, pinto con el evento ondrawcell en funcion de unas banderas que activo desde otras secciones del codigo.

Alguna idea de pq puede pasar esto?

Un saludo!

aams01 10-05-2011 07:15:22

Hola como mencionas si solo pasa eso cuando dejas seleccionada una celda por que no quitas el focus de ese componente y lo pasas ya sea a un botón o a un edit.
El problema creo es por q tienes el stringrid en modo edición.
Si el stringrid solo lo quieres para mostrar y jalar información yo diría que le quites la opción de editar, ya q te ahorraras tiempo en validar

mizzard 10-05-2011 11:43:34

Hola aams01,

Al final tras muchos intentos consigo quitar el foco, pero estableciéndoselo a otro elemento no funciona. Buscando por el foro he encontrado este que sí que funciona (aporte de escafandra):

Código:

    TGridRect NoRect;
    NoRect.Top = NoRect.Left = NoRect.Right = NoRect.Bottom = -1;
    frmMain->StrGridTique->Selection=NoRect;

El caso es que funciona, pero nuevamente vuelvo a tener problemas. En mi StringGrid cambio a la opcion de editar cuando hago clic en una columna, en el resto de casos deshabilito la opcion de editar (esto parece que funciona bien).

El error en concreto sale cuando al terminar de realizar rellenar el StringGrid y de realizar las operaciones pertinentes (únicamente cuando edito un dato en una celda, si no lo edito no sale el error), quito la celda seleccionada, la limpio de información y entonces aparece un mensaje de error, a continuación muestro el codigo y cuándo aparece el mensaje:

Cuando termino de operar con el StringGrid... (hasta aquí sin problemas) hago lo siguiente:

Código:

quitarFocoGrid(); // función que contiene el código de arriba

// Limpiamos el Grid de información
// RowCount-2 porque no me interesa la fila fija ni la que se crea despues de la que contiene informacion (esto probado a parte funciona)
        for (int cont = frmMain->StrGridTique->RowCount-2; cont > 0; cont--) {
            frmMain->StrGridTique->Rows[cont]->Clear();
            frmMain->StrGridTique->RowCount=frmMain->StrGridTique->RowCount-1;
        }
        frmMain->StrGridTique->Cells[0][0]="";
        frmMain->StrGridTique->Cells[1][0]="";
        frmMain->StrGridTique->Cells[2][0]="";
        frmMain->StrGridTique->Cells[3][0]="";

// Mostramos un mensaje informativo
Application->MessageBoxA(L"Puede retirar la tarjeta.",L"Información",MB_OK+ MB_ICONINFORMATION);

Y cuando le doy a aceptar sale el siguiente mensaje de error:

EListError with message 'List indexof bounds (-1)'

He probado a poner la de quitar el foco al final del borrado de la tabla y pasa lo mismo pero el mensaje sale en otra parte del código, por lo que el problema es el mismo.

Alguna idea de cómo puedo hacer el borrado y quitar la fila seleccionada (o al revés) sin que dé este problema??

Muchas gracias y un saludo!

aams01 10-05-2011 18:57:21

Hola por curiosidad solo cambia
Código:

    Application->MessageBoxA(L"Puede retirar la tarjeta.",L"Información",MB_OK + MB_ICONINFORMATION);
por
Código:

Application->MessageBoxA(L"Puede retirar la tarjeta.",L"Información",MB_OK| MB_ICONINFORMATION);
y con respecto al error que te manda probe tu codigo y no marca ningun error

mizzard 11-05-2011 23:20:38

Hola aams01, al final lo solucioné revisando código y añadiendo en una sección que no fuera editable.

Cita:

Hola por curiosidad solo cambia
Código:
Application->MessageBoxA(L"Puede retirar la tarjeta.",L"Información",MB_OK + MB_ICONINFORMATION);
por
Código:
Application->MessageBoxA(L"Puede retirar la tarjeta.",L"Información",MB_OK| MB_ICONINFORMATION);
y con respecto al error que te manda probe tu codigo y no marca ningun error
Probé esto pero es indiferente, funciona igual.

Muchas gracias por las molestias, un saludo!


La franja horaria es GMT +2. Ahora son las 21:48:10.

Powered by vBulletin® Version 3.6.8
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Traducción al castellano por el equipo de moderadores del Club Delphi