Problem with data showed in a recycler view when user log out and log in firebase realtime - kotlin

I have a weird behavior with a recyclerview. The app is a kind of social network where users publish post and I look for all the post of all users that authentifated user has bookmarked. My app works fine the first time an user logins. But if the user log out, and later log in, the first post it's not showed. But If the user start over again the app it works properly. I'm really lost why it's happening.
This is a picture of part of my database, but I can read the correctly. I think the problem has to be with the recyclew view.But not sure
Here is the code where I search the data. I use two functions buscarUsuarioSeguidos which store an array with the list of user's id that later I use to look for user's posts with mostrarPublicacionesSeguidas.
private fun buscarUsuariosSeguidos(usuarioUid:String){
//BUSCAMOS EN LA TABLA SEGUIR CADA ENTRADA DONDE APAREZCA COMO SEGUIDOR EL USUARIO AUTENTIFICADO
val seguidoresRef= referenciaBD.child("Seguir").child(usuarioUid).child("Siguiendo")
// LIMPIAMOS LAS PUBLICACIONES SEGUIDAS
usuariosSeguidosUid?.clear()
//CARGAMOS LAS PUBLICACIONES EN EL RECYCLERVIEW DATACHANGE SE ENCARGA DE CARGAR LOS DATOS
seguidoresRef.addValueEventListener(object :ValueEventListener{
override fun onDataChange(snapshot: DataSnapshot) {
if (snapshot.exists()){
for (snap in snapshot.children)
//RECUPERAMOS LA CLAVE DE CADA CHILDREN QUE REALMENTE ES EL UID DEL USUARIO
snap.key?.let { (usuariosSeguidosUid as ArrayList<String>).add(it) }
}
}
override fun onCancelled(error: DatabaseError) {
}
})
}
//ESTA FUNCION BUSCA LAS PUBLICACIONES DE TODOS LOS USUARIOS QUE SIGUE EL USUARIO AUTENTIFICADO
private fun mostrarPublicacionesSeguidas() {
//CREAMOS LA QUERY DE BUSQUEDA QUE APUNTA A LA TABLA PUBLICACIONES
val publicacionRef = referenciaBD.child("Publicaciones")
//listaPublicaciones?.clear()
publicacionRef.addValueEventListener(object : ValueEventListener {
//CARGAMOS TODOS LOS DATOS DE LA TABLA POSTS EN EL RECYCLERVIEW DATACHANGE SE ENCARGA DE CARGAR LOS DATOS
override fun onDataChange(snapshot: DataSnapshot) {
//LIMPIAMOS LA LISTA DE PUBLICACIONES
listaPublicaciones?.clear()
for (snapshot in snapshot.children) {
// SE VA RECUPÈRANDO CADA PUBLICACION
val publicacion = snapshot.getValue(Publicacion::class.java)
// Y SE RECORRE EL ARRAY CON LOS UID DE LOS USUARIOS SEGUIDOS PARA VER SI SE DEBE MOSTRAR
for (usuarioUid in usuariosSeguidosUid as ArrayList<String>)
// EN CASO DE QUE SE ENCUENTRE CONICIDENCIA SE AÑADE LA PUBLICACION A SU LISTA
if (usuarioUid == publicacion!!.nombreUsuario ){
listaPublicaciones?.add(publicacion!!)
}
}
//invertimos la lista para que los ultimos posts esten os primero
listaPublicaciones!!.reverse()
//SE ACTUALIZA EL LISTADO DE PUBLICACIONES MOSTRADOR EN EL ADAPTADOR
publicacionAdaptador?.notifyDataSetChanged()
}
override fun onCancelled(error: DatabaseError) {
//VER QUE HACE
}
})
}
And this is the function to log out.
private fun desconectarUsuario(){
FirebaseAuth.getInstance().signOut()
val intent = Intent(context,IdentificarseActivity::class.java)
startActivity(intent)
}
Thank

