В библиотеке Silverlthgt Toolkit есть удобный компонент BusyIndicator. Он позволяет сообщить пользователю о ходе выполнения той или иной операции. Компонент хорошо зарекомендовал себя в mvvm модели.
Следуя тем же принципам можно показывать ошибки в приложении. По разным причинам ход выполнения операции может быть прерван ошибкой, данные причины могу от нас и не зависеть, но проинформировать пользователя необходимо всегда. От того, как пользователь будет проинформирован об ошибке, зависит общее впечатление о приложении. С точки зрения пользователя, нет ничего хуже, нежели “страшные” окна со “страшным” StackTrace. В конечном счете компонент будет выглядеть так:
Для удобства использования компонента, можно реагировать на изменение значения свойства “ErrorContent”, и если оно не пустое - отображать ошибку. Так как описывать ошибку может любой тип данных (object), то и проверять переменную на null не достаточно, ведь строковое значение может быть пустым, но не быть null.
После того, как все части определены, можно описать состояния компонента, которые были задекларированы на уровне компонента:
Для того, чтобы показать сообщение об ошибке в удобной форме, а не просто текст, описывается шаблон по умолчанию ErrorContentTemplate, который был определен в классе ErrorIndicator.
Исходный код можно взять здесь.
![]() |
| BusyIndicator |
Следуя тем же принципам можно показывать ошибки в приложении. По разным причинам ход выполнения операции может быть прерван ошибкой, данные причины могу от нас и не зависеть, но проинформировать пользователя необходимо всегда. От того, как пользователь будет проинформирован об ошибке, зависит общее впечатление о приложении. С точки зрения пользователя, нет ничего хуже, нежели “страшные” окна со “страшным” StackTrace. В конечном счете компонент будет выглядеть так:
Example
|
XAML
| ||||
Code Behind
Чтобы контролировать элемент, который может произвести ошибку, его необходимо включить в состав компонента, информирующего пользователя. Поэтому он должен быть наследован oт ContentControl.public class ErrorIndicator : ContentControlДалее необходимо задекларировать атрибутами два состояния Error/NoError. Состояние - это сгруппированные значения свойств компонента с определенной меткой. К состояниям можно обращаться в любой момент времени выполнения программы. При обращении к состоянию, компонент заменяет значения своих свойств (которые могут меняться во время жизни компонента), значениями из состояния. Замена может происходить не сразу, а постепенно с заданным промежутком времени (анимация).
[TemplateVisualState(GroupName = VisualStateGroupErrorStates, Name = VisualStateError)]
[TemplateVisualState(GroupName = VisualStateGroupErrorStates, Name = VisualStateNoError)]
public class ErrorIndicator : ContentControl
{
private const string VisualStateError = "Error";
private const string VisualStateNoError = "NoError";
private const string VisualStateGroupErrorStates = "ErrorStates";
}
После того, как состояния компонента задекларированы, необходимо добавить свойства зависимостей для настройки стилей отображения ошибки и содержимого:
public static readonly DependencyProperty ErrorContentProperty =
DependencyProperty.Register("ErrorContent", typeof (object), typeof (ErrorIndicator),
new PropertyMetadata(null, OnErrorContentChanged));
public static readonly DependencyProperty ErrorContentTemplateProperty =
DependencyProperty.Register("ErrorContentTemplate", typeof(DataTemplate), typeof(ErrorIndicator),
new PropertyMetadata(null));
public static readonly DependencyProperty OverlayBrushProperty =
DependencyProperty.Register("OverlayBrush", typeof (Brush), typeof (ErrorIndicator),
new PropertyMetadata(null));
public static readonly DependencyProperty IsErroredProperty =
DependencyProperty.Register("IsErrored", typeof (bool), typeof (ErrorIndicator),
new PropertyMetadata(false, OnIsErroredChanged));
- ErrorContent - поле, которое содержит объект ошибки (например, текст или исключение).
- ErrorContentTemplate - определяет шаблон отображения ошибки. Если объект, описывающий ошибку, сложный тип данных, это поле поможет “разобрать” его и показать в доступном для пользователя виде. А в идеальном варианте и методы устранения.
- OverlayBrush - кисть которая “затеняет” тестируемый элемент при возникновении ошибки.
- IsErrored - поле индикатор, которое показывает/прячет сообщение об ошибке и в последствии делает недоступным внутреннее содержимое.
protected virtual void OnIsErroredChanged(DependencyPropertyChangedEventArgs e)
{
if ((bool)e.NewValue)
{
VisualStateManager.GoToState(this, VisualStateError, true);
}
else
{
VisualStateManager.GoToState(this, VisualStateNoError, true);
}
}
VisualStateManager - это класс, который переводит компонент в нужное состояние. Каждое состояние (VisualState) по сути представляет собой Storyboard. Реализация состояния заключается в том, что при переходе в это состояние срабатывает заданный Storyboard и устанавливал значения свойствам, т.е. запускается анимация.
Для удобства использования компонента, можно реагировать на изменение значения свойства “ErrorContent”, и если оно не пустое - отображать ошибку. Так как описывать ошибку может любой тип данных (object), то и проверять переменную на null не достаточно, ведь строковое значение может быть пустым, но не быть null.
protected virtual void OnErrorContentChanged(DependencyPropertyChangedEventArgs e)
{
if (e.NewValue is string)
{
IsErrored = !String.IsNullOrEmpty((string) e.NewValue);
}
else
{
IsErrored = e.NewValue != null;
}
}
Чтобы было проще использовать компонент при визуальном проектировании в программе Expression Bland, необходимо пометить свойства атрибутами, значение которых будет играть роль в отображении свойства в той или иной панели свойств, например:
[Category(“Common Properties”)]
public object ErrorContent
{
get { return GetValue(ErrorContentProperty); }
set { SetValue(ErrorContentProperty, value); }
}
[Category(“Brushes”)]
public Brush OverlayBrush
{
get { return (Brush)GetValue(OverlayBrushProperty); }
set { SetValue(OverlayBrushProperty, value); }
}
XAML
Теперь самое интересное - определение внешнего вида компонента. Чтобы поменять внешний вид компоненту и установить значения по умолчанию свойствам, необходимо в файле Themes/Generic.xaml редактировать стиль который будет применяться по умолчанию для всех компонентов с типом ErrorIndicator (в данном случае стиль создается с “нуля”, но можно создавать стили на базе уже имеющегося):<Style TargetType="ErrorIndicatorProj:ErrorIndicator"/>и присвоить значения свойствам по средством механизма Setter. Для определения внешнего вида, необходимо присвоить значение свойству Template:
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ErrorIndicatorProj:ErrorIndicator">
<Border Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" Margin="{TemplateBinding Padding}" BorderThickness="{TemplateBinding BorderThickness}">
<Grid>
<ContentPresenter x:Name="PART_ContentPresenter" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
<Border x:Name="PART_OverlayBorder" Background="{TemplateBinding OverlayBrush}" Opacity="0" Visibility="Collapsed"/>
<ContentPresenter x:Name="PART_ErrorPresenter" Content="{TemplateBinding ErrorContent}" ContentTemplate="{TemplateBinding ErrorContentTemplate}" Opacity="0" VerticalAlignment="Center" HorizontalAlignment="Center" Visibility="Collapsed"/>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
Шаблон состоит из 3-х главных частей (по соглашению все функциональные части представления должны иметь префикс “PART”) и элементов задающим общий вид компоненты:
- Строка 6 “PART_ContentPresenter” представляет содержимое, которое может породить ошибку, и которую необходимо сделать недоступным.
- Строка 7 “PART_OverlayBorder” отделяет элемент, который породил ошибку, от информации об ошибки.
- Строка 8 “PART_ErrorPresenter” показывает ошибку.
После того, как все части определены, можно описать состояния компонента, которые были задекларированы на уровне компонента:
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="ErrorStates">
<VisualState x:Name="Error">
<Storyboard>
<DoubleAnimation Duration="0:0:0.2" To="1" Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="PART_OverlayBorder" d:IsOptimized="True"/>
<DoubleAnimation Duration="0:0:0.2" To="1" Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="PART_ErrorPresenter" d:IsOptimized="True"/>
</Storyboard>
</VisualState>
<VisualState x:Name="NoError">
<Storyboard>
<DoubleAnimation Duration="0:0:0.3" To="0" Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="PART_OverlayBorder" d:IsOptimized="True"/>
<DoubleAnimation Duration="0:0:0.3" To="0" Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="PART_ErrorPresenter" d:IsOptimized="True"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
Состояния, которые относятся к выводу ошибки, объединены в “ErrorStates” группу.
- Error - возникает, когда компонент находится в состоянии ошибки, т.е. необходимо спрятать элемент, который спровоцировал ошибку, и проинформировать пользователя.
- NoError - состояние по умолчанию, при котором пользователь может взаимодействовать с “тестируемым” компонентом.
Для того, чтобы показать сообщение об ошибке в удобной форме, а не просто текст, описывается шаблон по умолчанию ErrorContentTemplate, который был определен в классе ErrorIndicator.
<Setter Property="ErrorContentTemplate">
<Setter.Value>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition MinWidth="16" Width="Auto"/>
<ColumnDefinition MinWidth="40" Width="*"/>
</Grid.ColumnDefinitions>
<Image Source="/ErrorIndicatorProj;component/Assets/Alert.png" Width="32"/>
<ContentPresenter Content="{Binding }" VerticalAlignment="Center" Grid.Column="1" Margin="10,0,0,0"/>
</Grid>
</DataTemplate>
</Setter.Value>
</Setter>
Помимо “голой" информации об ошибке, пользователь увидит пиктограмму. Шаблон вывода ошибки можно переопределить в клиентском коде.Исходный код можно взять здесь.






