在运行时访问和修改 UWP C++/CX 应用程序中 Button 的子项

Access and modify child items of Button in UWP C++/CX app at runtime

我是 UWP Xaml C++/CX 开发的新手,所以如果我使用了错误的术语,我深表歉意。

我正在尝试制作 image/photo 缩略图 select离子托盘。用户 select 浏览哪个文件夹,内容显示在可滚动的缩略图托盘中,文件名显示在下方。

页面是在 xaml 中构建的,我使用的是 C++/CX。缩略图托盘是使用以下 xaml

构建的
<ScrollViewer Grid.Row="1" Margin="20,20,20,20" ScrollViewer.VerticalScrollBarVisibility="Hidden">
        <Grid x:Name="thumb_grid">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*"/>
                <ColumnDefinition Width="*"/>
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>

            </Grid.RowDefinitions>
        </Grid>
</ScrollViewer>

我使用

通过代码动态添加新行
AddRow() {
   RowDefinition^ rd = ref new RowDefinition();
   rd->Height.Auto;
   thumb_grid->RowDefinitions->Append(rd);
}

然后我使用

用新的 Button 填充每个网格元素
AddImageButton(int row, int col) {
   Button^ b = ref new Button();

   auto style = R->Lookup("ButtonStyle1");
   b->Style = safe_cast<Windows::UI::Xaml::Style^>(style);

   thumb_grid->Children->Append(b);
   thumb_grid->SetRow(b, row);
   thumb_grid->SetColumn(b, col);
}

其中 "ButtonStyle1" 在我的 dictionary.xaml 中定义为

   <Style x:Key="ButtonStyle1" TargetType="Button">

    <Setter Property="Background" Value="Transparent"/>
    <Setter Property="Foreground" Value="{ThemeResource SystemControlForegroundBaseHighBrush}"/>
    <Setter Property="BorderBrush" Value="{ThemeResource SystemControlForegroundTransparentBrush}"/>
    <Setter Property="BorderThickness" Value="{ThemeResource ButtonBorderThemeThickness}"/>
    <Setter Property="Padding" Value="8,4,8,4"/>
    <Setter Property="Margin" Value="10"/>
    <Setter Property="HorizontalAlignment" Value="Stretch"/>
    <Setter Property="VerticalAlignment" Value="Stretch"/>
    <Setter Property="FontFamily" Value="{ThemeResource ContentControlThemeFontFamily}"/>
    <Setter Property="FontWeight" Value="Normal"/>
    <Setter Property="FontSize" Value="{ThemeResource ControlContentThemeFontSize}"/>
    <Setter Property="UseSystemFocusVisuals" Value="True"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="Button">
                <Grid x:Name="RootGrid" Background="{TemplateBinding Background}">
                    <Grid.RowDefinitions>
                        <RowDefinition Height="*"/>
                        <RowDefinition Height="Auto"/>
                    </Grid.RowDefinitions>

                    <VisualStateManager.VisualStateGroups>

                    <!--REMOVED THIS SECTION TO REDUCE EXAMPLE CODE-->

                    </VisualStateManager.VisualStateGroups>
                    <ContentPresenter x:Name="ContentPresenter" AutomationProperties.AccessibilityView="Raw" 
                                      BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" 
                                      ContentTemplate="{TemplateBinding ContentTemplate}" ContentTransitions="{TemplateBinding ContentTransitions}" 
                                      Content="{TemplateBinding Content}" HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" 
                                      Padding="{TemplateBinding Padding}" VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"/>

                    <Image x:Name="thumb_image" Grid.Row="0" HorizontalAlignment="Stretch" Source="Assets/demo.jpg"/>

                    <TextBlock x:Name="thumb_filename" Grid.Row="1" Text="demo.jpg" FontSize="20" Foreground="White"
                            HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
                            TextAlignment="Center"/>

                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

所有这些都会创建所需的输出,如下图所示。显然,我目前只是为按钮内容使用硬编码图像和文本。

问题
问题是如何动态更改自定义图像按钮的内容?

目前我可以通过按名称访问内容来更改按钮内的图像。然而,这要求按钮的图像内容具有唯一的名称。

Xaml代码

<Button x:Name="btnToggleShowHide" Grid.Row="1"  Height="50" Width="50" 
            HorizontalAlignment="Left" VerticalAlignment="Bottom" 
            Background="Transparent"
            Click="btnToggleShowHide_Click">
    <Image x:Name="imgToggleShowHideBtn"  
        Source="Assets/Graphics/show.png"
        HorizontalAlignment="Stretch" 
        VerticalAlignment="Stretch"/>
</Button>

