web-dev-qa-db-ja.com

スタイルセッターでのUWPバインディングが機能しない

Xamlコントロールの作成に問題があります。ユニバーサルアプリでVS2015の新しいプロジェクトを書いています。グリッドを作成したい。このグリッドにはボタンが必要です。モデルでは、列(レベル)と行を指定します。これは私のコードです:

<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にありました。なにが問題ですか? 「古い」WPFではすべて問題ありませんでしたが、Universal App for Windows10ではエラーが発生します。誰かが私を助けることができますか?

13
Babel

MSDNの Setter.Valueプロパティ ページのセクション移行ノートに記載されているように、UWP/Windowsランタイムはバインディングをサポートしていませんスタイルセッターで。

Windows Presentation Foundation(WPF)とMicrosoft Silverlightは、バインディング式を使用してスタイル内のセッターの値を提供する機能をサポートしていました。 Windowsランタイムは、Setter.Valueのバインディングの使用をサポートしていません(バインディングは評価されず、Setterは効果がなく、エラーは発生しませんが、目的の結果も得られません)。 XAMLスタイルをWPFまたはSilverlightXAMLから変換する場合は、Binding式の使用法を、値を設定する文字列またはオブジェクトに置き換えるか、Bindingで取得した値ではなく共有の{StaticResource}マークアップ拡張値として値をリファクタリングします。

回避策は、バインディングのソースパスのプロパティがアタッチされたヘルパークラスである可能性があります。ヘルパープロパティの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プロパティ)、 この回答 を参照してください。

25
Clemens

@clemensからのこのBindingHelperアイデアの私の経験を追加したかった。これは優れたソリューションですが、ListViewItemをターゲットにすると、バインディングが基になるビューモデルにアクセスしないことがわかりました。デバッグした後、アイテムのビューモデルに正しくリンクできるようにするには、バインディングがListViewItem自体と関連する.Contentプロパティに関連していることを確認する必要があることがわかりました。

私の特定のユースケースは、ビューモデルの値に基づいてIsTabStopListViewItemプロパティを設定することでした。

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

他の誰かが問題を抱えている場合、これが役立つことを願っています。

1
Colin B