如何在StackPanel中切换项的可见性

时间:2013-11-29 20:12:42

标签: c# xaml windows-phone-7 windows-phone-8 listbox

我有一个ListBox,其中包含两个项目的StackPanel,一个Image和一个Textblock。根据用户的要求,我希望能够打开或关闭TextBlock的可见性,从而只显示图像。就像现在一样,每个项目的Image和TextBlock组合是垂直堆叠的,Image是一个完美的正方形(当每个图像下显示TextBlock时,最终会创建一个矩形形状)。当用户希望隐藏TextBlock时,我希望ListBox只显示StackPanel项目作为图像的统一正方形(希望这有意义)。

我所拥有的内容如下

<ListBox Name="ListBoxEffects" SelectionMode="Single" ItemsSource="{Binding}" Margin="{Binding}"
                     toolkit:TiltEffect.IsTiltEnabled="True" SelectionChanged="ListBox_SelectionChanged" 
                         ItemContainerStyle="{StaticResource ListBoxItemStyle1}">
                    <ListBox.ItemsPanel>
                        <ItemsPanelTemplate>
                            <toolkit:WrapPanel ItemWidth="159" ItemHeight="Auto" />
                        </ItemsPanelTemplate>
                    </ListBox.ItemsPanel>
                    <ListBox.ItemTemplate>
                        <DataTemplate>
                            <StackPanel Orientation="Vertical"  >
                                <Image Source="{Binding Thumbnail}" Width="155" Height="155" />
                                <TextBlock Text="{Binding Name}" Visibility="{Binding TextBlockVisibility}" TextWrapping="Wrap" FontSize="{StaticResource PhoneFontSizeNormal}" VerticalAlignment="Center" HorizontalAlignment="Center" />
                            </StackPanel>
                        </DataTemplate>
                    </ListBox.ItemTemplate>
                </ListBox>

在后面的代码中创建的ApplicationBar中我有一个菜单项,允许用户选择他或她在每个图像下显示或隐藏TextBlock的首选项

private void BuildLocalizedApplicationBar()
    {
        ApplicationBar = new ApplicationBar();

        ApplicationBarMenuItem showFilterNamesMenuItem = new ApplicationBarMenuItem();
        if (Settings.ShowFilterNames.Value)
            showFilterNamesMenuItem.Text = "Hide names";
        else
            showFilterNamesMenuItem.Text = "Show names";
        showFilterNamesMenuItem.Click += showFilterNamesMenuItem_Click;
        ApplicationBar.MenuItems.Add(showFilterNamesMenuItem);
    }

void showFilterNamesMenuItem_Click(object sender, EventArgs e)
    {
        if(Settings.ShowFilterNames.Value)
        {
            ((ApplicationBarMenuItem)ApplicationBar.MenuItems[0]).Text = "Hide names";
            Settings.ShowFilterNames.Value = false;

            //Toggle the text block visibility to show text here
        }
        else
        {
            ((ApplicationBarMenuItem)ApplicationBar.MenuItems[0]).Text = "Show names";
            Settings.ShowFilterNames.Value = true;

            //Toggle the text block visibility to hide text here
        }        
    }

在导航页面时执行检查,以便可以正确显示或隐藏每个图像下的TextBlock

protected override void OnNavigatedTo(NavigationEventArgs e)
    {
        base.OnNavigatedTo(e);

        if (Settings.ShowFilterNames.Value)     
            //Show the TextBlocks here
        else
            //Hide the TextBlocks here
    }

据我所知,上述实现确实正确切换了菜单项文本并保存了用户的偏好,以便在返回菜单项时根据用户选择的最后一个选项显示文本,但我不确定如何更改ListBox中每个图像下面的TextBlock的可见性?

编辑**

BooleanToVisibilityConverter.cs

//Error on BooleanToVisibilityConverter stating does not implement interface member 'System.Windows.Data.IValueConverter.Convert(object, System.Type, object, System.Globalization.CultureInfo)
public class BooleanToVisibilityConverter : IValueConverter   
{   
    public object Convert(object value, Type targetType, object parameter, CultureInfo language)      
    {      
        return (value is bool && (bool)value) ? Visibility.Visible : Visibility.Collapsed;      
    }
    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo language)      
    {      
        return value is Visibility && (Visibility)value == Visibility.Visible;      
    }   
}

和XAML

xmlns:common="clr-namespace:TestApp.Common"

<phone:PhoneApplicationPage.Resources>
    <common:BooleanToVisibilityConverter x:Key="BoolToVisConv" />
</phone:PhoneApplicationPage.Resources>

<ListBox Name="ListBoxEffects" SelectionMode="Single" ItemsSource="{Binding}" Margin="{Binding}"
                     toolkit:TiltEffect.IsTiltEnabled="True" SelectionChanged="ListBox_SelectionChanged" 
                         ItemContainerStyle="{StaticResource ListBoxItemStyle1}">
                    <ListBox.ItemsPanel>
                        <ItemsPanelTemplate>
                            <toolkit:WrapPanel ItemWidth="159" ItemHeight="Auto" />
                        </ItemsPanelTemplate>
                    </ListBox.ItemsPanel>
                    <ListBox.ItemTemplate>
                        <DataTemplate>
                            <StackPanel Orientation="Vertical"  >
                                <Image Source="{Binding Thumbnail}" Width="155" Height="155" />
                                <TextBlock Text="{Binding Name}" Visibility="{Binding IsTextBlockVisible, Converter={StaticResource BoolToVisConv}}" TextWrapping="Wrap" FontSize="{StaticResource PhoneFontSizeNormal}" VerticalAlignment="Center" HorizontalAlignment="Center" />
                            </StackPanel>
                        </DataTemplate>
                    </ListBox.ItemTemplate>
                </ListBox>

