#c# #wpf #mvvm
#c# #wpf #mvvm
Вопрос:
У меня небольшая проблема с WPF и привязкой моего listbox / listview к пользовательскому списку.
В этом списке реализованы интерфейсы IEnumerator и IEnumerable. Если я привяжу к ним свой элемент управления, я никогда не увижу первый объект этого списка.
Когда я создаю gridview, он показывает мой первый объект. Итак, по какой-то причине listbox / listview делают разные вещи для перечисления моего пользовательского списка.
Моя привязка для обеих настроек выполняется точно так же, используя общедоступное свойство в моей ViewModel.
Привязка (у моего PersonObject есть общедоступное свойство ProjectList, которое получает пользовательский список, о котором я говорю).
public Person Person
{
get
{
return this._person;
}
set
{
if (this._person != value)
{
this._person = value;
RaisePropertyChanged("Person");
}
}
}
XAML:
<ListBox ItemsSource="{Binding Path=Person.ProjectList,UpdateSourceTrigger=PropertyChanged}" AlternationCount="2" ItemContainerStyle="{StaticResource CustomListBoxItemStyle}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock>
<TextBlock.Text>
<MultiBinding StringFormat="{}{0} - {1}">
<Binding Path="Name" />
<Binding Path="Number" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
<TextBlock>
<TextBlock.Text>
<MultiBinding StringFormat="{}{0} - {1}">
<Binding Path="StartDate" />
<Binding Path="EndDate" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Класс Customlist:
public class ProjectList : DeletableSupport, IEnumerator, IEnumerable
{
private IList<Project> _pList;
private int _position;
public ProjectList()
{
this._pList = new ActivatableList<Project>();
this._position = -1;
}
public void Add(Project p)
{
Activate(ActivationPurpose.Write);
this._pList.Add(p);
}
public void Remove(Project p)
{
Activate(ActivationPurpose.Write);
this._pList.Remove(p);
}
public int Count()
{
Activate(ActivationPurpose.Read);
return this._pList.Count();
}
public bool Contains(Project p)
{
Activate(ActivationPurpose.Read);
return this._pList.Contains(p);
}
public Project this[int i]
{
get
{
Activate(ActivationPurpose.Read);
return this._pList[i];
}
set
{
Activate(ActivationPurpose.Write);
this._pList[i] = value;
}
}
public static ProjectList operator (ProjectList pList, Project p)
{
pList.Add(p);
return pList;
}
public static ProjectList operator -(ProjectList pList, Project p)
{
pList.Remove(p);
return pList;
}
#region IEnumerable Members
public IEnumerator GetEnumerator()
{
Activate(ActivationPurpose.Read);
return (IEnumerator)this;
}
#endregion
#region IEnumerator Members
public object Current
{
get
{
try
{
Activate(ActivationPurpose.Read);
return this._pList[_position];
}
catch (IndexOutOfRangeException)
{
throw new InvalidOperationException();
}
}
}
public bool MoveNext()
{
Activate(ActivationPurpose.Read);
this._position ;
if (_position < this._pList.Count)
{
return true;
}
else
{
return false;
}
}
public void Reset()
{
Activate(ActivationPurpose.Write);
_position = -1;
}
#endregion
}
Активация происходит из db4o, ActivatableList реализует IList
Ответ №1:
Здесь я рискну высказать предположение, просто чтобы проверить свои навыки экстрасенсорной отладки 🙂
ListBox / ListView используют IEnumerable.Any() (или какой-то эквивалент), чтобы проверить, пуст ли список. Это возвращает true, поэтому затем он использует вашу реализацию IEnumerable для фактического перебора списка.
Обратите внимание, что он не вызвал reset в вашем классе, что означает, что первый элемент, который был извлечен вызовом Any (), будет пропущен.
Обычно вызов GetEnumerator для IEnumerable возвращает новый экземпляр IEnumerator для вашего класса, но ваша фактическая реализация списка содержит все состояние для перечислителя. Вы подумали, что произойдет, если вы передадите этот список в свой ListBox во второй раз? Я не думаю, что вы вообще что-нибудь увидите.
Хорошо, итак, как это можно исправить? Ну, учитывая имеющийся у вас код, вы могли бы просто вызывать Reset () всякий раз, когда кто-то вызывает GetEnumerator() . Однако имеющаяся у вас реализация не является потокобезопасной (возможно, сейчас это не проблема, но кто знает, как это будет использоваться в будущем?). Если вы действительно не хотите использовать что-то вроде ObservableCollection для хранения вашего списка элементов, я бы, по крайней мере, взглянул на возврат отдельного экземпляра IEnumerator из метода GetEnumerator со всеми состояниями для процесса перечисления, хранящимися там.
Комментарии:
1. Действительно, что касается моего другого ответа, сброс не вызывается в определенный момент, здесь выполняется некоторая отладка, чтобы все заработало снова… ваши навыки экстрасенсорной отладки не подвели, да 🙂