2015-09-07 61 views
1

我在網上搜索了mvvm安裝程序,發現當我創建我的示例時,應用程序啓動時看不到標籤。然而,我不完全清楚爲什麼。除了沒有創建標籤,我還有一些其他問題...WPF數據綁定不填充選項卡?

  1. [完成]爲什麼標籤不出現,即使在設置綁定後? (主要問題)
  2. 當用戶點擊'添加'時,它如何將其他項目添加到數據?
  3. 我該如何做到這一點,當只有選項卡被選中「刪除按鈕已啓用」?
  4. 當用戶單擊「刪除」時,如果選擇了選項卡,則刪除選定的選項卡。

我在網上發現了這些東西的一些例子,但很多都是複雜或不完整。我很感激幫助,謝謝。

的MainWindow.cs

using System.Collections.ObjectModel; 
using System.Windows; 

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

     public class ViewModel 
     { 
      public ObservableCollection<TabItem> Tabs { get; set; } 
      public ViewModel() 
      { 
       Tabs = new ObservableCollection<TabItem>(); 
       Tabs.Add(new TabItem { Header = "One", Content = "One's content" }); 
       Tabs.Add(new TabItem { Header = "Two", Content = "Two's content" }); 
       Tabs.Add(new TabItem { Header = "Three", Content = "Three's content" }); 
      } 
     } 
     public class TabItem 
     { 
      public string Header { get; set; } 
      public string Content { get; set; } 
     } 

     private void AddItem(object sender, RoutedEventArgs e) 
     { 
      // Adds new item and generates a new tab 
     } 

     private void DeleteItem(object sender, RoutedEventArgs e) 
     { 
      // Deletes the selected tab 
     } 

    } 
} 

的MainWindow.xaml

<Window x:Class="listBinding.MainWindow" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     Title="MainWindow" 
     Width="525" 
     Height="350"> 

     <DockPanel> 
     <Menu DockPanel.Dock="Top"> 
     <MenuItem Header="_Add" Click="AddItem"></MenuItem> 
     <MenuItem Header="_Delete" Click="DeleteItem"></MenuItem> 
     </Menu> 


     <TabControl ItemsSource="{Binding Tabs}"> 

      <TabControl.ItemTemplate> 
       <DataTemplate> 
        <TextBlock Text="{Binding Header}" /> 
       </DataTemplate> 
      </TabControl.ItemTemplate> 

      <TabControl.ContentTemplate> 
       <DataTemplate> 
        <TextBlock 
        Text="{Binding Content}" /> 
       </DataTemplate> 
      </TabControl.ContentTemplate> 

     </TabControl> 

     </DockPanel> 
</Window> 
+0

這對我來說看起來很混亂。給我一些時間,我建立你的樣本 – Tomtom

+0

你有內部類!爲什麼? –

+0

@Nikita內部類只是爲了舉例簡單,他們不必是內部類 – JokerMartini

回答

3

好的。首先,不要將DataContext放在視圖的代碼背後。

我建議你在你的解決方案就像創建一個小的文件夾層次結構:

  • 轉換
  • 助手
  • 型號
  • 查看
  • 視圖模型

ViewModel

在這個文件夾中,您的類中包含您的邏輯。視圖模型對任何視圖對象(xaml-file)都知之甚少。

在你的情況我會創建一個名爲MainWindowViewModel類,它看起來像:

internal class MainWindowViewModel : INotifyPropertyChanged 
{ 
    private ICommand addCommand; 
    private ObservableCollection<ContentItem> contentItems; 
    private ICommand deleteCommand; 
    private ContentItem selectedContentItem; 

    public MainWindowViewModel() 
    { 
     ContentItems.Add(new ContentItem("One", "One's content")); 
     ContentItems.Add(new ContentItem("Two", "Two's content")); 
     ContentItems.Add(new ContentItem("Three", "Three's content")); 
    } 

    public ObservableCollection<ContentItem> ContentItems 
    { 
     get { return contentItems ?? (contentItems = new ObservableCollection<ContentItem>()); } 
    } 

    public ICommand AddCommand 
    { 
     get { return addCommand ?? (addCommand = new RelayCommand(AddContentItem)); } 
    } 

    public ICommand DeleteCommand 
    { 
     get { return deleteCommand ?? (deleteCommand = new RelayCommand(DeleteContentItem, CanDeleteContentItem)); } 
    } 

