none
xaml で Resource にバインディングを指定するにはどうすればよいでしょうか。 RRS feed

  • 質問

  • xaml で Resource にバインディングを指定するにはどうすればよいでしょうか。
    コードで実行する場合は下記の通りで、実際に期待したとおりに動いています。
    Resources["CmdInfos"] = ((各種設定モデル)DataContext).CmdInfos;
    これと同じことを、xaml で指定したいのです。下記はエラーでした。
    <system:Object x:Key="CmdInfos" Value="{Binding CmdInfos}"/>

    このようなことをしたくなった背景は下記の通りです。
    ヘッダー・フッターを共通化したくて ComtrolTemplate をつくり、App.xaml におきました。
    ContentPresenter でヘッダー・フッター以外の部分は各ウィンドウで差し替えることができました。
    ヘッダー・フッターにも各画面で一部異なる部分があり、変更部分を各ウィンドウの Resources で指定することにしました。
    タイトルは下記のように指定することで差し替えることができました。
    App.xaml 側
    <TextBlock Text="{DynamicResource Title}" Foreground="White" FontWeight="Bold" FontSize="24"  Margin="5" Padding="10,5"/>
    Window 側
    <system:String x:Key="Title">各種設定</system:String>
    フッターにはボタンが並んでいて、その内容はモデルが保持しています。
    そこで Resource としてこれをバインディングで指定する必要が出てきました。
    App.xaml 側
    <controls:ButtonBand Grid.Row="2" ButtonCount="8"  Background="{TemplateBinding Background}" CmdInfos="{DynamicResource CmdInfos}" OriginOffset="-1">
    Window 側


    http://systemartlaboratory.com/

    2017年5月15日 12:58