以及换图的C++/CX代码(不含实际逻辑,仅展示使用的代码)

show = ref new BitmapImage(ref new Uri(imgToggleShowHideBtn->BaseUri->RawUri, "Assets/Graphics/show.png"));
hide = ref new BitmapImage(ref new Uri(imgToggleShowHideBtn->BaseUri->RawUri, "Assets/Graphics/hide.png"));

imgToggleShowHideBtn->Source = show;
imgToggleShowHideBtn->Source = hide;

但是我无法使用自定义按钮图像 "demo.jpg" 中的名称直接访问文本

thumb_filename->Text = "test.jpg";

因为文本块 thumb_filename 仅在运行时存在。在 AddImageButton() 中,我需要以某种方式设置内容,但不知道如何访问按钮中的子对象。在我看来,我想做类似

的事情
Button^ b = ref new Button();
auto style = R->Lookup("ButtonStyle1");
b->Style = safe_cast<Windows::UI::Xaml::Style^>(style);

//NOT ACTUAL CODE - THIS IS WHAT I'M TRYIN TO ACHIEVE
b->Image->Source = img;        //obviously wont work because it has no knowledge whether ButtonStyle1 contains an image
b->TextBlock->Text = filename; //obviously wont work because it has no knowledge whether ButtonStyle1 contains a textblock

所以我最终使用了 Nico Zhu - MSFT 建议的方法,并使用 GridView 和数据绑定。

首先我创建了一个按钮class(注意:我还在完善中,所以可能会有一些多余的片段)

namespace Models {

    [Windows::UI::Xaml::Data::Bindable]
    public ref class FileBrowser sealed
    {

    private:
        Platform::String^ _fname;
        Platform::String^ _path;
        ImageSource^ _thumbImage;
        BitmapImage^ _imagedata;

    public:
        FileBrowser();

        property Platform::String^ Filename
        {
            Platform::String^ get()
            {
                return _fname;
            }
            void set(Platform::String^ value)
            {
                _fname = value;
            }
        }

        property Platform::String^ Filepath
        {
            Platform::String^ get()
            {
                return _path;
            }
            void set(Platform::String^ value)
            {
                _path = value;
            }
        }

        property ImageSource^ ThumbImage
        {
            ImageSource^ get()
            {
                return _thumbImage;
            }
            void set(ImageSource^ value)
            {
                _thumbImage = value;
            }
        }

        property BitmapImage^ ImageData
        {
            BitmapImage^ get()
            {
                return _imagedata;
            }
            void set(BitmapImage^ value)
            {
                _imagedata = value;
            }
        }

    };
}

将#include "FileBrowser.h" 添加到 "MainPage.xaml.h"。在包含图像 browser/list 的 View.xaml 页面中,将 xmlns:local="using:Models" 添加到标签中。

我的GridView是按以下方式创建的

<GridView x:Name="thumb_grid" Grid.Row="1" Margin="20,20,20,20" IsItemClickEnabled="True" SelectionMode="Single" ItemClick="thumb_grid_ItemClick">
            <GridView.ItemTemplate>
                <DataTemplate>
                    <Grid MaxHeight="200" MinHeight="200" MinWidth="200" MaxWidth="200">
                        <Grid.RowDefinitions>
                            <RowDefinition Height="*"/>
                            <RowDefinition Height="auto"/>
                        </Grid.RowDefinitions>
                        <Image Grid.Row="0"  Source="{Binding ImageData}"/>
                        <TextBlock Text="{Binding Filename}" Grid.Row="1" FontSize="20" Foreground="Gray" VerticalAlignment="Center" HorizontalAlignment="Center"/>

                    </Grid>
                </DataTemplate>
            </GridView.ItemTemplate>
            <GridView.Items>

            </GridView.Items>
        </GridView>

在运行时使用(f 和 I 提供相关文件和图像数据)

将一个新按钮添加到 GridView
auto btn = ref new Models::FileBrowser();
btn->Filename = f->GetAt(i)->DisplayName + f->GetAt(i)->FileType;
btn->Filepath = f->GetAt(i)->Path;
btn->ImageData = I;
thumb_grid->Items->Append(btn);

这现在会生成显示文件夹内容的所需图像托盘。每个图像 clickable/touchable 并调用相同的函数,在这种情况下,GridView 中的每个项目在按下时调用 thumb_grid_ItemClick()。

按钮的数据可以使用以下方法获取

void View::thumb_grid_ItemClick(Platform::Object^ sender, Windows::UI::Xaml::Controls::ItemClickEventArgs^ e)
{
    auto btn = (Models::FileBrowser^)e->ClickedItem;
}