    public ContentItem SelectedContentItem 
    { 
     get { return selectedContentItem; } 
     set 
     { 
      selectedContentItem = value; 
      OnPropertyChanged(); 
     } 
    } 

    private bool CanDeleteContentItem(object parameter) 
    { 
     return SelectedContentItem != null; 
    } 

    private void DeleteContentItem(object parameter) 
    { 
     ContentItems.Remove(SelectedContentItem); 
    } 

    private void AddContentItem(object parameter) 
    { 
     ContentItems.Add(new ContentItem("New content item", DateTime.Now.ToLongDateString())); 
    } 

    public event PropertyChangedEventHandler PropertyChanged; 

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) 
    { 
     PropertyChangedEventHandler handler = PropertyChanged; 
     if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName)); 
    } 
} 

的ContentItems收集包含要顯示在視圖的TabItems所有項目。 SelectedContentItem-Property始終包含TabControl中當前選定的TabItem。

命令AddCommandDeleteCommand是在單擊添加或刪除時執行的命令。在MVVM中,您通常不會使用View和ViewModel之間的通信事件。

助手

在此文件夾,我已經把一個叫RelayCommand類,我已經在MainWindowViewModel使用。這個類看起來是這樣的:

public class RelayCommand : ICommand 
{ 
    private readonly Predicate<object> canExecute; 
    private readonly Action<object> execute; 

    public RelayCommand(Action<object> execute, Predicate<object> canExecute = null) 
    { 
     if (execute == null) 
      throw new ArgumentNullException("execute"); 
     this.execute = execute; 
     this.canExecute = canExecute; 
    } 

    public bool CanExecute(object parameter) 
    { 
     return canExecute == null || canExecute(parameter); 
    } 

    public void Execute(object parameter) 
    { 
     execute(parameter); 
    } 

    public event EventHandler CanExecuteChanged 
    { 
     add { CommandManager.RequerySuggested += value; } 
     remove { CommandManager.RequerySuggested -= value; } 
    } 
} 

你需要這個類(或類似的實現的ICommand)做要點擊您的視圖對象之間的相互作用(菜單項的,按鈕,...)和對應的ViewModel。

型號

這裏是你的數據對象。在這種情況下,它是具有兩個屬性的ContentItem。

public class ContentItem : INotifyPropertyChanged 
{ 
    private string contentText; 
    private string header; 

    public ContentItem(string header, string contentText) 
    { 
     Header = header; 
     ContentText = contentText; 
    } 

    public string Header 
    { 
     get { return header; } 
     set 
     { 
      header = value; 
      OnPropertyChanged(); 
     } 
    } 

    public string ContentText 
    { 
     get { return contentText; } 
     set 
     { 
      contentText = value; 
      OnPropertyChanged(); 
     } 
    } 

    public event PropertyChangedEventHandler PropertyChanged; 

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) 
    { 
     PropertyChangedEventHandler handler = PropertyChanged; 
     if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName)); 
    } 
} 

查看

在這個文件夾是你的應用程序的用戶還見到。在你的情況下,有剛上稱爲MainWindowView.xaml文件,它看起來像:

<Window x:Class="MVVMDemo.View.MainWindowView" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     Title="MainWindowView" Height="350" Width="525" 
     WindowStartupLocation="CenterScreen"> 
    <DockPanel> 
     <Menu DockPanel.Dock="Top"> 
      <MenuItem Header="_Add" Command="{Binding AddCommand}"/> 
      <MenuItem Header="_Delete" Command="{Binding DeleteCommand}"/> 
     </Menu> 
     <TabControl ItemsSource="{Binding ContentItems}" 
        SelectedItem="{Binding SelectedContentItem, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"> 
      <TabControl.ItemTemplate> 
       <DataTemplate> 
        <TextBlock Text="{Binding Header}"/> 
       </DataTemplate> 
      </TabControl.ItemTemplate> 
      <TabControl.ContentTemplate> 
       <DataTemplate> 
        <TextBlock Text="{Binding ContentText}"/> 
       </DataTemplate> 
      </TabControl.ContentTemplate> 
     </TabControl> 
    </DockPanel> 
</Window> 

正如你可以看到的MenuItems綁定到視圖模型的ICommand的性能。所以你不需要在這裏進行交流活動。而且因爲你的TabControl綁定到視圖模型中的模型對象的集合,所以你可以添加或刪除tabcontrol中的項目,只需從綁定集合中添加或刪除模型對象即可。

