简体   繁体   中英

XAML TemplateBinding within a DataTemplate

I'm creating a templated control for a UWP app, and I've hit a snag when trying to bind within a nested DataTemplate. Here's my control XAML in Themes/Generic.xaml:

<Style TargetType="local:EnhancedListView">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="local:EnhancedListView">
                <Grid HorizontalAlignment="Stretch">
                    <Grid.Resources>
                        <DataTemplate x:Key="ListViewTemplate">
                            <Grid>
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition />
                                    <ColumnDefinition />
                                </Grid.ColumnDefinitions>
                                <CheckBox Visibility="{Binding RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource BooleanToVisibilityConverter}, Path=IsCheckModeEnabled}" Grid.Column="0" />
                                <TextBlock Text="{Binding}" Grid.Column="1" />
                            </Grid>
                        </DataTemplate>
                    </Grid.Resources>
                    <Grid.RowDefinitions>
                        <RowDefinition />
                        <RowDefinition />
                    </Grid.RowDefinitions>
                    <CheckBox Visibility="{Binding RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource BooleanToVisibilityConverter}, Path=IsCheckModeEnabled}" Grid.Row="0">Hello</CheckBox>
                    <ListView Grid.Row="1" ItemsSource="{TemplateBinding ItemsSource}" ItemTemplate="{StaticResource ListViewTemplate}" />
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

Here's my actual control/converter:

public class EnhancedListView : Control
{
    public EnhancedListView()
    {
        DefaultStyleKey = typeof(EnhancedListView);
    }

    public object ItemsSource
    {
        get { return GetValue(ItemsSourceProperty); }
        set { SetValue(ItemsSourceProperty, value); }
    }

    public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register("ItemsSource", typeof(object), typeof(EnhancedListView), new PropertyMetadata(null));

    public bool IsCheckModeEnabled
    {
        get { return (bool)GetValue(IsCheckModeEnabledProperty); }
        set { SetValue(IsCheckModeEnabledProperty, value); }
    }

    public static readonly DependencyProperty IsCheckModeEnabledProperty = DependencyProperty.Register("IsCheckModeEnabled", typeof(bool), typeof(EnhancedListView), new PropertyMetadata(null));
}

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

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

Here's the MainPage.xaml:

<local:EnhancedListView IsCheckModeEnabled="False" x:Name="ctlListView">
</local:EnhancedListView>

And finally, my MainPage.xaml.cs:

    public MainPage()
    {
        this.InitializeComponent();

        ctlListView.ItemsSource = new List<string> { "Item 1", "Item 2" };
    }

As I would expect, when the page loads, the first checkbox is hidden because IsCheckModeEnabled is false, but all the checkboxes nested within the DataTemplate are still visible.

I've tried wrapping this into a StaticResource as suggested here , but it doesn't work with some complex types, such as nesting another DataTemplate within my DataTemplate.

I'm certain the binding isn't quite right on the Visibility property here:

<CheckBox Visibility="{Binding RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource BooleanToVisibilityConverter}, Path=IsCheckModeEnabled}" Grid.Column="0" />

Thanks for your help!

Just a guess but how about this:

<CheckBox Visibility="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=local:EnhancedListView}, Converter={StaticResource BooleanToVisibilityConverter}, Path=IsCheckModeEnabled}" DataContext="{Binding}" Grid.Column="0" />

btw: man this is convoluted...

I believe I found a "cleaner" solution that works. I created a new EnhancedListViewItem class which can mimic the properties on the EnhancedListView. Then I assign and style that EnhancedListViewItem independently from the parent EnhancedListView. Here's my updated Generic.xaml:

<Style TargetType="local:EnhancedListView">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="local:EnhancedListView">
                <Grid HorizontalAlignment="Stretch">
                    <Grid.RowDefinitions>
                        <RowDefinition />
                        <RowDefinition />
                    </Grid.RowDefinitions>
                    <CheckBox Visibility="{Binding RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource BooleanToVisibilityConverter}, Path=IsCheckModeEnabled}" Grid.Column="0" />
                    <ListView Grid.Row="1" ItemsSource="{TemplateBinding ItemsSource}" />
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

<Style TargetType="local:EnhancedListViewDataItem">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="local:EnhancedListViewDataItem">
                <Grid HorizontalAlignment="Stretch">
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition />
                        <ColumnDefinition />
                    </Grid.ColumnDefinitions>
                    <CheckBox Visibility="{Binding RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource BooleanToVisibilityConverter}, Path=IsCheckModeEnabled}" Grid.Column="0" />
                    <TextBlock Text="{Binding}" Grid.Column="1" />
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

And here's my updated code behind for the controls:

public class EnhancedListView : Control, INotifyPropertyChanged
{
    public EnhancedListView()
    {
        DefaultStyleKey = typeof(EnhancedListView);
    }

    public event PropertyChangedEventHandler PropertyChanged;

    public object ItemsSource
    {
        get { return GetValue(ItemsSourceProperty); }
        set
        {
            var boundItems = new List<EnhancedListViewDataItem>();

            foreach (var obj in (List<string>)value)
            {
                boundItems.Add(new EnhancedListViewDataItem(this)
                {
                    DataContext = obj
                });
            }

            SetValue(ItemsSourceProperty, boundItems);
        }
    }

    public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register("ItemsSource", typeof(object), typeof(EnhancedListView), new PropertyMetadata(null));

    public bool IsCheckModeEnabled
    {
        get { return (bool)GetValue(IsCheckModeEnabledProperty); }
        set
        {
            SetValue(IsCheckModeEnabledProperty, value);
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("IsCheckModeEnabled"));
        }
    }

    public static readonly DependencyProperty IsCheckModeEnabledProperty = DependencyProperty.Register("IsCheckModeEnabled", typeof(bool), typeof(EnhancedListView), new PropertyMetadata(null));
}

public class EnhancedListViewDataItem : ListViewItem
{
    public EnhancedListViewDataItem(EnhancedListView listView)
    {
        _listView = listView;
        _listView.PropertyChanged += _listView_PropertyChanged;
        DefaultStyleKey = typeof(EnhancedListViewDataItem);
    }

    private readonly EnhancedListView _listView;

    private void _listView_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        IsCheckModeEnabled = _listView.IsCheckModeEnabled;
    }

    public bool IsCheckModeEnabled
    {
        get { return (bool)GetValue(IsCheckModeEnabledProperty); }
        set { SetValue(IsCheckModeEnabledProperty, value); }
    }

    public static readonly DependencyProperty IsCheckModeEnabledProperty = DependencyProperty.Register("IsCheckModeEnabled", typeof(bool), typeof(EnhancedListViewDataItem), new PropertyMetadata(null));
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM