IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Composants graphiques Android Discussion :

Impossible d'afficher une Alert Dialog dans un thread!


Sujet :

Composants graphiques Android

  1. #1
    Membre régulier
    Profil pro
    Inscrit en
    Décembre 2007
    Messages
    348
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2007
    Messages : 348
    Points : 103
    Points
    103
    Par défaut Impossible d'afficher une Alert Dialog dans un thread!
    Bonjour à tous,

    Je suis en train de développer une application dans laquelle j'ai une activité qui exécute un thread. Dans ce thread, j'appelle une fonction qui envoie une requête HTTP à un serveur, puis qui parse avec le Framework jSoup la page renvoyée.

    Cette fonction (appelée requestAndMakeSheet() dans mon code ci-dessous) renvoie une exception si le parsing jSoup ne trouve pas certains éléments dans la page (ce qui veut dire que le login/mot de passe n'est pas bon, en fait).

    Je voudrais que ce thread affiche un message d'erreur à l'utilisateur quand cette exception est levée, puis qu'il le renvoie vers l'activité principale, où il doit saisir ses identifiants.

    Si par contre l'exception n'est pas levée, l'activité affiche la page HTML dans une webView.

    Voici le code de mon thread:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
            thereIsSomethingToShow=true;
     
    		//Envoyer la requête au site
            t1 = new Thread(new Runnable() //ReservationInfo.this.runOnUiThread(new Runnable() -> si je mets ça ici, l'app plante!
    		{	
    			public void run() 
    			{
    				Looper.prepare();
    				StringBuffer stringBuffer = new StringBuffer("");
    				BufferedReader bufferedReader = null;
    				URI uri = null;
     
    				try
    				{
    					requestAndMakeSheet(stringBuffer, bufferedReader, uri);
    				}
    				catch (Exception e)
    				{
    					thereIsSomethingToShow=false;
    					ReservationInfo.this.runOnUiThread(new Runnable()
    					{
    						public void run() 
    						{
    							AlertDialog.Builder parsingErrorBox = new AlertDialog.Builder(ReservationInfo.this);
    							parsingErrorBox.setTitle("Login error");
    							parsingErrorBox.setMessage("You may have to check your credentials and then try again.");
    							parsingErrorBox.setNeutralButton("OK",
    							new DialogInterface.OnClickListener() 
    							{
    								public void onClick(DialogInterface dialog, int which) 
    								{
    									dialog.dismiss();
    								}
    						    });
    							parsingErrorBox.show();
    						}
    					});
    				}
    				finally
    				{
    					if (bufferedReader!=null)
    					{
    						try
    						{
    							bufferedReader.close();
    						}
    						catch (IOException ioe)
    						{
    							Log.e("Web Request Error", ioe.getMessage());
    						}
    					}
    				}
    			}			
    		});
    		t1.start();
    		try 
    		{
    			t1.join();
    			if (thereIsSomethingToShow)
    			{
    				mWebview.loadUrl("file:///"+Environment.getExternalStorageDirectory()+"/ReservationSheet3.html");
    				setContentView(mWebview);
    			}
    			else
    			{
    				//Return to main activity
    				Intent myIntent = new Intent(this.getBaseContext(), MainActivity.class);
                    startActivityForResult(myIntent, 0);
    			}
    		} 
    		catch (InterruptedException e) 
    		{
    			e.printStackTrace();
    		}
    	}
    Le seul problème que je rencontre avec ce code par rapport à ce que je veux, c'est que le message d'erreur ne s'affiche pas avant le retour à l'activité principale.

    J'ai essayé plein de trucs vus sur StackOverflow, mais y a rien à faire! Ce sont eux entre autres qui m'ont dit d'entourer mon message d'un thread runOnUiThread, mais le mettre ou pas n'a donné aucune différence. Et si je le mets en lieu et place du thread t1 tout en haut, l'app plante dès qu'elle arrive sur l'activité qui contient ce thread!?

    Pourriez-vous m'aider à comprendre ce qui se passe?
    Merci d'avance.

  2. #2
    Expert éminent

    Homme Profil pro
    Ingénieur systèmes et réseaux
    Inscrit en
    Février 2007
    Messages
    4 253
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Ingénieur systèmes et réseaux
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Février 2007
    Messages : 4 253
    Points : 7 618
    Points
    7 618
    Billets dans le blog
    3
    Par défaut
    Y a des trucs bizarres quand même:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    t1.start();
    		try 
    		{
    			t1.join();
    ?????? quel intérêt de faire un thread alors si c'est pour attendre qu'il se termine aussitôt ? D'autant que si t1 rame un peu... l'application sera interrompue avec un ANR. Et puis de toute manière c'est interdit ^^

    Sinon, dans le code, tout est mélangé... le thread, le dialog d'erreur ... c'est à se perdre à mon avis:

    Le thread fait une action (pourquoi passer un new Runnable() ? autant surcharger run() du thread non ?)
    Éventuellement on code comme cela:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    Thread t1 = new Thread() {
       public void run() {
          try {
              .... tout le truc en background...
              final result = xxxxx;
              runOnUIThread(new Runnable() { public void run() { NomDeLACtivite.this.onThreadResult(result); } });
          } catch (Exception ex) {
              runOnUIThread(new Runnable() { public void run() { NomDeLActivite.this.onThreadError(ex); } });
          }
    }
    Voila, ensuite, deux fonction à implémenter coté activité (UI thread) onThreadResult() et onThreadError().
    A noter que l'utilisation d'AsyncTask aurait grandement simplifier les choses (voire d'un 'loader' si le but était de charger quelque chose):
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
     
    AsyncTask<?,?,?> t = new ASyncTask<Void,RESULT,Void>()
    {
        Exception error;
        public RESULT doInBackground(Void ... params) {
            try {
              .... tout le truc en background...
              return result;
            } catch (Exception ex) {
              this.error = ex;
              return null;
            }
        }
        public void onPostExecute(RESULT res) {
           if (res != null)
               NomDeLActivite.this.onThreadResult(res);
           else
               NomDeLActivite.this.onThreadError(this.error);
        }
    }
    N'oubliez pas de cliquer sur mais aussi sur si un commentaire vous a été utile !
    Et surtout

  3. #3
    Membre régulier
    Profil pro
    Inscrit en
    Décembre 2007
    Messages
    348
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2007
    Messages : 348
    Points : 103
    Points
    103
    Par défaut
    La vache, je ne connaissais rien de tout ce que tu m'as évoqué, je connais à peine l'utilité d'un thread, et je l'utilise parce que je suis obligé pour mes requêtes HTTP dedans.

    En fait ce que fait mon thread, c'est charger une page web et la parser avec jSoup pour l'afficher d'une manière particulière dans une webView. Mais dans le fond, je ne sais pas si c'est la meilleure façon de faire, sauf que c'est la SEULE qui ait marché jusqu'à présent. :/

    Je vais voir ce qu'est un Loader si j'ai encore le temps (projet à finir pour bientôt, donc j'ai peur de faire de grosses modifs qui partent en sucette).

    ASyncTask -> très mal expliqué sur le web. Tu aurais un bon exemple?

    Sinon, j'ai réussi à afficher mon alert dialog comme ça:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
        private void nuclearRequest(final Bundle bundle)
        {
        	//Send request to website
            t1 = new Thread(new Runnable()
    		{	
    			public void run() 
    			{
    				Looper.prepare();
    				
    				StringBuffer stringBuffer = new StringBuffer("");
    				BufferedReader bufferedReader = null;
    				URI uri = null;
    				
    				Log.i("goCheckOnline", bundle.getString("goCheckOnline"));
    				if (bundle.getString("goCheckOnline").equals("Yes"))
    				{
    					//Check Internet connectivity
    					ConnectivityManager cm = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
    					NetworkInfo networkInfo = cm.getActiveNetworkInfo();
    					
    					try
    					{
    						int networkType = networkInfo.getType();
    						android.net.NetworkInfo.State networkState = networkInfo.getState();
    						if (networkState.compareTo(android.net.NetworkInfo.State.CONNECTED)==0)
    						{	
    							requestAndMakeSheet(stringBuffer, bufferedReader, uri);
    							
    							if (!rdb.containsReference(Ref))
    							{
    								rdb.insertReservation(new Reservation(Ref, Name, 1));	
    							}
    						}
    						else
    						{
    							Toast.makeText(getBaseContext(), "Please check your Internet connection and then try again.", Toast.LENGTH_LONG).show();
    							
    							//Return to main activity
    							Intent myIntent = new Intent(getBaseContext(), MainActivity.class);
    			    			myIntent.putExtra("Ref", Ref);
    			    			myIntent.putExtra("Name", Name);
    			                startActivityForResult(myIntent, 0);
    						}
    					}
    					catch (NullPointerException n)
    					{
    						//n.printStackTrace();
    						//Toast.makeText(getBaseContext(), "A NullPointerException has occured.", Toast.LENGTH_LONG).show();
    						displayBox("No connnection", "You may be using Airplane mode. It must be switched off in order to check your trip.");
    						thereIsSomethingToShow = false;
    					}
    					catch (URISyntaxException urle)
    					{
    						uponExceptionCatch();
    					}
    					catch (IOException ioe)
    					{
    						uponExceptionCatch();
    					}
    					catch (ParsingException pe)
    					{
    						uponExceptionCatch();
    					}
    					finally
    					{
    						//Hide progress dialog
    						//progressDialog.dismiss();
    						
    						if (bufferedReader!=null)
    						{
    							try
    							{
    								bufferedReader.close();
    							}
    							catch (IOException ioe)
    							{
    								Log.e("Error closing buffer", ioe.getMessage());
    							}
    						}
    					}
    				} //End of "if goCheckOnline"
    				progressDialog.dismiss();
    			} //End of "run" method
    		});
    		t1.start();
    		try 
    		{
    			t1.join();
    			if (thereIsSomethingToShow)
    			{
    				File dir = getDir("MyApp", MODE_WORLD_READABLE);
    	            File newfile = new File(dir.getAbsolutePath() + File.separator + Ref+"-"+Name+".html");
    				mWebview.loadUrl("file://"+dir.getAbsolutePath()+File.separator+Ref+"-"+Name+".html"); //("file:///"+Environment.getExternalStorageDirectory()/*+"//MyApp//ReservationSheets//"*/+"//"+Ref+"-"+Name+".html");
    				setContentView(mWebview);
    				if (bundle.getString("goCheckOnline").equals("No"))
    				{
    					Toast.makeText(getBaseContext(), "This is an offline file. To refresh trip data, use \"Refresh\" in the menu.", Toast.LENGTH_LONG).show();
    				}
    			}
    Avec displayBox contenant:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
     
        private void displayBox(final String title, final String message)
        {
    		ReservationInfo.this.runOnUiThread(new Runnable()
    		{
    			public void run() 
    			{
    				//Display error alert dialog
    				AlertDialog.Builder parsingErrorBox = new AlertDialog.Builder(ReservationInfo.this);
    				parsingErrorBox.setTitle(title);
    				parsingErrorBox.setMessage(message);
    				parsingErrorBox.setNeutralButton("OK",
    				new DialogInterface.OnClickListener() 
    				{
    					public void onClick(DialogInterface dialog, int which) 
    					{
    						dialog.dismiss();
     
    						//Return to main activity
    						Intent myIntent = new Intent(getBaseContext(), MainActivity.class);
    		    			myIntent.putExtra("ReservationReference", ReservationReference);
    		    			myIntent.putExtra("LastName", LastName);
    		                startActivityForResult(myIntent, 0);
    					}
    			    });
    				parsingErrorBox.show();
    			}
    		});
        }
    Maintenant ce que j'aimerais faire, c'est afficher une fenêtre de chargement pour toute la durée de mon thread.

    J'ai déclaré ceci avant la grosse méthode nuclearRequest (elle s'appelle comme ça du fait de son énorme taille, justement ):

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    private ProgressDialog progressDialog;
     
    progressDialog = new ProgressDialog(this);
            progressDialog.setMessage("Checking trip information...");
            progressDialog.show();
    Et à la fin de mon thread, j'ai mis progressDialog.dismiss();

    En fait, mon application est prévue pour, lorsque je suis dans l'activité principale, j'ai un bouton qui envoie en extras le login et le mot de passe que l'user a saisis dedans, à l'activité qui contient le code ci-dessus et le thread.

    Cette seconde activité va requêter un serveur en HTML, parser la page avec jSoup, afficher un message d'erreur si le parsage a des problèmes (ce qui signifie forcément que le login/mot de passe est mauvais), et afficher la page si tout se passe bien.

    Le problème, c'est que entre le moment où je clique sur le bouton qui m'emmène de la première activité à la seconde, j'ai pendant un long moment une page noire, puis pendant un très bref instant la fenêtre de chargement, et enfin le chargement de la page ou le message d'erreur.

    Pourquoi?

  4. #4
    Membre à l'essai

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juillet 2009
    Messages
    16
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juillet 2009
    Messages : 16
    Points : 18
    Points
    18
    Par défaut Il faut utiliser un AsyncTask
    La classe AsyncTask est typiquement faite pour cela, et très simple à utiliser.

    Explication dans les cours et tutoriels pour apprendre Android : http://android.developpez.com/cours/

  5. #5
    Membre régulier
    Profil pro
    Inscrit en
    Décembre 2007
    Messages
    348
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2007
    Messages : 348
    Points : 103
    Points
    103
    Par défaut
    Ah d'accoooooooord!
    ASyncTask c'est un objet, qu'on instancie dans notre activité principale!?
    Je croyais que c'était un type d'activité en soi.

    Edit: Merci infiniment pour ce magnifique tuto qui démystifie bien cette notion tarabiscottée.

  6. #6
    Expert éminent sénior
    Avatar de sinok
    Profil pro
    Inscrit en
    Août 2004
    Messages
    8 765
    Détails du profil
    Informations personnelles :
    Âge : 43
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Août 2004
    Messages : 8 765
    Points : 12 977
    Points
    12 977
    Par défaut
    Citation Envoyé par nicroman Voir le message
    Le thread fait une action (pourquoi passer un new Runnable() ? autant surcharger run() du thread non ?)
    Pour moi c'est la façon propre de faire.
    Voir les débats sur composition/délégation vs héritage. (http://en.wikipedia.org/wiki/Composi...er_inheritance)

    Dans l'absolue, l'idée, c'est que tu n'obtiendras aucun gain par l'extension de Thread, et tu te trouveras avec un objet possédant tout un paquet de méthodes dont tu n'as absolument pas besoin.
    Il est préférable de limiter l'utilisation de l'héritage à des composants/classes dont on veut redéfinir le mode de fonctionnement.
    Hey, this is mine. That's mine. All this is mine. I'm claiming all this as mine. Except that bit. I don't want that bit. But all the rest of this is mine. Hey, this has been a really good day. I've eaten five times, I've slept six times, and I've made a lot of things mine. Tomorrow, I'm gonna see if I can't have sex with something.

  7. #7
    Expert éminent

    Homme Profil pro
    Ingénieur systèmes et réseaux
    Inscrit en
    Février 2007
    Messages
    4 253
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Ingénieur systèmes et réseaux
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Février 2007
    Messages : 4 253
    Points : 7 618
    Points
    7 618
    Billets dans le blog
    3
    Par défaut
    Citation Envoyé par sinok Voir le message
    Dans l'absolue, l'idée, c'est que tu n'obtiendras aucun gain par l'extension de Thread, et tu te trouveras avec un objet possédant tout un paquet de méthodes dont tu n'as absolument pas besoin.
    Il est préférable de limiter l'utilisation de l'héritage à des composants/classes dont on veut redéfinir le mode de fonctionnement.
    Tout à fait d'accord, mais là on a deux objets anonymes
    Et puis de toute manière faut utiliser AsyncTask
    N'oubliez pas de cliquer sur mais aussi sur si un commentaire vous a été utile !
    Et surtout

  8. #8
    Membre régulier
    Profil pro
    Inscrit en
    Décembre 2007
    Messages
    348
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2007
    Messages : 348
    Points : 103
    Points
    103
    Par défaut
    Bon, j'ai fini par y arriver, youpiiiiiiii!

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
     
    private requestAndMakeSheetTask ramst;
    ...
     
    //Dans onCreate()...
     
    ramst = new requestAndMakeSheetTask();
    ramst.execute();
     
    //La Task
     
        private class requestAndMakeSheetTask extends AsyncTask<Void, Integer, Void>
        {
     
        	@Override
        	protected void onPreExecute() 
        	{
        		super.onPreExecute();
        		//Toast.makeText(getApplicationContext(), "Début du traitement asynchrone", Toast.LENGTH_LONG).show();
        	}
     
        	@Override
        	protected void onProgressUpdate(Integer... values)
        	{
        		super.onProgressUpdate(values);
        		// Mise à jour de la ProgressBar
        		//progressBar.setProgress(values[0]);
        	}
     
        	@Override
        	protected Void doInBackground(Void... arg0) 
        	{
        		int progress;
        		for (progress=0;progress<=100;progress++)
        		{
        			for (int i=0; i<1000000; i++){}
        			//la méthode publishProgress met à jour l'interface en invoquant la méthode onProgressUpdate
        			publishProgress(progress);
        			progress++;				
        		}
        		nuclearRequest(bundle);
        		return null;
        	}
     
        	@Override
        	protected void onPostExecute(Void result) 
        	{
        		progressDialog.dismiss();
        		if (bundle.getString("goCheckOnline").equals("No") && Refresh.equals("No"))
    			{
        			Log.i("bundle.getString(\"goCheckOnline\")", bundle.getString("goCheckOnline"));
        			Log.i("Refresh", Refresh);
    				Toast.makeText(getBaseContext(), "This is an offline file. To refresh trip data, use \"Refresh\" in the menu.", Toast.LENGTH_LONG).show();
    			}
        		//Toast.makeText(getApplicationContext(), "Le traitement asynchrone est terminé", Toast.LENGTH_LONG).show();
        	}
        }
    Me reste un petit problème: j'ai un menu où j'ai ça:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
        public boolean onOptionsItemSelected(MenuItem item) 
        {
            // Handle item selection
            switch (item.getItemId()) 
            {
                case R.id.refresh:
                	Refresh="Yes";
                	ramst = new requestAndMakeSheetTask();
                	ramst.execute();
                    //nuclearRequest(bundle);
                    Refresh="No";
                    return true;
                case R.id.send:
                	sendByMail();
                    return true;
                default:
                    return super.onOptionsItemSelected(item);
            }
        }
    Normalement, quand j'appuie sur Refresh dans le menu, la variable Refresh se met à Yes et la task ne doit pas afficher le toast final. Mais là j'ai Refresh qui reste interprété à "No" par la task!

    Pour être honnête, je m'attendais à ce genre de problème, mais je ne me rappelle plus du pourquoi. Comment y remédier?

    Merci d'avance.

  9. #9
    Expert éminent

    Avatar de Feanorin
    Profil pro
    Inscrit en
    Avril 2004
    Messages
    4 589
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2004
    Messages : 4 589
    Points : 9 149
    Points
    9 149
    Par défaut
    Bonjour,

    Cela n'est pas normal

    Tu remet ta variable à No après avoir exécuté ton AsyncTask, là ou il me semble ou tu interprètes ta variable pour savoir si tu dois afficher ton dialogue se fait dans le onPostExecute ce qui veux dire que tu auras forcément ta variable à No.

    Tu met à jour trop vite ta variable, ou tu ne met pas à jour ta variable au bon endroit .
    Responsable Android de Developpez.com (Twitter et Facebook)
    Besoin d"un article/tutoriel/cours sur Android, consulter la page cours
    N'hésitez pas à consulter la FAQ Android et à poser vos questions sur les forums d'entraide mobile d'Android.

Discussions similaires

  1. Réponses: 2
    Dernier message: 28/02/2015, 22h46
  2. Impossible d'afficher une valeur existe dans une autre table
    Par mcharmat dans le forum Requêtes et SQL.
    Réponses: 2
    Dernier message: 06/02/2013, 06h38
  3. Réponses: 14
    Dernier message: 18/07/2011, 23h54
  4. [C#] Impossible d'afficher une form dans la barre des taches
    Par padodanle51 dans le forum Windows Forms
    Réponses: 2
    Dernier message: 03/05/2006, 09h30
  5. [VB.NET] afficher une image stockée dans une table
    Par matonfire dans le forum ASP.NET
    Réponses: 4
    Dernier message: 19/03/2004, 11h21

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo