0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

【WPF】ControlTemplateの中にViewModelのプロパティをバインドする

Last updated at Posted at 2019-03-21

やりたいこと

ItemsControlの中身をカスタムするときに、下記のように書いて、Borderの太さをViewModelのプロパティで変化させようとしたが、うまくいかなかった。どうにかして、うまくバインドしたい。

うまくいかなかったのでまねしないでください.xaml
<ItemsControl ItemsSource="{Binding Pl}" Width="600" Height="600">
    <ItemsControl.Template>
        <ControlTemplate TargetType="ItemsControl">
            <!-- このBorderのThicknessに、ViewModelのプロパティMyThicknessをバインドする -->
            <Border BorderThickness="{Binding Path=MyThickness}">
                <ItemsPresenter/>
            </Border>
        </ControlTemplate>
    </ItemsControl.Template>
</ItemsControl>

結果

「Binding RelativeSource」を使うとうまくいった。
相対参照で一番上の親のWindowまでさかのぼり、そいつのDataContext(=ViewModel)のプロパティを見よ、ということをしないといけない様子。
(ControlTemplateとその外側は分断されている)

MainWindow.xaml
<Window x:Class="WpfApp1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp1"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800" >
    <Window.DataContext>
        <local:ViewModel />
    </Window.DataContext>
    
    <Grid Background="#33990000" Name="MainGrid">
        <Viewbox>
            <ItemsControl ItemsSource="{Binding Pl}" Width="600" Height="600">
                <ItemsControl.Template>
                    <ControlTemplate TargetType="ItemsControl">
                        <!-- このBorderのThicknessに、ViewModelのプロパティをバインドする -->
                        <Border BorderThickness="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Grid}}, Path=DataContext.MyThickness}">
                            <ItemsPresenter />
                        </Border>
                    </ControlTemplate>
                </ItemsControl.Template>

                <ItemsControl.ItemsPanel>
                    <ItemsPanelTemplate>
                        <Grid />
                    </ItemsPanelTemplate>
                </ItemsControl.ItemsPanel>
            </ItemsControl>
        </Viewbox>
    </Grid>
</Window>

ViewModelはこのような感じ。
(ちなみに、ここではItemsControlを使って、List(ObservableCollection)に入れたPolylineを複数重ねて表示するということをしようとしている。ただの実験)

ViewModel.cs
namespace WpfApp1
{
    class ViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        // ItemsControlのSourceに設定するコレクション
        public ObservableCollection<Polyline> Pl { get { return pl; } }
        public ObservableCollection<Polyline> pl = new ObservableCollection<Polyline>();
        
        // DataTemplateの中に直接バインドしたいプロパティ
        public Thickness MyThickness { get; } = new Thickness(20);

        public ViewModel()
        {
            // 画面に描くPolylineをつくる
            Polyline pl1 = new Polyline();
            pl1.Points.Add(new Point(10, 10));
            pl1.Points.Add(new Point(200, 150));
            pl1.Stroke = new SolidColorBrush(Colors.Red);
            pl1.StrokeThickness = 5;
            pl.Add(pl1);

            Polyline pl2 = new Polyline();
            pl2.Points.Add(new Point(15, 15));
            pl2.Points.Add(new Point(250, 600));
            pl2.Points.Add(new Point(600, 400));
            pl2.Stroke = new SolidColorBrush(Colors.Yellow);
            pl2.StrokeThickness = 5;
            pl.Add(pl2);
        }
    }
}

image.png

追記(19/04/04)

他にも書き方はいろいろあるっぽい。

MainWindow.xaml
<Window x:Class="WpfApp1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp1"
        mc:Ignorable="d"
        Title="MainWindow" Height="Auto" Width="Auto"
        Name="Root">
    <Window.DataContext>
        <local:ViewModel />
    </Window.DataContext>

    <Grid Background="#33990000" Name="MainGrid">
        <ItemsControl Width="Auto" Height="Auto">
            <ItemsControl.Template>
                <ControlTemplate TargetType="ItemsControl">
                    <!-- このBorderのThicknessに、ViewModelのプロパティをバインドする -->

                    <!-- OKな書き方①:自分の親をたどりGridまで行ったらそのDataContext(=VM)のプロパティを見る -->
                    <Border BorderBrush="Green"
                            BorderThickness="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.MyThickness}">

                    <!-- OKな書き方②:一番上の親(Window)のDataContext(VM)の中のプロパティにバインドする -->
                    <!--<Border BorderBrush="Green"
                        BorderThickness="{Binding Path=DataContext.MyThickness, ElementName=Root}">-->

                    <!-- OKな書き方③:DataContextにMyThicknessがあればそれをBinding、なければItemsControlの選択項目(自分に該当する項目)のMyThicknessをBinding -->
                    <!-- 今回の場合、DataContextにMyThicknessがあってItemsControlにないので、VMにバインドされてる -->
                    <!--<Border BorderBrush="Green"
                        BorderThickness="{Binding Path=MyThickness}">-->
                        
                    <!-- NGな書き方:Root=Windowで、その中のプロパティにバインド、とすると、VMのプロパティではなくコードビハインドにあるプロパティ、になってしまう。-->
                    <!-- 逆に、コードビハインドに書いたプロパティにバインドするのであれば、これでOK -->
                    <!--<Border BorderBrush="Green"
                        BorderThickness="{Binding Path=MyThickness, ElementName=Root}">-->
                    
                        <ItemsPresenter />
                    </Border>

                </ControlTemplate>
            </ItemsControl.Template>

            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <!-- ここに、Gridを指定する。-->
                    <Canvas />
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
        </ItemsControl>
    </Grid>
</Window>

参考

データバインディングの概要
https://docs.microsoft.com/ja-jp/dotnet/framework/wpf/data/data-binding-overview

WPF の {Binding Path=/}
https://ufcpp.wordpress.com/2011/01/28/wpf-%E3%81%AE-binding-path/

stack overflow
https://stackoverflow.com/questions/33694091/bind-from-controltemplate-to-viewmodel

自分のページ
https://qiita.com/tera1707/items/73cda312b7cd9c4df40d

コード

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?