Tuesday, May 01, 2007

End-to-end Expression Studio RTM

Finally, after the dry run of MIX '07 (...and I'm not there :( ), Expression Studio (Expression Blend, Expression Web, Expression Designer and Expression Media) is shipping with RTM tag. The new version fully supports Silverlight. But the most exciting is that Blend 2 May Preview, that you can download starting today works tightly with this new technology

Saturday, April 28, 2007

Snipping tool integration plugin for WLW

 Playing a lot with Live Writer API, I found a lot of nice features and possibilities, as well as not-so-nice problems, but one thing is really clear - writing plugins for Windows Live Writer is fun and quick work. Let's take an example of Yossi's request. It took me about a half an hour to write it, or today's plugin for snipping tool integration - 25 minutes, including installer. Don't is really nice?

OK, so, you can download it - this plugin integrates Windows Snipping Tool (Windows Vista and Windows XP tablet edition) into Live writer. All you have to do is click "Insert snip capture" from right or upper menu and the application invokes Snipping Tool. After the screenshot will be taken, the image will be inserted automatically into your current post. Then, you can manage is as regular image inside the Writer (and that's really great feature). So easy, so fast, so useful. Download and use it.

snipbig

Friday, April 27, 2007

Is "web scrapping" legal?

Most of those fun webparts widgets gadgets use technology named "web scrapping" (an ability to "wrap off" content from web sites and display it within own layout). But not all of those gadgets provided by real content owners, most of them were developed by someone else. And almost no one of those developers has legal permissions for using and distributing content this way. So I have a question: "Is it legal?"

From one hand, such gadgets, as well as RSS feeds provide outstanding push for content distribution, so owners really love and support developers to use their content. From the other hand, if everything is accessible alternative way, why to visit owner's web site, so users will no see and click valuable ads there. So, is gadget - a good thing for content providers?

As you, probably, know, I was the first person is Israel, who fully implement EIP. It was about 8 years ago. Days, when Shay Agassi tried to sell me his TopTier, days when YNet asked for big bucks for their news content to be integrated into intranet portal (now they have free RSS)

Maybe, I'm bit outdated, but when 've published Israel Traffic Information gadget, I asked for fax permission of IBA to be sure about the legality of my stuff.

Today, I wrote another gadget, that display gas price information all over the US by using information provided by OPIS. I asked MSN legal guys about this and they did not understand what I'm talking about. "If it's public domain, use it, but be sure to provide link to the owner of the information" - they responded.

Am I odd fish? Maybe, but I'm still waiting for MS partner legality team to send me written authorization for using their partner information. When I'll get it, I'll sleep better with work I done.

gaspricesbig

Thursday, April 26, 2007

RTL and LTR in Windows Live Writer

Someone is crying for a couple of days about text direction support in WLW. I decided to help all those, who have to wait for MS to understand, that there are right-to-left languages in the market, that even with really good HTML/RichText control we should support it. So, I decided to write simple plugin for Live Writer and change the direction of input to RTL or LTR, send Change Input Language signal to the workspace and add two magic tags, Yossi is very worry about dir="rtl" and align="right". So, start blogging easily, Hebrew men :)

image

P.S. Due to bug with v1.0.1(6) [the official release], after clicking "Insert RTL Block" or "Insert LTR block", you should click the workspace to get back and write. In next build, this annoying bug will be fixed.

Download Text Direction Change Plugin for Windows Live Writer.

Sunday, April 22, 2007

Thread safe observable collection

If you ever wrote something more, then standard UI input-output, you sure got into wide range of exceptions like "The calling thread cannot access this object because a different thread owns it" or "This type of CollectionView does not support changes to its SourceCollection from a thread different from the Dispatcher thread". What the problem? The problem is old like condom in my wallet world-old, the try to get into resources from different threads. Let me show you some code. Very-very bad code. It's unbelievable, that this code can ever work.

void onAdd(object sender, RoutedEventArgs e)
{
new Thread(new ParameterizedThreadStart(addProc)).Start(Resources["source"] as MyCollection);
}
void onRem(object sender, RoutedEventArgs e)
{
new Thread(new ParameterizedThreadStart(remProc)).Start(Resources["source"] as MyCollection);
}
void onMov(object sender, RoutedEventArgs e)
{
new Thread(new ParameterizedThreadStart(movProc)).Start(Resources["source"] as MyCollection);
}

<Window.Resources>
<
l:MyCollection x:Key="source"/>
</
Window.Resources>
<
StackPanel DataContext="{StaticResource source}">
<
Button Click="onAdd">Add</Button>
<
Button Click="onMov">Move</Button>
<
Button Click="onRem">Remove</Button>
<ListBox ItemsSource="{Binding}"/>
</
StackPanel>

 


Does not it looks scare? Such code might open unbelievable number of thread. Nothing will stay for it. But, wait. Let's try to imagine, that we have a bunch of system methods, such as RAM queries, or kind of Amazon web service with million of transactions per second. Even very smart programmer will work with at least 10 threads to update the same collection. So, what to do?


Let's write Thread Safe Observable Collection, that knows, that it might be called from big number of threads and take care on itself. Sounds good? Let's start.


First of all we'll try to get current STAThread (UI) dispatcher. How to do it? Simple. The only place we'll initialize our collection is in XAML, so default constructor will be called from the thread we need. Now, we save it


Dispatcher _dispatcher;
public ThreadSafeObservableCollection()
{
_dispatcher =
Dispatcher.CurrentDispatcher;
}

 


