I've heard that many people had problems using the language localization features, plus you can only translate the interface (I can't translate messages from a messagebox as far as I know).
So I just wanted to know what do you think about my approach to translate my winform app.
namespace Tris
{
class Translations
{
public string draws;
public string language;
public string help;
public string newgame;
public bool lang_en;
public bool lang_it;
public bool lang_ru;
namespace Tris
{
class TranslationStrings
{
public void engTranslation(Translations TS)
{
TS.draws = "Draws";
TS.language = "Language";
TS.help = "Help";
TS.newgame = "New Game";
TS.lang_en = true;
TS.lang_it = false;
TS.lang_ru = false;
public void rusTranslation(Translations TS)
{
TS.draws = "Ничья";
TS.language = "Язык";
TS.help = "Помощь";
TS.newgame = "Новая Игра";
TS.lang_en = false;
TS.lang_it = false;
TS.lang_ru = true;
When I need something I just call the right method:
case 2:
pic = Properties.Resources.end3;
if (lang_en)
{
strings.winTranslationsEng(TS);
phrase = loser + TS.win3;
End1 end3 = new End1(lang_en, lang_it, lang_ru, winner, loser, phrase, pic);
end3.ShowDialog();
}
if (lang_it)
{
strings.winTranslationsIta(TS);
phrase = loser + TS.win3;
End1 end3 = new End1(lang_en, lang_it, lang_ru, winner, loser, phrase, pic);
end3.ShowDialog();
}
if (lang_ru)
{
strings.winTranslationsRus(TS);
phrase = winner + TS.win1;
End1 end1 = new End1(lang_en, lang_it, lang_ru, winner, loser, phrase, pic);
end1.ShowDialog();
}
break;
Before all the code was like:
if(lang_en)
{
phrase="hi";
}
if(lang_it)
{
phrase="ciao";
}
etc
Now it's way better than before, but I'm not still satisfied.
This way of managing translations makes me use a boolean, so I have to add a new "if else" on which translation method I want to pick for every new language I add.
Do you have any suggestions about how to implement a better translation system?
Do you think mine is legit or inappropriate?
edit:
The application should be in English, then I can press a button to use either the Italian or the Russian language
Your implementation of localization will work, but it will involve heavy maintenance, lots and lots of if statements and your entire codebase will need to be revisited when you want to add another language option to your application. You can imagine the headache that that will make for yourself or a future maintainer of the application.
Instead of rolling a self made, very heavy localization implementation, I would recommend that you familiarize yourself with how to do localization and globalization within C# on an application scale, utilizing resource files. Full information obtainable here
This method will even allow for localization of message dialogue texts, menu text, form headers and most importantly numerical representations of values
(Eg. US = $23 432.22 vs DE = $23.432,22 note the difference in thousand and decimal seperators)
Here is also a very good explanation from a similar question on SO:
How to use localization in C#
Related
This may be a noob question, but I need some help. I have written two simple methods in C#: ReadCsv_IT and GetTranslation. The ReadCsv_IT method reads from a csv file. The GetTransaltion method calls the ReadCsv_IT method and returns the translated input (string key).
My problem is that in the future I will have to request a lot of times GetTranslation, but I obviously don't want to read the .csv files every time. So I was thinking about ways to use cache Memory to optimize my program, so that I don't have to read the .csv file on every request. But I am not sure how to do it and what I could do to optimize my program. Can anyone please help ?
public string ReadCsv_IT(string key)
{
string newKey = "";
using (var streamReader = new StreamReader(#"MyResource.csv"))
{
CsvReader csv = new CsvReader(streamReader);
csv.Configuration.Delimiter = ";";
List<DataRecord> rec = csv.GetRecords<DataRecord>().ToList();
DataRecord record = rec.FirstOrDefault(a => a.ORIGTITLE == key);
if (record != null)
{
//DOES THE LOCALIZATION with the help of the .csv file.
}
}
return newKey;
}
Here is the GetTranslation Method:
public string GetTranslation(string key, string culture = null)
{
string result = "";
if (culture == null)
{
culture = Thread.CurrentThread.CurrentCulture.Name;
}
if (culture == "it-IT")
{
result = ReadCsv_IT(key);
}
return result;
}
Here is also the class DataRecord.
class DataRecord
{
public string ORIGTITLE { get; set; }
public string REPLACETITLE { get; set; }
public string ORIGTOOLTIP { get; set; }
}
}
Two options IMO:
Turn your stream into an object?
In other words:
Make a class stream so you can refer to that object of the class stream.
Second:
Initialize your stream in the scope that calls for GetTranslation, and pass it on as an attribute to GetTranslation and ReadCSV_IT.
Brecht C and Thom Hubers have already given you good advice. I would like to add one more point, though: using csv files for localization in .NET is not really a good idea. Microsoft recommends using a resource-based approach (this article is a good starting point). It seems to me that you are trying to write code for something that is already built into .NET.
From a translation point of view csv files are not the best possible format either. First of all, they are not really standardized: many tools have slightly different ways to handle commas, quotes, and line breaks that are part of the translated text. Besides, translators will be tempted to open them in Excel, and -unless handled with caution- Excel will write out translations in whatever encoding it deems best.
If the project you are working on is for learning please feel free to go ahead with it, but if you are developing software that will be used by customers, updated, translated into several target languages, and redeployed, I would recommend to reconsider your internationalization approach.
#Brecht C is right, use that answer to start. When a variable has to be cached to be used by multiple threads or instances: take a look at InMemoryCache or Redis when perfomance and distribution over several clients gets an issue.
Good evening; I have an application that has a drop down list; This drop down list is meant to be a list of commonly visited websites which can be altered by the user.
My question is how can I store these values in such a manor that would allow the users to change it.
Example; I as the user, decide i want google to be my first website, and youtube to be my second.
I have considered making a "settings" file however is it practical to put 20+ websites into a settings file and then load them at startup? Or a local database, but this may be overkill for the simple need.
Please point me in the right direction.
Given you have already excluded database (probably for right reasons.. as it may be over kill for a small app), I'd recommend writing the data to a local file.. but not plain text..
But preferably serialized either as XML or JSON.
This approach has at least two benefits -
More complex data can be stored in future.. example - while order can be implicit, it can be made explicit.. or additional data like last time the url was used etc..
Structured data is easier to validate against random corruption.. If it was a plain text file.. It will be much harder to ensure its integrity.
The best would be to use the power of Serializer and Deserializer in c#, which will let you work with the file in an Object Oriented. At the same time you don't need to worry about storing into files etc... etc...
Here is the sample code I quickly wrote for you.
using System;
using System.IO;
using System.Collections;
using System.Xml.Serialization;
namespace ConsoleApplication3
{
public class UrlSerializer
{
private static void Write(string filename)
{
URLCollection urls = new URLCollection();
urls.Add(new Url { Address = "http://www.google.com", Order = 1 });
urls.Add(new Url { Address = "http://www.yahoo.com", Order = 2 });
XmlSerializer x = new XmlSerializer(typeof(URLCollection));
TextWriter writer = new StreamWriter(filename);
x.Serialize(writer, urls);
}
private static URLCollection Read(string filename)
{
var x = new XmlSerializer(typeof(URLCollection));
TextReader reader = new StreamReader(filename);
var urls = (URLCollection)x.Deserialize(reader);
return urls;
}
}
public class URLCollection : ICollection
{
public string CollectionName;
private ArrayList _urls = new ArrayList();
public Url this[int index]
{
get { return (Url)_urls[index]; }
}
public void CopyTo(Array a, int index)
{
_urls.CopyTo(a, index);
}
public int Count
{
get { return _urls.Count; }
}
public object SyncRoot
{
get { return this; }
}
public bool IsSynchronized
{
get { return false; }
}
public IEnumerator GetEnumerator()
{
return _urls.GetEnumerator();
}
public void Add(Url url)
{
if (url == null) throw new ArgumentNullException("url");
_urls.Add(url);
}
}
}
You clearly need some sort of persistence, for which there are a few options:
Local database
- As you have noted, total overkill. You are just storing a list, not relational data
Simple text file
- Pretty easy, but maybe not the most "professional" way. Using XML serialization to this file would allow for complex data types.
Settings file
- Are these preferences really settings? If they are, then this makes sense.
The Registry - This is great for settings you don't want your users to ever manually mess with. Probably not the best option for a significant amount of data though
I would go with number 2. It doesn't sound like you need any fancy encoding or security, so just store everything in a text file. *.ini files tend to meet this description, but you can use any extension you want. A settings file doesn't seem like the right place for this scenario.
On my form I have a button click
private void button1_Click(object sender, EventArgs e)
{
do something
}
How on the click would I load my do something from a text file, for example my text file looks like this:
MessageBox.Show("hello");
label1.Text = "Hello";
on click it does everything in my text file, if possible.
Here is a very simple example, just to prove this is possible. Basically, you use CodeDomProvider to compile source at runtime, then execute using reflection.
var provider = CodeDomProvider.CreateProvider("C#");
string src=#"
namespace x
{
using System;
public class y
{
public void z()
{
Console.WriteLine(""hello world"");
}
}
}
";
var result = provider.CompileAssemblyFromSource(new CompilerParameters(), src);
if (result.Errors.Count == 0)
{
var type = result.CompiledAssembly.GetType("x.y");
var instance = Activator.CreateInstance(type);
type.GetMethod("z").Invoke(instance, null);
}
Edit
As #Agat points out, the OP seems to require a sort of scripting framework (it makes use of label1, a property of the current object), whereas my answer above obviously does not provide that. The best I can think of is a limited solution, which would be to require dependencies to be specified explicitly as parameters in the "script". Eg, write the scripted code like this:
string src = #"
namespace x
{
using System.Windows;
public class y
{
public void z(Label label1)
{
MessageBox.Show(""hello"");
label1.Text = ""Hello"";
}
}
}
";
Now you can have the caller examine the parameters, and pass them in from the current context, again using reflection:
var result = provider.CompileAssemblyFromSource(new CompilerParameters(), src);
if (result.Errors.Count == 0)
{
var type = result.CompiledAssembly.GetType("x.y");
var instance = Activator.CreateInstance(type);
var method = type.GetMethod("z");
var args = new List<object>();
// assume any parameters are properties/fields of the current object
foreach (var p in method.GetParameters())
{
var prop = this.GetType().GetProperty(p.Name);
var field = this.GetType().GetField(p.Name);
if (prop != null)
args.Add(prop.GetValue(this, null));
else if (field != null);
args.Add(field.GetValue(this));
else
throw new InvalidOperationException("Parameter " + p.Name + " is not found");
}
method.Invoke(instance, args.ToArray());
}
Like the other answers have stated, it isn't an easy thing to implement and can possibly be done through reflection depending on how advanced your scripts are.
But no one #BrankoDimitrijevic mentioned Roslyn and it is a great tool. http://msdn.microsoft.com/en-us/vstudio/roslyn.aspx
It hasn't been updated in quite awhile (Sept.2012) and doesn't have all of the features of C# implemented, however, it did have a lot of it implemented when I played around with this release.
By adding your assembly as a reference to the scripting session, you're able to gain access to all of your assembly's types and script against them. It also supports return values so you can return any data that a scripted method generates.
You can find what isn't implemented here.
Below is a quick and dirty example of Roslyn that I just wrote and tested. Should work right out of box after installing Roslyn from NuGet. The small bloat at the initialization of the script engine can easily be wrapped up in a helper class or method.
The key is passing in a HostObject. It can be anything. Once you do, your script will have full access to the properties. Notice that you just call the properties and not the host object in the script.
Basically, your host object will contain properties of the data you need for your script. Don't necessarily think of your host object as just a single data object, but rather a configuration.
public class MyHostObject
{
public string Value1 { get; set; }
public string Value2 { get; set; }
}
public class RoslynTest
{
public void Test()
{
var myHostObject = new MyHostObject
{
Value1 = "Testing Value 1",
Value2 = "This is Value 2"
};
var engine = new ScriptEngine();
var session = engine.CreateSession(myHostObject);
session.AddReference(myHostObject.GetType().Assembly.Location);
session.AddReference("System");
session.AddReference("System.Core");
session.ImportNamespace("System");
// "Execute" our method so we can call it.
session.Execute("public string UpdateHostObject() { Value1 = \"V1\"; Value2 = \"V2\"; return Value1 + Value2;}");
var s = session.Execute<string>("UpdateHostObject()");
//s will return "V1V2" and your instance of myHostObject was also changed.
}
}
No. You can not.
At least in any simple way.
The thing you want is something like eval('do something') from javascript.
That's not possible to do with C#. C# is a language which needs compilation before execution unlike javascript (for instance).
The only way to implement that is to build your own (pretty complicated as for beginner) parser and execute it in such way.
UPDATED:
Actually, as JDB fairly noticed, that's really not the only way. I love programming! There are so many ways to make a freakky (or even sometimes that really can be necessary for some custom interesting tasks (or even learning)!) code. he he
Another approach I've got in my mind is building some .cs file, then compiling it on-the-fly and working with it as some assembly or some other module. Right.
I am reading the The Pragmatic programmer and doing the following exercise in .net world (Chapter 3 - Code Generators)
The Exercise
"Write a code generator that takes the input in Listing 1, and generates output in two languages of your choice. Try to make it easy to add new languages."
Listing 1
# Add a product
# to the 'on-order' list
M AddProduct
F id int
F name char[30]
F order_code int
E
How would you implement the solution in T4 or anything else in .net world (CodeDOM is too complex) so that we can generate the code in c# and in one other .net language (visual basic or IronRuby etc.)
I know you say CodeDOM is too complex, but I would suggest using CodeDOM =). Here's a short example to start with: http://asp.dotnetheaven.com/howto/doc/listbuilder.aspx. For your example, you probably want to add a CodeMemberMethod to CodeTypeDeclaration's members - MSDN has some examples.
T4 could work, but it I don't think it's really ideal for this situation.
I don't think this is intended to be an exercise in working with existing code generators. Actually there's much more to it. The goal, I believe, is to build your own code generator, domain-specific language and deal with concepts like parsing and extensibility/pluggability. Perhaps I am reading too much into the exercise, but perhaps it's more about developing core developer skills/knowledge than educating oneself on existing tools.
Taking Ben Griswold advice, I think it is a good idea to implement it myself. And while just a little into implementing code generator in C#, I realized few things -
1. Need text manipulation language like Python etc.
2. Need to learn Regular expressions
I do intend to implement it in Ruby but for now, I implemented it in C# as -
static void Main(string[] args)
{
CodeGenerator gen = new CodeGenerator();
gen.ReadFile("Input.txt");
}
public class CodeGenerator
{
public void ReadFile(string filename)
{
StreamReader fs = new StreamReader(filename);
string line;
CSharpCode CG = new CSharpCode();
while ((line = fs.ReadLine()) != null)
{
line = line.TrimEnd('\n');
if (Regex.IsMatch(line, #"^\s*S"))
CG.BlankLine();
else if (Regex.IsMatch(line, #"^\#(.*)")) // match comments
CG.Comment(line.TrimStart('#'));
else if (Regex.IsMatch(line, #"^M\s*(.+)")) // start msg
CG.StartMsg(line.Split(' ')[1]);
else if (Regex.IsMatch(line, #"^E")) // end msg
CG.EndMsg();
else if (Regex.IsMatch(line, #"^F\s*(\w+)")) // simple type
CG.SimpleType(Regex.Split(line, #"^F\s*(\w+)")[1], Regex.Split(line, #"^F\s*(\w+)")[2]);
else
Console.WriteLine("Invalid line " + line);
}
}
}
// Code Generator for C#
public class CSharpCode
{
public void BlankLine() { Console.WriteLine(); }
public void Comment(string comment) { Console.WriteLine("//" + comment); }
public void StartMsg(string name) { Console.WriteLine("public struct " + name + "{"); }
public void EndMsg() { Console.WriteLine("}"); }
public void SimpleType(string name, string type)
{
if(type.Contains("char["))
type = "string";
Console.WriteLine(string.Format("\t{0} {1};", type.Trim(), name));
}
}
I'll post my entire class and maybe someone with MUCH more experience can help me design something better. I'm really new to doing things Asynchronously, so I'm really lost here. Hopefully my design isn't TOO bad. :P
IMDB Class:
public class IMDB
{
WebClient WebClientX = new WebClient();
byte[] Buffer = null;
public string[] SearchForMovie(string SearchParameter)
{
//Format the search parameter so it forms a valid IMDB *SEARCH* url.
//From within the search website we're going to pull the actual movie
//link.
string sitesearchURL = FindURL(SearchParameter);
//Have a method download asynchronously the ENTIRE source code of the
//IMDB *search* website, and save it to the byte[] "Buffer".
WebClientX.DownloadDataCompleted += new DownloadDataCompletedEventHandler(WebClientX_DownloadDataCompleted);
WebClientX.DownloadDataAsync(new Uri(sitesearchURL));
//Convert the byte[] to a string so we can easily find the *ACTUAL*
//movie URL.
string sitesearchSource = Encoding.ASCII.GetString(Buffer);
//Pass the IMDB source code to method FindInformation() to FIND the movie
//URL.
string MovieURL = FindMovieURL(sitesearchSource);
//Download the source code from the recently found movie URL.
WebClientX.DownloadDataAsync(new Uri(MovieURL));
//Convert the source code to readable string for scraping of information.
string sitemovieSource = Encoding.ASCII.GetString(Buffer);
string[] MovieInformation = ScrapeInformation(sitemovieSource);
return MovieInformation;
}
void WebClientX_DownloadDataCompleted(object sender, DownloadDataCompletedEventArgs e)
{
Buffer = e.Result;
throw new NotImplementedException();
}
/// <summary>
/// Formats a valid IMDB url for ease of use according to a search parameter.
/// </summary>
/// <param name="sitesearchSource"></param>
/// <returns></returns>
private string FindMovieURL(string sitesearchSource)
{
int Start = sitesearchSource.IndexOf("<link rel=\"canonical\" href=\"");
string IMDBCode = sitesearchSource.Substring((Start + 28), 35);
return IMDBCode;
}
private string[] ScrapeInformation(string Source)
{
string[] Information = new string[5];
Information[0] = FindTitle(Source);
Information[1] = FindDirector(Source);
Information[2] = FindYear(Source);
Information[3] = FindPlot(Source);
Information[4] = FindPoster(Source);
return Information;
}
/*************************************************************************/
private string FindURL(string Search)
{
string[] SearchArray = Search.Split(' ');
string FormattedQuery = "";
foreach (string X in SearchArray)
{
FormattedQuery += X + "+";
}
FormattedQuery.Remove((FormattedQuery.Length - 1), 1);
string TheFormattedQuery = "http://www.imdb.com/find?s=all&q=" + FormattedQuery + "&x=0&y=0";
return TheFormattedQuery;
}
private string FindTitle(string Source)
{
//<title>Couples Retreat (2009)</title>
int Start = Source.IndexOf("<title>");
string Bookmark = Source.Substring((Start + 7), 400);
int End = Bookmark.IndexOf("</title>");
string Title = Bookmark.Substring(0, End - 7);
return Title;
}
private string FindDirector(string Source)
{
int Start = Source.IndexOf("<h5>Director:</h5>");
string Bookmark = Source.Substring((Start + 18), 250);
Start = Bookmark.IndexOf(">");
Bookmark = Bookmark.Substring(Start + 1, 100);
int End = Bookmark.IndexOf("</a>");
string Director = Bookmark.Substring(0, End - 1);
return Director;
}
private string FindYear(string Source)
{
int Start = Source.IndexOf("<h5>Release Date:</h5>");
string Bookmark = Source.Substring((Start + 22), 40);
int End = Bookmark.IndexOf("<a class=");
string ReleaseYear = Bookmark.Substring(0, End - 1);
return ReleaseYear;
}
private string FindPlot(string Source)
{
int Start = Source.IndexOf("<h5>Plot:</h5>");
string Bookmark = Source.Substring((Start + 14), 700);
int End = Bookmark.IndexOf("<a class");
string Plot = Bookmark.Substring(0, End - 1);
return Plot;
}
private string FindPoster(string Source)
{
int Start = Source.IndexOf("<a name=\"poster\" href=");
string Bookmark = Source.Substring((Start + 22), 700);
Start = Bookmark.IndexOf("src=\"");
string PosterURL = Bookmark.Substring((Start + 5), 103);
return PosterURL;
}
}
Form1.cs Class (My windows Form):
public partial class MainSearchForm : Form
{
public MainSearchForm()
{
InitializeComponent();
}
SearchFunctions.IMDB IMDBClass = new QuickFlick.SearchFunctions.IMDB();
int YPosition = 5;
private void btnSearch_Click(object sender, EventArgs e)
{
string[] MovieInformation = IMDBClass.SearchForMovie(txtSearch.Text);
LoadMovieInformation(MovieInformation);
}
public void LoadMovieInformation(string[] FoundInfo)
{
MovieItem TheMovieItem = new MovieItem();
TheMovieItem.SetTitle(FoundInfo[0]);
TheMovieItem.SetDirector(FoundInfo[1]);
TheMovieItem.SetRelease(FoundInfo[2]);
TheMovieItem.SetPlot(FoundInfo[3]);
TheMovieItem.SetPoster(FoundInfo[4]);
TheMovieItem.Location = new Point(5, YPosition);
YPosition += 196;
panel1.Controls.Add(TheMovieItem);
}
}
Now, the gist of what my program is trying to accomplish.
A user will write the name down of a movie, and I'll pull up the information about it. Nothing else! :P It's mostly intended for me to learn Async functions etc. but I'm scared I might be approaching this the completely wrong way.
Once again, I'm not looking for much code, just design of the program. Methods, order of methods, unnecesary methods, etc. :D
Thanks a bunch SO, as always, you rock!
I'm not going to comment on the entire design because, like #Vinko, I think you ought to focus your question a little more in that respect. I will say though that you are fundamentally misunderstanding how the asynchronous methods work. They will return immediately to the calling thread without completing first. That's the whole point with asynchronous threads - they don't wait until they have finished before they return.
For this to work, you must either use synchronous calls or wait on some event after the call and signal that event in the asynchronous callback handler so that your code doesn't attempt to access the returned data until after the call has completed. The benefit in this approach is that your thread is free to perform other tasks while waiting on the call to complete, but you must absorb the complexity of managing the waiting process (it's not much).
See the sample code for the DownloadDataCompleted event at MSDN. You'll need to do a wait for each of the web client downloads that you do. Note that you need to invoke the Set() method on the AutoResetEvent object passed to your handler in the example in your handler.
You need to have models.
For example the FindTitle() and FindDirector() methods should be in a separate class.. perhaps called 'Movie' or 'IMDBMovie'. This class should have attributes such as 'title' and 'director' with getters / setters.
This article could prove helpful to you: MVC
I would agree with clownbaby on the need for models, but would disagree on the need for FindTitle, FindDirectory etc. needing to be in separate classes.
I think that you should structure your code as follows:
Have a class that takes in the search parameters in a generic way and then calls a search through some kind of factory class.
Have the factory class internally create a specialized version of the search class based on either a specification by the user, or a specification in the configuration settings of the application.
Have the search class format the search query and then call another class which simply makes the HTTP request and gets back the HTTP response contents to the search class.
Have the search class then parse the HTTP response to generate the results to be fed back to the class that called the search in Item 1.
The advantages of this sort of approach are that you can then isolate the code that is specialized for processing website specific parsing routines into it's own class and isolate it from the UI as well as utilize a single class to make the HTTP request, regardless of how many websites you want to scrape.
Put another way, the View layer should not care how exactly the data is retrieved and parsed and should have the flexibility to call on various sources of information.
Likewise, the Data layer should not care how the data should be processed and should only be concerned with where to get the data from.
The Domain layer is the one that will sit in the middle, take raw data from the Data layer, parse it and make sense of it and then format it into a shape that the View can understand.
This way, if say you want to also target Yahoo! Movies, Wikipedia or any of the other sources of movie data, you can do that. All you would have to do is add another class in the Domain layer and inform the View that a new source is available in the Domain layer.
Good design is about limiting the impact of changes so that additional features can be built into the application.
Good design is also about being able to test individual components. A design such as the one I have described will allow you to test the various classes that comprise individual layers and ensure that they work as advertised.
Something I find very useful is that if a class cannot be tested very easily using NUnit and Rhino.Mocks, it's probably not very well-designed.
Better to split the model object from the data retrieval object.
User sees View, uses btnSearch_Click
Search starts on other thread, btnSearch_Click action finishes
Search finishes, data may be available, new object creation and addition to view.
View has new data event and redisplays content.
Some adjusted code to do the async calls.
public string[] SearchForMovie(string SearchParameter)
{
//Format the search parameter so it forms a valid IMDB *SEARCH* url.
//From within the search website we're going to pull the actual movie
//link.
string sitesearchURL = FindURL(SearchParameter);
//Have a method download asynchronously the ENTIRE source code of the
//IMDB *search* website, and save it to the byte[] "Buffer".
WebClientX.DownloadDataCompleted += new DownloadDataCompletedEventHandler(WebClientX_DownloadSearchCompleted);
WebClientX.DownloadDataAsync(new Uri(sitesearchURL));
}
void WebClientX_DownloadSearchCompleted(object sender, DownloadDataCompletedEventArgs e)
{
Buffer = e.Result;
//Convert the byte[] to a string so we can easily find the *ACTUAL*
//movie URL.
string sitesearchSource = Encoding.ASCII.GetString(Buffer);
//Pass the IMDB source code to method FindInformation() to FIND the movie
//URL.
string MovieURL = FindMovieURL(sitesearchSource);
//Download the source code from the recently found movie URL.
WebClientX.DownloadDataCompleted += new DownloadDataCompletedEventHandler(WebClientX_DownloadMovieCompleted);
WebClientX.DownloadDataAsync(new Uri(MovieURL));
}
void WebClientX_DownloadMovieCompleted(object sender, DownloadDataCompletedEventArgs e)
{
Buffer = e.Result;
//Convert the source code to readable string for scraping of information.
string sitemovieSource = Encoding.ASCII.GetString(Buffer);
// would create a movie object here rather than have the scrape function on this class
string[] MovieInformation = ScrapeInformation(sitemovieSource);
Model.LoadMovieInformation(MovieInformation);
}