繁体   English   中英

Data绑定DataTemplate中的ItemsControl

[英]Data bind an ItemsControl in a DataTemplate

我似乎有一个简单的数据绑定问题,但无法找到正确的方法来做到这一点。 有一个TabControl定义了两个DataTemplate ,一个用于选项卡标题,另一个用于选项卡内容。

内容模板包含ItemsControl ItemsControl尝试绑定到动态创建的ViewModel (ConnectionInfoVM)。

当我显示UI时,绑定失败,但输出中没有关于它的错误消息。

我如何设置DataContext和绑定,以便绑定工作,实际显示DataBuffer? 任何帮助非常感谢。

ConnectionsControl:

<UserControl x:Class="XXXViewer.Views.ConnectionsControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:viewModels="clr-namespace:XXXViewer.ViewModels"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <Grid> 
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <TabControl Grid.Row="0" Name="TabDynamic" SelectionChanged="tabDynamic_SelectionChanged">
            <TabControl.Resources>
                <DataTemplate x:Key="TabHeader" DataType="TabItem">
                    <DockPanel>
                        <TextBlock Text="{Binding RelativeSource={RelativeSource AncestorType={x:Type TabItem}}, Path=Header}" />
                        <Button Name="btnDelete" DockPanel.Dock="Right" Margin="5,0,0,0" Padding="0" Click="btnTabDelete_Click" CommandParameter="{Binding RelativeSource={RelativeSource AncestorType={x:Type TabItem}}, Path=Name}">
                            <Image Source="{DynamicResource DeleteImg}" Height="11" Width="11"></Image>
                        </Button>
                    </DockPanel>
                </DataTemplate>

                <DataTemplate x:Key="TabContent" DataType="viewModels:ConnectionInfoVM">
                    <StackPanel>
                        <ScrollViewer Name="Scroller" Background="Black">
                            <StackPanel>
                                <TextBlock Text="This line gets printed" Foreground="White" FontFamily="Consolas"/>
                                <ItemsControl Name="ItemCtrl" ItemsSource="{Binding DataBuffer}">
                                    <ItemsControl.ItemTemplate>
                                        <DataTemplate>
                                            <TextBlock Text="{Binding Path=.}" Foreground="White" FontFamily="Consolas"/>
                                        </DataTemplate>
                                    </ItemsControl.ItemTemplate>
                                </ItemsControl>
                            </StackPanel>
                        </ScrollViewer>
                    </StackPanel>
                </DataTemplate>

            </TabControl.Resources>
        </TabControl>
    </Grid>
</UserControl>

ConnectionsControl代码背后:

namespace XXXViewer.Views
{
    public partial class ConnectionsControl : UserControl
    {
        private readonly ObservableCollection<TabItem> _tabItems = new ObservableCollection<TabItem>();

        public ConnectionsControl()
        {
            InitializeComponent();

            // bindings
            TabDynamic.ItemsSource = _tabItems;
            TabDynamic.DataContext = this;
        }

        // assume this gets called
        private void AddTabItem(ConnectionInfoVM ci)
        {
            DataTemplate headerTemplate = TabDynamic.FindResource("TabHeader") as DataTemplate;
            DataTemplate contentTemplate = TabDynamic.FindResource("TabContent") as DataTemplate;

            // create new tab item
            TabItem tab = new TabItem
            {
                Header = $"Tab {ci.ConnectionID}",
                Name = $"T{ci.ConnectionID}",
                HeaderTemplate = headerTemplate,
                ContentTemplate = contentTemplate,
                DataContext = ci
            };

            _tabItems.Insert(0, tab);

            // set the new tab as active tab
            TabDynamic.SelectedItem = tab;
        }
    }
}

ConnectionInfoVM:

namespace XXXViewer.ViewModels
{
    public class ConnectionInfoVM : ViewModelBase
    {
        private readonly ObservableQueue<string> _dataBuffer = new ObservableQueue<string>();
        public ObservableQueue<string> DataBuffer => _dataBuffer;
    }
}

创建的选项卡的屏幕截图: 结果选项卡