4 个答案:

答案 0 :(得分:3)

使用此方法查找每个ListBoxItem的Textblock

public static T FindFirstElementInVisualTree<T>(DependencyObject parentElement) where T : DependencyObject
        {
            try
            {
                int childCount = VisualTreeHelper.GetChildrenCount(parentElement);
                if (childCount == 0)
                    return null;

                for (int i = 0; i < childCount; i++)
                {
                    var child = VisualTreeHelper.GetChild(parentElement, i);
                    if (child != null && child is T)
                    {
                        return (T)child;
                    }
                    else
                    {
                        var result = FindFirstElementInVisualTree<T>(child);
                        if (result != null)
                            return result;
                    }
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
            return null;
        }

此方法将从DataTemplate返回指定类型的第一个元素。并允许您使用该个人元素。

为此您可以使用以下代码段

    for(i=0;i<ListBoxEffects.count;i++)
    {
     ListBoxItem item = ListBoxEffects.ItemContainerGenerator.ContainerFromIndex(i) as ListBoxItem;
     StackPanel TargetStackPanel = common.FindFirstElementInVisualTree<StackPanel>(item);
     TextBlock TargetTextBlock= TargetStackPanel.Children[1] as TextBlock;
     TargetTextBlock.Visibility = Visibility.Visible;
     ListBoxEffects.UpdateLayout();
    }

使用上面的代码分别显示或隐藏文本块只需更改行

即可
 TargetTextBlock.Visibility = Visibility.Visible;

 TargetTextBlock.Visibility = Visibility.Collapsed;

答案 1 :(得分:0)

Visibility属性是Visibility类型的枚举。如果它是一个布尔值,这将会更容易,但它不是。

您应该定义一个静态资源来实例化BooleanToVisibility转换器,然后将Visibility属性绑定到DataContext中的布尔属性。这是一个有效的例子:

<Window x:Class="WpfApplication4.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <BooleanToVisibilityConverter x:Key="BoolToVisConv" />
    </Window.Resources>
    <StackPanel>
        <TextBlock Text="Hide me" Visibility="{Binding IsTextBlockVisible, Converter={StaticResource BoolToVisConv}}" />
        <Button Content="Toggle TextBlock" Name="ToggleItButton" Click="ToggleItButton_Click" />
</StackPanel>

public partial class MainWindow : Window, INotifyPropertyChanged {
    private bool m_IsTextBlockVisible = true;
    public bool IsTextBlockVisible {
        get { return m_IsTextBlockVisible; }
        set { m_IsTextBlockVisible = value; NotifyPropertyChanged("IsTextBlockVisible"); }
    }

    public MainWindow() {
        InitializeComponent();
        DataContext = this;
    }

    private void ToggleItButton_Click(object sender, RoutedEventArgs e) {
        IsTextBlockVisible = !IsTextBlockVisible;
    }

    public event PropertyChangedEventHandler PropertyChanged;

    private void NotifyPropertyChanged(string name) {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(name));
    }
}

答案 2 :(得分:0)

您可以创建两个DataTemplate。一个DataTemplate有名字,另一个没有名字。

XAML:

<Window ...
        >
    <Window.Resources>
        <DataTemplate x:Key="TemplateWithName">
            <StackPanel Orientation="Vertical"  >
                <Image Source="{Binding Thumbnail}" Width="155" Height="155" />
                <TextBlock Text="{Binding Name}" TextWrapping="Wrap" VerticalAlignment="Center" HorizontalAlignment="Center" />
            </StackPanel>
        </DataTemplate>
        <DataTemplate x:Key="TemplateWithoutName">
            <StackPanel Orientation="Vertical"  >
                <Image Source="{Binding Thumbnail}" Width="155" Height="155" />               
            </StackPanel>
        </DataTemplate>
    </Window.Resources>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="30" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <StackPanel>
            <ToggleButton x:Name="tbName" Content="Name" Click="tbName_Click" />
        </StackPanel>
        <ListBox Name="ListBoxEffects" SelectionMode="Single" ItemsSource="{Binding}"
                 Grid.Row="1" ItemTemplate="{StaticResource TemplateWithName}">
            <ListBox.ItemsPanel>
                <ItemsPanelTemplate>
                    <WrapPanel ItemWidth="159" ItemHeight="Auto" />
                </ItemsPanelTemplate>
            </ListBox.ItemsPanel>           
        </ListBox>
    </Grid>
</Window>

代码隐藏:

private void tbName_Click(object sender, RoutedEventArgs e)
{
    if (tbName.IsChecked.Value)
    {
        ListBoxEffects.ItemTemplate = this.FindResource("TemplateWithoutName") as DataTemplate;
    }
    else
    {
        ListBoxEffects.ItemTemplate = this.FindResource("TemplateWithName") as DataTemplate;
    }
}

答案 3 :(得分:0)

马修,除了你的问题:

public class BooleanToVisibilityConverter : IValueConverter
{
    private object GetVisibility(object value)
    {
        if (!(value is bool))
            return Visibility.Collapsed;
        bool objValue = (bool)value;
        if (objValue)
        {
            return Visibility.Visible;
        }
        return Visibility.Collapsed;
    }
    public object Convert(object value, Type targetType, object parameter, string language)
    {
        return GetVisibility(value);
    }
    public object ConvertBack(object value, Type targetType, object parameter, string language)
    {
        throw new NotImplementedException();
    }


}