December 2005 - Posts

I'd like to apologize if any users of PostXING have to go thru proxies...I've been bugchasing this evening and it occurred to me that specifying a proxy for a new blog is kind of...well, weird.

First, it's a wizard-style dialog, so you expect the next/back buttons to work like it would in say an installer (well, I would anyways). Instead, you've got to specify at least a host, "page", and port, click on a special linkbutton (not next), click back to specify now the rest of the properties (including re-typing what you've already typed) and then you can go to select which blog you want to use.

I don't have an answer for this yet, but I'm working on it.

I wanted to install the newest version of Konfabulator to one of my computers, so I went to www.konfabulator.com and tried to install the latest version (3.0.1 earlier today, 3.0.2 this evening) but instead of getting the 11MB download that the site reports, I get a little <500KB file called widgetsus.exe.

According to some forum posts, I'm not the only person having problems here. My problem was that no computers that didn't already have an earlier version of Konfabulator would actually install off of this file. So I looked thru a few more forum posts and found a url to 3.0.1 (http://us.dl1.yimg.com/download.yahoo.com/dl/widgets/us/yahoowidgets3x77.exe) and was then able to guess for the 3.0.2 version (http://us.dl1.yimg.com/download.yahoo.com/dl/widgets/us/yahoowidgets3x79.exe) these are the files that the web installer are supposed to download, but don't.

Hopefully me posting this won't tip off the developers and make them choose a different naming scheme. Better yet, why not just let end users download the installer instead of having to go thru the web installer.

And now I can post there with PostXING (v2).

Dare has put up a couple of tutorials on how to setup Metablog support for w.bloggar and BlogJet. Since I "borrowed" ideas from both of these fine applications, the setup for w.bloggar is nearly identical to what you need to do in PostXING v2. Sorry for those v1 folks, I don't think it'll work in that version. Maybe I can put out a patch so that v1 will work too.

I'd also like to thank Dare for being so helpful in the beta stage. He personally replied to my questions and gave insight on a couple of things that were very helpful (like the BOM thing, which may or may not be a fluke in the beta version of spaces)

A few people asked for the source code/a download for the little widget I whipped up on Friday, so I spent this morning adding a little bit of persistence. The original code for switching my networks is still in there (and still works on my machine if I change the code to point there) but I made it more general and also used it as an excuse to explore the Settings API as well as use a couple of neat features of VS 2005. (If you don't care about any of that stuff and just want to see if you can build the project and make some use out of it, it can be found here on projectdistributor. )

So the settings goop in VS 2005 is pretty neat, but there's a limited subset (it seems) that you can specify as types in the designer. I wanted to use a Domain Object, tho - so, no designer love for me. I ended up making a simple string setting just to see what code it generated for UserScopedSetting properties. I ended up with this:

[global::System.Configuration.UserScopedSetting]
public ConfigItem Disconnected {
	get {
		if (this["Disconnected"] == null) {
			ConfigItem item = new ConfigItem();
			item.IPAddress = "127.0.0.1";
			item.Condition = ConditionKind.Equals;
			item.DisplayText = "disconnected.";
			item.Icon = IconKind.Disconnected;
			this["Disconnected"] = item;
		}
		return (ConfigItem)this["Disconnected"];
	}
	set {
		this["Disconnected"] = value;
	}
}

I found that a suitable default value was needed to start off with in settings, so I did something similar for a Home, Work, Unknown, and Other network. This way I can persist the Settings using the Settings API instead of something home cooked (although I'm not sure if you would have to to avoid version conflicts...) UserScopedSettings look to me a lot like IsolatedStorage, that is the version and the product name help determine where settings should go. If you wanted to use the same settings across different versions, I guess you would have to copy the user.config file from one version location to the next.

Next I wanted to see what the designer had to offer. The table layout panel was real nice. It took care of all of my layout issues with respect to space. To get what I wanted done, I first created a dummy Collection that inherits from System.ComponentModel.BindingList<>:

internal class ConfigItemList : BindingList<ConfigItem> {}

This is how I was able to add it as an Object datasource to the Data Sources window:

From there, you can change the type of control you would like to output. I simply Dock.Fill 'ed the TableLayoutPanel and added the correct amount of rows. To get the labels to line up with the controls, I set all of their AnchorStyles to Top | Left (that's Top AND Left). When each usercontrol is loaded up, I manually bind to the information. When the Save button is clicked, all of the info gets put back into the Settings object from the controls and Settings.Default.Save() is called. This automatically saves the changed data to a safe location under Documents And Settings (specifically Local Settings\Application Data\CompanyName\FileName+ a bunch of other stuff\ProductVersion).

Now, for the main reason I created this thing: I host a few external sites at work, and I can't see them without an entry in my hosts file (%windir%\system32\drivers\etc\hosts) for each one. At home (or anywhere else) however, I need to be able to rely on DNS to be able to get me to those sites, so I created 2 extra hosts files, hosts.work and hosts.home. hosts.home pretty much just has the single entry:

127.0.0.1    localhost

whereas hosts.work contains definitions to the internal network address of the servers involved. I created 2 batch files that copy over the hosts file depending on where I am. The one to switch to work has the following command:

copy /Y %windir%\system32\drivers\etc\hosts.work %windir%\system32\drivers\etc\hosts

and the one for home simply replaces hosts.home into the hosts file the same way. So the command to execute for work is the fully qualified path to the bat file. Everything else has the fully qualified path to the switch to home batch script. So now, I just have this small utility run on startup and stay in the tray, and whenever the network is changed, I am automatically configured to go with this utility.

[ Currently Playing : Quarantined - At the Drive-In - Relationship of Command (5:24) ]

This is pretty cool:

System.Net.NetworkInformation.NetworkChange.NetworkAddressChanged += 
new System.Net.NetworkInformation.NetworkAddressChangedEventHandler
(NetworkChange_NetworkAddressChanged);

I have a laptop that everyday switches between my wireless home network and my wired work domain. I was looking for a way to add scripting abilities to the interface itself (i.e. when a network connects, run some script) when I stumbled upon this jem.

So, I now have a little winforms app that sits in my tray (yet another one) and doesn't do anything but run a script (that in turn modifies my hosts file) and change its tray icon when I'm connected to different networks.

Here's what connected at home looks like: Connected At Home

It began as a quick'n'dirty POC, and I started to make it more generalized by extracting the common elements to an object model and persisting/loading. Then I stopped myself. I'll just let this one remain quick'n'dirty for now. Why? Because it works. And it saves me from having to remember to execute those batch scripts every time I come home and logon or go to work and logon.

nice.

Peter, bro, you must be inspirin' or something. I hope I didn't miss anything (it was one of those "it can't be that easy©" deals), but I'll just let the screenshot do the talking:

[ Currently Playing : Precious - Depeche Mode ]

Peter gives me some awesome feedback:

Hey man. Glad to know you're listening. I really think that PostXING could rock with a little more TLC. You're doing great.

A few comments on your comments:

1. I don't think you understand my request. Assume for a second that I know that I want to open post ID 117, I should be able to "Open 117" instead of "open, browse, wait, wait, 117, OK". I know this can be done with MWAPI 'cause I wrote one. metaWeblog.getPost takes postid, username and password, right?

2. For categories, I want to be able to select the category(s) for the post quickly and without waiting for stuff. So cache them, let me reload the cache, and enabled me to select them with the keyboard without a lot of fuss. BlogJet is pretty good on this front.

3. I understand your choice here, but I think you would be better to choose the standard keystroke used by Outlook, FrontPage, BlogJet, etc: Ctrl+M and Ctrl+Shift+M. Not the best keystrokes in the world, but they are what people are used to.

4. Make all the keystrokes configurable! :) That solves many/all of my issues., eh?

5. What I'm saying is that after using Ctrl+K (which I realize is a MSHTML command), and the focus returns back to the app, the input focus should stay where it was. I suspect you have something going on in Form_Activate that sets the focus somewhere. Try it, you'll see what I mean.

6. Ctrl+Enter should be Post & Publish. Personally I don't think Post (w/o Publish) has any value at all. If I can save locally, I don't need to post without publishing.

Thanks! Keep up the good work!

We sure like lists, eh? :) Here's mine in response again:

  1. This can be done, absolutely. (meaning I understand now ;) Maybe a command that just loads a post into the editing surface?
  2. This is something I've actually had on the plate for a long time that will probably never make it into v1 of PostXING. Since I'm working on v2, tho, it can definitely make it into that code. This will also improve the offline story (currently falls back to an empty textbox where each category gets its own line.)
  3. Sounds good, definitely will look into this one too.
  4. Wow, that sounds like a lot to do...how many keystrokes should be supported, how could I map them to logical commands, should it be per-user? Lots of questions on that one, I had never thought of doing something like this before.
  5. I actually got a tip from Dmitry (creator of BlogJet) on how to deal with this issue. Apparently, mshtml has a focus issue that needs to be explicitly handled.
  6. Consider it done.

Thanks again, Peter. Sorry it took so long for me to reply, but my CommunityServer install is not sending me emails when I get comments :(

*sigh* Winforms team, winforms team, when will you start making my life easier? As others have said (and I completely agree ) windowsforms.net is pretty useless. Well, there's some new Whidbey .NET 2.0 content, but all of the things I enjoyed when starting w/ v1 of .NET (like, erm, QuickStarts? What happened to installing those locally?) seem to be gone. To be fair, they do have a presence over on forums.microsoft.com where there are a few PM's and devs answering questions. I even found a hack fix for my dilemma via one of the posts that links to a download from Beta 2 or somewhere close.

At first I was optimistic about the DataGridView samples (overview)(Download The DataGridView samples), but I didn't see anything that did what I wanted to do: take an enum value and represent it in the grid with a combobox. So, here's how I did it.

First, I just plopped a DataGridView onto a form and gave it a BindingSource. The bindingSource gets its DataSource property set via a call into my DAL that returns a DataTable:

//CF: set to null before setting it to the current to 
//clear out previous results
this.bindingSource1.DataSource = null;

this.bindingSource1.DataSource = MyDal.GetDataTable();

Then comes the hackery. Since I have about 20 columns in this DataTable, all of interest as far as values but not all needing to be visible, directly after (like the line after) setting the datasource, I call a method to format the grid. This method looks something like this:

private void _formatGrid(){
	this.dataGridView1.Columns["ID"].Visible = false;
	this.dataGridView1.Columns.Remove("Status");//CF: this is where we want enums.
	...
	this._formatStatusColumn();
}

Then the _formatStatusColumn method is where the magick happens:

DataTable dt = new DataTable("Status");

//CF: actually how it's stored in the db.
dt.Columns.Add("Status", typeof(int)); 
dt.Columns.Add("Status_Name", typeof(string));

//CF: this way I can add or remove enum values, and
//the combo will always reflect the correct values.
foreach(int index in Enum.GetValues(typeof(Status)){
	dt.Rows.Add(index, Enum.GetName(typeof(Status), index));
}

//CF: now for the UI column
DataGridViewComboBoxColumn statusColumn = 
	new DataGridViewComboBoxColumn();

//CF: seems to control where the column is placed.
statusColumn.DisplayIndex = 3;
statusColumn.HeaderText = "Status";

statusColumn.DataPropertyName = "Status";
statusColumn.DataSource = dt;
statusColumn.DisplayMember = "Status_Name";
statusColumn.ValueMember = "Status";

this.dataGridView1.Columns.Add(statusColumn);

I was hoping that there would be some way to predefine columns so I don't have to filter and rebuild every time I get data from the database, but I couldn't find anything on it. There is no SelectedIndex member of the DataGridViewComboBoxCell, either.

I really hope this helps someone else out there: I tried for a day and a half to get this to work, and I still think it looks like a hack.

This is awesome. 

Dmitry Chestnykh, creator of BlogJet gave me a suggestion on my weblogs.asp.net blog for maintaining focus control with MSHTML (the html editing engine behind PostXING). BlogJet is a fantastic blogging application that has support for lots of different blogging platforms, not just the MetaWeblog API. It's the app that I reccomend if someone is looking for a more polished feel, and it has a lot of cool features that PostXING doesn't (like rel=nofollow support in links and Flickr integration? come on! that's cool.)

More Posts