DCSIMG
April 2007 - Posts - Just code - Tamir Khason

April 2007 - Posts

[This blog was migrated. You will not be able to comment here.
The new URL of this post is http://khason.net/blog/dependency-property-getters-and-setters-in-multithreaded-environment/]


Recently I blogged about Thread safe ObservableCollection. And what's about regular single Dependency Properties? Actually, the story is rather same and the rule is clear - Bring into your STAThread first, then do whatever you want to do. So how to do it? Simple Let's create regular window with two DPs MyTextProperty and ButtonTitleProperty. We'll bind TextBox to MyTextProperty and will change it from other thread, based on ButtonTitleProperty value.

image

So, first of all those properties

        public static readonly DependencyProperty ButtonTitleProperty = DependencyProperty.Register("ButtonTitle", typeof(ButtonStates), typeof(Window1), new UIPropertyMetadata(ButtonStates.Start));
        public static readonly DependencyProperty MyTextProperty = DependencyProperty.Register("MyText", typeof(string), typeof(Window1));

Now, the crazy thread

Random r;
        void updateText()
        {
            r = new Random();
            while (ButtonTitle == ButtonStates.Stop)
            {
                StringBuilder sb = new StringBuilder();
                for (int i = 0; i < 10; i++)
                {
                    sb.Append((char)r.Next(0x41, 0x51));
                }

                MyText = sb.ToString();

            }
        }

 

And finally the method to invoke it

void onClick(object sender, RoutedEventArgs e)
        {
            if (ButtonTitle == ButtonStates.Start)
            {
                //start
                new Thread(new ThreadStart(updateText)).Start();
                ButtonTitle = ButtonStates.Stop;
            }
            else
            { 
                //end
                ButtonTitle = ButtonStates.Start;
            }
        }

<Button Click="onClick" Content="{Binding ElementName=myWindow, Path=ButtonTitle}"/>
<TextBlock Text="{Binding ElementName=myWindow, Path=MyText}"/>

 

 

Now, try to run - the first thing will happen is exception: The calling thread cannot access this object because a different thread owns it. in ButtonTitle getter The next - the same exception in MyText setter. What to do? Bring the value into STAThread. How? Simple instead of regular getter

 get { return (ButtonStates)GetValue(ButtonTitleProperty); }

 

We'll use thread safe getter

get
            {

                try
                {
                    return (ButtonStates)this.Dispatcher.Invoke(
                       System.Windows.Threading.DispatcherPriority.Background,
                       (DispatcherOperationCallback)delegate { return GetValue(ButtonTitleProperty); },
                       ButtonTitleProperty);
                }
                catch
                {
                    
                    return (ButtonStates)ButtonTitleProperty.DefaultMetadata.DefaultValue;
                }
            }

 

And instead of regular setter

set { SetValue(MyTextProperty, value); }

 

We'll use thread safe setter

set
            {
                this.Dispatcher.BeginInvoke(DispatcherPriority.Background,
                    (SendOrPostCallback)delegate { SetValue(MyTextProperty, value); },
                    value);
            }

 

I believe it's clear why getter do synchronous and setter does not? :)

Try to experiment with DispatcherPriority enum to see how busy your system will be. Good luck

Source code for this article

[This blog was migrated. You will not be able to comment here.
The new URL of this post is http://khason.net/blog/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

[This blog was migrated. You will not be able to comment here.
The new URL of this post is http://khason.net/blog/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

UPD: What legal issues we can discuss about if Live Team released this gadget within a couple of hours after it was submitted...

UPD2: New version is available.

[This blog was migrated. You will not be able to comment here.
The new URL of this post is http://khason.net/blog/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.

[This blog was migrated. You will not be able to comment here.
The new URL of this post is http://khason.net/blog/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

[This blog was migrated. You will not be able to comment here.
The new URL of this post is http://khason.net/blog/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)

[This blog was migrated. You will not be able to comment here.
The new URL of this post is http://khason.net/blog/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

[This blog was migrated. You will not be able to comment here.
The new URL of this post is http://khason.net/blog/treeview-and-observablecollection-weird-bug/]


Recently my client points me to strange bug, if observable collection is binded to treeview and you are trying to perform Move or Replace action on the ObservableCollection, the exception "Unexpected collection change action 'Replace'" or "Unexpected collection change action 'Move'" will be thrown. This not happens with other ItemsControl e.g. ListBox. So, this is one of WPF confirmed bugs (which actually already fixed). How to build workaround? Simple, you do not have to know WPF in order to do it. Just replace internal method MoveItem with, e.g. InsertItem and RemoveItem and that'll work fine. BTW, you can not do straight assignment (replace), 'cos this will throw the same exception. So, please see proposed workaround for this problem.

    public class FixedObservableCollection<T> : ObservableCollection<T>
    {
        protected override void MoveItem(int oldIndex, int newIndex)
        {
            //base.MoveItem(oldIndex, newIndex);
            T oItem = base[oldIndex];
            base.InsertItem(oldIndex, base[newIndex]);
            base.RemoveAt(oldIndex + 1);
 
            base.InsertItem(newIndex, oItem);
            base.RemoveAt(newIndex + 1);
 
        }    
    }

UPD 21-Apr-07: Thanks to Anthony, that points me to kind of bug with this workaround. In this case, CollectionChanged event will be fired 4 time for only one Move, action. The solution is simple, override this event and suppress firing while you do not want it. But re really problem is much more complicated. The reason for this bug is not move or replace action, but the event fired. Just to test, try fire real move event from your collection within this workaround. You'll get into the same problem.

        protected override void MoveItem(int oldIndex, int newIndex)
        {
            //base.MoveItem(oldIndex, newIndex);  
            suppress = true;
            T oItem = base[oldIndex];
            base.InsertItem(oldIndex, base[newIndex]);
            base.RemoveAt(oldIndex + 1);
            base.InsertItem(newIndex, oItem);
            base.RemoveAt(newIndex + 1);
            suppress = false;
            OnCollectionChanged(new System.Collections.Specialized.NotifyCollectionChangedEventArgs(
                System.Collections.Specialized.NotifyCollectionChangedAction.Move, oItem, newIndex, oldIndex));
        }
        bool suppress = false;
        protected override void OnCollectionChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            if (!suppress)
            {
                base.OnCollectionChanged(e);
            }
        }

 

So, what's the solution? Real, override MoveItem event by suppressing event invocation and fire neutral event, such as Reset at the end

        protected override void MoveItem(int oldIndex, int newIndex)
        {
            suppress = true;
            base.MoveItem(oldIndex, newIndex);
            suppress = false;
            OnCollectionChanged(new System.Collections.Specialized.NotifyCollectionChangedEventArgs(
                System.Collections.Specialized.NotifyCollectionChangedAction.Reset));
        }

        bool suppress = false;
        protected override void OnCollectionChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            if (!suppress)
            {
                base.OnCollectionChanged(e);
            }
        }

 

That's all. Have a nice weekend

[This blog was migrated. You will not be able to comment here.
The new URL of this post is http://khason.net/blog/listboxitem-index-or-enumirating-of-listbox-items-over-filtered-data-source/]


One of my clients asked me today, how it possible to put the index of ListBox Item near the value, while data source of it might be filtered. How to get the index from inside data source all of you, I believe, knows, but how to ask out host (it might be any ItemsCountrol) about what the real index of the binded item, while some of items are hidden.

So, in order to do it, we have to be able to "ask" items control about the real position of it's items. How to do it? Simple, ItemsControl has really useful static method, named ItemsControlFromItemsContainer and the control's ItemContainerGenerator has method named IndexFromContainer. This means, that if we have an item and the item control, we can figure it's index. But first of all, how to get into Item, while using templates? I explained it earlier when spoke about Autoexplainable listbox items. You should use FindAncestor type of Relative binding to get an ancestor of the template.

 

<DataTemplate x:Key="myItem">
        <TextBlock Text="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=ListBoxItem}}"/>
</DataTemplate>

Now, let's think a bit, who can add not data relevant information to our DataTemplate? Who invoked each time the item do something with binding? The right answer is converter. Let's create it.

 

class MyConverter:IValueConverter
    {
        #region IValueConverter Members
 
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            ListBoxItem item = value as ListBoxItem;
            ListBox view = ItemsControl.ItemsControlFromItemContainer(item) as ListBox;
            int index = view.ItemContainerGenerator.IndexFromContainer(item);
 
 
            return index.ToString()+" - "+item.Content.ToString();
        }

So, we done, the only thing I want to explain here is how to filter data for those, who do not know. ICollectionView - the interface, that implements the collection has Filter property, that receives strange class named Predicate<object>. This one will do your work. So let's first create a simple strings collection and set it as data context for our application

 

List<string> numbers = new List<string>();
            for (int i = 0; i < 10; i++)
            {
                numbers.Add("Item " + i);
            }
            myPanel.DataContext = numbers;

Next let's add filter property while check box (see screenshot) is checked. Steps? Get collection and filter it :)

 

void onChecked(object sender, RoutedEventArgs e)
        {
            ICollectionView view = CollectionViewSource.GetDefaultView(myPanel.DataContext) as ListCollectionView;
 
            view.Filter = new Predicate<object>(FilterOdds);
        }

On filter let's remove all odd numbers from our collection. Like this:

 

bool FilterOdds(object item)
        {
            return int.Parse(item.ToString().Remove(0, 5)) % 2 == 1;
        }

Done, remove the filter on Unchecked event and we done

 

void onUnchecked(object sender, RoutedEventArgs e)
        {
            ICollectionView view = CollectionViewSource.GetDefaultView(myPanel.DataContext) as ListCollectionView;
 
            view.Filter = null;
        }

That's all, folks.

Source code for this article

[This blog was migrated. You will not be able to comment here.
The new URL of this post is http://khason.net/blog/now-its-official-wpfesilverlight/]


New "flash killer" is here and this time it's serious. Today at NAB conference, Microsoft announced and demonstrates it's next generation cross-platform web technology Microsoft Silverlight. Actually, it's totaly rebranded WPF/E . Microsoft Silverlight will enable content providers to deliver media experiences and rich interactive applications that incorporate media, graphics, animation, and much, much more with full application functionality on both Windows and Mac platforms and inside IE, Firefox and Safari. Silverlight users will also enjoy compatibility with the broad ecosystem of Windows Media (VC-1) enabled tools and solutions, including existing and upcoming IIS and Windows Streaming Media server technologies.

So it comes with new server side products - Expression Media Encoder, IIS7 Media Pack and broadcast system. You can learn more about this new technology here.

Also, there are some interesting comparison tables between WPF, SilverLight and Flash at MED blog as well as at off. site of this platform.

 

[This blog was migrated. You will not be able to comment here.
The new URL of this post is http://khason.net/blog/generic-grid-with-column-autodetection/]


There are a lot of questions such as "how can I generate columns in my grid, based on my XML data?", how to implement generic sort algorithm?" etc. In this post I'll try to explain how to use ListView with GridView, how to sort your data presentation, without sorting data source and lost binding. How to parse generic Excel or CSV data and put it into grid. 

So, first thing, you should do is to create generic ListView with Grid as view. We'll put it into UserControl in order us to be able to use it externally. This part is really simple.

 

<ListView Name="myListView" >
        <ListView.View>
          <GridView x:Name="myGridView"/>
        </ListView.View>
</ListView>

Now, we'll create dependency property for our data source. We'll have to handle the assignment of data source in order to great columns dynamically within the GridView

 

public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register("ItemsSource", typeof(IEnumerable), typeof(MyGrid),
            new PropertyMetadata(null, new PropertyChangedCallback(OnItemsSourceChanged)));
 
        static void OnItemsSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (e.NewValue == null)
                return;
 
            MyGrid grid = d as MyGrid;
 
            grid.myListView.SetValue(ItemsControl.ItemsSourceProperty, e.NewValue);
 
            ReadOnlyObservableCollection<XmlNode> col = e.NewValue as ReadOnlyObservableCollection<XmlNode>;
            if (col != null)
            {
                grid.myGridView.Columns.Clear();
                if (col.Count > 0)
                {
                    foreach (XmlNode node in col[0].ChildNodes)
                    {
                        bindGridViewColumn(node, grid.myGridView);
                    }
                    foreach (XmlAttribute attr in col[0].Attributes)
                    {
                        bindGridViewColumn(attr, grid.myGridView);
                    }
                }
                else
                {
                    throw new NotSupportedException("No description row found in data provided. You should have at least one row to populate control columns");
                }
            }
            else
            {
                throw new NotImplementedException("This patch is working only for XmlSource by now");
            }
 
        }

So, what are we doing here? First of all, if we have null or unsupported data as datasource we'll do nothing. Then, if the data is valid, we'll clear all old columns and create new set, based on data passed. We'll look into all child nodes of DocumentRoot in order to build markup. The next step is to bind each column data of our data source to the column and then bind all data to datasource of the grid. We'll do it either for elements and attributes of our XML. Here is comes

 

static internal void bindGridViewColumn(XmlNode node, GridView view)
        {
            bindGridViewColumn(node.Name, view);
        }
static internal void bindGridViewColumn(XmlAttribute attr, GridView view)
        {
            bindGridViewColumn("@"+attr.Name, view);
        }
static void bindGridViewColumn(string XPath, GridView view)
        {
            GridViewColumn gvc = new GridViewColumn();
            gvc.Header = XPath[0]=='@'?XPath.Substring(1):XPath;
            Binding b = new Binding();
            b.XPath = XPath;
            gvc.DisplayMemberBinding = b;
            view.Columns.Add(gvc);
        }

Simple setter and getter for DP and we done.

 

public IEnumerable ItemsSource
        {
            get
            {
                GetValue(ItemsSourceProperty);
                return (IEnumerable)myGridView.GetValue(ItemsControl.ItemsSourceProperty);
            }
            set
            {
                SetValue(ItemsSourceProperty, value);
                myGridView.SetValue(ItemsControl.ItemsSourceProperty, value);
            }
        }

Now, let's take care on data sorting. We'll handle click on grig view column header as trigger for sort so, GridViewColumnHeader.Click="onSort". Now, let's handle it. The code is rather straight forward, so I'll now going to explain it

 

GridViewColumnHeader m_lastHeaderClicked = null;
        ListSortDirection m_lastDirection = ListSortDirection.Ascending;
 
        void onSort(object sender, RoutedEventArgs e)
        {
            GridViewColumnHeader headerClicked = e.OriginalSource as GridViewColumnHeader;
            ListSortDirection direction;
 
            if (headerClicked != null)
            {
                if (headerClicked.Role != GridViewColumnHeaderRole.Padding)
                {
                    if (headerClicked != m_lastHeaderClicked)
                    {
                        direction = ListSortDirection.Ascending;
                    }
                    else
                    {
                        if (m_lastDirection == ListSortDirection.Ascending)
                        {
                            direction = ListSortDirection.Descending;
                        }
                        else
                        {
                            direction = ListSortDirection.Ascending;
                        }
                    }
 
                    string header = headerClicked.Column.Header as string;
                    Sort(header, direction);
 
                    if (direction == ListSortDirection.Ascending)
                    {
                        headerClicked.Column.HeaderTemplate =
                          Resources["HeaderTemplateArrowUp"] as DataTemplate;
                    }
                    else
                    {
                        headerClicked.Column.HeaderTemplate =
                          Resources["HeaderTemplateArrowDown"] as DataTemplate;
                    }
 
                    m_lastHeaderClicked = headerClicked;
                    m_lastDirection = direction;
                }
            }
        }
 
void Sort(string sortBy, ListSortDirection direction)
        {
            ICollectionView dataView = CollectionViewSource.GetDefaultView(myListView.ItemsSource);
 
            dataView.SortDescriptions.Clear();
            SortDescription sd = new SortDescription(sortBy, direction);
            dataView.SortDescriptions.Add(sd);
 
            dataView.Refresh();
        }

That's it, we done. The next step is to create control to handle drag and drop of Excel data and convert it into XML data source for our smart grid view. We'll inherit from Canvas control and implement INotifyPropertyChanged in order to notify about changes in datasource and providing an ability to bind data between controls. So, first of all, let's create a couple of DPs to provide interface for dropped object and dropped data.

 

    
    class ExcelDataReader:Canvas,INotifyPropertyChanged
    {
        public ExcelDataReader():base()
        {
            this.AllowDrop = true;
        }
 
        
public static DependencyPropertyKey DroppedObjectPropertyKey = 
            DependencyProperty.RegisterReadOnly("DroppedObject", 
            typeof(ReadOnlyObservableCollection<XmlNode>), 
            typeof(ExcelDataReader),
            new PropertyMetadata(null));
 
 
        public static readonly DependencyProperty DroppedObjectProperty = DroppedObjectPropertyKey.DependencyProperty;
 
        public ReadOnlyObservableCollection<XmlNode> DroppedObject
        {
            get { return (ReadOnlyObservableCollection<XmlNode>)GetValue(DroppedObjectProperty); }
        }
 
        
public static DependencyPropertyKey DroppedDocumentPropertyKey =
    DependencyProperty.RegisterReadOnly("DroppedDocument",
    typeof(XmlDocument),
    typeof(ExcelDataReader),
    new PropertyMetadata(null));
 
        public static readonly DependencyProperty DroppedDocumentProperty = DroppedDocumentPropertyKey.DependencyProperty;
 
        public XmlDocument DroppedDocument
        {
            get { return (XmlDocument)GetValue(DroppedDocumentProperty); }
        }

The next step is to handle preview of drop event. Why preview? In order to leave future developers to add custom logic to drop event of this control

 

protected override void OnPreviewDrop(System.Windows.DragEventArgs e)
        {  
 
            if (e.Data.GetDataPresent(DataFormats.CommaSeparatedValue))
            {
                List<string> strs = new List<string>();
                using (StreamReader sr = new StreamReader((Stream)e.Data.GetData(DataFormats.CommaSeparatedValue)))
                {
                    while (sr.Peek() > 0)
                    {
                        strs.Add(sr.ReadLine());
                    }
                }
 
                SetDroppedObject(strs);
                base.OnDrop(e);
 
            }
            else
            {
                e.Effects = DragDropEffects.None;
            }
 
          base.OnPreviewDrop(e);
 
 
        }

Now the logic. Once I got something I can handle dropped, I'll convert it into well known XML representation in order to be able to provide it as data source for our previous control.

 

        private void SetDroppedObject( List<string> data)
        {
 
            ObservableCollection<XmlNode> nodes = new ObservableCollection<XmlNode>();
            XmlDocument doc = new XmlDocument();
            doc.LoadXml("<root></root>");
            bool fData = MessageBox.Show("Treat first row as title?", "Data dropped", MessageBoxButton.YesNo, MessageBoxImage.Question) == MessageBoxResult.Yes;
            if (fData && data.Count < 1)
            {
                throw new NotSupportedException("No description row found in data provided. You should have at least one row to populate control columns");
            }
            int startRow = fData ? 1 : 0;
 
            string[] titles = data[0].Split(',');
 
            for (int i = startRow; i < data.Count; i++)
            {
                XmlElement elem = doc.CreateElement("node");
                string[] items = data[i].Split(',');
 
                for (int j = 0; j < items.Length; j++)
                {
                    string title = fData ? titles[j].Trim() : "data" + j.ToString();
                    XmlElement el = doc.CreateElement(title);
                    XmlText txt = doc.CreateTextNode(items[j]);
                    el.AppendChild(txt);
                    elem.AppendChild(el);
                }
                doc.DocumentElement.AppendChild(elem);
                nodes.Add(elem);
            }
 
            this.SetValue(DroppedDocumentPropertyKey, doc);
            this.SetValue(DroppedObjectPropertyKey, new ReadOnlyObservableCollection<XmlNode>(nodes));
 
            if (PropertyChanged != null)
            { 
                PropertyChanged(this,new PropertyChangedEventArgs("DroppedDocument"));
                PropertyChanged(this, new PropertyChangedEventArgs("DroppedObject"));
            }
        }

Implement INotifyPropertyChanged interface, a little logic to prevent dropping unsupported data and we done. Now, in my application I can do the following

 

<Window.Resources>
    <XmlDataProvider x:Key="Test" Source="XMLFile1.xml" XPath="/root/node"/>
 
 </Window.Resources>
  <StackPanel>
    <local:ExcelDataReader Width="100" Height="100" Background="Yellow" x:Name="excelData"/>
    <local:MyGrid x:Name="myGrid"  Background="Blue" ItemsSource="{Binding Source={StaticResource Test}}"/>
  </StackPanel>

We done. Open an application, create some table in Excel, select in and drop into yellow rectangle. The information will be parsed and all grid columns will be created automatically. Isn't is really cool?

Source code for this article

[This blog was migrated. You will not be able to comment here.
The new URL of this post is http://khason.net/blog/converter-and-validator-in-one-place/]


Today, Doug asked about strange behavior with Binding Validation, while using custom converters. Follow XAML and C# code for his question:

 

<StackPanel>
    <StackPanel.DataContext>
      <x:Array Type="s:Int32" xmlns:s="clr-namespace:System;assembly=mscorlib">
        <s:Int32>0</s:Int32>
        <s:Int32>1</s:Int32>
      </x:Array>
    </StackPanel.DataContext>
    <TextBox Margin="3">
      <TextBox.Text>
        <Binding Path="[0]">
          <Binding.ValidationRules>
            <ExceptionValidationRule />
          </Binding.ValidationRules>
        </Binding>
      </TextBox.Text>
    </TextBox>
    <TextBox Margin="3">
      <TextBox.Text>
        <Binding Path="[1]">
          <Binding.Converter>
            <local:MyInt32Converter xmlns:local="clr-namespace:myNamespace" />
          </Binding.Converter>
          <Binding.ValidationRules>
            <ExceptionValidationRule />
          </Binding.ValidationRules>
        </Binding>
      </TextBox.Text>
    </TextBox>
  </StackPanel>
 
 
public class MyInt32Converter : IValueConverter
    {
        public object Convert(object value, Type targetType,
            object parameter, System.Globalization.CultureInfo culture)
        {
            return value.ToString();
        }
 
        public object ConvertBack(object value, Type targetType,
            object parameter, System.Globalization.CultureInfo culture)
        {
            return Int32.Parse(value.ToString());
        }
 
    }

Compile and run it. Then change value to something bad (e.g. "aaa") in first textbox - the validator will be fired and we'll get red border in our textbox. Then change the value of the second textbox - you'll get exception. Why this?

The answer is really simple - the sequence of binding is straight forward - get,convert,validate result. So, how to get this work with custom converters? Just convert it to validator. Following code in this converter will check and validate input value first, then, if the input is valid, convert and return results.

 

    [ValueConversion(typeof(String), typeof(Int32))]
    public class MyInt32Converter : ValidationRule, IValueConverter
    {
        public object Convert(object value, Type targetType,
            object parameter, System.Globalization.CultureInfo culture)
        {
            return value.ToString();
        }
 
        public object ConvertBack(object value, Type targetType,
            object parameter, System.Globalization.CultureInfo culture)
        {
            if (this.Validate(value, culture) == ValidationResult.ValidResult)
            {
                return Int32.Parse(value.ToString());
            }
            return value;
        }
 
 
 
        public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
        {
            int i=0;
            return new ValidationResult(Int32.TryParse(value.ToString(),out i),"The string is in incorrect format");
        }
    }

Have a nice day.

[This blog was migrated. You will not be able to comment here.
The new URL of this post is http://khason.net/blog/kb933493-does-not-come-with-office-update/]


For some reason the new patch for Outlook 2007 does not come with Windows Update as well as I was not notifies about the availability of this, in spite of the fact, that reported it. I run into the issue, and now it's fixed. My Outlook start to work much faster after it. Download (Validation required)

[This blog was migrated. You will not be able to comment here.
The new URL of this post is http://khason.net/blog/check-whether-executable-is-managed/]


So, another interesting question: "How to check whether the executable file managed or not by code". Answering - ask framework about it version.

        [DllImport("mscoree.dll")]
        private static extern int GetFileVersion(
                        [MarshalAs(UnmanagedType.LPWStr)] string szFilename,
                        [MarshalAs(UnmanagedType.LPWStr)] StringBuilder szBuffer,
                        int cchBuffer,
                        out int dwLength);

But any, even very unmanaged file has file version - you ask. Any managed file has managed signature, so if the file is not managed, the method return COR_E_ASSEMBLYEXPECTED, else it return main core framework version.

 

        private string GetRuntimeVersion(string filename)
        {
            const int CCH = 0x400;
            StringBuilder sb = new StringBuilder(CCH);
            int len;
            int hr = GetFileVersion(filename, sb, sb.Capacity, out len);
 
            if (hr == unchecked((int)0x8007000B))
                return string.Empty; 
            else if (hr < 0)
                throw new Win32Exception(hr);
            else
                return sb.ToString(0, len - 1);
        }

Pretty simple, isn't is?

Source code for this article

[This blog was migrated. You will not be able to comment here.
The new URL of this post is http://khason.net/blog/transparent-wpf-control-over-unmanaged-window-handle/]


All those, how even once tried to put opaque or transparent WPF control over Windows Forms control or, even unmanaged window found themselves within "Airspace problem". So, what to do? How can we use transparent WPF UI over our legacy windows? This how.

See the image above. As you can see, there is tarsparent XAML page (corners, plus and text) over legacy game window. How to do it? We'll put our transparent WPF window over Windows Forms window. First of all, we have to find the handle of our WPF application. We have only one for whole application, so let's get it

 

protected override void OnSourceInitialized(EventArgs e)
        {
            HwndSource hwndSource = PresentationSource.FromVisual(this) as HwndSource;
 
            if (hwndSource != null)
            {
                handle = hwndSource.Handle;
                WindowFinder.Start(title, handle);
                WindowFinder.OnWindowFound += new WindowFinder.OnWindowFoundHandler(WindowFinder_OnWindowFound);                
            }
        }

So far, so good. Now we have to find the handle of our legacy window. In order to do it, we have to put ourselves into user32.dll and dig FindWindow method from there.

 

[DllImport("user32.dll", SetLastError = true)]
        static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

Now let's enter endless loop for finding our handle

 

IntPtr hWnd = FindWindow(null, (string)e.Argument);
            while (!w.CancellationPending && hWnd == m_ptr)
            {
                System.Threading.Thread.Sleep(100);
                hWnd = FindWindow(null, (string)e.Argument);
            }

Once got it by name, we have to measure it and then resize our WPF window to fit the new size and position. To do it, we'll use another method GetWindowRect

 

[DllImport("user32.dll")]
        static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);
 
RECT r = new RECT();
            while (!w.CancellationPending && ((r.Left == r.Right) & (r.Bottom == r.Top)))
            {
                System.Threading.Thread.Sleep(100);
                GetWindowRect(m_ptr, out r);
            }
 
 
            m_rect = new Rect(r.Left,r.Top,r.Right-r.Left,r.Bottom-r.Top);

Got it and it's different then 0. Now let's resize the WPF window and put it over the legacy one and run from the beginning to track any change. Sure, the better way is to build global hook to legacy window messages and adjust properties of our WPF window. Do it. I do not really need it.

 

void WindowFinder_OnWindowFound(IntPtr handle, Rect position)
        {
            this.Left = position.Left;
            this.Top = position.Top;
            this.Width = position.Width;
            this.Height = position.Height;
            WindowFinder.Start(title, handle);
        }

We finished. Now we'll always have WPF window over legacy window, or control, or anything, that have name or handle. Even Windows Media Player window

A couple of things in the code attached not covered by article:

  • Half and MultipleNumber converters - those converters will help up to bind corners of WPF window into black Polylines, that you can see in the image. Those are really simple

    class HalfNumberConverter:IValueConverter
        {
            #region IValueConverter Members
     
            public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
            {
                double half = 0;
                if (double.TryParse(value.ToString(), out half))
                    half /= 2;
                double offset = 0;
                if(double.TryParse(parameter.ToString(), out offset))
                    half += offset;
     
                return half;
            }
    class MultipleNumberConverter:IValueConverter
        {
            #region IValueConverter Members
     
            public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
            {
                double val = 0;
                if (double.TryParse(value.ToString(), out val))
                {
                    char op = parameter.ToString()[0];
                    if (op == '*')
                        val *= double.Parse(parameter.ToString().Remove(0, 1));
                    else if (op == '/')
                        val /= double.Parse(parameter.ToString().Remove(0, 1));
                    if (op == '+')
                        val += double.Parse(parameter.ToString().Remove(0, 1));
                    else if (op == '-')
                        val -= double.Parse(parameter.ToString().Remove(0, 1));
                    else
                        val += double.Parse(parameter.ToString());
                }
                return val;
            }

     
  • StrokeTextBlock - this is hi-contrast textblock, that visible on any background. I wrote about how to build it earlier. Very useful control and any, especially this application
  • In case that you do not know - in order to move borderless transparent window you have to overwrite any OnMouse.... event and DragMove() the control there. The example of usage is OnMouseLeftButtonDown one.

    protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
            {
                base.OnMouseLeftButtonDown(e);
                DragMove();
     
            }
  • Do you remember, that there is no hittest within transparent window, so you can use underlying window through our WPF form
  • To do you work, use BackgroundWorker - this save you actions related to thread context stuff.

I think, that most of things were covered by this, so see the source in attachment and when using it, don't forget to remember me.

Source code for this article

More Posts Next page »