diff --git a/libedataserverui/e-contact-store.c b/libedataserverui/e-contact-store.c
index c49d72d..a92f0a6 100644
--- a/libedataserverui/e-contact-store.c
+++ b/libedataserverui/e-contact-store.c
@@ -46,6 +46,7 @@ struct _EContactStorePrivate {
 	gint stamp;
 	EBookQuery *query;
 	GArray *contact_sources;
+	GHashTable *sort_cache; /* for e_contact_source_peek_sort_string_value */
 };
 
 static void e_contact_store_tree_model_init (GtkTreeModelIface *iface);
@@ -139,6 +140,7 @@ contact_store_finalize (GObject *object)
 	priv = E_CONTACT_STORE_GET_PRIVATE (object);
 
 	g_array_free (priv->contact_sources, TRUE);
+	g_hash_table_destroy (priv->sort_cache);
 
 	/* Chain up to parent's finalize() method. */
 	G_OBJECT_CLASS (e_contact_store_parent_class)->finalize (object);
@@ -183,6 +185,7 @@ e_contact_store_init (EContactStore *contact_store)
 	contact_store->priv = E_CONTACT_STORE_GET_PRIVATE (contact_store);
 	contact_store->priv->stamp = g_random_int ();
 	contact_store->priv->contact_sources = contact_sources;
+	contact_store->priv->sort_cache = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_free);
 }
 
 /**
@@ -553,6 +556,7 @@ view_contacts_removed (EContactStore *contact_store, const GList *uids, EBookVie
 			/* Current view */
 			contact = g_ptr_array_index (source->contacts, n);
 			g_object_unref (contact);
