2016-06-27 4 views
1

간단한 데이터 바인딩 문제가있는 것 같지만 올바른 방법은 없습니다. 두 개의 DataTemplate을 정의하는 TabControl이 있습니다. 하나는 탭 헤더 용이고 다른 하나는 탭 내용 용입니다.DataTemplate에서 ItemsControl을 바인딩하는 데이터

콘텐츠 템플릿에 ItemsControl이 포함되어 있습니다. ItemsControl은 동적으로 생성 된 ViewModel (ConnectionInfoVM)에 바인딩을 시도합니다.

UI를 표시하면 바인딩이 실패하지만 출력에 오류 메시지가 표시되지 않습니다.

바인딩이 작동하고 DataBuffer가 실제로 표시되도록 DataContext와 바인딩을 설정해야하는 이유는 무엇입니까? 어떤 도움이라도 대단히 감사합니다.

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 : resulting tab

,536,913 : 작성되는 탭의

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

스크린 샷

답변

0

당신은 ContentTemplate하지만 결코 내용을 설정합니다. DataContext = ci 대신 Content = ci을 작성하십시오.

한편 DataContext는 이미 DataTemplate이 적용된 개체이기 때문에 DataContext = ci은 쓸모가 없었습니다.

편집

당신이 WPF, 사용의 핵심 기능의 남용 사용하는 것처럼 : 바인딩.

당신의 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> 

당신 CS 코드가 훨씬 간단하게

:

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); 
     } 
    } 
} 
(I 전체 MVVM 호환 코드를 사용하지 않은 경우) 나는 당신의 코드를 작성했을 어떻게

코드를 다시 읽는 동안 코드 숨김으로 바인딩하는 것에 대해 혼동 스러웠을 것이라고 언급했습니다.

코드 TabDynamic.ItemsSource = _tabItems;은 바인딩이 아니며 한 번만 설정됩니다.

어쨌든, 당신은 MVVM에 대해 조금 읽으십시오. TabItem은 코드 숨김 대신 ViewModel 클래스에 있어야합니다.

+0

제안 된 변경 사항과 함께 작동합니다. 따라서이 권리를 이해한다면 이것이 작동하는 방법입니다. 항목의 내용을 설정해야합니다. 내용에 따라 일치하는 템플리트가 선택됩니다. DataContext는 암시 적으로 내가 설정 한 컨텐트가 될 것입니다 (DataTemplate이 적용된 객체 임). 이 문제를 해결해 주셔서 감사합니다. – ptair

+0

@ptair는 더 많은 정보와 더 많은 "WPF"접근법을 포함하도록 내 게시물을 편집했습니다. ItemsSource를 XAML에 바인딩하면 뒤에 컨트롤을 만들 필요가 없습니다. 실제로 DataTemplate에 키를 제공하지 않으면 ContentTemplate이 내용 유형에 따라 암시 적으로 선택됩니다. – nkoniishvt

+0

@ptair 코드를 여러 번 수정하여 실수를 저 지르려고했습니다. 거의 작동해야합니다. – nkoniishvt

0

귀하의 코딩에 따라 Tabcontrol에 포함되어 있지 않습니다 DataContext viewmodel하지만 컨트롤; 그래서 VM을 보유하고있는 컨트롤이나 다른 것을 찾아야합니다. 페이지가 VM을 DataContext에 보유하고있는 것으로 보이지 않습니다.

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을 지정할 수 있습니다

나는 하나 개의 경로는 등 뒤에 코드에서 지정하는 것과 VM을 유지하기 위해있는 TabControl의 Tag 속성을 사용하는 것이 좋습니다 '이름과 같은 다음 ContentTemplate이 적용되지 않습니다 그래서이 내용 세트있을 경우에만 적용 있기 때문에

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

TabControl의 DataContext가 문제가되는 이유가 표시되지 않습니다. TabItem은 ConnectionInfoVM 인 지정된 DataContext로 생성됩니다. – nkoniishvt

+0

@nkoniishvt 앞서 언급했듯이 TabItem에 VM이 있으면 해당 경로가 작동합니다. 대답으로 제공하십시오. 상대 소스 바인딩을 사용하고 싶지 않았습니다. – OmegaMan

+0

또한 몇 가지 탭이 있으며 각 탭에는 DataContext로 자체 ConnectionInfoVM이 있으므로 TabControl에서 ConnectionInfoVM을 한 번 설정하면 작동하지 않습니다 – ptair

관련 문제