UWP 样式绑定 Setter 不工作

UWP Binding in Style Setter not working

我在创建 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) 在 edytor 中而不是在 运行 代码中。 怎么了?在 "old" WPF 中一切正常,但在 Windows 10 的通用应用程序中出现错误。 谁能帮我 ?

如 MSDN Setter.Value property 页面上的 迁移说明 部分所述,UWP/Windows 运行时不支持 Style Setters 中的绑定。

Windows Presentation Foundation (WPF) and Microsoft Silverlight supported the ability to use a Binding expression to supply the Value for a Setter in a Style. The Windows Runtime doesn't support a Binding usage for Setter.Value (the Binding won't evaluate and the Setter has no effect, you won't get errors, but you won't get the desired result either). When you convert XAML styles from WPF or Silverlight XAML, replace any Binding expression usages with strings or objects that set values, or refactor the values as shared {StaticResource} markup extension values rather than Binding-obtained values.

解决方法可以是一个助手 class,它具有绑定源路径的附加属性。它在助手 属性:

的 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.Leftcanvas.Top 属性),请参阅

想从@clemens 添加我对这个 BindingHelper 想法的经验。这是一个很好的解决方案,但我发现当以 ListViewItem 为目标时,绑定不会访问底层视图模型。调试后,我发现我需要确保绑定是相对于 ListViewItem 本身和关联的 .Content 属性 才能使其正确地 link 到项目的查看模型。

我的特定用例是根据视图模型值设置 ListViewItemIsTabStop 属性:

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);
    }
}

如果其他人遇到问题,希望这对您有所帮助。