Related

I don't understand what is wrong

I am new in kotlin and i don't understand what is wrong, when i do a "CLICK" in the imageView (val_jugadorX.alpha = 0.5f) i can't unselect it again (val_jugadorX.alpha = 1f)
fun comprobarSeleccion(val_jugadorX: ImageView): Boolean{
// si el boleano es false, no está seleccionado
// si el boleano es verdadero, está seleccionado
var devolver: Boolean = false
if (val_jugadorX.alpha == 1f){
val_jugadorX.setOnClickListener {
val_jugadorX.alpha = 0.5f
println("Ahora está seleccionado")
devolver = true
}
}else if(val_jugadorX.alpha== 0.5f) {
val_jugadorX.setOnClickListener{
val_jugadorX.alpha = 1f
println("Ahora no está selccio")
devolver = false
}
}
return devolver
}
You're setting a click listener based on the ImageView's current alpha, and that click listener changes the alpha. But it doesn't call comprobarSeleccion again to set a new click listener based on its new alpha. You probably want to do something like this:
fun comprobarSeleccion(val_jugadorX: ImageView): Boolean{
// si el boleano es false, no está seleccionado
// si el boleano es verdadero, está seleccionado
var devolver: Boolean = false
if (val_jugadorX.alpha == 1f){
val_jugadorX.setOnClickListener {
val_jugadorX.alpha = 0.5f
println("Ahora está seleccionado")
devolver = true
// now the alpha has changed, we need a new click listener
comprobarSeleccion(val_jugadorX)
}
} ...
}
Although something like this might be better (maybe set up during initialisation):
someImageView.setOnClickListener {
with(someImageView) {
val selected = alpha == 1f
// update display
alpha = if (selected) 0.5f else 1f
// handle the selection change event
setSelected(selected)
}
}
That way you just set a click listener once, and it handles things depending on its current state. By having a separate setSelected function, you can tell another part of the app that this selection state has changed:
var devolver = false
fun setSelected(selected: Boolean) {
println(if (selected) "Ahora está seleccionado" else "Ahora no está selccio")
devolver = selected
// any other stuff that needs to happen because of the selection change
}
That fixes your other problem - in your example, devolver is a local variable inside comprobarSeleccion. You set it to false, then return it (i.e. return false). If a click listener changes it later, nothing can access it. Next time you call comprobarSeleccion you get a new devolver set to false again.
By making it a top-level variable, your setSelected function can change it (or your click listener can if you want) and everything else can see its current value.

How to get the current UserID

