読者です 読者をやめる 読者になる 読者になる

ぺぷしのーげん

大企業からスタートアップに転職したアプリケーションエンジニアのブログ

WPFのWrapPanelがScrollViewerの中で動かない!を解決しました

f:id:hazakurakeita:20160326235746p:plain
WPFのWrapPanelがScrollViewerの中で動かなくて泣きそうになったので、書き残しておきます。WPFやってる人少ないので情報が全然入らなかったのです。WrapPanelについては結構あるんですけどねえ…。

WrapPanelとは

WrapPanelとはウインドウのサイズに合わせて、パネル内のコンポーネントを自動で再配置してくれる便利なパネルです。これでモニタの解像度の違いや、スマホの画面サイズの違いに対応できます。まずはWrapPanelをウインドウに組み込んだXAMLがこちら。

<Window x:Class="BlogProject.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:BlogProject"
        mc:Ignorable="d"
        Title="MainWindow" Height="377.713" Width="446.994">
    <Grid>
        <WrapPanel>
            <Button x:Name="button1" Content="Button" Width="75" Margin="10"/>
            <Button x:Name="button2" Content="Button" Width="75" Margin="10"/>
            <Button x:Name="button3" Content="Button" Width="75" Margin="10"/>
            <Button x:Name="button4" Content="Button" Width="75" Margin="10"/>
            <Button x:Name="button5" Content="Button" Width="75" Margin="10"/>
            <Button x:Name="button6" Content="Button" Width="75" Margin="10"/>
            <Button x:Name="button7" Content="Button" Width="75" Margin="10"/>
            <Button x:Name="button8" Content="Button" Width="75" Margin="10"/>
        </WrapPanel>

    </Grid>
</Window>

あとはデザイナーで四方向アンカーをオンにしておけばOK。ウインドウのサイズに合わせてWrapPanelの大きさが変わるので、WrapPanelの大きさに応じてButtonの位置を自動で再配置してくれます。

f:id:hazakurakeita:20160327001610p:plain

ウインドウを小さくしてみると、
f:id:hazakurakeita:20160327001758p:plain

ウインドウを大きくしてみると、
f:id:hazakurakeita:20160327001813p:plain

と、ここまでは色んなブログで言及されています。

ScrollViewerの中にViewBoxを設置し、その中にWrapPanelを設置する

マニアックすぎて日本人は誰もやってませんでした。実はViewerの中にズーム機能が必要で、ScrollViewerの中にViewBoxを設置する必要があったのです。そして、そのViewBoxの中にWrapPanelを設置することに。XAMLは以下のようになりました。

<Window x:Class="BlogProject.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:BlogProject"
        mc:Ignorable="d"
        Title="MainWindow" Height="377.713" Width="446.994">
    <Grid>
        <ScrollViewer RenderTransformOrigin="0.5,0.5"  HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
            <Viewbox HorizontalAlignment="Left" VerticalAlignment="Top" Stretch="None">
                <WrapPanel Height="347">
                    <Button x:Name="button1" Content="Button" Width="75" Margin="10"/>
                    <Button x:Name="button2" Content="Button" Width="75" Margin="10"/>
                    <Button x:Name="button3" Content="Button" Width="75" Margin="10"/>
                    <Button x:Name="button4" Content="Button" Width="75" Margin="10"/>
                    <Button x:Name="button5" Content="Button" Width="75" Margin="10"/>
                    <Button x:Name="button6" Content="Button" Width="75" Margin="10"/>
                    <Button x:Name="button7" Content="Button" Width="75" Margin="10"/>
                    <Button x:Name="button8" Content="Button" Width="75" Margin="10"/>
                </WrapPanel>
            </Viewbox>
        </ScrollViewer>
    </Grid>
</Window>

すると、さっきは設定できたアンカーがデザイナーに表示されなくなりました。プロパティにも設定メニューは見当たりません。そして実行してみると…
f:id:hazakurakeita:20160327003540p:plain
あー!WrapPanelがウインドウと連動してサイズが変わらない!!
どうやらViewBoxの中になってしまったので、ウインドウにアンカーできないようです…。ナンテコッタイ。調べても同じようなことやってる人がいない。しょうがないので、英語で調べてみることに。そしたら対応方法が分かりました。さすが世界はデカイわ。

<Window x:Class="BlogProject.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:BlogProject"
        mc:Ignorable="d"
        Title="MainWindow" Height="377.713" Width="446.994">
    <Grid>
        <ScrollViewer Name="MyScrollViewer" RenderTransformOrigin="0.5,0.5"  HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
            <Viewbox HorizontalAlignment="Left" VerticalAlignment="Top" Stretch="None">
                <WrapPanel Height="347" Width="{Binding ElementName=MyScrollViewer, Path=ViewportWidth}">
                    <Button x:Name="button1" Content="Button" Width="75" Margin="10"/>
                    <Button x:Name="button2" Content="Button" Width="75" Margin="10"/>
                    <Button x:Name="button3" Content="Button" Width="75" Margin="10"/>
                    <Button x:Name="button4" Content="Button" Width="75" Margin="10"/>
                    <Button x:Name="button5" Content="Button" Width="75" Margin="10"/>
                    <Button x:Name="button6" Content="Button" Width="75" Margin="10"/>
                    <Button x:Name="button7" Content="Button" Width="75" Margin="10"/>
                    <Button x:Name="button8" Content="Button" Width="75" Margin="10"/>
                </WrapPanel>
            </Viewbox>
        </ScrollViewer>
    </Grid>
</Window>

WrapPanelの横幅をScrollViewerの横幅にバインディングしてしまうのです。なるほどー。WrapPanelのElementNameはバインディングしたいコンポーネントを指定してください。そしてPathにViewportWidthを指定します。これはそのコンポーネントの横幅を取得するプロパティのようです。

さあ動作確認だ

小さくしてみます。
f:id:hazakurakeita:20160327004825p:plain

大きくしてみます。
f:id:hazakurakeita:20160327004701p:plain
よっしゃ!動いてるー!!

いやー、英語読めて良かったわー笑。

stackoverflow.com


おしまい。