0

I'm writing a uwp app. The main page contains a listbox. Each list item displays a line of text and a button. The button's visibility is 'collapsed' and I want to change its visibility to 'visible' when the item is selected.

As there are no style triggers in uwp, so far I've tried using the VisualStateManager in within a DataTemplate in the ListBox's ItemTemplate definition. That didn't work.

I've tried using the VisualStateManager with a definition of the ListBox's ItemContainerStyle. That didn't work either.

It seems to me quite a simple thing to want do yet I can't find any way of doing it. Has anyone got any ideas?

Frank van Wijk
  • 3,234
  • 20
  • 41
RobDev
  • 29
  • 4

3 Answers3

0

you can use DataTriggerBehaviors and ChangePropertyAction to change the visibility of your button if you are using MVVM and Binding approach

You need to install a Nuget package Microsoft.xaml.behaviors.uwp.managed

https://www.nuget.org/packages/Microsoft.Xaml.Behaviors.Uwp.Managed/

or the most easy way is opening your page or UserControl with Blend and find the behavior and blend will install for your the necessary packages to use it.

https://blogs.msdn.microsoft.com/wsdevsol/2014/12/10/using-a-datatriggerbehavior-to-change-an-itemtemplate-in-a-windows-store-app/

Update:

You can't use VisualStateManager inside of ItemDataTemplate is not supported but there to do some tricks to use it

AdaptiveTrigger and DataTemplate

Community
  • 1
  • 1
RicardoPons
  • 1,323
  • 9
  • 14
  • Hi Ricardo, many thanks for your reply. I hadn't heard of DataTriggerBehaviors before and your link provides some very valuable information as to how to work around my problem, so thank you very much for that. I'm still very confused about the role of the VisualStateManager and why it doesn't seem to work the way the documentation implies it should. Do you have any ideas about it? – RobDev Mar 24 '16 at 14:07
  • Hi Ricardo, I did try using a UserControl inside my DataTemplate but that didn't work for me either. I'm trying the DataTriggerBehaviors method (in your link) but I'm having a few problems the VS 2015 saying "The name "Interaction" does not exist in the namespace "using:Microsoft.Xaml.Interactivity" - which it does according to the Object Browser. There's a couple of other 'does not exist' names as well - not sure what's going on here. – RobDev Mar 24 '16 at 16:15
  • Hi Ricardo, I think there may be a bit of a bug in VS 2015. I created the DataTriggerBehaviors entry in Blend then saved the file and reverted to VS 2015, reloading the relevant file. It was at this point that VS gave the error. However, I exited VS and when I restarted it the 'error' was no longer there and everything compiled correctly. Pretty strange huh? I would imagine that with something as complex as VS 2015 there might be a few unexpected and weird things happening now and again but thanks for your input. – RobDev Mar 25 '16 at 14:15
  • ok! perfect please mark this answer if it was useful for you! thank you! – RicardoPons Mar 25 '16 at 15:47
  • Hi Ricardo, I've applied the DataTriggerBehaviors solution from your link but, unfortunately, all it does is apply the selected DataTemplate to all ListBoxItems. So as soon as you select one item all items have the new DataTemplate applied, which wasn't what I wanted. I only wanted the 'Selected' DataTemplate top be applied to the 'Selected' Item. Originally I did think of using a DataTemplate Selector but from what I've read, it's only supposed to be used for displaying different item types (e.g. different classes in the ItemsSource). However, I think I might give it a try. – RobDev Mar 25 '16 at 16:14
0

If you're using the MVVM pattern, you can add an additional IsSelected property to your items that you're binding to the ListBox:

public bool IsSelected
{
    get { return _isSelected; }
    set
    {
        _isSelected = value;
        RaisePropertyChanged();
    }
}

On the page view model add a SelectedItem property which will set IsSelected to all the items correctly:

public Item SelectedItem
{
    get { return _selectedItem; }
    set
    {
        if (_selectedItem != value)
        {
            if (_selectedItem != null)
            {
                _selectedItem.IsSelected = false;
            }
            if (value != null)
            {
                value.IsSelected = true;
            }
            _selectedItem = value;
            RaisePropertyChanged();
        }
    }
}

Now you need to bind these two properties:

<ListBox ItemsSource="{Binding Items}" SelectedItem="{Binding SelectedItem, Mode=TwoWay}">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="{Binding Name}" />
                <Button Content="Click Me" Visibility="{Binding IsSelected, Converter={StaticResource VisibilityConverter}}" />
            </StackPanel>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

You also need a converter for converting the bool value to Visibility:

