2017-09-14 30 views
0

正當我認爲自己在這方面越來越好時,TabControl現在給我帶來了問題。我已經在StackOverflow上閱讀過相關文章,但一直無法讓我的簡單演示應用程序按照我希望的方式工作。在WPF中使用TabControl和MVVM進行正確的數據綁定

爲了保持專注,我將從一個關於我不明白的問題開始。

我有一個TabControl的TabItems每個託管相同的UserControl。當我將TabControl.ContentTemplate的DataTemplate設置爲我的UserControl時,將出現該控件的呈現,但它看起來像是每個選項卡的相同控件。或者,也許它並不依賴於任何標籤。

MainWindow.xaml

<Window x:Class="TabControlMvvm.MainWindow" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:localviews="clr-namespace:TabControlMvvm.Views" 
    Title="MainWindow" Height="350" Width="525"> 
    <TabControl ItemsSource="{Binding Tabs}" SelectedIndex="{Binding Selected}"> 
     <TabControl.ContentTemplate> 
      <DataTemplate> 
       <localviews:PersonMainPanel /> 
      </DataTemplate> 
     </TabControl.ContentTemplate>   
    </TabControl> 
</Window> 

代碼隱藏僅設置視圖模型作爲其的DataContext:

namespace TabControlMvvm { 
    /// <summary> 
    /// Interaction logic for MainWindow.xaml 
    /// </summary> 
    public partial class MainWindow : Window { 
     public MainWindow() 
     { 
      InitializeComponent(); 
      DataContext = new TabControlMvvm.ViewModels.MainViewModel(); 
     } 
    } 
} 

的TabItem的的內容應該是另一個用戶控件,PersonMainPanel.xaml:

<UserControl x:Class="TabControlMvvm.Views.PersonMainPanel" 
      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:localviews="clr-namespace:TabControlMvvm.Views" 
      mc:Ignorable="d" 
      d:DesignHeight="300" d:DesignWidth="300"> 
    <Border BorderBrush="Red" BorderThickness="2"> 
     <TabControl TabStripPlacement="Bottom"> 
      <TabItem Header="Tab 1"> 
       <localviews:MyTabItem /> 
      </TabItem> 
      <TabItem Header="Tab 2"> 
       <TextBlock Text="This was left blank intentionally" /> 
      </TabItem> 
      <TabItem Header="Tab 3"> 
       <TextBlock Text="This was also left blank intentionally" /> 
      </TabItem> 
     </TabControl> 
    </Border> 
</UserControl>  

代碼隱藏:

namespace TabControlMvvm.Views { 
    /// <summary> 
    /// Interaction logic for PersonMainPanel.xaml 
    /// </summary> 
    public partial class PersonMainPanel : UserControl { 
     public PersonMainPanel() 
     { 
      InitializeComponent(); 
     } 
    } 
} 

而且MainViewModel:

namespace TabControlMvvm.ViewModels { 
    public class MainViewModel : ViewModelBase { 
     public ICollectionView Tabs { get; set; } 
     public int Selected { get; set; } 

     public class Person 
     { 
      public string Name { get; set; } 
     } 

     public class DummyController { 
      public List<Person> Persons { get; private set; } 

      public DummyController() 
      { 
       Persons = new List<Person> { 
        new Person { Name = "Larry" }, 
        new Person { Name = "Darryl" }, 
        new Person { Name = "Other brother Darryl" } 
       }; 
      } 
     } 

     public DummyController Controller { get; private set; } 

     public RelayCommand HelloCommand { get; set; } 

     public MainViewModel() 
     { 
      Controller = new DummyController(); 

      /* 
      IEnumerable<TabItem> tabs = Enumerable.Range(1, _controller.Persons.Count()) 
                .Select(x => new TabItem { Header = String.Format("Person {0}", x), 
                       Content = new PersonMainPanel() }); 
      */ 
      IEnumerable<TabItem> tabs = Enumerable.Range(1, Controller.Persons.Count()) 
                .Select(x => new TabItem { Header = String.Format("Person {0}", x)}); 
      Tabs = CollectionViewSource.GetDefaultView(tabs.ToList()); 
      Tabs.MoveCurrentToFirst(); 

      InitializeCommands(); 
     } 