現在Unti沒有View和ViewModel之間的連接。如果您現在運行代碼,TabControl中將不會有條目。

有幾種方法可以實現視圖和視圖模型之間的連接。

在App.xaml中/ App.xaml.cs:

打開的App.xaml並取出部分StartupUri="MainWindow.xaml"並用Startup="App_OnStartup"替換它。現在,你必須在App.xaml.cs看起來像創建事件處理程序App_OnStartup:

private void App_OnStartup(object sender, StartupEventArgs e) 
{ 
    MainWindowViewModel viewModel = new MainWindowViewModel(); 
    MainWindowView view = new MainWindowView 
    { 
     DataContext = viewModel 
    }; 
    view.ShowDialog(); 
} 

現在你所擁有的連接。

在你MainWindowView

另一種方式來連接視圖到視圖模型的XAML是直接在XAML設置視圖的DataContext的。

要做到這一點,你必須添加了xmlns到您的MainWindowViewModel它看起來像:xmlns:viewModel="clr-namespace:MVVMDemo.ViewModel",然後你可以在Window-標籤後添加以下XAML權:

<Window.DataContext> 
    <viewModel:MainWindowViewModel/> 
</Window.DataContext> 

我希望這個樣本可以幫助您。如果您有任何疑問,請隨時詢問

+0

非常感謝,這是一個巨大的幫助,並且非常滿意它的工作原理。你在解釋一切背後的理由方面做得很好,非常感謝你,我很欣賞這一點。 – JokerMartini

+0

嘿湯姆幾個問題,你有一個你自己的在線教程。似乎你會有一些很棒的教程。其次,你是否爲WPF工具做過任何自由職業?如果是的話,我怎麼能聯繫你? – JokerMartini

+0

嘿JokerMartine:我沒有任何在線教程。我通過強迫自己開發MVVM模式的每一個小工具來學習我所知道的一切。我爲自己設定了幾個練習。只要認爲自己是一個項目並使用MVVM模式進行編碼即可。如果你陷入困境只是谷歌。那裏總有人解決了這個問題。 – Tomtom

1

簡單地改變

this.DataContext = this; 

this.DataContext = new ViewModel(); 

如果你看Visual Studio的輸出窗口,你會經常很容易地看到這些類型的錯誤,當你運行程序。例如,你的原代碼,你會看到

"System.Windows.Data Error: 40 : BindingExpression path error: 'Tabs' property not found on 'object' ''MainWindow' (Name='')'. BindingExpression:Path=Tabs; DataItem=" 

您也可以找到這個WPF DataBinding Cheatsheet很有幫助。


對於許多WPF/MVVM應用,我發現,MVVM Light Toolkit是一個有用的庫。

你可以在一般模式和一些使用http://davisnw.github.io/mvvm-palindrome/Introduction/工具包的示例中找到我的筆記(示例代碼可能會做一些更新,但基本知識仍然適用)。

0

通過設置this.DataContext = this;,您可以將MainWindow()的Datacontext分配爲MainWindow()本身,因此它不會綁定視圖模型中的內容。所以你必須爲MainWindow()分配viewmodel作爲dataContext。作如下變更

更換

public MainWindow() 
     { 
      this.DataContext = this; 
      InitializeComponent(); 
     } 

隨着

public MainWindow() 
     { 
      this.DataContext = new ViewModel(); 
      InitializeComponent(); 
     } 
1

你經歷的答案之前,你需要得到MVVM的一個想法: Basic MVVM and ICommand Usage Example

回答您的問題:
1. a。視圖的DataContext應該是ViewModel。

this.DataContext = new ViewModel(); 
  1. 灣ViewModel應該始終執行INotifyPropertyChanged。因此,如果您正在初始化製表符,則當前通過代碼時,它將不會顯示在屏幕上,因爲沒有通知&也是錯誤的數據上下文。

  2. 您需要使用命令添加按鈕結合,應與視圖模型的AddCommand屬性(ICommand的類型)被綁定&那麼附加的AddItem功能的命令(使用構造函數)。新增新的TabItem可在標籤列表&它會自動反映到屏幕上,因爲它是可觀察的集合&實現INPC。

  3. 您可以通過兩種方式來實現:使用Converter on Delete按鈕的Visibity或CanExecute of DeleteCommand。

  4. 使DeleteCommand指向ViewModel中的DeleteItem()。