У меня проблема с пониманием того, как работает привязка к пользовательским элементам управления и почему она работает не так, как на страницах. То, что я пытаюсь сделать, - это создать отображение ошибки (Имя ошибки, Описание и Советы, в котором рассказывается, как ее устранить), которое будет отображаться в элементе управления контентом, если есть ошибка, или другое, если ошибки нет.
Я делаю это с помощью пользовательского элемента управления, который, по сути, является вложенным представлением на странице, чтобы избежать грубых всплывающих окон, и будет повторно использоваться на нескольких страницах. У меня работает привязка управления контентом, поэтому мы отображаем пользовательский элемент управления, но никакой информации.
Для целей «СУХОЙ» я создал модель ошибок с требуемыми свойствами, а затем использую класс для реализации этого. Модель как список ошибок. В конструкторе я просто добавляю новые ошибки в список ... таким образом, все ошибки приложения находятся в одном месте для простоты обслуживания.
Класс системных ошибок:
public List<ErrorMessageModel> errors;
/// <summary>
/// Constructor creates list with all errors in the program
/// </summary>
public SystemErrors()
{
errors = new List<ErrorMessageModel>()
{
//*** No Error ***/
new ErrorMessageModel(ErrorCodes.noError, "", "", ""),
/*** No Devices Found Error ***/
new ErrorMessageModel(ErrorCodes.noDevicesConnected,
"No Devices Found",
"We couldn't find any attached USB devices.",
"This error occurs when there's no connection between the device and the computer ")
/*** Next Error ***/
};
}
private ErrorMessageModel _activeError;
public ErrorMessageModel ActiveError
{
get { return _activeError; }
set
{
if (value == _activeError)
return;
_activeError = value;
RaisePropertyChanged();
}
}
public void SetActiveError (byte index)
{
// Changed to ActiveError = after Mark's answer. No effect.
_activeError = errors[index];
}
В модели представления страницы мы используем enum ErrorCodes, чтобы имя указывало на индекс ошибки. Поэтому, когда у нас возникает ошибка, мы передаем errorCode методу, который преобразует его в байт, а затем вызывает SetActiveError (byte errorCodeToIndex).
Page ViewModel:
...
private void parseErrorCode(ErrorCodes error)
{
// Convert Error Code into Index number
var errorCodeToIndex = (byte)error;
// Create new error list and populate list
SystemErrors errors = new SystemErrors();
errors.SetActiveError(errorCodeToIndex);
}
Теперь идеяздесь нужно установить контекст данных пользовательского элемента управления на SystemError и, таким образом, связать с ActiveError (ActiveError.ErrorName, ActiveError.ErrorDescription и т. д.). Я думал, что это позволит нам использовать один текстовый текст, потому что независимо от того, на какой странице мы находимся, когда у нас возникает ошибка, информация об ошибке всегда поступает из SystemErrors.
Контроль пользователя:
<UserControl x:Class="FirmwareUpdaterUI.Views.ConnectionErrorView"
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:FirmwareUpdaterUI.Views"
xmlns:vm="clr-namespace:FirmwareUpdaterUI.ViewModels"
xmlns:e="clr-namespace:FirmwareUpdaterUI.Errors"
mc:Ignorable="d"
d:DesignHeight="250" d:DesignWidth="400" BorderBrush="Red" BorderThickness="1px">
<UserControl.DataContext>
<e:SystemErrors/>
</UserControl.DataContext>
<Grid x:Name="ConnectionErrorView" Visibility="Visible">
<Grid.RowDefinitions>
<RowDefinition Height=".5*"/>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
<RowDefinition Height="6*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1.5*"/>
<ColumnDefinition Width=".5*"/>
<ColumnDefinition Width="10*"/>
<ColumnDefinition Width="1*"/>
</Grid.ColumnDefinitions>
<!-- Row 1-->
<StackPanel Grid.Row="1" Grid.Column="2" Orientation="Horizontal">
<TextBlock>
Error:
</TextBlock>
<TextBlock Text="{Binding ActiveError.ErrorName,
RelativeSource={RelativeSource AncestorType={x:Type e:SystemErrors}}}"/>
</StackPanel>
<!-- Row 2 -->
<TextBlock Grid.Row="2" Grid.Column="2" Grid.ColumnSpan="2"
Text="{Binding ErrorDescription}"/>
<!-- Row 3 -->
<TextBlock Grid.Row="3" Grid.Column="2" Grid.RowSpan="2" Grid.ColumnSpan="2"
Text="{Binding Path=ActiveError.ErrorTips, StringFormat=Tips: {0}}" />
</Grid>
</UserControl>
Но я не могу заставить его работать. Вы можете увидеть все мои оставшиеся неудачные подходы в XAML, но это только царапает поверхность того, что я пробовал. Я могу заставить это работать, если я срежу кишки UC и вставлю его в страницу, так что то, что говорит мне, что привязка к странице имеет иной механизм, чем для пользовательского элемента управления.
I 'мы прочитали кучу уроков, посмотрели несколько видео, но все они как бы пропускают то, как это работает;всегда «чтобы это работало, нам нужен уже работающий код», который помогает, только если у вас точно такая же проблема. Я видел свойства зависимостей, то, что кажется нормальным связыванием, относительным источником к себе, относительным источником к предку и т. Д.
Вопросы:
Так почему пользовательский элемент управления имеетотличается механизм связывания от окон / страниц (почему контекст данных не работает так, как в других местах)? Если нам нужны свойства зависимостей, то почему они не нужны для привязки к страницам? А также в отношении DP, если необходимо, в этом случае, я бы просто сделал ActiveErrorProperty типа ErrorModel, или нам нужен один для каждого подчиненного свойства (ErrorName типа string)? Как связать DP со свойством, с которым мы хотим связать?
Обновление:
Сегодня весь день пытался заставить это работать, поэтому я начал отслеживать и выводить на консоль. Обе ошибки не были связаны, и если бы я вставил Trace.WriteLine
в публичном объявлении ActiveError
после RaisePC()
, ActiveError
будет установлено на правильную ошибку. Затем я попытался отследить привязку в XAML, и есть некоторые интересные вещи:
ErrorName(_activeError)= No Devices Found
ErrorName(ActiveError)= No Devices Found
System.Windows.Data Warning: 56 : Created BindingExpression (hash=62991470) for Binding (hash=23560597)
System.Windows.Data Warning: 58 : Path: 'ActiveError.ErrorName'
System.Windows.Data Warning: 60 : BindingExpression (hash=62991470): Default mode resolved to OneWay
System.Windows.Data Warning: 62 : BindingExpression (hash=62991470): Attach to System.Windows.Controls.TextBlock.Text (hash=2617844)
System.Windows.Data Warning: 67 : BindingExpression (hash=62991470): Resolving source
System.Windows.Data Warning: 70 : BindingExpression (hash=62991470): Found data context element: TextBlock (hash=2617844) (OK)
System.Windows.Data Warning: 78 : BindingExpression (hash=62991470): Activate with root item SystemErrors (hash=52209455)
System.Windows.Data Warning: 108 : BindingExpression (hash=62991470): At level 0 - for SystemErrors.ActiveError found accessor RuntimePropertyInfo(ActiveError)
System.Windows.Data Warning: 104 : BindingExpression (hash=62991470): Replace item at level 0 with SystemErrors (hash=52209455), using accessor RuntimePropertyInfo(ActiveError)
System.Windows.Data Warning: 101 : BindingExpression (hash=62991470): GetValue at level 0 from SystemErrors (hash=52209455) using RuntimePropertyInfo(ActiveError): <null>
System.Windows.Data Warning: 106 : BindingExpression (hash=62991470): Item at level 1 is null - no accessor
System.Windows.Data Warning: 80 : BindingExpression (hash=62991470): TransferValue - got raw value {DependencyProperty.UnsetValue}
System.Windows.Data Warning: 88 : BindingExpression (hash=62991470): TransferValue - using fallback/default value ''
System.Windows.Data Warning: 89 : BindingExpression (hash=62991470): TransferValue - using final value ''
Обратите внимание, что это показывает, что ActiveError
установлен правильно (первые две строки, «Устройства не найдены» - это ErrorName) до мы видим сбой привязки. Я слишком новичок в WPF, но если я правильно интерпретирую трассировку, похоже, она находит ActiveError
в текстовом тексте SystemErrors
, но не может получить что-либо из ActiveError.ErrorName
, которое, как мы знаем, установлено на правильное значение. О чем это?