Rose Rose - 1 month ago 7
C# Question

Photo and description on Flipview

I have a flipview which contains photos and descriptions. I want when photo1 be tapped, then descbox1 not visible. And if photo1 be tapped again, then descbox1 will appear.

XAML:

<FlipView x:Name="narrowFlipview"
ItemsSource="{Binding Group.Items}"
SelectedItem="{Binding Item, Mode=TwoWay}"
Foreground="{x:Null}"
Visibility="Collapsed">
<FlipView.ItemTemplate>
<DataTemplate>
<Grid x:Name="content1"
Margin="0,0,0,0">
<Image x:Name="photo1"
Margin="0,0,10,10"
Source="{Binding ImagePath}"
Tapped="photo_Tapped" />
<Grid x:Name="detail"
VerticalAlignment="Bottom"
Height="200">
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
<RowDefinition Height="auto" />
</Grid.RowDefinitions>
<Button x:Name="hideBtn"
Height="50"
Width="50"
Margin="0,0,5,0"
HorizontalAlignment="Right"
VerticalAlignment="Bottom"
Grid.Row="0"
Click="hideBtn_Click">
<Button.Background>
<ImageBrush Stretch="Uniform"
ImageSource="images/media/ikon-56-app-white-down.png" />
</Button.Background>
</Button>
<Button x:Name="detailBtn"
Height="50"
Width="50"
Margin="0,0,5,0"
HorizontalAlignment="Right"
VerticalContentAlignment="Bottom"
Grid.Row="1"
Visibility="Collapsed"
Click="detailBtn_Click">
<Button.Background>
<ImageBrush Stretch="Uniform"
ImageSource="images/media/ikon-56-app-white-up.png" />
</Button.Background>
</Button>
<ScrollViewer x:Name="descBox1"
Grid.Row="1"
Height="150"
Background="#95000000"
VerticalScrollBarVisibility="Auto">
<TextBlock x:Name="descriptionText1"
Text="{Binding Description}"
Margin="20,20,20,0"
TextWrapping="Wrap"
TextAlignment="Justify"
VerticalAlignment="Top"
Height="auto"
Foreground="White"
FontSize="21" />
</ScrollViewer>
</Grid>
</Grid>
</DataTemplate>
</FlipView.ItemTemplate>
</FlipView>


I tried using the code below:

private DependencyObject FindChildControl<T>(DependencyObject control, string ctrlName)
{
int childNumber = VisualTreeHelper.GetChildrenCount(control);
for (int i = 0; i < childNumber; i++)
{
DependencyObject child = VisualTreeHelper.GetChild(control, i);
FrameworkElement fe = child as FrameworkElement;
// Not a framework element or is null
if (fe == null) return null;

if (child is T && fe.Name == ctrlName)
{
// Found the control so return
return child;
}
else
{
// Not found it - search children
DependencyObject nextLevel = FindChildControl<T>(child, ctrlName);
if (nextLevel != null)
return nextLevel;
}
}
return null;
}
public bool _IsOn;
public bool IsOn
{
get
{
return _IsOn;
}
set
{
_IsOn = value;
}
}
private void photo_Tapped(object sender, TappedRoutedEventArgs e)
{
ScrollViewer descBox1 = FindChildControl<ScrollViewer>(this, "descBox1") as ScrollViewer;
Button hideBtn = FindChildControl<Button>(this, "hideBtn") as Button;
Button detailBtn = FindChildControl<Button>(this, "detaiBtn") as Button;
IsOn = !IsOn;
if (_IsOn)
{
descBox1.Visibility = Visibility.Collapsed;
hideBtn.Visibility = Visibility.Collapsed;
detailBtn.Visibility = Visibility.Visible;
}
else
{
descBox1.Visibility = Visibility.Visible;
hideBtn.Visibility = Visibility.Visible;
detailBtn.Visibility = Visibility.Collapsed;
}
}


But when I tap photo1, descBox1 would not collapse, hiddenBtn would not collapsed, detailBtn would not visible.
How to handle it?

Answer

According to your requirement, I'd think using Binding here to control these control's visibility would be a simpler solution. For example, we can take advantage of the Tag property.

Ref Remarks in FrameworkElement.Tag property:

The scenario for the Tag property is to provide an general-purpose property on all FrameworkElement classes that supports data binding, animation and styles for itself but where the property's value does not have any implicit meaning to platform subsystems like layout, app model, text, input and so on.

Here we can use this property to store the Visibility and in other controls, bind ther Visibility property to this property like:

<ScrollViewer x:Name="descBox1"
              Grid.Row="1"
              Height="150"
              Background="#95000000"
              VerticalScrollBarVisibility="Auto"
              Visibility="{Binding Tag, ElementName=photo1}">
    <TextBlock x:Name="descriptionText1"
               Height="auto"
               Margin="20,20,20,0"
               VerticalAlignment="Top"
               FontSize="21"
               Foreground="White"
               Text="{Binding Description}"
               TextAlignment="Justify"
               TextWrapping="Wrap" />
