locked
smf:SMFPlayerをExpressionBlend4でコントロールを追加したときに、C#で追加したコントロールの属性をGetTemplateChild()で取得したいけど取得できない RRS feed

  • 質問

  • はじめて質問します

    smf:SMFPlayerのオリジナルプレイヤーを作っています。

    XAMLにsmf:SMFPlayerを記載するとデフォルトのプレイヤーが表示されます。

        <Grid x:Name="LayoutRoot" Background="White">
            <smf:SMFPlayer
                x:Name="Player"
                Style="{StaticResource SMFPlayerMyStyle}"
                BufferingTime="0:0:0.3"
                ContinuousPlay="False"
                MediaEnded="Player_MediaEnded"/>
        </Grid>

    これをカスタマイズしています。

    ExpressionBlend4で「テンプレートの編集」でPlayerRoot以下の不要なコントロールを削除して、新しくパネルやボタンコントロールを追加しました。

    このときやりたいことは、C#のコード内から既存のplayercontrolsや追加したパネルやコントロールを操作することです。

    たとえばマウスが一定時間動いてなければplayercontrolsのd:IsHiddenを"True"にして非表示にしたり、コンボボックスを追加してその中の選択されている数値を取得するとかです。

    追加したコントロールには「x:Name="名前"」といった感じで名前を付けました。

    コード内で以下のようにコントロールが参照できると思ったらできず、

    this.Player.名前

    ネットで調べたらGetTemplateChild("名前")やFindName("名前")で参照できるようなことが書いてありました。

    http://msdn.microsoft.com/ja-jp/library/system.windows.controls.control.gettemplatechild%28v=vs.95%29.aspx

    http://msdn.microsoft.com/ja-jp/magazine/ff646972.aspx

    しかし、コード内でそのように記述してもnullが返ってきて取得できません。

    C#コード内から<UserControl.Resources>内のコントロールを参照する方法をご教授ください。

    以上よろしくお願いします。

    【C#コード内】

                //パネルを非表示にする

                Grid grd = (Grid)this.GetTemplateChild("playercontrols");
                grd.Visibility = Visibility.Collapsed;

                //選択された項目を取得する

                ComboBox cmb = (ComboBox)this.GetTemplateChild("ComboStepValue");
                cmb.SelectedItem

     

    【XAML内抜粋】

    <UserControl

        xmls ...省略
        d:DesignHeight="480" d:DesignWidth="640" mc:Ignorable="d">
        <UserControl.Resources>
            <Style x:Key="SMFPlayerJissStyle" TargetType="smf:SMFPlayer" x:Name="GanasonicPlayerStyle">
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="smf:SMFPlayer">
                            <Grid x:Name="PlayerRoot" Background="Black" HorizontalAlignment="{TemplateBinding HorizontalAlignment}" VerticalAlignment="{TemplateBinding VerticalAlignment}" d:DesignWidth="756" d:DesignHeight="387" Width="{TemplateBinding Width}" Height="{TemplateBinding Height}">
                                <Grid x:Name="playercontrols"  Grid.ColumnSpan="1" Margin="0" HorizontalAlignment="Left" Height="150" VerticalAlignment="Bottom" MouseRightButtonDown="onClickPlayerControlPanel">
                                    <ComboBox x:Name="ComboStepValue" Grid.Column="11" Margin="0,0,0,4" VerticalAlignment="Bottom" HorizontalAlignment="Left" Width="60" Style="{StaticResource ComboBoxMyStyle}" ToolTipService.ToolTip="ジャンプ幅(指定ミリ秒)を選択する">
                                        <ComboBoxItem>500</ComboBoxItem>
                                        <ComboBoxItem>1000</ComboBoxItem>
                                        <ComboBoxItem>1500</ComboBoxItem>
                                        <ComboBoxItem>2000</ComboBoxItem>
                                        <ComboBoxItem>3000</ComboBoxItem>
                                        <ComboBoxItem>4000</ComboBoxItem>
                                        <ComboBoxItem>5000</ComboBoxItem>
                                        <ComboBoxItem>10000</ComboBoxItem>
                                        <ComboBoxItem>15000</ComboBoxItem>
                                        <ComboBoxItem>30000</ComboBoxItem>
                                        <ComboBoxItem>60000</ComboBoxItem>
                                       </ComboBox>               
                                    <Button x:Name="NJumpFW" Grid.Column="12" Content="FJMP" Margin="0" VerticalAlignment="Bottom" HorizontalAlignment="Left" Width="40" Style="{StaticResource ButtonStyleJissFWJump}" ToolTipService.ToolTip="指定ミリ秒だけ後ろへジャンプする" Click="onClickJumpForward"/>
                                </Grid>
                            </Grid>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>
        </UserControl.Resources>
        <Grid x:Name="LayoutRoot" Background="White">
            <smf:SMFPlayer
                x:Name="Player"
                Style="{StaticResource SMFPlayerJissStyle}"
                BufferingTime="0:0:0.3"
                ContinuousPlay="False"
                MediaEnded="Player_MediaEnded"/>
        </Grid>
    </UserControl>

    2011年5月11日 7:34