     private void InitializeCommands() 
     { 
      HelloCommand = new RelayCommand(() => { MessageBox.Show(String.Format("Hello, Person {0} named {1}!", 
                     Selected, Controller.Persons[Selected].Name)); }); 
     } 
    } 
} 

PersonMainPanel舉辦另一TabControl的,在標籤1的內容是MyTabItem.xaml:

<UserControl x:Class="TabControlMvvm.Views.MyTabItem" 
      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" 
      mc:Ignorable="d" 
      d:DesignHeight="300" d:DesignWidth="300"> 
    <StackPanel Orientation="Vertical"> 
     <StackPanel Orientation="Horizontal"> 
      <TextBlock Text="Name:" /> 
      <TextBox Text="{Binding Name}" Width="100" />    
     </StackPanel> 
     <Button Command="{Binding HelloCommand}" Content="Say Hello" /> 
    </StackPanel> 
</UserControl> 

代碼隱藏:

namespace TabControlMvvm.Views { 
    /// <summary> 
    /// Interaction logic for MyTabItem.xaml 
    /// </summary> 
    public partial class MyTabItem : UserControl { 
     public MyTabItem() 
     { 
      InitializeComponent(); 
     } 
    } 
} 

它看起來像這樣在運行時間:

enter image description here

問題我至今:

  1. 當我輸入第1個人的名稱,然後單擊此人2片,1個人的名稱仍是可見的,因此我假設控件沒有正確數據綁定。我明白ItemsControls不會將他們的DataContext傳遞給他們的孩子,但我不知道如何解決這個問題,而不需要在代碼隱藏中關聯View。
  2. 由於缺少DataContext,我會期望在Output窗口中獲得數據綁定錯誤,但我沒有收到任何錯誤。我假設DataContext爲null,但是這不會導致綁定錯誤嗎?
  3. 如何有效地使用Snoop來調試這樣的問題?

這裏的樣品溶液:http://www.filedropper.com/tabcontrolmvvm

+0

也許有所幫助:https://stackoverflow.com/a/1870658/1136211 – Clemens

+0

如何你MainViewModel看? – sTrenat

+0

哎呀,忘了明顯!編輯。另外,@Clemens從你的鏈接文章中獲取的一點是,後備集合不應該是TabItems,它應該可能是MyTabItem的ViewModel。指定DataTemplate的XAML應該保持原樣。我現在會嘗試,以防萬一這是我的問題! – Dave

回答

0

這裏是解決方案:

在主窗口修改TabControl的模板,從你的模型綁定標題:

<TabControl ItemsSource="{Binding Tabs}" SelectedIndex="{Binding Selected}"> 
    <TabControl.ContentTemplate> 
     <DataTemplate> 
      <localviews:PersonMainPanel /> 
     </DataTemplate> 
    </TabControl.ContentTemplate> 
    <TabControl.ItemContainerStyle> 
     <Style TargetType="TabItem"> 
      <Setter Property="Header" Value="{Binding Header}"/> 
     </Style> 
    </TabControl.ItemContainerStyle> 
</TabControl> 

在MyTabItem。XAML,設置UpdateTrigger,因爲默認情況下一個 'OnLostFocus' 有時會無法保存您的數據:

<TextBox Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}" Width="100" />  

在MainViewModel修改標籤的創建,因此它具有Name屬性太:

IEnumerable<TabItem> tabs = Enumerable.Range(1, Controller.Persons.Count()) 
             .Select(x => new TabItem { Header = String.Format("Person {0}", x), Name = Controller.Persons[x-1].Name }); 

而且,最重要的是,打造一個屬於自己的TabItem類包含一些有限的數據:

public class TabItem 
{ 
    public string Name { set; get; } 
    public string Header { set; get; }  
} 
+0

我將其標記爲正確的解決方案,因爲它足夠接近。我的實現有一個TabItemViewModel的集合,我的XAML略有不同。我稍後會發布我的實際答案。但這基本上是一樣的,最重要的是TabControl的ItemsSource不應該是TabItem,而應該是ViewModel的。 – Dave

+0

嗯,我寫的代碼你發佈:) – sTrenat