しっぽを追いかけて

ぐるぐるしながら考えています

Unity と猫の話題が中心   掲載内容は個人の私見であり、所属組織の見解ではありません

GridView などの DataTemplate の中にあるビジュアル要素を参照する

f:id:matatabi_ux:20140217075449j:plain

GridView、ListView、ListBox、ItemsControl、Hub などで DataTemplate によるデータの見え方をカスタマイズした場合、単にコントロールを XAML 内に配置した場合と違い、コードビハインドで x:Name プロパティによる直接参照ができません・・・

できるかぎりこの問題を回避したいところですが、それでもどうしても DataTemplate 内のビジュアル要素を参照したい!という場合もあります

そういった場合は強引に引っ張り出すしかない!

たとえばストアアプリの場合はこんな風に・・・

/// <summary>
/// タイルクリックイベントハンドラ
/// </summary>
/// <param name="sender">イベント発行者</param>
/// <param name="e">イベント引数</param>
private async void OnItemClick(object sender, ItemClickEventArgs e)
{
    // ViewModel から Item を取得する
    var viewItem = this.itemGridView.ContainerFromItem(e.ClickedItem) as GridViewItem;
    if (viewItem == null)
    {
        await this.Dispatcher.RunAsync(
        CoreDispatcherPriority.Normal,
        () =>
        {
            viewItem = this.itemGridView.ContainerFromItem(e.ClickedItem) as GridViewItem;
        });
    }
    if (viewItem == null)
    {
        this.Dispatcher
    }

    // VisualTree をたどって Image を引っ張り出す
    var image = FindChild<TextBlock>(viewItem.ContentTemplateRoot, "Image");
    if (image == null)
    {
        return;
    }

    var flyout = new Flyout()
    {
        Placement = FlyoutPlacementMode.Top,
        Content = new TextBlock() 
        {
            Text = "画像の説明をここに書いたりする",
        }
    };

    // Image の上に Flyout を表示
    flyout.ShowAt(image);
}

/// <summary>
/// 指定した型の子要素で最初に見つかったビジュアル要素を返す
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="root">探索対象のビジュアル要素</param>
/// <returns>見つかった場合はその要素</returns>
public static T FindChild<T>(DependencyObject root, string name = null) where T : FrameworkElement
{
    var result = root as T;
    if (result != null && (string.IsNullOrEmpty(name) || name.Equals(result.Name)))
    {
        return result;
    }

    int childCount = VisualTreeHelper.GetChildrenCount(root);
    for (int i = 0; i < childCount; i++)
    {
        var child = FindChild<T>(VisualTreeHelper.GetChild(root, i), name);
        if (child != null)
        {
            return child;
        }
    }
    return null;
}

GridView などの ItemsControl の仲間は ContainerFromItem や ContainerFromIndex メソッドによって対応するアイテムのビジュアル要素が取得できます

そこから ContentTemplateRoot の子要素を VisualTreeHelper でたどっていけば DataTemplate 内部にいるビジュアル要素を引っ張り出せます

・・・が、結構めんどくさい上にコード保守性を損なうのでできればこういった処理は回避したいところです;