回答

  • こんにちは。
    書いてあるC#コードだと、this(Page)のテンプレートを探しちゃってます。
    テンプレートを使用しているコントロールのGetTemplateChildならば、期待通り取得できます。

    しかし、GetTemplateChildメソッドはprotectedなんですよね。
    Pageからアクセスできません。

    ですので、こういう場合は
    VisualTreeHelper クラスを使うと、やりたいことができるのではないかなと思います。

    少し使いにくいヘルパークラスなんですが、
    ちょっと改造すれば子孫を再帰的に取得したり、名前を指定して取得したりはすぐ出来るようになると思います。
    できそうになければまた聞いてみてください。

    以上、ご参考になれば幸いです。
    • 回答としてマーク 山本春海 2011年5月25日 8:44
    2011年5月11日 10:16
  • >ganasonicさん

    こんにちは。
    おかしいですね。。。
    そんなはずはないはずです。。。

    1つ試していただきたいのですが、

    var cc = VisualTreeHelper.GetChildrenCount(Player);

    のように、Playerを直接指定しても「1」が返ってこないでしょうか。

    また、
    返信文を見ていてResouceとビジュアルツリーの関係を勘違いしていらっしゃるようなので少しだけ。

    >個人的には最初の
    >count = VisualTreeHelper.GetChildrenCount(this.TopElement);
    >で2が返ると思ったのですが、1でした。

    VisualTreeHelperは、その名の通りビジュアルツリーをたどりますので、Resourceは含まれません。

    >SMFPlayerの子要素を取得してみてもnullが返ってきます。
    >smf:SMFPlayer要素には子要素がないのでそうだろうななと思いました。

    XAMLとしては要素が無いのですが、リソースから生成されたコントロールが描画されますので、
    ビジュアルツリーとしては要素が追加されています。
    ですので、smf:SMFPlayerはビジュアルツリーでは子要素を持ちます。

    #実際に出力されている要素、のように考えると良いかもです。
    #実際に出力されている要素を取得しないと、ganasonicさんが取得したいと思っているコントロールは取得できませんよね?
    #Resourceと実際のコントロールは1:nになりますので、Resourceの値を取得しても困りませんか?

    論理ツリー、ビジュアルツリー等の概念は最初わかりにくかったりしますよね。
    (実は僕もわかるのに苦労しました・・・。)

    下記はWPFのものですが、Silverlightもこれに準拠していますので参照してください。
    ■WPF のツリー
    http://msdn.microsoft.com/ja-jp/library/ms753391.aspx
     
    以上、おそらく記述ミスか何かで取得できていないだけだと思いますので、
    上記を試していただけますか。

    よろしくお願いします。

    • 回答としてマーク 山本春海 2011年5月25日 8:44
    2011年5月12日 11:51
  • おはようございます

    いつも丁寧な説明ありがとうございます。

     

    >  var cc = VisualTreeHelper.GetChildrenCount(Player);

    これを試してもやはりダメでした。

    が、もしやと思い、記述している箇所をみたらコンストラクタ内でした。

    画面にまだ描画される前だったせいか子要素の数が0だったようです。

    画面構築後の処理部分に記述箇所を変更してみたら1が返り、GetChildで子要素が取得できました。

    そして再帰的に要素を検索することで希望のコントロールが取得できました。

    あれこれ試行錯誤をして2週間かかりましたが、ご教授頂いたおかげで解決しました。

    いろいろありがとうございました。

    Resouceとビジュアルツリーの関係についての理解も低かったためこれを機に精進します。

     

    • 回答としてマーク ganasonic 2011年5月30日 6:00
    2011年5月13日 0:51

