Monday, April 30, 2007
MasFoxPro
A "petition" has been circulating around the Internet that is trying to gather enough signatures to get Microsoft to reverse their decision to not continue development on FoxPro. I have not signed the petition and have been questioned as to my motives. I have even been called some nasty names because of my stand.
Doug, Alex, and Rick have posted about their reasons.
I'm not going to post here my reasons for not signing (the above links list some of them). I stated on the UT why I wasn't signing and then called on it. Frankly, it's no one's business why I'm not signing and because I don't want to sign should not call in to play my ethics, my feelings about the Fox community, FoxPro in general, or my relationship with Microsoft.
I will say that no one inside or outside at Microsoft asked me to not sign. Being an MVP in no way means that I just go along for the ride and agree with everything Microsoft says. I blogged about the independence of MVPs here.
Because I don't want to sign does not mean I don't agree with the cause. Those of you who are saying that people that don't sign, particularly MVPs, are just following the Microsoft line and can't decide for ourselves, and then our loyalties are questioned, shame on you.
Doug, Alex, and Rick have posted about their reasons.
I'm not going to post here my reasons for not signing (the above links list some of them). I stated on the UT why I wasn't signing and then called on it. Frankly, it's no one's business why I'm not signing and because I don't want to sign should not call in to play my ethics, my feelings about the Fox community, FoxPro in general, or my relationship with Microsoft.
I will say that no one inside or outside at Microsoft asked me to not sign. Being an MVP in no way means that I just go along for the ride and agree with everything Microsoft says. I blogged about the independence of MVPs here.
Because I don't want to sign does not mean I don't agree with the cause. Those of you who are saying that people that don't sign, particularly MVPs, are just following the Microsoft line and can't decide for ourselves, and then our loyalties are questioned, shame on you.
Catching up is hard to do
My last RSS Reader post on April 19 has sparked a couple of emails asking the status of the project. As happens with everyone, I've been busy with other things...
Friday, April 20: I took the six hour drive south to Las Vegas to see Spamalot at the Wynn. It's a musical based on Monty Python's Search for the Holy Grail. It was the 2005 Tony winner for best musical and has been "jiggered" a bit for Vegas. It starred John O'Hurley, who is best known as J. Peterman on Seinfeld. I have to say, what a FANTASTIC show. Laughs from beginning to end. The road company is coming to Salt Lake in September and I'm trying to get tickets to see it here. See it if you can!
Mondy, April 23-Wednesday, April 25: I was in a three day course, "Programming with XML in the Microsoft .Net Framework". This Microsoft Official Course (MOC) was pretty good. The manual was written in 2001 and is now somewhat outdated with changes in Visual Studio. The instructor was great and knew his stuff.
The rest of the week was spent doing catch up and handling the mundane tasks at the office.
Friday, April 20: I took the six hour drive south to Las Vegas to see Spamalot at the Wynn. It's a musical based on Monty Python's Search for the Holy Grail. It was the 2005 Tony winner for best musical and has been "jiggered" a bit for Vegas. It starred John O'Hurley, who is best known as J. Peterman on Seinfeld. I have to say, what a FANTASTIC show. Laughs from beginning to end. The road company is coming to Salt Lake in September and I'm trying to get tickets to see it here. See it if you can!
Mondy, April 23-Wednesday, April 25: I was in a three day course, "Programming with XML in the Microsoft .Net Framework". This Microsoft Official Course (MOC) was pretty good. The manual was written in 2001 and is now somewhat outdated with changes in Visual Studio. The instructor was great and knew his stuff.
The rest of the week was spent doing catch up and handling the mundane tasks at the office.
Friday, April 20, 2007
VFP Training is Coming
Just because Microsoft has announced no more development on VFP after this summer, doesn't mean the VFP world is coming to an end. Two conferences were announced after Microsoft's statement. Now comes word of two training events.
The first is LAFox Boot Camp. I've never been to it, but I've great things about it. It's a one day, hands on, in-depth training session. You'll build an application from the ground up. And, it's only $99. LAFox Boot Camp is happening on Saturday, April 28.
Next up is VFP training classes from TakeNote Technologies in Raleigh. Even better, not only will you get to learn VFP from Jim Duffy, you'll be able to laugh at Jim Duffy. (Notice I didn't say laugh with.) Jim actually has two different classes. The first, Introduction to Application Development with VFP is May 21-22. The second, Application Development with VFP follows it on May 23-25.
I commend both LAFox and TakeNote for continuing to support the Fox community.
The first is LAFox Boot Camp. I've never been to it, but I've great things about it. It's a one day, hands on, in-depth training session. You'll build an application from the ground up. And, it's only $99. LAFox Boot Camp is happening on Saturday, April 28.
Next up is VFP training classes from TakeNote Technologies in Raleigh. Even better, not only will you get to learn VFP from Jim Duffy, you'll be able to laugh at Jim Duffy. (Notice I didn't say laugh with.) Jim actually has two different classes. The first, Introduction to Application Development with VFP is May 21-22. The second, Application Development with VFP follows it on May 23-25.
I commend both LAFox and TakeNote for continuing to support the Fox community.
Thursday, April 19, 2007
Orcas in the wild
RSS Reader: Day 6
No progress yesterday on the RSS Reader. I'm still digging into Typed DataSets. But, even that didn't go very far for reasons too boring to discuss here. I did find out why the calls to authors.AcceptChanges() didn't work. First, I looked in the help file. It says, "Resets the object’s state to unchanged by accepting the modifications." Umm. ok. So, the state of the object is set to unchanged but the modifications were accepted.
Next, I went to the book Programming with ADO.Net 2.0 Core Reference. It explained things much better:
"If you call the AcceptChanges method on your DataSet, ADO.Net will accept all pending changes stored in the DataRow objects in your DataSet. Any row whose RowState property is set to Added or Modified will have their RowState property set to Unchanged. Doing this will also reset the "original" values for the DataRow to the current contents of the DataRow. Any DataRow objects marked as Deleted will be removed from your DataSet when you call AcceptChanges.
When the SqlDataAdapter object successfully submits pending changes stored in a DataRow object, it implicitly calls the AcceptChanges method on that DataRow."
So, what happens is that when you call AcceptChanges, it updates the values in the row and resets the flag that indicates there were changes. But it doesn't update the database! To do that, you need to call AcceptChanges of the SqlDataAdapter. But it looks at the flag and sees there are no changes, so doesn't do anything. Once again, here is a case of all the books and examples showing calls to AcceptChanges when that isn't what you should be doing.
The solution is exactly what I ended up doing. Don't call AcceptChanges. Just call the TableAdapter's Update method.
Next, I went to the book Programming with ADO.Net 2.0 Core Reference. It explained things much better:
"If you call the AcceptChanges method on your DataSet, ADO.Net will accept all pending changes stored in the DataRow objects in your DataSet. Any row whose RowState property is set to Added or Modified will have their RowState property set to Unchanged. Doing this will also reset the "original" values for the DataRow to the current contents of the DataRow. Any DataRow objects marked as Deleted will be removed from your DataSet when you call AcceptChanges.
When the SqlDataAdapter object successfully submits pending changes stored in a DataRow object, it implicitly calls the AcceptChanges method on that DataRow."
So, what happens is that when you call AcceptChanges, it updates the values in the row and resets the flag that indicates there were changes. But it doesn't update the database! To do that, you need to call AcceptChanges of the SqlDataAdapter. But it looks at the flag and sees there are no changes, so doesn't do anything. Once again, here is a case of all the books and examples showing calls to AcceptChanges when that isn't what you should be doing.
The solution is exactly what I ended up doing. Don't call AcceptChanges. Just call the TableAdapter's Update method.
Wednesday, April 18, 2007
RSS Reader: Day 5
I finally found a example of Typed DataSets that wasn't all drag and drop and had everything I needed. It's Programming Microsoft ADO.NET 2.0 Core Reference by David Sceppa. His bio in the book says he used to do tech support for Microsoft, supporting Visual Basic and Visual FoxPro. Chapter 9 is devoted entirely to Typed DataSets. After reading part of the chapter, I updated my Pubs test application. Here's the code:
I still have more questions, so Typed DataSet research continues today. For example, why does taAuthors.Update() fail if I call authors.AcceptChanges()?
using System;
using System.Collections.Generic;
using System.Text;
using Pubs.AuthorsTableAdapters;
namespace Pubs
{
class Program
{
static void Main(string[] args)
{
authorsTableAdapter taAuthors = new authorsTableAdapter();
taAuthors.Connection.ConnectionString =
Pubs.Properties.Settings.Default.pubsConnectionString;
// Get all the data and loop through it
Authors.authorsDataTable authors = taAuthors.GetData();
foreach (Authors.authorsRow row in authors)
{
Console.WriteLine("{0} {1}", row.au_fname, row.au_lname);
}
// Add a row
Console.WriteLine("Adding a new row");
Authors.authorsRow newRow = authors.NewauthorsRow();
newRow.au_id = "111-11-1111";
newRow.au_fname = "Joe";
newRow.au_lname = "Tester";
newRow.phone = "555-1212";
newRow.contract = true;
authors.AddauthorsRow(newRow);
//authors.AcceptChanges();
taAuthors.Update(authors);
// Update a row
Console.WriteLine("Updating a row");
Authors.authorsRow editRow = authors.FindByau_id("111-11-1111");
editRow.au_lname = "Examiner";
//authors.AcceptChanges();
taAuthors.Update(authors);
// Delete a row
Console.WriteLine("Deleting a row");
Authors.authorsRow delRow = authors.FindByau_id("111-11-1111");
delRow.Delete();
taAuthors.Update(authors);
Console.WriteLine("Done!");
Console.ReadLine();
}
}
}
Tuesday, April 17, 2007
RSS Reader: Day 4
No progress today on the project itself. After my last post, I got emails telling me that I should be using Typed DataSets. I thought, "what are they and why should I use them?", so today was a research day to figure out what they are.
The first thing I found was that there is a real lack of information about them. I looked in several books on VS 2005 and they either didn't mention Typed DataSets (TDS) or glossed over them. I did finally find some information in one book, but it only gave half of an example and even that wasn't explained well.
I also didn't have lots of luck with web searches. I found some examples that explained them, but from the perspective of moving from VS 2003 to VS 2005. The examples I did find also showed two tables in a parent-child relationship. A Windows form or Web form were then created and the tables were hooked up to standard UI controls. I wanted something simpler.
Before getting into the code, I want to explain a Typed DataSet.
In VFP, we're used to referring to a field as Table.Field. For example, Authors.au_fname. The way I coded my RSS application, the fieldname was a string. So, imagine if in VFP, you treated it like an array, referencing the field either by its index or name. You'd have something like authors(1) or authors("au_fname"). If the field order changed, you'd have to rewrite the code. Or if there was a typo in the fieldname, you wouldn't know until runtime. (Ignore the fact that in VFP, you wouldn't know until runtime anyway.)
With a TDS, you know right away that the fieldname is wrong. It moves errors from runtime to compile time. With a TDS, a class is created that knows all the fieldnames and their datatypes and you can use syntax similar to what we're used to in VFP.
There are still some questions running around in my head. How do I insert, update, and delete a record? How do I update the TDS schema when I modify the table schema? I've been told that contrary to all the examples, there should only be one table per TDS. How do I then relate a parent to its child table? Some people have told me that they use a TDS, but without the TableAdapter. How do I do that? Other people, and some links I read, say that a TDS and a business object are mutually exclusive. Is that true? If so, which should I use?
The first thing I found was that there is a real lack of information about them. I looked in several books on VS 2005 and they either didn't mention Typed DataSets (TDS) or glossed over them. I did finally find some information in one book, but it only gave half of an example and even that wasn't explained well.
I also didn't have lots of luck with web searches. I found some examples that explained them, but from the perspective of moving from VS 2003 to VS 2005. The examples I did find also showed two tables in a parent-child relationship. A Windows form or Web form were then created and the tables were hooked up to standard UI controls. I wanted something simpler.
Before getting into the code, I want to explain a Typed DataSet.
In VFP, we're used to referring to a field as Table.Field. For example, Authors.au_fname. The way I coded my RSS application, the fieldname was a string. So, imagine if in VFP, you treated it like an array, referencing the field either by its index or name. You'd have something like authors(1) or authors("au_fname"). If the field order changed, you'd have to rewrite the code. Or if there was a typo in the fieldname, you wouldn't know until runtime. (Ignore the fact that in VFP, you wouldn't know until runtime anyway.)
With a TDS, you know right away that the fieldname is wrong. It moves errors from runtime to compile time. With a TDS, a class is created that knows all the fieldnames and their datatypes and you can use syntax similar to what we're used to in VFP.
- I finally got a simple example of reading data with a TDS working. Here's what you need to do:
In your VS project, right click on the project and select Add -> New Item. The Add New Item dialog will appear. - Select DataSet and name it (I chose Authors because I'm using the Pubs database) then click Add.
- Drag and drop the Authors table from the Pubs database from the Server Explorer onto the design surface. This will create the Authors TDS and the AuthorsTableAdapter.
- Right-click on the the AuthorsTableAdapter and select Configure. I kept all the default settings and just clicked through the wizard.
The file Authors.xsd and three children files, Authors.designer.cs, Authors.xsc, and Authors.xss were added to the project. Here is the console application code I wrote to list the author's names.
using System;
using System.Collections.Generic;
using System.Text;
using Pubs.AuthorsTableAdapters;
namespace Pubs
{
class Program
{
static void Main(string[] args)
{
authorsTableAdapter taAuthors = new authorsTableAdapter();
taAuthors.Connection.ConnectionString =
Pubs.Properties.Settings.Default.pubsConnectionString;
Authors.authorsDataTable authors = taAuthors.GetData();
foreach (Authors.authorsRow row in authors)
{
Console.WriteLine("{0} {1}", row.au_fname, row.au_lname);
}
Console.ReadLine();
}
}
}
There are still some questions running around in my head. How do I insert, update, and delete a record? How do I update the TDS schema when I modify the table schema? I've been told that contrary to all the examples, there should only be one table per TDS. How do I then relate a parent to its child table? Some people have told me that they use a TDS, but without the TableAdapter. How do I do that? Other people, and some links I read, say that a TDS and a business object are mutually exclusive. Is that true? If so, which should I use?
I'm in meetings about half the day today, so I won't make much progress in resolving these questions.
Monday, April 16, 2007
WPF/E is now Silverlight
It's now official. WPF/E is now Silverlight.
Thursday, April 12, 2007
RSS Reader: Day 3
Day 3 turned out to be very frustrating. I had to take a step back and fix two problems, something I did Tuesday broke both the add and delete of folders in the tree view. Each of these required a different fix.
Adding records
I'm not sure what I did that broke this, but it was very difficult to find a fix. I was getting an error that one column in the DataSet could not be null when adding a new row. Yes, I know this...it's the PK that's generated by the database. It shouldn't have a value at this point. So, this one problem was really two problems: how to add the row without generating the error and how to get the PK back from the database once it's been updated.
I searched through four different books and did lots of searching on line. Everything I found either showed how to use the drag and drop tools onto the form, ignored PKs, or showed how to do this with a Stored Procedure. None would work for my simple application. So, I did more web searches and finally found the solution here. The link uses OleDb instead of SQLClient, but it was easy to change that.
So, here's what I did. First, I stored a dummy value in the PK column before adding the new row to the DataSet.
Next, I needed to get the new PK. It turns out that an event is fired when a row is updated. So, first I added the call to the event handler.
Then, add the code for the handler.
Now, coming from the Fox world, what I had to do sucks. It took me most of the day to track down the solution. Working with data should not be this hard. Why can't .Net automatically identify that the column was the PK and not throw the error? Why can't it automatically retrieve the new PK after the database has been updated? Why can't Microsoft show us the way this should be coded instead of always concentrating on drag and drop? For that matter, why can't the book authors do the same?
Treeview
Now that I've ranted about data access in .Net, I want to gripe about TreeView controls in general. I've wrestled with TreeViews in VFP and looked at LOTS of third party TreeViews. All are downright HARD to use. Why should they be? You need to do a few things, add, and delete a node, know where you clicked and right-clicked, maybe sort and rename. You need ways to store additional information about each node to track it back to some data source. NONE of the TreeViews I've ever looked at made this easy. The TreeView in .Net is no different.
The problem I had with deleting a folder in the TreeView occurred when I changed how I was handling right-click to display the context menu. At first, I was trapping the NodeMouseClick event and using the X, Y coordinates to know where the click occurred, getting the node at that location, and using that information to know what context menu to display (there's a different one depending on what level the node is at). Then, I found a property for each node that would allow you to hook in the ContextMenu. .Net would handle the display of the menu. This sounds great, but the problem is, you can't figure out what node got the click.
So, I went back to the first solution. Here's the code:
So, today, I'm back to where I was Tuesday evening. Next step is to be able to add new subscriptions. It will be next week before I can get that done. I'm in meetings most of the day today and off tomorrow to close up the ski season.
Adding records
I'm not sure what I did that broke this, but it was very difficult to find a fix. I was getting an error that one column in the DataSet could not be null when adding a new row. Yes, I know this...it's the PK that's generated by the database. It shouldn't have a value at this point. So, this one problem was really two problems: how to add the row without generating the error and how to get the PK back from the database once it's been updated.
I searched through four different books and did lots of searching on line. Everything I found either showed how to use the drag and drop tools onto the form, ignored PKs, or showed how to do this with a Stored Procedure. None would work for my simple application. So, I did more web searches and finally found the solution here. The link uses OleDb instead of SQLClient, but it was easy to change that.
So, here's what I did. First, I stored a dummy value in the PK column before adding the new row to the DataSet.
DataRow row = dsAllData.Tables["Groups"].NewRow();
row["GroupPK"] = -1;
row["Description"] = folderName;
dsAllData.Tables["Groups"].Rows.Add(row);
daGroups.Update(dsAllData, "Groups");
Next, I needed to get the new PK. It turns out that an event is fired when a row is updated. So, first I added the call to the event handler.
daGroups.RowUpdated += new SqlRowUpdatedEventHandler(daGroups_OnRowUpdate);
Then, add the code for the handler.
static void daGroups_OnRowUpdate(object sender, SqlRowUpdatedEventArgs e)
{
if (e.StatementType == StatementType.Insert)
{
SqlCommand cmd = new SqlCommand("SELECT @@IDENTITY", e.Command.Connection);
e.Row["GroupPK"] = cmd.ExecuteScalar();
e.Row.AcceptChanges();
}
}
Now, coming from the Fox world, what I had to do sucks. It took me most of the day to track down the solution. Working with data should not be this hard. Why can't .Net automatically identify that the column was the PK and not throw the error? Why can't it automatically retrieve the new PK after the database has been updated? Why can't Microsoft show us the way this should be coded instead of always concentrating on drag and drop? For that matter, why can't the book authors do the same?
Treeview
Now that I've ranted about data access in .Net, I want to gripe about TreeView controls in general. I've wrestled with TreeViews in VFP and looked at LOTS of third party TreeViews. All are downright HARD to use. Why should they be? You need to do a few things, add, and delete a node, know where you clicked and right-clicked, maybe sort and rename. You need ways to store additional information about each node to track it back to some data source. NONE of the TreeViews I've ever looked at made this easy. The TreeView in .Net is no different.
The problem I had with deleting a folder in the TreeView occurred when I changed how I was handling right-click to display the context menu. At first, I was trapping the NodeMouseClick event and using the X, Y coordinates to know where the click occurred, getting the node at that location, and using that information to know what context menu to display (there's a different one depending on what level the node is at). Then, I found a property for each node that would allow you to hook in the ContextMenu. .Net would handle the display of the menu. This sounds great, but the problem is, you can't figure out what node got the click.
So, I went back to the first solution. Here's the code:
private void tvSubscriptions_NodeMouseClick(object sender,
TreeNodeMouseClickEventArgs e)
{
if (e.Button == MouseButtons.Right)
{
TreeNode currentNode = tvSubscriptions.SelectedNode;
TreeNode clickedNode = tvSubscriptions.GetNodeAt(e.X, e.Y);
tvSubscriptions.SelectedNode = clickedNode;
switch (e.Node.Level)
{
case 0:
tvRootContextMenuStrip.Show(tvSubscriptions, e.X, e.Y);
break;
case 1:
tvNodeContextMenuStrip.Show(tvSubscriptions, e.X, e.Y);
break;
case 2:
tvItemContextMenuStrip.Show(tvSubscriptions, e.X, e.Y);
break;
}
}
}
So, today, I'm back to where I was Tuesday evening. Next step is to be able to add new subscriptions. It will be next week before I can get that done. I'm in meetings most of the day today and off tomorrow to close up the ski season.
Wednesday, April 11, 2007
RSS Reader: Day 2 Revisited
Christof Wollenhaupt emailed me about yesterday's post. He said, This sentences confuses me: "and put it into it's own static class. The class is instantiated in the main form." If you have a static class (type), you use it directly without instantiating it." Christof's right. The wording comes from my Fox background. I'll make an effort to use the right terms in the future.
Also, after posting yesterday, I found that I broke two things. I can't add or delete a Folder. So, first up this morning is to fix that.
Also, after posting yesterday, I found that I broke two things. I can't add or delete a Folder. So, first up this morning is to fix that.
Tuesday, April 10, 2007
RSS Reader: Day 2
I spent quite a bit of time today tracking down why I can't delete a row from the Folders table. I discovered that the QueryBuilder is not generating the delete code. I then tried to trick the QueryBuilder into creating the code, but was unsuccessful. I finally coded the Delete command by hand. I posted a message about it on the UT and Michael Mclain responded that I don't want to use the QueryBuilder anyway and to look at the TableAdapter Wizard in VS 2005. In trying to solve the problem, I looked at four different books and all showed using the QueryBuilder.
After that was working, I returned to wiring up the tree view. I found that it has a ContextMenuStrip property for each Node for handling Right Click events. I had been trapping the click in code, figuring out if it was right or left click, and if right click, displaying the proper context menu. I hooked up the ContextMenuStrip property and took out the code I had written.
I then started to look at how to determine when a node is selected and which node is currently selected. The AfterSelect event method looks to be just what I need. I put in some test code to display the selected item in the browser control.
Tomorrow I need to work on the New Subscription dialog and the RSS feed parser to pull apart the feed and save its different pieces. I did some preliminary experimenting on the parser, so I have an idea of where to start on it.
After that was working, I returned to wiring up the tree view. I found that it has a ContextMenuStrip property for each Node for handling Right Click events. I had been trapping the click in code, figuring out if it was right or left click, and if right click, displaying the proper context menu. I hooked up the ContextMenuStrip property and took out the code I had written.
I then started to look at how to determine when a node is selected and which node is currently selected. The AfterSelect event method looks to be just what I need. I put in some test code to display the selected item in the browser control.
Tomorrow I need to work on the New Subscription dialog and the RSS feed parser to pull apart the feed and save its different pieces. I did some preliminary experimenting on the parser, so I have an idea of where to start on it.
ComputerWorld on Fox
No, no.. that's not a promo for an upcoming TV show. ComputerWorld has published a very good article on the MasFoxPro campaign. The article also has a very good history of Fox.
RSS Reader: Day 2
OK, it isn't really day 2, but I'll call it that for this post...and continue numbering subsequent days incrementally. Here's what I did yesterday...
I added a form for entering a new folder name. Folders are used in the tree view to organize the feeds. Because I now have a second form, I pulled out the data access code from the main form and put it into it's own static class. The class is instantiated in the main form, then used in other forms. Got the Add Folder feature working. The folder is added to the database and the tree view, but not in alphabetical order. I'll save that for another day.
I then started work on the Delete Folder code. I can remove the folder from the tree view, but get an error when deleting that the key into the table isn't specified. I am setting it in the code, but for some reason it isn't seen. Ran out of time before I could track it down.
I added a form for entering a new folder name. Folders are used in the tree view to organize the feeds. Because I now have a second form, I pulled out the data access code from the main form and put it into it's own static class. The class is instantiated in the main form, then used in other forms. Got the Add Folder feature working. The folder is added to the database and the tree view, but not in alphabetical order. I'll save that for another day.
I then started work on the Delete Folder code. I can remove the folder from the tree view, but get an error when deleting that the key into the table isn't specified. I am setting it in the code, but for some reason it isn't seen. Ran out of time before I could track it down.
Monday, April 09, 2007
RSS Reader - Getting started
Well, I'm finally sitting down and developing my .Net project that I wanted to do a couple of years ago. It's a learning project. I'm building an RSS aggregator. It's a good project because it involves WinForms, XML, database, and other technolgies. I'm doing in all in C# because my boss told me to learn C# and has given me work time to do so. I should have started keeping my work log here on it when I started it last week. So far I have a WinForm with a menu, toolbar and tabs on the left to organize the feeds. On the right is tabs with a web browser control. I can read the database and populate the treeview. Last Friday, I implemented right-click on the tree view. Updates will follow on the progress.
Friday, April 06, 2007
Get Well, Rocky
Some of you may know Rocky Lhotka. Some of you may have noticed his absence from the blogosphere recently. Turns out, Rocky has been sick. Really sick. He even had to cancel some of his sessions at VSLive. Read Rocky's account of what's going on, then read his wife, Teresa's account. Rocky, I hope you have a speedy and full recovery.
Update: Fixed Rocky's wife's name
Update: Fixed Rocky's wife's name
Subscribe to Posts [Atom]