public class VisibilityConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, string language)
    {
        if (value is bool)
        {
            return (bool) value ? Visibility.Visible : Visibility.Collapsed;
        }
        else
        {
            return Visibility.Collapsed;
        }
    }

    public object ConvertBack(object value, Type targetType, object parameter, string language)
    {
        throw new System.NotImplementedException();
    }
}

Declare it as a static resource on the page:

<Page.Resources>
    <local:VisibilityConverter x:Key="VisibilityConverter" />
</Page.Resources>

Without the MVVM pattern you're best off using the Cimbalino multibinding behavior. First reference the Cimbalino Toolkit. Then you can configure the multibinding in XAML:

<ListBox ItemsSource="{Binding Items}" x:Name="MyListBox">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="{Binding Name}" />
                <Button Content="Click Me">
                    <interactivity:Interaction.Behaviors>
                        <behaviors:MultiBindingBehavior PropertyName="Visibility" Converter="{StaticResource VisibilityMultiConverter}">
                            <behaviors:MultiBindingItem Value="{Binding}"/>
                            <behaviors:MultiBindingItem Value="{Binding SelectedItem, ElementName=MyListBox}"/>
                        </behaviors:MultiBindingBehavior>
                    </interactivity:Interaction.Behaviors>
                </Button>
            </StackPanel>
        </DataTemplate>
    </ListBox.ItemTemplate>

Again you need a converter that checks if the item is currently selected:

public class VisibilityMultiConverter : MultiValueConverterBase
{
    public override object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        return values[0] != null && values[0] == values[1] ? Visibility.Visible : Visibility.Collapsed;
    }

    public override object[] ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new System.NotImplementedException();
    }
}

And add the converter as a page resource:

<Page.Resources>
    <local:VisibilityMultiConverter x:Key="VisibilityMultiConverter" />
</Page.Resources>
Damir Arh
  • 17,637
  • 2
  • 45
  • 83
  • Hi Damir, many thanks for your reply. I had thought of doing something similar to your suggestion but in all the documentation/advice I've seen regarding uwp apps they all say to use the VisualStateManager but are pretty vague on exactly how it should be used. As I mentioned, I tried a couple of ways but I couldn't get it working. – RobDev Mar 24 '16 at 13:48
  • Interesting. Can you share a link to one of these `VisualStateManager` recommendations? – Damir Arh Mar 28 '16 at 11:37
  • Hi Damir, here's a couple of links that I couldn't get working, I know one of them is Silverlight and the other is for Windows 8 Phone but I thought that they might work with Windows 10 UWP: https://stackoverflow.com/questions/29249313/wp8-change-the-listboxitem-datatemplate-on-selection-changed/29249800 https://social.msdn.microsoft.com/Forums/silverlight/en-US/85082268-b7cb-4196-b3c6-d6fff6023a01/visualstatemanager-doesnt-work-when-defined-in-listboxitem-template?forum=silverlightnet – RobDev Mar 29 '16 at 09:04
  • I am getting an error as VisibilityMultiConverter doesn't exist in local. Any idea what's wrong? – yalematta May 25 '16 at 22:17
  • @LayaleMatta You probably need to map the `local` prefix to the namespace containing `VisibilityMultiConverter`: `` – Damir Arh May 26 '16 at 04:49
  • I did but it's still giving the error of not found in the namespace. And a build error as well "Interaction is not supported in a Windows Universal Project. – yalematta May 26 '16 at 06:35
  • @LayaleMatta Not sure, what the reason could be. I checked my sample project for this answer and also attempted creating a new project using the above instructions. Both worked, without the error you encountered. I uploaded [my sample project](https://dl.dropboxusercontent.com/u/10676122/SO_36183558_Selection.zip) in case you want to check it out. – Damir Arh May 26 '16 at 18:37
  • Thank you so much! Much appreciated, worked perfectly! – yalematta May 26 '16 at 23:10
0

this seems a old post but still answering for newcomers :)

Its quite easy you just have to use a SelectionChanged property in your ListBox in your .xaml page

For example:

ButtonVisibility.xaml

<ListBox
    x:Name="ListBox"
    HorizontalAlignment="Stretch"
    VerticalAlignment="Stretch"
    SelectionMode="Single"
    SelectionChanged="ListBox_SelectionChanged">
</ListBox>

Now Call the SelectionChanged event in your code behind

ButtonVisibility.xaml.cs

private void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    YourButton.Visibility = Visibility.Visible;
}
Dharman
  • 30,962
  • 25
  • 85
  • 135