Silverlight: небольшой, но неприятный баг с сортировкой в DataGrid
- modified:
- reading: 3 minutes
Неделю назад напоролся на баг в Silverlight, точнее в DataGrid контроле, а он пришел, вроде, с Toolkit. Потребовалось больше часа, чтобы понять в чем проблема.
Мы в приложении для части администрирования используем свои контролы, которые могут по схемы таблицы базы данных построить контролы списка объектов и форму для редактирования объектов. Более того, даже строятся join’ы. Сделано это для того, чтобы быстро в административную часть нашего решения быстро добавлять настройки только поменяв метаданные на интерфейсе, не трогая сервисы, и любые другие слои нашего приложения. Пользователям мы эти интерфейсы не показываем, а работаем чаще всего с ними только мы-разработчики и немного наши консультанты и менеджеры, так что внешний вид там не важен.
Все данные объекта содержатся в словаре, и если описать по-простому структуру наших объектов, с которыми мы работаем в административной части, то она, примерно, следующая:
public class Entity
{ private readonly Dictionary<string, object> _properties = new Dictionary<string, object>();
public Entity(Dictionary<string, object> values)
{ _properties = values;
}
public string this[string column]
{ get { return (_properties[column] ?? string.Empty).ToString(); }
}
}
Это уж очень примерно. Понятно, что у нас там есть куча валидации, методов работы с метаданными, подстановка дефолтных значений для вновь созданных объектов и т.п. Но принцип такой, есть индексируемое свойство по строке, к которому осуществляется байдинг.
Обрисую тестовую ViewModel:
public class TestViewModel
{ public TestViewModel()
{ Collection = new ObservableCollection<Entity>();
for (int i = 0; i < 5; i++)
{ Collection.Add(new Entity(new Dictionary<string, object>()
{ {"Person.Name", "User " + i},
{"Person.PersonID", i }
}));
}
}
public ObservableCollection<Entity> Collection { get; set; }
}
Тут все просто, коллекция с 5-ю элементами. Ключи для коллекции у меня называют так, потому что они строятся из имен таблицы и имени колонки.
Дальше накидаем простую форму с одним элементом DataGrid:
<UserControl.DataContext>
<SilverlightApplication1:TestViewModel />
</UserControl.DataContext>
<Controls:DataGrid x:Name="LayoutRoot" ItemsSource="{Binding Collection}" AutoGenerateColumns="False">
<Controls:DataGrid.Columns>
<Controls:DataGridTextColumn Binding="{Binding [Person.PersonID]}" CanUserSort="True" Header="ID" />
<Controls:DataGridTextColumn Binding="{Binding [Person.Name]}" CanUserSort="True" Header="Name" />
</Controls:DataGrid.Columns>
</Controls:DataGrid>
На этапе создания контрола, который работал с нашим динамическим объектом я не добавил поддержку сортировки. Просто объектов было не очень много, да и не нужно было, забыл. Недавно менеджеры попросили добавить сортировку. Я прикинул, что в простом случае, когда все элементы подгружаются за раз, сделать сортировку очень просто, нужно всего добавить CanUserSort к колонкам и все. Задачу оценили в час (вместе с тестированием и развертыванием).
Добавил, чтобы при создании колонок выставлялось true в свойство CanUserSort, а не работает сортировка. И тут началось, я начал пересматривать весь код, пытаясь осознать в чем же проблема, как ее можно побороть. В Output, вообще, ничего не выводилось, что может какой-то байдинг не верно установлен или еще чего.
Попробовал создать новый проект, в котором все было, примерно, так как показал выше. И все работало! Тут до меня дошло в чем может быть проблема. В ново созданном проекте я использовал в качестве имен колонок простые “PersonID”, “Name”, вместо “Person.PersonID”, “Person.Name”. Видимо ребята, кто писал DataGrid сначала пытаются создать путь, по которому нужно осуществлять поиск объекта, а только потом задумываются о том, что поля могут быть еще и с индексами. Попытался посмотреть в исходниках, но кода у DataGrid контрола слишком много. Свою проблему решил заменой для байдинга точек на символ ‘@’, ну и в свойстве-индексе меняю обратно этот символ на точки, чтобы получить верное значение.
Проверял, в WPF все работает отлично. В общем, создал баг на connect. Бага не критичная, но в целом складывает не очень хорошее впечатление о качестве контролов в Silverlight.