回答

  • リソースで与えることにこだわっているようですが、Bindingで参照することができない理由があるのでしょうか?
    変更したい部分だけをDataTemplateなどで変更したり、RelativeSourceで外側のDataContextを参照すれば大抵のことはできると思うのですが。

    いちおう、ResourceにいれるのをFrameworkElementなどにして、さらに小細工すればできないことは無いですが。

    <!-- App.xaml -->
    <Application x:Class="WpfApplication1.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" StartupUri="MainWindow.xaml">
        <Application.Resources>
            <!-- 共通ヘッダ -->
            <ControlTemplate x:Key="Header" >
                <Border BorderThickness="1" BorderBrush="Red">
                    <StackPanel Background="LightBlue">
                        <TextBlock Text="{DynamicResource Title}" HorizontalAlignment="Center" Foreground="White" FontWeight="Bold" FontSize="24"  Margin="5" Padding="10,5"/>
    
                        <!-- 個別部分定義用 -->
                        <ContentPresenter Content="{Binding}" ContentTemplate="{DynamicResource ResourceKey=HeaderCustom}" />
                    </StackPanel>
                </Border>
            </ControlTemplate>
            <!-- 共通フッタ -->
            <ControlTemplate x:Key="Footer" >
                <Border BorderThickness="1" BorderBrush="Green">
                    <StackPanel >
                       <!--リソースに定義してあるFrameworkElementからとりだしてみる -->
                        <ItemsControl ItemsSource="{Binding Path=Value}" DataContext="{DynamicResource CmdInfos}">
                            <ItemsControl.ItemTemplate>
                                <DataTemplate>
                                    <StackPanel>
                                        <TextBlock>
                                            <Run>リソースからとった</Run>
                                            <Run Text="{Binding Mode=OneWay}" />
                                        </TextBlock>
                                    </StackPanel>
                                    
                                </DataTemplate>
                            </ItemsControl.ItemTemplate>
                        </ItemsControl>
                        
                        <!-- 個別部分定義用 -->
                        <ContentPresenter Content="{Binding}" ContentTemplate="{DynamicResource ResourceKey=FooterCustom}" />
    
                        <TextBlock Text="{Binding Path=PageNumber}" HorizontalAlignment="Center"/>
                    </StackPanel>
                </Border>
            </ControlTemplate>
    
            <!-- 共通ページのテンプレート -->
            <ControlTemplate x:Key="HeaderFooterPage" TargetType="{x:Type Control}">
                <Border BorderBrush="Black" BorderThickness="1">
                    <DockPanel>
                        <Grid DockPanel.Dock="Top" HorizontalAlignment="Stretch">
                            <Control Template="{DynamicResource ResourceKey=Header}" 
                                     DataContext="{Binding HeaderContent}"/>
                        </Grid>
    
                        <Grid DockPanel.Dock="Bottom" HorizontalAlignment="Stretch">
                            <Control Template="{DynamicResource ResourceKey=Footer}"
                                     DataContext="{Binding FooterContent}"/>
                        </Grid>
    
                        <ContentPresenter Content="{Binding Path=Body}" />
                    </DockPanel>
                </Border>
            </ControlTemplate>
        </Application.Resources>
    </Application>
    <Window x:Class="WpfApplication1.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:system="clr-namespace:System;assembly=mscorlib"
            xmlns:app="clr-namespace:WpfApplication1"
            Title="MainWindow" Height="350" Width="600" 
            x:Name="wnd">
        <Window.Resources>
            <!-- バインドしたいリソース対象 -->
            <app:DependencyProxy Value="{Binding Path=CmdInfos}" DataContext="{Binding Path=DataContext, ElementName=wnd}" x:Key="CmdInfos" />
        </Window.Resources>
        <Grid>
            <!-- Resourceに定義したFrameworkElementをVisualTreeにおいてDataContextを取るための小細工 -->
            <ContentControl Content="{Binding Source={StaticResource CmdInfos}}" Visibility="Collapsed"/>
    
            <UniformGrid Columns="2">
    
                <Control x:Name="Page0" Template="{StaticResource HeaderFooterPage}" Margin="10">
                </Control>
                
                <Control x:Name="Page1" Template="{StaticResource HeaderFooterPage}" Margin="10">
                    <Control.Resources>
                        <system:String x:Key="Title">各種設定</system:String>
    
                        <!-- このページのみヘッダに表示する部品のテンプレート -->
                        <DataTemplate x:Key="HeaderCustom" >
                            <TextBlock Text="Custom Text Block" />
                        </DataTemplate>
                    </Control.Resources>
                </Control>
    
                <Control x:Name="Page2" Template="{StaticResource HeaderFooterPage}" Margin="10">
                    <Control.Resources>
                        <system:String x:Key="Title">へっだーたいとる</system:String>
    
                        <!-- このページのみフッタに表示する部品のテンプレート -->
                        <DataTemplate x:Key="FooterCustom" >
                            <ItemsControl ItemsSource="{Binding Path=DataContext.CmdInfos,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=Window}}" >
                                <ItemsControl.ItemsPanel>
                                    <ItemsPanelTemplate>
                                        <WrapPanel />
                                    </ItemsPanelTemplate>
                                </ItemsControl.ItemsPanel>
                                <ItemsControl.ItemTemplate>
                                    <DataTemplate>
                                        <Button Content="{Binding}" Margin="2"/>
                                    </DataTemplate>
                                </ItemsControl.ItemTemplate>
                            </ItemsControl>
                        </DataTemplate>
                    </Control.Resources>
                </Control>
    
                <Control x:Name="Page3" Template="{StaticResource HeaderFooterPage}" Margin="10">
                    <Control.Resources>
                        <!-- このページのみヘッダをまるごと置換してみる -->
                        <ControlTemplate x:Key="Header" >
                            <Grid Margin="10" Background="LightPink" >
                                <StackPanel Margin="2">
                                    <TextBlock Text="ヘッダまるごと変更" />
                                    <TextBlock Text="{Binding Path=.}" HorizontalAlignment="Center"/>
                                </StackPanel>
                                <Rectangle Stroke="Black" StrokeDashArray="10,2" StrokeThickness="2" VerticalAlignment="Stretch">
                                </Rectangle>
                            </Grid>
                        </ControlTemplate>
                    </Control.Resources>
                </Control>
    
    
            </UniformGrid>
        </Grid>
    </Window>
    namespace WpfApplication1
    {
        public partial class MainWindow : System.Windows.Window
        {
            public MainWindow()
            {
                InitializeComponent();
                this.DataContext = new Model();
            }
        }
    
        class Model
        {
            public Model()
            {
                CmdInfos = new System.Collections.Generic.List<string>() { "A", "B", "C" };
                HeaderContent = "へっだ";
                FooterContent = "ふった";
            }
            public System.Collections.Generic.List<string> CmdInfos { get; set; }
            public object HeaderContent { get; set; }
            public object FooterContent { get; set; }
        }
    
    
        class DependencyProxy : System.Windows.FrameworkElement
        {
            public object Value
            {
                get { return (object)GetValue(ValueProperty); }
                set { SetValue(ValueProperty, value); }
            }
    
            public static readonly System.Windows.DependencyProperty ValueProperty
                = System.Windows.DependencyProperty.Register
                    ("Value", typeof(object), typeof(DependencyProxy), new System.Windows.PropertyMetadata(null));
        }
    }


    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    • 編集済み gekkaMVP 2017年5月15日 16:31
    • 回答としてマーク 三輪の牛 2017年5月16日 2:16
    2017年5月15日 16:29

