【问题标题】:XAML TemplateBinding within a DataTemplateDataTemplate 中的 XAML TemplateBinding
【发布时间】:2016-10-28 07:48:18
【问题描述】:

我正在为 UWP 应用创建模板化控件,但在尝试在嵌套的 DataTemplate 中绑定时遇到了障碍。这是我在 Themes/Generic.xaml 中的控制 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>

这是我的实际控制/转换器:

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();
    }
}

这是 MainPage.xaml:

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

最后,我的 MainPage.xaml.cs:

    public MainPage()
    {
        this.InitializeComponent();

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

正如我所料,当页面加载时,第一个复选框被隐藏,因为 IsCheckModeEnabled 为 false,但嵌套在 DataTemplate 中的所有复选框仍然可见。

我已尝试按照建议 here 将其包装到 StaticResource 中,但它不适用于某些复杂类型,例如在我的 DataTemplate 中嵌套另一个 DataTemplate。

我确定这里的 Visibility 属性的绑定不太正确:

&lt;CheckBox Visibility="{Binding RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource BooleanToVisibilityConverter}, Path=IsCheckModeEnabled}" Grid.Column="0" /&gt;

感谢您的帮助!

【问题讨论】:

  • 如果没有可靠地重现问题的良好minimal reproducible example,就不可能确定问题出在哪里,更不用说最好的解决方法了。但看起来您似乎错误地认为ListView 中项目的模板化父项与包含该ListView 的模板的模板化父项相同。如果这还不足以为您指明正确的方向,请改进您的问题。
  • DataContext="{Binding}"的意义何在?
  • @PeterDuniho - 我添加了额外的细节来改进这个问题。我也不会“错误地认为”ListView 项的模板父项与父容器相同,只是不知道如何表示并绑定到它。
  • @Clemens - 对于我的最终产品来说,将整个对象绑定为 CheckBox 的一部分非常重要。我从我的示例中删除了它。

标签: c# windows xaml uwp


【解决方案1】:

只是猜测,但如何:

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

顺便说一句:伙计,这很令人费解......

【讨论】:

  • 谢谢,但不幸的是,这似乎不起作用,因为 AncestorType 无效:Unknown member 'AncestorType' on element 'RelativeSource'
  • 另一个建议,您是否尝试过使用 ListViews SelectionMode = multiple?我可以想象在应用此模式时启用复选框的样式...
  • 看看它 (msdn.microsoft.com/en-us/library/windows/apps/…) 标准的 listviewitem 确实有复选框,所以如果你启用多选,它们应该是可见的。为什么不走那条路?
  • 是的,确实如此,但我还试图支持其他一些东西。复选框示例是我正在尝试做的简化版本,但存在同样的问题;无法从 DataTemplate 中绑定到 TemplatedParent 中的某些内容。
  • 由于这是 UWP,您甚至无法访问可继承的附加属性,所以我认为您最好的办法是让您的主 ViewModel 拥有一个 Property IsCheckModeEnabled 并拥有 ListView 的 Items ViewModels有一个Parent 属性来访问该主视图模型,然后使用一个包含CheckBoxDataTemplate
【解决方案2】:

我相信我找到了一个“更干净”的解决方案。我创建了一个新的EnhancedListViewItem 类,它可以模仿EnhancedListView. 上的属性然后我独立于父EnhancedListView. 分配和设置EnhancedListViewItem 的样式这是我更新的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>

这是我更新后的控件代码:

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));
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-06-05
    • 1970-01-01
    • 2014-06-06
    • 2012-11-14
    • 2013-03-01
    • 2011-08-16
    • 2019-04-12
    • 2013-04-28
    相关资源
    最近更新 更多