すべての返信

  • こんにちは。
    書いてあるC#コードだと、this(Page)のテンプレートを探しちゃってます。
    テンプレートを使用しているコントロールのGetTemplateChildならば、期待通り取得できます。

    しかし、GetTemplateChildメソッドはprotectedなんですよね。
    Pageからアクセスできません。

    ですので、こういう場合は
    VisualTreeHelper クラスを使うと、やりたいことができるのではないかなと思います。

    少し使いにくいヘルパークラスなんですが、
    ちょっと改造すれば子孫を再帰的に取得したり、名前を指定して取得したりはすぐ出来るようになると思います。
    できそうになければまた聞いてみてください。

    以上、ご参考になれば幸いです。
    • 回答としてマーク 山本春海 2011年5月25日 8:44
    2011年5月11日 10:16
  • こんにちは

    返信ありがとうございました。

    ちなみにC#コードは「MyPlayer.xaml.cs」で、xamlは「MyPlayer.xaml」なので同じテンプレートだと思ってました。

     

    adobeのflexのような感覚ではできないんですね...

     

    まずはVisualTreeHelper クラスを使って出来るか調査してみます。

    また行き詰まったら質問させて頂きます。
    2011年5月12日 0:08
  • ちなみにC#コードは「MyPlayer.xaml.cs」で、xamlは「MyPlayer.xaml」なので同じテンプレートだと思ってました。

    あ、ちょっと誤解があったみたいなので補足です。

    私の書いた

    「書いてあるC#コードだと、this(Page)のテンプレートを探しちゃってます。」

    「テンプレートを使用しているコントロールのGetTemplateChildならば、期待通り取得できます。」

    というのは、

    thisMyPlayer.xaml.csを指し(正確にはそこで定義されてるクラスですが)、

    テンプレートを使用しているコントロールは、Style経由ですがXamlの下の方の<smf:SMFPlayer>を指すので、

    MyPlayer.xaml.csでなく、<smf:SMFPlayer>でGetTemplateChildしなければいけませんよ~ってことを言いたかったのでした。

     

    どちらにせよ、VisualTreeHelper クラスを使った方が良いと思いますけど、補足させていただきました。

    以上、がんばってください。

     

    2011年5月12日 3:03
  •  

    こんにちは

    補足の件、ありがとうございます。

     

    本題ですが、結論から言うとダメでした。

    int count = VisualTreeHelper.GetChildrenCount(this.TopElement);

    で1件の子要素があるので以下のように子要素を取得してみます。

    DependencyObject obj = VisualTreeHelper.GetChild(this.TopElement, 0);

    objの内容をウォッチで見ると「{System.Windows.Controls.Grid}」なのでこれはOKでした。

    さらにobjの子要素を取得すると以下のSMFPlayerが返ってきます。

    {Microsoft.SilverlightMediaFramework.Core.SMFPlayer}

    これもOKでした。

    しかし、さらにSMFPlayerの子要素の件数を取得しようとすると0が返ってきます。

    int count1 = VisualTreeHelper.GetChildrenCount(obj);

    DependencyObject obj1 = VisualTreeHelper.GetChild(obj, 0);

    SMFPlayerの子要素を取得してみてもnullが返ってきます。

    smf:SMFPlayer要素には子要素がないのでそうだろうななと思いました。

    個人的には最初の

    count = VisualTreeHelper.GetChildrenCount(this.TopElement);

    で2が返ると思ったのですが、1でした。

    結果、<UserControl.Resources>要素は取得できず<Grid>要素しか取得できませんでした。

    2が返ってくればGetChildの0指定でUserControl.Resourcesが取得でき、1指定でGridが返るはずだったのに残念な結果になりました。

     

    ちなみに以下のようにFindNameを使っても取得できませんでした。

    ComboBox cmb = (ComboBox)this.Player.FindName("ComboStepValue");

    ほかにも

    foreach (object i in this.Resources.Values)

    を利用してみたりしましたが、希望するところまでいきつけませんでした。

    微力ながらいろいろ調査した結果、本日も<UserControl.Resources>内のコントロールを参照することはできませんでした。

    再度、ご教授頂ければ幸いです。

    以上よろしくお願いします。

     

    【XAML内抜粋】

    <UserControl

        xmls ...省略

    Name="TopElement"
        d:DesignHeight="480" d:DesignWidth="640" mc:Ignorable="d">
        <UserControl.Resources>
            <Style x:Key="SMFPlayerJissStyle" TargetType="smf:SMFPlayer" x:Name="GanasonicPlayerStyle">
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="smf:SMFPlayer">
                            <Grid x:Name="PlayerRoot" Background="Black" HorizontalAlignment="{TemplateBinding HorizontalAlignment}" VerticalAlignment="{TemplateBinding VerticalAlignment}" d:DesignWidth="756" d:DesignHeight="387" Width="{TemplateBinding Width}" Height="{TemplateBinding Height}">
                                <Grid x:Name="playercontrols"  Grid.ColumnSpan="1" Margin="0" HorizontalAlignment="Left" Height="150" VerticalAlignment="Bottom" MouseRightButtonDown="onClickPlayerControlPanel">
                                    <ComboBox x:Name="ComboStepValue" Grid.Column="11" Margin="0,0,0,4" VerticalAlignment="Bottom" HorizontalAlignment="Left" Width="60" Style="{StaticResource ComboBoxMyStyle}" ToolTipService.ToolTip="ジャンプ幅(指定ミリ秒)を選択する">
                                        <ComboBoxItem>500</ComboBoxItem>
                                        <ComboBoxItem>1000</ComboBoxItem>
                                        <ComboBoxItem>1500</ComboBoxItem>
                                        <ComboBoxItem>2000</ComboBoxItem>
                                       </ComboBox>               
                                    <Button x:Name="NJumpFW" Grid.Column="12" Content="FJMP" Margin="0" VerticalAlignment="Bottom" HorizontalAlignment="Left" Width="40" Style="{StaticResource ButtonStyleJissFWJump}" ToolTipService.ToolTip="指定ミリ秒だけ後ろへジャンプする" Click="onClickJumpForward"/>
                                </Grid>
                            </Grid>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>
        </UserControl.Resources>
        <Grid x:Name="LayoutRoot" Background="White">
            <smf:SMFPlayer
                x:Name="Player"
                Style="{StaticResource SMFPlayerJissStyle}"
                BufferingTime="0:0:0.3"
                ContinuousPlay="False"
                MediaEnded="Player_MediaEnded"/>
        </Grid>
    </UserControl>

    2011年5月12日 9:53
  • >ganasonicさん

    こんにちは。
    おかしいですね。。。
    そんなはずはないはずです。。。

    1つ試していただきたいのですが、

    var cc = VisualTreeHelper.GetChildrenCount(Player);

    のように、Playerを直接指定しても「1」が返ってこないでしょうか。

    また、
    返信文を見ていてResouceとビジュアルツリーの関係を勘違いしていらっしゃるようなので少しだけ。

    >個人的には最初の
    >count = VisualTreeHelper.GetChildrenCount(this.TopElement);
    >で2が返ると思ったのですが、1でした。

    VisualTreeHelperは、その名の通りビジュアルツリーをたどりますので、Resourceは含まれません。

    >SMFPlayerの子要素を取得してみてもnullが返ってきます。
    >smf:SMFPlayer要素には子要素がないのでそうだろうななと思いました。

    XAMLとしては要素が無いのですが、リソースから生成されたコントロールが描画されますので、
    ビジュアルツリーとしては要素が追加されています。
    ですので、smf:SMFPlayerはビジュアルツリーでは子要素を持ちます。

    #実際に出力されている要素、のように考えると良いかもです。
    #実際に出力されている要素を取得しないと、ganasonicさんが取得したいと思っているコントロールは取得できませんよね?
    #Resourceと実際のコントロールは1:nになりますので、Resourceの値を取得しても困りませんか?

    論理ツリー、ビジュアルツリー等の概念は最初わかりにくかったりしますよね。
    (実は僕もわかるのに苦労しました・・・。)

    下記はWPFのものですが、Silverlightもこれに準拠していますので参照してください。
    ■WPF のツリー
    http://msdn.microsoft.com/ja-jp/library/ms753391.aspx
     
    以上、おそらく記述ミスか何かで取得できていないだけだと思いますので、
    上記を試していただけますか。

    よろしくお願いします。

    • 回答としてマーク 山本春海 2011年5月25日 8:44
    2011年5月12日 11:51
  • おはようございます

    いつも丁寧な説明ありがとうございます。

     

    >  var cc = VisualTreeHelper.GetChildrenCount(Player);

    これを試してもやはりダメでした。

    が、もしやと思い、記述している箇所をみたらコンストラクタ内でした。

    画面にまだ描画される前だったせいか子要素の数が0だったようです。

    画面構築後の処理部分に記述箇所を変更してみたら1が返り、GetChildで子要素が取得できました。

    そして再帰的に要素を検索することで希望のコントロールが取得できました。

    あれこれ試行錯誤をして2週間かかりましたが、ご教授頂いたおかげで解決しました。

    いろいろありがとうございました。

    Resouceとビジュアルツリーの関係についての理解も低かったためこれを機に精進します。

     

    • 回答としてマーク ganasonic 2011年5月30日 6:00
    2011年5月13日 0:51