すべての返信

  • リソースで与えることにこだわっているようですが、Bindingで参照することができない理由があるのでしょうか?
    変更したい部分だけをDataTemplateなどで変更したり、RelativeSourceで外側のDataContextを参照すれば大抵のことはできると思うのですが。

    いちおう、ResourceにいれるのをFrameworkElementなどにして、さらに小細工すればできないことは無いですが。

    <!-- App.xaml -->
    <Application x:Class="WpfApplication1.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" StartupUri="MainWindow.xaml">
        <Application.Resources>
            <!-- 共通ヘッダ -->
            <ControlTemplate x:Key="Header" >
                <Border BorderThickness="1" BorderBrush="Red">
                    <StackPanel Background="LightBlue">
                        <TextBlock Text="{DynamicResource Title}" HorizontalAlignment="Center" Foreground="White" FontWeight="Bold" FontSize="24"  Margin="5" Padding="10,5"/>
    
                        <!-- 個別部分定義用 -->
                        <ContentPresenter Content="{Binding}" ContentTemplate="{DynamicResource ResourceKey=HeaderCustom}" />
                    </StackPanel>
                </Border>
            </ControlTemplate>
            <!-- 共通フッタ -->
            <ControlTemplate x:Key="Footer" >
                <Border BorderThickness="1" BorderBrush="Green">
                    <StackPanel >
                       <!--リソースに定義してあるFrameworkElementからとりだしてみる -->
                        <ItemsControl ItemsSource="{Binding Path=Value}" DataContext="{DynamicResource CmdInfos}">
                            <ItemsControl.ItemTemplate>
                                <DataTemplate>
                                    <StackPanel>
                                        <TextBlock>
                                            <Run>リソースからとった</Run>
                                            <Run Text="{Binding Mode=OneWay}" />
                                        </TextBlock>
                                    </StackPanel>
                                    
                                </DataTemplate>
                            </ItemsControl.ItemTemplate>
                        </ItemsControl>
                        
                        <!-- 個別部分定義用 -->
                        <ContentPresenter Content="{Binding}" ContentTemplate="{DynamicResource ResourceKey=FooterCustom}" />
    
                        <TextBlock Text="{Binding Path=PageNumber}" HorizontalAlignment="Center"/>
                    </StackPanel>
                </Border>
            </ControlTemplate>
    
            <!-- 共通ページのテンプレート -->
            <ControlTemplate x:Key="HeaderFooterPage" TargetType="{x:Type Control}">
                <Border BorderBrush="Black" BorderThickness="1">
                    <DockPanel>
                        <Grid DockPanel.Dock="Top" HorizontalAlignment="Stretch">
                            <Control Template="{DynamicResource ResourceKey=Header}" 
                                     DataContext="{Binding HeaderContent}"/>
                        </Grid>
    
                        <Grid DockPanel.Dock="Bottom" HorizontalAlignment="Stretch">
                            <Control Template="{DynamicResource ResourceKey=Footer}"
                                     DataContext="{Binding FooterContent}"/>
                        </Grid>
    
                        <ContentPresenter Content="{Binding Path=Body}" />
                    </DockPanel>
                </Border>
            </ControlTemplate>
        </Application.Resources>
    </Application>
    <Window x:Class="WpfApplication1.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:system="clr-namespace:System;assembly=mscorlib"
            xmlns:app="clr-namespace:WpfApplication1"
            Title="MainWindow" Height="350" Width="600" 
            x:Name="wnd">
        <Window.Resources>
            <!-- バインドしたいリソース対象 -->
            <app:DependencyProxy Value="{Binding Path=CmdInfos}" DataContext="{Binding Path=DataContext, ElementName=wnd}" x:Key="CmdInfos" />
        </Window.Resources>
        <Grid>
            <!-- Resourceに定義したFrameworkElementをVisualTreeにおいてDataContextを取るための小細工 -->
            <ContentControl Content="{Binding Source={StaticResource CmdInfos}}" Visibility="Collapsed"/>
    
            <UniformGrid Columns="2">
    
                <Control x:Name="Page0" Template="{StaticResource HeaderFooterPage}" Margin="10">
                </Control>
                
                <Control x:Name="Page1" Template="{StaticResource HeaderFooterPage}" Margin="10">
                    <Control.Resources>
                        <system:String x:Key="Title">各種設定</system:String>
    
                        <!-- このページのみヘッダに表示する部品のテンプレート -->
                        <DataTemplate x:Key="HeaderCustom" >
                            <TextBlock Text="Custom Text Block" />
                        </DataTemplate>
                    </Control.Resources>
                </Control>
    
                <Control x:Name="Page2" Template="{StaticResource HeaderFooterPage}" Margin="10">
                    <Control.Resources>
                        <system:String x:Key="Title">へっだーたいとる</system:String>
    
                        <!-- このページのみフッタに表示する部品のテンプレート -->
                        <DataTemplate x:Key="FooterCustom" >
                            <ItemsControl ItemsSource="{Binding Path=DataContext.CmdInfos,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=Window}}" >
                                <ItemsControl.ItemsPanel>
                                    <ItemsPanelTemplate>
                                        <WrapPanel />
                                    </ItemsPanelTemplate>
                                </ItemsControl.ItemsPanel>
                                <ItemsControl.ItemTemplate>
                                    <DataTemplate>
                                        <Button Content="{Binding}" Margin="2"/>
                                    </DataTemplate>
                                </ItemsControl.ItemTemplate>
                            </ItemsControl>
                        </DataTemplate>
                    </Control.Resources>
                </Control>
    
                <Control x:Name="Page3" Template="{StaticResource HeaderFooterPage}" Margin="10">
                    <Control.Resources>
                        <!-- このページのみヘッダをまるごと置換してみる -->
                        <ControlTemplate x:Key="Header" >
                            <Grid Margin="10" Background="LightPink" >
                                <StackPanel Margin="2">
                                    <TextBlock Text="ヘッダまるごと変更" />
                                    <TextBlock Text="{Binding Path=.}" HorizontalAlignment="Center"/>
                                </StackPanel>
                                <Rectangle Stroke="Black" StrokeDashArray="10,2" StrokeThickness="2" VerticalAlignment="Stretch">
                                </Rectangle>
                            </Grid>
                        </ControlTemplate>
                    </Control.Resources>
                </Control>
    
    
            </UniformGrid>
        </Grid>
    </Window>
    namespace WpfApplication1
    {
        public partial class MainWindow : System.Windows.Window
        {
            public MainWindow()
            {
                InitializeComponent();
                this.DataContext = new Model();
            }
        }
    
        class Model
        {
            public Model()
            {
                CmdInfos = new System.Collections.Generic.List<string>() { "A", "B", "C" };
                HeaderContent = "へっだ";
                FooterContent = "ふった";
            }
            public System.Collections.Generic.List<string> CmdInfos { get; set; }
            public object HeaderContent { get; set; }
            public object FooterContent { get; set; }
        }
    
    
        class DependencyProxy : System.Windows.FrameworkElement
        {
            public object Value
            {
                get { return (object)GetValue(ValueProperty); }
                set { SetValue(ValueProperty, value); }
            }
    
            public static readonly System.Windows.DependencyProperty ValueProperty
                = System.Windows.DependencyProperty.Register
                    ("Value", typeof(object), typeof(DependencyProxy), new System.Windows.PropertyMetadata(null));
        }
    }


    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    • 編集済み gekkaMVP 2017年5月15日 16:31
    • 回答としてマーク 三輪の牛 2017年5月16日 2:16
    2017年5月15日 16:29
  • gekka さんありがとうございます。
    リソースで与えることしか思いつかなかったというのが実態です。
    「変更したい部分だけをDataTemplateなどで変更したり、RelativeSourceで外側のDataContextを参照」についても調べてみます。
    また、サンプルをありがとうございます。内容を理解したいと思います。


    http://systemartlaboratory.com/

    2017年5月15日 22:38
  • 色々な方法をサンプルに書いてくださりありがとうございました。
    参考にさせていただくことはいっぱいありました。
    リソース内にヘッダー・フッターを独立したテンプレートにした上で、ページ用のテンプレートから参照する方法はより再利用しやすい方法と思いました。
    RelativeSource の指定を参考にさせていただきました。
    DependencyProxy を作成し、リソースに指定した上で、非表示の要素のコンテントに指定することでVisualTreeに含め、DataContextを参照させる方法を理解しました。

    >リソースで与えることにこだわっているようですが、Bindingで参照することができない理由があるのでしょうか?
    >変更したい部分だけをDataTemplateなどで変更したり、RelativeSourceで外側のDataContextを参照すれば大抵のことはできると思うのですが。

    上記のように書いてくださっている部分ですが、コントロールテンプレート側を下記のようにすると個別Window側のリソースに頼らずに CmdInfos を参照することはできました。
    変更前
    <ItemsControl ItemsSource="{Binding Path=Value}" DataContext="{DynamicResource CmdInfos}">
    変更後
    <ItemsControl ItemsSource="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}, Path=DataContext.CmdInfos}">
    モデルに CmdInfos プロパティがあることが前提になってしまいますが、この場合はそのようにモデルを作るつもりですので使えそうに思います。
    ありがとうございました。

    http://systemartlaboratory.com/

    2017年5月16日 2:15