The next step is to override all vital methods of our collection to be invoked from this thread.


        protected override void ClearItems()
{
if (_dispatcher.CheckAccess())
{
base.ClearItems();
            }
else
{
_dispatcher.Invoke(
DispatcherPriority.DataBind, (SendOrPostCallback)delegate { Clear(); });
}
}

 


 Let's understand it. First we're checking if the current thread can access the instance of the object, if it can - we'll just do our work, but if not, we'll invoke it in the context of the current thread by sending anonymous delegate with or without parameters.


Why I'm casting the anonymous delegate into SendOrPostCallback? The answer is simple - look into Reflector. You can not implement your delegate with parameter and without return value better :)


The next step is to take care on locking. We can use old Lock(object) method, but why to do it, if I can continue read my information, while someone writing it. Let's use ReaderWriterLock. This class makes us able to write and read information concurrently, but still preventing us from writing things at the same simple. Due to the fact, that we are using this lock instance inside at one place we can use UpgradeToWriterLock and DowngradeFromWriterLock instead of real locking. Let's see how to do it


        protected override void InsertItem(int index, T item)
{
if (_dispatcher.CheckAccess())
{
if (index > this.Count)
return;
LockCookie c = _lock.UpgradeToWriterLock(-1);
base.InsertItem(index, item);
_lock.DowngradeFromWriterLock(
ref c);
}
else
{
object[] e = new object[] { index, item };
_dispatcher.Invoke(
DispatcherPriority.DataBind, (SendOrPostCallback)delegate { InsertItemImpl(e); }, e);
}
}
void InsertItemImpl(object[] e)
{
if (_dispatcher.CheckAccess())
{
InsertItem((
int)e[0], (T)e[1]);
}
else
{
_dispatcher.Invoke(
DispatcherPriority.DataBind, (SendOrPostCallback)delegate { InsertItemImpl(e); });
}
}

 


Sure, you have to initialize _lock = new ReaderWriterLock() in the constructor. Pay attention, that I'm using the same delegate even with two parameters. It still much better and faster, that write your own delegates for each type of action. Let the framework to do work for you.


After overwriting all vital methods for ObservableCollection, we're actually, finished. Now, you can derive from your new class and perform the code at the beginning on the article very fast. The smart thread safe observable collection takes care on all the least.


Download and run following example, after playing a bit, check "Fast" checkbox and start pressing buttons as wide spirit. The application will work as requested.


Source code for this article

Friday, April 20, 2007

New source copy helper for Windows Live Writer

Are you already tiered of BlogMyCode or CopyAsHTML popup windows and non-standard right-mouse-click-do-something-then-maybe-I-help-you dialogs? Let me introduce my last plugin for Windows Live Writer blog client - Code Past Plugin. It's really simple as it should be. This plugin uses standard copy functions from anywhere (no only from VS2005). Then it converts code into XHTML and put it into WLW window. Don't this should work this way? Without right clicks and unnecessary windows? Download it now and use it. Here the sample format of code. You can optionally add header or line numbering. You can even break lines (if you wish). The other feature I added is new clipboard detection, so if you has something really wrong there and already opened code adding dialog, you don't have to close it and copy something else, just switch to Studio, copy new code and the plugin will detect all your changes.

Code Snippet
        private void btOK_Click(object sender, EventArgs e)
{
this.Close();
}

string getOptionText()
{
if (actCode != string.Empty)
{
StringBuilder sb = new StringBuilder();
if (cbLinesNum.Checked)
{
string[] outp = actCode.Replace(@"\r\n", "\r").Split('\r');

int offset = 0;
for (int i = 0; i < outp.Length; i++)
{
if (outp[i].Trim() == string.Empty)
{
sb.Append(
"<br/>");
offset++;
}

 


Here it's configuration window with preview.


image


If you catch bugs or have feedbacks, please notice me via email or in comment to this post. I'll appreciate you :)


Download Code Past Plugin (372 KB, MSI installer)

Thursday, April 19, 2007

How to read GPS metadata from image

Someone asks about how to get GPS (and other) metadata from Image properties. It's easy once we have Windows Imaging Component, which now the integrate part of .NET framework 3.0, so you can get meta information directly from your image. How to do it? First of all, you should get BitmapMetadata out from your source. In order to do it you should use BitmapDecoder, there are some of them, one for each image type (Jpg, Bmp, Png, Gif etc). So let's take Jpeg for now.

            using (Stream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read))
            {
                JpegBitmapDecoder decoder = new JpegBitmapDecoder(stream, BitmapCreateOptions.None, BitmapCacheOption.None);
                BitmapMetadata meta = (BitmapMetadata)decoder.Frames[0].Metadata;
                ulong[] latitude = meta.GetQuery("/app1/ifd/gps/subifd:{ulong=2}") as ulong[];
                ulong[] longitude = meta.GetQuery("/app1/ifd/gps/subifd:{ulong=4}") as ulong[];
            }

Next step is to convert weird ulong array into readable coordinates. Simple calculation in static method for each array and you'r done. I will not enter the math calculation and explain them. Just use it "as-is". They are rather precise, trust me :)



static double ConvertCoordinate(ulong[] coordinates)
        {
            int lDash = (int)(coordinates[0] - ((ulong)0x100000000));
            int lF = (int)(coordinates[1] - ((ulong)0x100000000));
            double lR = ((double)(coordinates[2] - ((ulong)0x6400000000))) / 100;
            double tRes = (lDash + (((double)lF) / 60)) + (lR / 3600);
            return (Math.Floor((double)(tRes * 1000000)) / 1000000);
        }

We finished, you'r got your coordinates in decimal variation, e.g. 26.34433:37.332344. Have a nice day