</ScrollViewer>

Then in photo_Tapped method, we can change Tag's value to control the visibility of "descBox1".

private void photo_Tapped(object sender, TappedRoutedEventArgs e)
{
    var tag = (sender as Image)?.Tag?.ToString();
    //if we didn't set Tag in Image, its value should be null and descBox1 will be Visible 
    if (string.IsNullOrEmpty(tag) || tag.Equals("Visible"))
    {
        (sender as Image).Tag = Visibility.Collapsed;
    }
    else
    {
        (sender as Image).Tag = Visibility.Visible;
    }
}

"hiddenBtn" is similar, we only need to set the Mode of the Binding to TwoMay, so that when we change its visibility, "descBox1" and "detailBtn" will also change their visibility automatically.

<Button x:Name="hideBtn"
        Grid.Row="0"
        Width="50"
        Height="50"
        Margin="0,0,5,0"
        HorizontalAlignment="Right"
        VerticalAlignment="Bottom"
        Click="hideBtn_Click"
        Visibility="{Binding Tag, ElementName=photo1, Mode=TwoWay}">
    <Button.Background>
        <ImageBrush ImageSource="images/media/ikon-56-app-white-down.png" Stretch="Uniform" />
    </Button.Background>
</Button>

private void hideBtn_Click(object sender, RoutedEventArgs e)
{
    ChangeButtonVisibility(sender);
}

private void ChangeButtonVisibility(object sender)
{
    var visibility = (sender as Button)?.Visibility;
    if (visibility.Equals(Visibility.Visible))
    {
        (sender as Button).Visibility = Visibility.Collapsed;
    }
    else
    {
        (sender as Button).Visibility = Visibility.Visible;
    }
}

"detailBtn" is the same as "hiddenBtn", but its visibility is opposite to "hiddenBtn". So we will need a InvertedVisibilityConverter like following:

public class InvertedVisibilityConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, string language)
    {
        return InvertedVisibility(value);
    }

    public object ConvertBack(object value, Type targetType, object parameter, string language)
    {
        return InvertedVisibility(value);
    }

    private object InvertedVisibility(object value)
    {
        var visibility = value?.ToString();
        if (string.IsNullOrEmpty(visibility) || visibility.Equals("Visible"))
        {
            return Visibility.Collapsed;
        }
        else
        {
            return Visibility.Visible;
        }
    }
}

And in "detailBtn", setting Binding like following:

<Button x:Name="detailBtn"
        Grid.Row="1"
        Width="50"
        Height="50"
        Margin="0,0,5,0"
        HorizontalAlignment="Right"
        VerticalContentAlignment="Bottom"
        Click="detailBtn_Click"
        Visibility="{Binding Tag, ElementName=photo1, Mode=TwoWay, Converter={StaticResource InvertedVisibilityConverter}}">
    <Button.Background>
        <ImageBrush ImageSource="images/media/ikon-56-app-white-up.png" Stretch="Uniform" />
    </Button.Background>
</Button>

private void detailBtn_Click(object sender, RoutedEventArgs e)
{
    ChangeButtonVisibility(sender);
}

Update: To make the visibilities in all FlipViewItems have the same behavior, we need a dependence property that can store the visibility "globally". So we can use FlipView's Tag property instead of Image's Tag property and in Image set the Binding like following:

<Image x:Name="photo1"
       Margin="0,0,10,10"
       Source="{Binding ImagePath}"
       Tag="{Binding Tag, ElementName=narrowFlipview, Mode=TwoWay}"
       Tapped="photo_Tapped" />

After this all Image's Tag will have the same value. And as this is a two-way binding, when we change one Image's Tag value, it will automatically change FlipView.Tag's value and then propagate to all other Images.

In other controls like "descBox1", "hiddenBtn" and "detailBtn", we can also change their Binding by change ElementName from photo1 to narrowFlipview. Using "detailBtn" for example:

<Button x:Name="detailBtn"
        Grid.Row="1"
        Width="50"
        Height="50"
        Margin="0,0,5,0"
        HorizontalAlignment="Right"
        VerticalContentAlignment="Bottom"
        Click="detailBtn_Click"
        Visibility="{Binding Tag, ElementName=narrowFlipview, Mode=TwoWay, Converter={StaticResource InvertedVisibilityConverter}}">
    <Button.Background>
        <ImageBrush ImageSource="images/media/ikon-56-app-white-up.png" Stretch="Uniform" />
    </Button.Background>
</Button>

And there is no need to change the code-behind, after editing the Bindings in XAML, it should be able to achieve what you want.