Связывание UWP в Style Setter не работает
У меня проблема с созданием элемента управления xaml. Я пишу новый проект в VS 2015 в универсальном приложении. Я хочу создать сетку. В этой сетке я хочу иметь кнопку. В модели я указываю столбец (Уровень) и Строка. это мой код:
<ItemsControl Grid.Row="1" ItemsSource="{Binding Path=TechnologyList}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="10*"/>
<RowDefinition Height="10*"/>
<RowDefinition Height="10*"/>
<RowDefinition Height="10*"/>
<RowDefinition Height="10*"/>
<RowDefinition Height="10*"/>
<RowDefinition Height="10*"/>
<RowDefinition Height="10*"/>
<RowDefinition Height="10*"/>
<RowDefinition Height="10*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="14*"/>
<ColumnDefinition Width="14*"/>
<ColumnDefinition Width="14*"/>
<ColumnDefinition Width="14*"/>
<ColumnDefinition Width="14*"/>
<ColumnDefinition Width="14*"/>
<ColumnDefinition Width="14*"/>
</Grid.ColumnDefinitions>
</Grid>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style TargetType="Control">
<Setter Property="Grid.Column" Value="{Binding Level}" />
<Setter Property="Grid.Row" Value="{Binding Row}" />
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Content="{Binding Name}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Я получаю сообщение об ошибке <Setter Property="Grid.Column" Value="{Binding Level}" />
Ошибка: исключение из HRESULT: 0x8000FFFF (E_UNEXPECTED) был в редакторе, а не в запущенном коде. Что случилось? В "старом" WPF все было в порядке, но в Universal App for Windows 10 у меня ошибка. Может кто-нибудь мне помочь?
2 ответа
Как отмечено в разделе Примечания по миграции на странице свойств Setter.Value в MSDN, среда выполнения UWP/Windows не поддерживает привязки в установщиках стилей.
Windows Presentation Foundation (WPF) и Microsoft Silverlight поддерживали возможность использовать выражение Binding для предоставления значения для Setter в стиле. Среда выполнения Windows не поддерживает использование Binding для Setter.Value (Binding не будет оцениваться, а Setter не будет работать, вы не получите ошибок, но вы также не получите желаемого результата). При преобразовании стилей XAML из WPF или Silverlight XAML замените все используемые выражения Binding строками или объектами, которые устанавливают значения, или реорганизуйте значения как общие значения расширения разметки {StaticResource}, а не полученные из Binding значения.
Обходным решением может быть вспомогательный класс с прикрепленными свойствами для исходных путей привязок. Он создает привязки в коде в PropertyChangedCallback вспомогательного свойства:
public class BindingHelper
{
public static readonly DependencyProperty GridColumnBindingPathProperty =
DependencyProperty.RegisterAttached(
"GridColumnBindingPath", typeof(string), typeof(BindingHelper),
new PropertyMetadata(null, GridBindingPathPropertyChanged));
public static readonly DependencyProperty GridRowBindingPathProperty =
DependencyProperty.RegisterAttached(
"GridRowBindingPath", typeof(string), typeof(BindingHelper),
new PropertyMetadata(null, GridBindingPathPropertyChanged));
public static string GetGridColumnBindingPath(DependencyObject obj)
{
return (string)obj.GetValue(GridColumnBindingPathProperty);
}
public static void SetGridColumnBindingPath(DependencyObject obj, string value)
{
obj.SetValue(GridColumnBindingPathProperty, value);
}
public static string GetGridRowBindingPath(DependencyObject obj)
{
return (string)obj.GetValue(GridRowBindingPathProperty);
}
public static void SetGridRowBindingPath(DependencyObject obj, string value)
{
obj.SetValue(GridRowBindingPathProperty, value);
}
private static void GridBindingPathPropertyChanged(
DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
var propertyPath = e.NewValue as string;
if (propertyPath != null)
{
var gridProperty =
e.Property == GridColumnBindingPathProperty
? Grid.ColumnProperty
: Grid.RowProperty;
BindingOperations.SetBinding(
obj,
gridProperty,
new Binding { Path = new PropertyPath(propertyPath) });
}
}
}
Вы бы использовали их в XAML следующим образом:
<ItemsControl.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<Setter Property="local:BindingHelper.GridColumnBindingPath" Value="Level"/>
<Setter Property="local:BindingHelper.GridRowBindingPath" Value="Row"/>
</Style>
</ItemsControl.ItemContainerStyle>
Для простого обходного пути для абсолютного позиционирования (т.е. Canvas.Left
а также canvas.Top
свойства), см. этот ответ.
Хотел добавить свой опыт этого BindingHelper
идея от @clemens. Это отличное решение, но я обнаружил, что при ListViewItem
привязка не будет обращаться к базовой модели представления. После отладки я обнаружил, что необходимо убедиться, что привязка относительно ListViewItem
сам и связанный .Content
свойство, позволяющее правильно ссылаться на модель представления элемента.
Мой конкретный случай использования был установить IsTabStop
собственность ListViewItem
на основе значения модели представления:
private static void BindingPathPropertyChanged(DependencyObject obj,
DependencyPropertyChangedEventArgs e)
{
if (e.NewValue is string propertyPath)
{
var binding = new Binding
{
Path = new PropertyPath($"Content.{propertyPath}"),
Mode = BindingMode.OneWay,
RelativeSource = new RelativeSource
{
Mode = RelativeSourceMode.Self
}
};
BindingOperations.SetBinding(obj, Control.IsTabStopProperty, binding);
}
}
Надеюсь, это поможет, если у кого-то еще есть проблема.