Thursday, June 5, 2008

Correct use ItemsSource in WPF for controls inherited from ItemsControl

Use NET.ReflectorI sometimes read MSDN forums of WPF. I have founded post with interesting problem. I was interested this trouble.

We can download sample from there. Pay attention that ListView have strange behavior: pressing "s" moves the focus to the next item. I was interested this problem and little investigate it. This action relations with search functionality. If we don't need search functionality we can easy fix it, override ToString() method:

public override string ToString()
{
return string.Empty;
}

FYI:
If you need more details why does it do, you can read next:

After you type letter 's', next methods were called:
...................
- void OnTextInput( TextCompositionEventArgs e ) of ItemsControl;
- bool DoSearch(string nextChar) of TextSearch;
- int FindMatchingPrefix(ItemsControl itemsControl, string primaryTextPath, string prefix, string newChar, int startItemIndex, bool lookForFallbackMatchToo, ref bool wasNewCharUsed) of TextSearch;

- string GetPrimaryText(object item, BindingExpression primaryTextBinding, DependencyObject primaryTextBindingHome) of TextSearch
private static string GetPrimaryText(object item, BindingExpression primaryTextBinding, DependencyObject primaryTextBindingHome)
{
//item is EmployeeInfo (!!)
DependencyObject obj2 = item as DependencyObject;

if (obj2 != null) // false
{
string str = (string) obj2.GetValue(TextProperty);
if (!string.IsNullOrEmpty(str))
{
return str;
}
}
if ((primaryTextBinding != null) && (primaryTextBindingHome != null)) //false
{
primaryTextBinding.Activate(item);
return ConvertToPlainText(primaryTextBinding.Value);
}
return ConvertToPlainText(item); //It was called
}

-string ConvertToPlainText(object o) of TextSearch
private static string ConvertToPlainText(object o)
{
FrameworkElement element = o as FrameworkElement;
if (element != null) //false
{
string plainText = element.GetPlainText();
if (plainText != null)
{
return plainText;
}
}
if (o == null) // false
{
return string.Empty;
}
return o.ToString(); //return "SDKSample.EmployeeInfo"
}


- In this FindMatchingPrefix method we prepare this string as follows
........................
string str2 = GetPrimaryText(obj3, bindingExpr, itemsControl);
if ((str2 != null) && str2.StartsWith(str, true, culture)) //"SDKSample.EmployeeInfo" is started with "s" ;) (!!!!!)
{
wasNewCharUsed = true;
num = num4;
break;
}
........................
return num;


- and in the DoSearch(string nextChar) method
........................
int itemIndex = FindMatchingPrefix(this._attachedTo, primaryTextPath, this.Prefix, nextChar, startItemIndex, lookForFallbackMatchToo, ref wasNewCharUsed);

if (itemIndex != -1)
{
if (!this.IsActive || (itemIndex != startItemIndex))
{
object item = items[itemIndex];

this._attachedTo.NavigateToItem(item, itemIndex, new ItemsControl.ItemNavigateArgs(Keyboard.PrimaryDevice, ModifierKeys.None)); //wrong code is started
this.MatchedItemIndex = itemIndex;
}

........................

Nothing is perfect..