I try to get the current User Id in my action method of my Home Controller.
public async Task<IActionResult> Index(int? evenementId, string? accepteRV)
{
string userID = User.FindFirstValue(ClaimTypes.NameIdentifier);
if (evenementId != null)
{
StatutEvenement statutEvenement = accepteRV == "true" ? StatutEvenement.Accepté : StatutEvenement.Refusé;
// Mise à jour de l'évènement destiné au vendeur
_traitement.MAJEvenement(evenementId , statutEvenement);
// Récupération de l'évènement en cours
Evenement evenement = await _context.Evenements.AsNoTracking().FirstAsync(e => e.ID == evenementId);
// Enregistrement de l'évènement destiné à l'acheteur
_traitement.InsereEvenement(TypeEvenement.Info, userID, statutEvenement, DateTime.Now, null, evenement.CreePar, evenement.AgendaID, evenement.AnnonceID);
await _context.SaveChangesAsync();
// Envoyer un mail pour acceptation/refus du rendez-vous
The User.FindFirstValue works in other controllers but in this one, I have the compilation error message :
Compiler Error CS0841 : Cannot use local variable 'User' before it is declared
I think it's because it's the start controller but I don't know how to solve the problem.
Please help me,
Thanks.

Processing nullPointerException

I have an error nullPointerException at the line if (stringReceive.contains(tabStock[j])){ but I don't understand why there is an error with the index j of the array tabstock.
import oscP5.*;
import netP5.*;
OscP5 oscP5;
NetAddress myRemoteLocation;
String[] tabStock = new String[6];
String[] tabReceive = new String[6];
String stringReceive;
String oldStringReceive;
int cptEssai = 0;
int cpt = 0;
void setup() {
size(400,400);
frameRate(25);
/* start oscP5, listening for incoming messages at port 12000 */
oscP5 = new OscP5(this,12000);
myRemoteLocation = new NetAddress("127.0.0.1",12001);
tabStock[0] = "1";
tabStock[1] = "2";
tabStock[2] = "3";
tabStock[3] = "4";
tabStock[4] = "5";
tabStock[5] = "6";
//stringReceive = "A3E8F6";
tabReceive[0] = "1";
tabReceive[1] = "2";
tabReceive[2] = "3";
tabReceive[3] = "4";
tabReceive[4] = "3";
tabReceive[5] = "6";
}
void draw() {
background(0);
compare();
}
void mousePressed() {
OscMessage myMessage = new OscMessage("/test");
myMessage.add(123); /* add an int to the osc message */
/* send the message */
oscP5.send(myMessage, myRemoteLocation);
}
/* incoming osc message are forwarded to the oscEvent method. */
void oscEvent(OscMessage theOscMessage) {
/* print the address pattern and the typetag of the received OscMessage */
print("### received an osc message.");
print(" addrpattern: "+theOscMessage.addrPattern());
println(" typetag: "+theOscMessage.typetag());
stringReceive = theOscMessage.addrPattern();
println(stringReceive);
if (oldStringReceive != stringReceive){
oldStringReceive = stringReceive;
}
}
void compare() {
println(stringReceive);
println("compare()");
boolean failed=false;
int j = 0;
while (cptEssai < 3 && !failed){
for (int i= 0; i < tabStock.length; i++){
while(j<6){
if (stringReceive.contains(tabStock[j])){
print("tag existe et bien placé // ");
print("allumage de la lampe qui correspond à ce tag // ");
j++;
}
else {
print("tag n'existe pas ou mal placé // ");
print("extinction de toutes les lampes // ");
oldStringReceive = stringReceive;
if (oldStringReceive != stringReceive){
cptEssai++;
failed = true;
break;
}
else
{
if(j>0){
j--;
}
}
}
}
}
}
print("GAME OVER ! Désactivation de l'épreuve // ");
}
Either tabStock, tabStock[j] or stringReceive is null.
Print out their values before that line to check which one it is.
Once you know which one it is, trace back through your code to figure out why it's null.

Lucene Query (with shingles ? )

I have a Lucene Index containing documents like these :
_id | Name | Alternate Names | Population
123 Bosc de Planavilla (some names here in 5000
345 Planavilla other languages) 20000
456 Bosc de la Planassa 1000
567 Bosc de Plana en Blanca 100000
What's the best Lucene query type I should use and how should I structure it considering I need the following :
If a user queries for :
"Italian Restaurant near Bosc de Planavilla"
I want document with id 123 to be returned because its contains an exact match with the name of the doc.
If a user queries for :
"Italian Restaurant near Planavilla"
I want document with id 345 because query contains an exact match AND it has the highest population.
If a user queries for "Italian Restaurant near Bosc"
I want 567 because query contains "Bosc" AND of the 3 "Bosc" it has the highest pop.
there are probably many other use cases ... but you get the feeling of what i need ...
What kind of query will do this form me ?
Should I generate word N grams (shingles) and create an ORed boolean query using the shingles then apply custom scoring ? or will a regular phrase query will do ? I also saw DisjunctionMaxQuery but dont know if its what im looking for ...
The idea, as you might have anderstood by now, is to find the exact Location a user implied in his query. From that I can start my Geo search and add some further querying around that.
What's the best approach ?
Thanks in advance .
Here is the code for sorting as well. Although I think it would make more sense to add a custom scoring taking into account the city size rather than bruteforcing the sort on the population. Also please note that this uses the FieldCache, which may not be the best solution regarding memory usage.
public class ShingleFilterTests {
private Analyzer analyzer;
private IndexSearcher searcher;
private IndexReader reader;
private QueryParser qp;
private Sort sort;
public static Analyzer createAnalyzer(final int shingles) {
return new Analyzer() {
#Override
public TokenStream tokenStream(String fieldName, Reader reader) {
TokenStream tokenizer = new WhitespaceTokenizer(reader);
tokenizer = new StopFilter(false, tokenizer, ImmutableSet.of("de", "la", "en"));
if (shingles > 0) {
tokenizer = new ShingleFilter(tokenizer, shingles);
}
return tokenizer;
}
};
}
public class PopulationComparatorSource extends FieldComparatorSource {
#Override
public FieldComparator newComparator(String fieldname, int numHits, int sortPos, boolean reversed) throws IOException {
return new PopulationComparator(fieldname, numHits);
}
private class PopulationComparator extends FieldComparator {
private final String fieldName;
private Integer[] values;
private int[] populations;
private int bottom;
public PopulationComparator(String fieldname, int numHits) {
values = new Integer[numHits];
this.fieldName = fieldname;
}
#Override
public int compare(int slot1, int slot2) {
if (values[slot1] > values[slot2]) return -1;
if (values[slot1] < values[slot2]) return 1;
return 0;
}
#Override
public void setBottom(int slot) {
bottom = values[slot];
}
#Override
public int compareBottom(int doc) throws IOException {
int value = populations[doc];
if (bottom > value) return -1;
if (bottom < value) return 1;
return 0;
}
#Override
public void copy(int slot, int doc) throws IOException {
values[slot] = populations[doc];
}
#Override
public void setNextReader(IndexReader reader, int docBase) throws IOException {
/* XXX uses field cache */
populations = FieldCache.DEFAULT.getInts(reader, "population");
}
#Override
public Comparable value(int slot) {
return values[slot];
}
}
}
#Before
public void setUp() throws Exception {
Directory dir = new RAMDirectory();
analyzer = createAnalyzer(3);
IndexWriter writer = new IndexWriter(dir, analyzer, IndexWriter.MaxFieldLength.UNLIMITED);
ImmutableList<String> cities = ImmutableList.of("Bosc de Planavilla", "Planavilla", "Bosc de la Planassa",
"Bosc de Plana en Blanca");
ImmutableList<Integer> populations = ImmutableList.of(5000, 20000, 1000, 100000);
for (int id = 0; id < cities.size(); id++) {
Document doc = new Document();
doc.add(new Field("id", String.valueOf(id), Field.Store.YES, Field.Index.NOT_ANALYZED));
doc.add(new Field("city", cities.get(id), Field.Store.YES, Field.Index.ANALYZED));
doc.add(new Field("population", String.valueOf(populations.get(id)),
Field.Store.YES, Field.Index.NOT_ANALYZED));
writer.addDocument(doc);
}
writer.close();
qp = new QueryParser(Version.LUCENE_30, "city", createAnalyzer(0));
sort = new Sort(new SortField("population", new PopulationComparatorSource()));
searcher = new IndexSearcher(dir);
searcher.setDefaultFieldSortScoring(true, true);
reader = searcher.getIndexReader();
}
#After
public void tearDown() throws Exception {
searcher.close();
}
#Test
public void testShingleFilter() throws Exception {
System.out.println("shingle filter");
printSearch("city:\"Bosc de Planavilla\"");
printSearch("city:Planavilla");
printSearch("city:Bosc");
}
private void printSearch(String query) throws ParseException, IOException {
Query q = qp.parse(query);
System.out.println("query " + q);
TopDocs hits = searcher.search(q, null, 4, sort);
System.out.println("results " + hits.totalHits);
int i = 1;
for (ScoreDoc dc : hits.scoreDocs) {
Document doc = reader.document(dc.doc);
System.out.println(i++ + ". " + dc + " \"" + doc.get("city") + "\" population: " + doc.get("population"));
}
System.out.println();
}
}
This gives the following results:
query city:"Bosc Planavilla"
results 1
1. doc=0 score=1.143841[5000] "Bosc de Planavilla" population: 5000
query city:Planavilla
results 2
1. doc=1 score=1.287682[20000] "Planavilla" population: 20000
2. doc=0 score=0.643841[5000] "Bosc de Planavilla" population: 5000
query city:Bosc
results 3
1. doc=3 score=0.375[100000] "Bosc de Plana en Blanca" population: 100000
2. doc=0 score=0.5[5000] "Bosc de Planavilla" population: 5000
3. doc=2 score=0.5[1000] "Bosc de la Planassa" population: 1000
How do you tokenize the fields? Do you store them as complete string? Also, how do you parse the query?
Okay, so I am playing around a bit with this. I have been using a StopFilter to remove la, en, de. I then used a shingle filter to get multiple combination in order to do the "exact matches". So for example Bosc de Planavilla gets tokenized as [Bosc] [Bosc Planavilla] and Bosc de Plana en Blanca gets tokenized to [Bosc] [Bosc Plana] [Plana Blanca] [Bosc Plana Blanca]. This is so that you can have "exact matches" on parts of the query.
I then query the exact string the user passed, although there could be some adaptation there as well. I went with the simple case to make the results better match what you were looking for.
Here is code I am using (lucene 3.0.3):
public class ShingleFilterTests {
private Analyzer analyzer;
private IndexSearcher searcher;
private IndexReader reader;
public static Analyzer createAnalyzer(final int shingles) {
return new Analyzer() {
#Override
public TokenStream tokenStream(String fieldName, Reader reader) {
TokenStream tokenizer = new WhitespaceTokenizer(reader);
tokenizer = new StopFilter(false, tokenizer, ImmutableSet.of("de", "la", "en"));
if (shingles > 0) {
tokenizer = new ShingleFilter(tokenizer, shingles);
}
return tokenizer;
}
};
}
#Before
public void setUp() throws Exception {
Directory dir = new RAMDirectory();
analyzer = createAnalyzer(3);
IndexWriter writer = new IndexWriter(dir, analyzer, IndexWriter.MaxFieldLength.UNLIMITED);
ImmutableList<String> cities = ImmutableList.of("Bosc de Planavilla", "Planavilla", "Bosc de la Planassa",
"Bosc de Plana en Blanca");
ImmutableList<Integer> populations = ImmutableList.of(5000, 20000, 1000, 100000);
for (int id = 0; id < cities.size(); id++) {
Document doc = new Document();
doc.add(new Field("id", String.valueOf(id), Field.Store.YES, Field.Index.NOT_ANALYZED));
doc.add(new Field("city", cities.get(id), Field.Store.YES, Field.Index.ANALYZED));
doc.add(new Field("population", String.valueOf(populations.get(id)),
Field.Store.YES, Field.Index.NOT_ANALYZED));
writer.addDocument(doc);
}
writer.close();
searcher = new IndexSearcher(dir);
reader = searcher.getIndexReader();
}
#After
public void tearDown() throws Exception {
searcher.close();
}
#Test
public void testShingleFilter() throws Exception {
System.out.println("shingle filter");
QueryParser qp = new QueryParser(Version.LUCENE_30, "city", createAnalyzer(0));
printSearch(qp, "city:\"Bosc de Planavilla\"");
printSearch(qp, "city:Planavilla");
printSearch(qp, "city:Bosc");
}
private void printSearch(QueryParser qp, String query) throws ParseException, IOException {
Query q = qp.parse(query);
System.out.println("query " + q);
TopDocs hits = searcher.search(q, 4);
System.out.println("results " + hits.totalHits);
int i = 1;
for (ScoreDoc dc : hits.scoreDocs) {
Document doc = reader.document(dc.doc);
System.out.println(i++ + ". " + dc + " \"" + doc.get("city") + "\" population: " + doc.get("population"));
}
System.out.println();
}
}
I am now looking into sorting per population.
This prints out:
query city:"Bosc Planavilla"
results 1
1. doc=0 score=1.143841 "Bosc de Planavilla" population: 5000
query city:Planavilla
results 2
1. doc=1 score=1.287682 "Planavilla" population: 20000
2. doc=0 score=0.643841 "Bosc de Planavilla" population: 5000
query city:Bosc
results 3
1. doc=0 score=0.5 "Bosc de Planavilla" population: 5000
2. doc=2 score=0.5 "Bosc de la Planassa" population: 1000
3. doc=3 score=0.375 "Bosc de Plana en Blanca" population: 100000

Improving box of controls speed (C++-CLI WinForms)

I've made a control in c++ to allow the user to select a position in an irregular box with different positions, some of them blocked, some of them filled, with this characteristics:
Can create multiple columns in multiple rows (not the same, so it can have the first row with 3 columns, the second with 1 and the third with 5)
Different states each position: blocked, filled, empty, selected (so it can have some of them at the same time: blocked & empty, blocked & filled, empty & selected)
The user can select one or multiple position
It has tooltip texts and different background images for each filled or empty position.
Contextual menu to add/delete rows/columns.
Row and Column headers, numeric/alphanumeric, ascending/descending.
The structure of the control is Container->(nRows x RowClass) and RowClass->(nCols x ColumnClass). Each column contains a TableLayoutPanel so I can simulate the stuck and raised effects.
Loocking different topics on the web, I found some ideas and the improvements I've done for the speed are basically SuspendDrawing with SendMessage (to create and resize all the rows and columns before painting them), double buffering each control and adding a BeginEdit/EndEdit method to the Container that SuspendDrawing and blocks row and column resizing while creating rows and columns (and the same for each RowClass).
It works not so bad with relative small boxes, lets say 20 rows x 20 columns, but when it has 40 rows x 30 columns (1.200 TableLayoutPanel's) it is going really slow even in a fast computer.
I also tried with one TableLayoutPanel (with the required number of columns) per RowClass, but the problem is how to draw borders in order to select each column separatedly, tooltips and background image for each cell.
So, the question: is there any improvements I can try?
I've been thinking that strategy games like warcraft or similar they have good speed on screen with thousands of graphics and calculations, but I'm not a professional programmer so I don't know wich could be the technology and if it is the right way..
Maybe it could be something like an image with a row of images, or something different than a class of classes of classes, but I have no idea...
Probably it is a design problem so, any idea on how would you create a fast control with those specifications will be great.
Thanks anyway for reading my question, any comments will be wellcome!
Miguel
There is no clean fix for this, you will have to give up on TLP. A control is a much too expensive object, any form that has more than a hundred of them is going to suck mud.
First look at the built-in control types that support a grid-like display. ListView with View = Details for non-editable grids, DataGridView for editable ones. If that doesn't fit your bill then you'll have to make your own. Key things you'll need to implement is the OnPaint() method to draw the visual appearance of the control and OnMouseDown or OnClick to do hit-testing and implement behavior.
Videogames offload the graphical representations atleast on the GPU (which are built for just that purpose). I do not think the representation of the boxes is the bottleneck, rather the calculations/steps to get there.
Either post your code / a snippet of your code or explain your use of algorithms, because a bad algorithm will make your program very slow when input increases -- have a look on Computational Complexity, something brought up by algorithm courses.
http://en.wikipedia.org/wiki/Computational_complexity_theory
Here is a part of the code (the whole code is really huge) where some calculations are done..
In ControlClass::
void BeginEdit(int nFilas) {
if (!_editando) {
if (_muestraBarraProgreso) {
this->BarraProgreso->Value = 0;
this->BarraProgreso->Visible = true;
}
_editando = true;
_lastEdit = nFilas;
this->Cursor = Cursors::WaitCursor;
this->Refresh();
SendMessageClass::SuspendDrawing();
}
};
/// <summary>Provoca se realicen los cambios visuales no realizados desde que se
/// llamó a BeginEdit, como this->Controls.Add(...), Ajustar los tamaños de los controles, ...</summary>
void EndEdit(void) {
if (_editando) {
int f;
_editando = false;
AjustaHeaders();
AjustaTamaños(); //Provoca el ajuste de las filas sin ajustar las columnas
//Esto lo hago para minimizar el número de AjustaTamaños()
for (f=_lastEdit; f<_nFilas; f++)
_fila[f]->EndEdit(false); //Provoca el ajuste de las columnas
this->MuestraBarraProgreso = false;
this->Cursor = Cursors::Default;
SendMessageClass::ResumeDrawing();
}
};
void AjustaTamaños(void) {
if (!_editando && _nFilas>0) {
int topOffset = 0, leftOffset = 0, bottomOffset = 0;
if (_showColumnHeaders) topOffset = this->tlpColumnHeaders->Height;
if (_showRowHeaders) leftOffset = this->tlpRowHeaders->Width;
if (_muestraEstado) bottomOffset = this->EstadoRecipiente->Height;
int hOffset = leftOffset + 2;
int vOffset = topOffset + bottomOffset + 2;
int i, j = 0, stp;
if (_varsCom->ordenFilas == TipoOrdenFilas::Descendiente) {
j = 0; stp = 1;
}
else {
j = _nFilas-1; stp = -1;
}
for (i=0; i<_nFilas; i++) {
_fila[j]->Left = hOffset;
_fila[j]->Width = this->Width - hOffset;
_fila[j]->Top = (_nFilas - 1 - i) * (this->Height - vOffset) / _nFilas + topOffset;
_fila[j]->Height = (this->Height - vOffset) / _nFilas;
j+=stp;
}
}
};
in FilaClass:
/// <summary>Provoca que no se realicen los cambios visuales hasta que se llame EndEdit</summary>
void BeginEdit(void) {
if (!_editando) {
_editando = true;
_lastEdit = _nColumnas;
SendMessageClass::SuspendDrawing();
}
};
/// <summary>Provoca se realicen los cambios visuales no realizados desde que se
/// llamó a BeginEdit, como this->Controls.Add(...), Ajustar los tamaños de los controles, ...</summary>
void EndEdit(bool resumirDrawing) {
if (_editando) {
_editando = false;
AjustaTamaños();
for (int c=_lastEdit; c<_nColumnas;c++)
_columna[c]->EndEdit(resumirDrawing);
if (resumirDrawing) SendMessageClass::ResumeDrawing();
}
};
void AjustaTamaños(void) {
if (!_editando) {
int j, stp;
if (_varsCom->ordenColumnas == TipoOrdenColumnas::IzquierdaDerecha) {
j = 0; stp = 1;
}
else {
j = _nColumnas-1; stp = -1;
}
for (int i=0; i<_nColumnas; i++) {
_columna[j]->Width = this->Width / _nColumnas;
_columna[j]->Left = i * this->Width / _nColumnas;
_columna[j]->Top = 0;
_columna[j]->Height = this->Height;
j+=stp;
}
}
};
in ColumnaClass:
void BeginEdit(void) {
_editando = true;
SendMessageClass::SuspendDrawing();
};
void EndEdit(bool resumirDrawing) {
if (_editando) {
_editando = false;
_CambiaEstado = _estado;
if (resumirDrawing) SendMessageClass::ResumeDrawing();
}
};
internal:
/// <summary>Obtiene o establece el estado de la posición sin activar el evento ColumnaChange</summary>
property EstadoColumna _CambiaEstado {
EstadoColumna get() { return _estado; }
void set(EstadoColumna value) {
_estado = value;
if ( (_varsCom->bloquearLlenas && ((value & EstadoColumna::llena) == EstadoColumna::llena)) ||
(_varsCom->bloquearVacias && ((value & EstadoColumna::llena) != EstadoColumna::llena)) ) {
if ((_estado & EstadoColumna::seleccionada) == EstadoColumna::seleccionada)
_estado = (EstadoColumna) (_estado ^ EstadoColumna::seleccionada);
_estado = (EstadoColumna) (_estado | EstadoColumna::bloqueada);
}
else {
if ((_estado & EstadoColumna::bloqueada) == EstadoColumna::bloqueada)
_estado = (EstadoColumna) (_estado | EstadoColumna::bloqueada);
}
if (!_editando) {
ActualizaEstado();
ActualizaToolTip();
}
}
};
/// <summary>Actualiza la visualización de la posición leyendo de nuevo los parámetros</summary>
void ActualizaEstado(void) {
if (!_editando && _varsCom->estilo) {
this->ColumnaCtl->BackColor = _varsCom->estilo->GetEstilo[(int) _estado]->colorFondo;
this->ColumnaCtl->CellBorderStyle = _varsCom->estilo->GetEstilo[(int) _estado]->bordeCelda;
this->ColumnaCtl->BackgroundImageLayout = _varsCom->imageLayout;
if (this->Estado[EstadoColumna::llena])
this->ColumnaCtl->BackgroundImage = _varsCom->imagenLlena;
else if (this->Estado[EstadoColumna::vacia])
this->ColumnaCtl->BackgroundImage = _varsCom->imagenVacia;
}
};
/// <summary>Actualiza los textos del ToolTipText de la posición</summary>
void ActualizaToolTip(void) {
if (!_editando && _varsCom->etiquetas) {
String^ sTmp = "";
int __tmp__ = 0;
if (_varsCom->tipoHeaderFilas == TipoHeader::Numerica)
sTmp = _varsCom->etiquetas->Fila + ": " + _nombreFila;
else if (System::Int32::TryParse(_nombreFila, __tmp__))
sTmp = _varsCom->etiquetas->Fila + ": " + gcnew String((wchar_t) ('A' + __tmp__ - 1), 1);
else
sTmp = _varsCom->etiquetas->Fila + ": " + _nombreFila;
if (_varsCom->tipoHeaderColumnas == TipoHeader::Numerica)
sTmp += ", " + _varsCom->etiquetas->Columna + ": " + _posicion + "\n";
else
sTmp += ", " + _varsCom->etiquetas->Columna + ": " + gcnew String((wchar_t) ('A' + _posicion - 1), 1) + "\n";
if (_nombre != "") {
if ((_estado & EstadoColumna::llena) == EstadoColumna::llena)
sTmp += _varsCom->etiquetas->Posicion + ": " + _nombre + "\n";
else
sTmp += _varsCom->etiquetas->PosicionVacia + ": " + _nombre + "\n";
}
if ((_estado & EstadoColumna::llena) == EstadoColumna::llena) sTmp += "Ocupada, ";
else sTmp += "Vacía, ";
if ((_estado & EstadoColumna::seleccionada) != EstadoColumna::vacia) sTmp += "Seleccionada";
else sTmp += "Sin seleccionar";
this->ToolTip1->ShowAlways = false;
this->ToolTip1->SetToolTip(this->ColumnaCtl, sTmp);
this->ToolTip1->ShowAlways = true;
}
};
I think those are most part of the calculations..
A little bit uncommented, and in spanish, but any help will be really useful!
Thanks anyway!