Я связываю свойство ItemsSource WPF Datagrid с ObservableCollection. Данные извлекаются нормально, но я замечаю странное поведение, когда нажимаю на ячейку (любую ячейку) и начинаю использовать клавиатуру для навигации.
- Клавиша Tab работает как положено (слева направо и переносится на следующую строку вниз).
- Клавиша «вверх» ничего не делает (фокус остается на выбранной ячейке).
- Клавиша «Вниз» перемещает фокус на верхнюю ячейку в столбце. Например, если я нахожусь в строке 10 столбца B и нажимаю «вниз», то выделенная ячейка становится строкой 0 столбца B.
- Левая или правая клавиши вызывают исключение ArgumentOutOfRangeException. Msgstr "Указанный аргумент вышел за пределы допустимых значений. Имя параметра: индекс". InnerException не существует, и трассировка стека также мало чем поможет.
- Двойной щелчок по любой из ячеек не включает режим редактирования.
- По какой-то причине в нижней части сетки появляется «лишняя» строка (rowcount = (count collection +1)), которая имеет правильную функциональность (работают кнопки вправо / влево, режим редактирования триггеров двойного щелчка). Когда я дважды щелкаю, чтобы войти в режим редактирования в этой дополнительной строке, а затем щелкаю в связанной строке над ней, в сетку добавляется дополнительная строка.
Я полагаю, что я изолировал инцидент с связанным классом ViewModel (в отличие от XAML). В предварительной отладке я сократил XAML до минимума (без стилей, 2 столбца, только связывание - это ItemsSource) и все еще получаю странное поведение.
<UserControl x:Name="FuelMileageView" x:Class="TRD.RaceStrategy.Views.FuelMileage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:TRD.RaceStrategy"
xmlns:views="clr-namespace:TRD.RaceStrategy.Views"
xmlns:vm="clr-namespace:TRD.RaceStrategy.ViewModels;assembly=RaceStrategy.Support"
xmlns:converters="clr-namespace:TRD.RaceStrategy.Converters;assembly=RaceStrategy.Support"
xmlns:Behaviours="clr-namespace:TRD.RaceStrategy.Behaviours;assembly=RaceStrategy.Support"
mc:Ignorable="d" DataContext="{Binding Path=Properties[PrimaryVM].CarEventVM.FuelMileage.FuelMileageLaps, Source={x:Static local:App.Current}}"
d:DesignHeight="410" d:DesignWidth="485">
<DataGrid x:Name="dgFMLaps" ItemsSource="{Binding}"
AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="Column 1" >
</DataGridTextColumn>
<DataGridTextColumn Header="Column 2" >
</DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
</UserControl>
Помимо вызова InitializeComponent (), нет никакого кода, о котором можно было бы говорить, который, кажется, оставляет связанную коллекцию FuelMiletLaps единственным виновником.
public class FuelMileageLapViewModel : LapViewModel
{
public FuelMileageLapViewModel() { }
}
ПРИМЕЧАНИЕ. ObservableCollectionEx - это расширение класса ObservableCollection, которое, по-видимому, объясняет проблемы с многопоточностью (?) Я использовал этот класс с другими коллекциями, которые, в свою очередь, подключены к сетям данных, у которых не было этой проблемы навигации по клавиатуре.
public class ObservableCollectionEx<T> : ObservableCollection<T>
{
// Override the event so this class can access it
public override event System.Collections.Specialized.NotifyCollectionChangedEventHandler CollectionChanged;
protected override void OnCollectionChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
// Be nice - use BlockReentrancy like MSDN said
using (BlockReentrancy())
{
System.Collections.Specialized.NotifyCollectionChangedEventHandler eventHandler = CollectionChanged;
if (eventHandler == null)
return;
Delegate[] delegates = eventHandler.GetInvocationList();
// Walk thru invocation list
foreach (System.Collections.Specialized.NotifyCollectionChangedEventHandler handler in delegates)
{
DispatcherObject dispatcherObject = handler.Target as DispatcherObject;
// If the subscriber is a DispatcherObject and different thread
if (dispatcherObject != null && dispatcherObject.CheckAccess() == false)
{
// Invoke handler in the target dispatcher's thread
dispatcherObject.Dispatcher.Invoke(DispatcherPriority.DataBind, handler, this, e);
}
else // Execute handler as is
handler(this, e);
}
}
}
}
На данный момент я нахожусь в конце своего ума относительно следующего шага для отладки. Там нет очевидного места, чтобы бросить в точку останова или блок try / catch. Я погуглил свою проблему до смерти и не нашел ничего стоящего. Пожалуйста, помогите!
Вот класс FuelMiletViewModel, в котором инициализируется FuelMiletLaps:
public class FuelMileageViewModel : WorkspaceViewModel
{
/// <summary>
/// View model for the fuel mileage calculator
/// </summary>
/// <param name="car">Car on which to base all calculations</param>
public FuelMileageViewModel()
{}
/// <summary>
/// A separate collection of laps that store the extra fuel mileage data
/// </summary>
public ObservableCollectionEx<FuelMileageLapViewModel> FuelMileageLaps
{
get
{
if (_fuelMileageLaps == null)
{
_fuelMileageLaps = new ObservableCollectionEx<FuelMileageLapViewModel>();
}
return _fuelMileageLaps;
}
set
{
_fuelMileageLaps = value;
OnPropertyChanged("FuelMileageLaps");
}
}
private ObservableCollectionEx<FuelMileageLapViewModel> _fuelMileageLaps;
/// <summary>
/// Number of laps in the race
/// Affects: Laps_G, Laps_Y
/// </summary>
public int NumberOfLaps
{
get
{
return FuelMileageLaps.Count;
}
set
{
int count = FuelMileageLaps.Count;
if (value < 0)
{
throw new ArgumentException("Number of laps must be a positive integer");
}
if (count != value)
{
if( count < value )
{
int diff = value - count;
for (int i = 0; i < diff; i++)
{
FuelMileageLapViewModel lapToAdd = new FuelMileageLapViewModel();
FuelMileageLaps.Add(lapToAdd);
}
}
OnPropertyChanged("NumberOfLaps");
}
}
}
}