Как часто вы думаете о том, что пользователь может нажать Double-Click, где это не предусмотрено?
- modified:
- reading: 3 minutes
Вспомните то время, когда обучали бабушку/дедушку/маму/папу/дядю/тетю основам компьютера. Когда показываете, что на кнопке нужно щелкнуть один раз, на ссылке один раз, но вот на иконке в проводнике два раза (зависит, конечно, от настроек, но все же). Но, если обучаемый научился делать двойной клик, то все. Потом такое ощущение, что он специально тренируясь делает его везде: и на меню, и на кнопке. Бывает, правда, и случайно. В общем, попался недавно неприятный баг. Код в котором баг очевиден видели и дорабатывали несколько раз несколько разных человек (я в их числе), баг не замечали и не обнаруживали. Правда, когда обнаружилась ошибка, я буквально за 10 секунд понял причину, даже не смотря код.
Расскажу на небольшом примере. Пускай у нас есть ViewModel:
public class ViewModel : DependencyObject
{ public static readonly DependencyProperty IsBusyProperty =
DependencyProperty.Register("IsBusy", typeof(bool), typeof(ViewModel), new PropertyMetadata(false));
public ViewModel()
{ ClickMeCommand = new DelegateCommand(OnClickMe);
ClickCollection = new ObservableCollection<ClickInfoViewModel>();
}
public DelegateCommand ClickMeCommand { get; set; }
public ObservableCollection<ClickInfoViewModel> ClickCollection { get; set; }
public bool IsBusy
{ get { return (bool)GetValue(IsBusyProperty); }
set { SetValue(IsBusyProperty, value); }
}
private void OnClickMe(object obj)
{ IsBusy = true;
new Thread(DoAsyncAction).Start(DateTime.Now);
}
private void DoAsyncAction(object date)
{
Thread.Sleep(2000);
Dispatcher.BeginInvoke(() =>
{ ClickCollection.Add(new ClickInfoViewModel((DateTime)date));
IsBusy = false;
});
}
}
Класс содержит команду ClickMeCommand, которая выполняет, точнее запускает какую-то команду асинхронно. В нашем примере – эта команда DoAsyncAction, которая просто спит 2 секунды, а затем добавляет в коллекцию сделанных кликов новый элемент типа ClickInfoViewModel:
public class ClickInfoViewModel
{ public ClickInfoViewModel(DateTime date)
{
Date = date;
}
public DateTime Date { get; set; }
public override string ToString()
{ return string.Format("Clicked at {0}", Date);
}
}
Так же ViewModel класс содержит свойство IsBusy, в которое мы выставляем значение, когда наше представление должно быть занято. Уже видите проблему?
Представление будет следующим:
<UserControl x:Class="SilverlightDoubleClick.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:SilverlightDoubleClick="clr-namespace:SilverlightDoubleClick"
xmlns:Controls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Toolkit">
<UserControl.DataContext>
<SilverlightDoubleClick:ViewModel />
</UserControl.DataContext>
<Grid Width="500" Height="250">
<StackPanel x:Name="LayoutRoot" Background="White" >
<Button Command="{Binding Path=ClickMeCommand}">Click Me</Button>
<ListBox ItemsSource="{Binding Path=ClickCollection}" Height="200" HorizontalAlignment="Stretch"
ScrollViewer.VerticalScrollBarVisibility="Auto" />
</StackPanel>
<Controls:BusyIndicator IsBusy="{Binding IsBusy}" />
</Grid>
</UserControl>
Основное на нем – это кнопка, список, отображающий коллекцию сделанных кликов и BusyIndicator, который не дает возможность выполнять действия, пока предыдущее действие не закончится. Собственно можно попробовать. И не забыть попробовать двойной клик:
Больше чем уверен, что такая ошибка может быть допущена не только мной. В нашем случае автора ошибки уже не найдешь, может быть был и я. У нас это было на форме аутентификации в приложении. Пользователь мог аутентифицироваться дважды. А так как у нас в приложении происходит слежение за сессиями (не давать одному и тому же пользователю работать дважды в одно время), то соответственно после второй аутентификации он видит сообщение о том, что сессия сброшена.
Решить данную проблему очень просто, добавив проверку в метод OnClickMe:
private void OnClickMe(object obj)
{ if (!IsBusy)
{ IsBusy = true;
new Thread(DoAsyncAction).Start(DateTime.Now);
}
}
Было такое? Или я все-таки один такой?
See Also
- Никогда не делайте классы и пространства имен с одним и тем же именем
- Silverlight. Основы. Валидация. Часть 2. IDataErrorInfo & INotifyDataErrorInfo
- Silverlight. Основы. Валидация. Часть 1. DataAnnotations & ValidatesOnExceptions
- Первые впечатления о LightSwitch
- Export data to Excel from Silverlight/WPF DataGrid