Tabcontrol根据你的编码不​​包含在它的DataContext的viewmodel而是控件; 所以我们需要找到一个控件或其他容纳VM的东西。 页面似乎不会将VM保存在其DataContext中。

我建议一条路由是使用TabControl的Tag属性来保存VM,例如在后面的代码中指定它,如下所示:

TabDynamic.ItemsSource = _tabItems;
TabDynamic.DataContext = this;
TabDynamic.Tag = {Wherever you are keeping your VM at this time its not clear in your code example};

然后,您可以通过指定TabControls的名称来指定模板绑定中的Tag

 <ItemsControl Name="ItemCtrl" 
                     ItemsSource="{Binding Tag.DataBuffer, 
                                           ElementName=TabDynamic}">

您设置ContentTemplate但从不设置Content,因此从不应用ContentTemplate,因为它仅在设置了Content时应用。 而不是DataContext = ciContent = ci

顺便说一句, DataContext = ci是无用的,因为DataContext已经隐含了应用DataTemplate的对象。

编辑

当您使用WPF时,请使用和滥用其核心功能:绑定。

如何编写代码(如果我没有使用完全符合MVVM的代码):

你的XAML:

<TabControl Grid.Row="0" Name="TabDynamic" 
            ItemsSource="{Binding TabItems, Mode=OneWay}" 
            SelectionChanged="tabDynamic_SelectionChanged">
    <TabControl.Resources>
        <DataTemplate x:Key="TabHeader" DataType="TabItem">
            <DockPanel>
                <TextBlock Text="{Binding RelativeSource={RelativeSource AncestorType={x:Type TabItem}}, Path=Header}" />
                <Button Name="btnDelete" DockPanel.Dock="Right" Margin="5,0,0,0" Padding="0" Click="btnTabDelete_Click" CommandParameter="{Binding RelativeSource={RelativeSource AncestorType={x:Type TabItem}}, Path=Name}">
                    <Image Source="{DynamicResource DeleteImg}" Height="11" Width="11"></Image>
                </Button>
            </DockPanel>
        </DataTemplate>
    </TabControl.Resources>
    <TabControl.ItemTemplate>
        <DataTemplate DataType="viewModels:ConnectionInfoVM">
            <TabItem Header="{Binding ConnectionID, Mode=OneWay}"
                     Name="{Binding ConnectionID, Mode=OneWay}"
                     HeaderTemplate="{StaticResources TabHeader}">
                <StackPanel>
                    <ScrollViewer Name="Scroller" Background="Black">
                        <StackPanel>
                            <TextBlock Text="This line gets printed" Foreground="White" FontFamily="Consolas"/>
                            <ItemsControl Name="ItemCtrl" ItemsSource="{Binding DataBuffer}">
                                <ItemsControl.ItemTemplate>
                                    <DataTemplate>
                                        <TextBlock Text="{Binding Path=.}" Foreground="White" FontFamily="Consolas"/>
                                    </DataTemplate>
                                </ItemsControl.ItemTemplate>
                            </ItemsControl>
                        </StackPanel>
                    </ScrollViewer>
                </StackPanel>
            </TabItem>

        </DataTemplate>
    </TabControl.ItemTemplate>
</TabControl>

你的代码变得更简单:

namespace XXXViewer.Views
{
    public partial class ConnectionsControl : UserControl
    {
        private readonly ObservableCollection<ConnectionInfoVM> _tabItems = new ObservableCollection<ConnectionInfoVM>();
        public ObservableCollection<ConnectionInfoVM> TabItems {get {return _tabItems;}}

        public ConnectionsControl()
        {
            InitializeComponent();

            // bindings
            //TabDynamic.ItemsSource = _tabItems;
            TabDynamic.DataContext = this;
        }

        // assume this gets called
        private void AddTabItem(ConnectionInfoVM ci)
        {
            TabItems.Add(ci);
        }
    }
}

我在重新阅读您的代码时注意到您可能对代码隐藏中的绑定感到困惑。

你的代码TabDynamic.ItemsSource = _tabItems; 不是绑定,它只会设置一次。

无论如何,我建议你阅读一些关于MVVM的内容。 TabItems应该在ViewModel类中,而不是在代码隐藏中。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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