+			g_hash_table_remove (contact_store->priv->sort_cache, contact);
 			g_ptr_array_remove_index (source->contacts, n);
 			row_deleted (contact_store, offset + n);
 		} else {
@@ -598,6 +602,7 @@ view_contacts_changed (EContactStore *contact_store, const GList *contacts, EBoo
 		/* Update cached contact */
 		if (cached_contact != contact) {
 			g_object_unref (cached_contact);
+			g_hash_table_remove (contact_store->priv->sort_cache, cached_contact);
 			cached_contacts->pdata[n] = g_object_ref (contact);
 		}
 
@@ -642,6 +647,7 @@ view_complete (EContactStore *contact_store, EBookViewStatus status, const gchar
 		if (result < 0) {
 			/* Contact is not in new view; removed */
 			g_object_unref (old_contact);
+			g_hash_table_remove (contact_store->priv->sort_cache, old_contact);
 			g_ptr_array_remove_index (source->contacts, i);
 			row_deleted (contact_store, offset + i);
 			i--;  /* Stay in place */
@@ -748,6 +754,7 @@ clear_contact_source (EContactStore *contact_store, ContactSource *source)
 			EContact *contact = g_ptr_array_index (source->contacts, i);
 
 			g_object_unref (contact);
+			g_hash_table_remove (contact_store->priv->sort_cache, contact);
 			g_ptr_array_remove_index_fast (source->contacts, i);
 
 			gtk_tree_path_prev (path);
@@ -1249,9 +1256,51 @@ e_contact_store_get_value (GtkTreeModel *tree_model,
 
 	row = ITER_GET (iter);
 	contact = get_contact_at_row (contact_store, row);
+
+	if (column == E_CONTACT_FIELD_FIRST - 1) {
+		g_value_set_pointer (value, contact);
+		return;
+	}
+
 	if (!contact || column < E_CONTACT_FIELD_FIRST)
 		return;
 
 	field_name = e_contact_field_name (column);
 	g_object_get_property (G_OBJECT (contact), field_name, value);
 }
+
+/**
+ * e_contact_source_peek_sort_string_value:
+ * 
+ * Returns cached value for comparison on sorted #column, which
+ * is supposed to be a string field on the contact. Returned value,
+ * if not NULL, can be passed to strcmp() for comparison, as it
+ * is a product of g_utf8_collate_key() function.
+ **/
+const gchar *
+e_contact_source_peek_sort_string_value (EContactStore *contact_store, GtkTreeModel *model, GtkTreeIter *iter, gint column)
+{
+	EContact *contact = NULL;
+	const gchar *str;
+
+	g_return_val_if_fail (contact_store != NULL, NULL);
+	g_return_val_if_fail (E_IS_CONTACT_STORE (contact_store), NULL);
+	g_return_val_if_fail (iter != NULL, NULL);
+
+	gtk_tree_model_get (model, iter, E_CONTACT_FIELD_FIRST - 1, &contact, -1);
+	if (!contact)
+		return NULL;
+
+	str = g_hash_table_lookup (contact_store->priv->sort_cache, contact);
+	if (str || column < E_CONTACT_FIELD_FIRST || column >= E_CONTACT_FIELD_LAST)
+		return str;
+
+	str = e_contact_get_const (contact, column);
+	if (!str)
+		return NULL;
+
+	str = g_utf8_collate_key (str, -1);
+	g_hash_table_insert (contact_store->priv->sort_cache, contact, (gpointer) str);
+
+	return str;
+}
diff --git a/libedataserverui/e-contact-store.h b/libedataserverui/e-contact-store.h
index 513615e..2c10caa 100644
--- a/libedataserverui/e-contact-store.h
+++ b/libedataserverui/e-contact-store.h
@@ -87,6 +87,8 @@ EBookView *	find_contact_source_by_book_return_view
 						(EContactStore *contact_store,
 						 EBook *book);
 
+const gchar *   e_contact_source_peek_sort_string_value (EContactStore *contact_store, GtkTreeModel *model, GtkTreeIter *iter, gint column);
+
 G_END_DECLS
 
 #endif  /* E_CONTACT_STORE_H */
diff --git a/libedataserverui/e-name-selector-dialog.c b/libedataserverui/e-name-selector-dialog.c
index 6fb38a8..2e54118 100644
--- a/libedataserverui/e-name-selector-dialog.c
+++ b/libedataserverui/e-name-selector-dialog.c
@@ -38,6 +38,9 @@
 /* backward-compatibility cruft */
 #include "gtk-compat.h"
 
+/* always should be a string field */
+#define CONTACT_SORT_FIELD E_CONTACT_FULL_NAME
+
 #define E_NAME_SELECTOR_DIALOG_GET_PRIVATE(obj) \
 	(G_TYPE_INSTANCE_GET_PRIVATE \
 	((obj), E_TYPE_NAME_SELECTOR_DIALOG, ENameSelectorDialogPrivate))
@@ -1187,6 +1190,48 @@ transfer_button_clicked (ENameSelectorDialog *name_selector_dialog, GtkButton *t
 	g_list_free (rows);
 }
 
+static gint
+sort_contacts_cb (GtkTreeModel *model, GtkTreeIter *iter1, GtkTreeIter *iter2, gpointer user_data)
+{
+	EContactStore *contact_store = user_data;
+	const gchar *v1, *v2;
+	gchar *str1 = NULL, *str2 = NULL;
+	gint res;
+
+	g_return_val_if_fail (model != NULL, 0);
+	g_return_val_if_fail (iter1 != NULL, 0);
+	g_return_val_if_fail (iter2 != NULL, 0);
+	g_return_val_if_fail (contact_store != NULL, 0);
+	g_return_val_if_fail (E_IS_CONTACT_STORE (contact_store), 0);
+
+	v1 = e_contact_source_peek_sort_string_value (contact_store, model, iter1, CONTACT_SORT_FIELD);
+	v2 = e_contact_source_peek_sort_string_value (contact_store, model, iter2, CONTACT_SORT_FIELD);
+
+	if (v1 != NULL || v2 != NULL) {
+		if (v1 == NULL && v2 == NULL)
+			return 0;
+
+		if (v1 == NULL)
+			return -1;
+		if (v2 == NULL)
+			return 1;
+
+		return strcmp (v1, v2);
+	}
+
+	/* just a fallback which may not be ever used, unless both contacts
+	   doesn't have filled CONTACT_SORT_FIELD */
+	gtk_tree_model_get (model, iter1, CONTACT_SORT_FIELD, &str1, -1);
+	gtk_tree_model_get (model, iter2, CONTACT_SORT_FIELD, &str2, -1);
+
+	res = g_utf8_collate (str1 ? str1 : "", str2 ? str2 : "");
+
+	g_free (str1);
+	g_free (str2);
+
+	return res;
+}
+
 /* --------------------- *
  * Main model management *
  * --------------------- */
@@ -1245,7 +1290,11 @@ setup_name_selector_model (ENameSelectorDialog *name_selector_dialog)
 	/* sort on full name as we display full name in name selector dialog */
 	gtk_tree_sortable_set_sort_column_id (
 		GTK_TREE_SORTABLE (name_selector_dialog->priv->contact_sort),
-		E_CONTACT_FULL_NAME, GTK_SORT_ASCENDING);
+		CONTACT_SORT_FIELD, GTK_SORT_ASCENDING);
+
+	gtk_tree_sortable_set_sort_func (
+		GTK_TREE_SORTABLE (name_selector_dialog->priv->contact_sort),
+		CONTACT_SORT_FIELD, sort_contacts_cb, contact_store, NULL);
 
 	gtk_tree_view_set_model (
 		name_selector_dialog->priv->contact_view,
