WPF数据绑定不填充选项卡?

时间:2015-09-07 05:45:07

标签: c# wpf xaml

我在网上搜索了mvvm设置,并注意到我创建我的示例时,启动应用程序时标签似乎没有生成。但是,我并不完全清楚为什么会这样。除了没有创建标签之外,我还有一些其他问题......

  1. [DONE]为什么即使设置了绑定后标签也没有出现? (主要问题)
  2. 当用户点击“添加”按钮时如何在数据中添加其他项?
  3. 如果只选择了一个标签,我怎么能这样做?'删除按钮已启用'?
  4. 当用户点击'删除'如果选择了选项卡,它将删除选定的选项卡。
  5. 我在网上发现了这些东西的一些例子,但很多都是复杂的或不完整的。感谢您的帮助,谢谢。

    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>
    

4 个答案:

答案 0 :(得分:3)

确定。首先,不要将DataContext放在视图的代码后面。

我建议您在解决方案中创建一个小文件夹层次结构,如:

  • 转换器
  • 辅助
  • 模型
  • 查看
  • 视图模型

ViewModel

在此文件夹中,您的类包含逻辑。视图模型对任何视图对象(xaml文件)都一无所知。

在你的情况下,我会创建一个名为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-Collection包含您要在视图中显示为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的类似实现)来执行视图中要单击的对象之间的交互(MenuItem&#39; s,Buttons,...)和相应的ViewModel。

<强>模型

这是您的DataObjects。在这种情况下,它是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绑定到ViewModel中的ICommand-Properties。因此,您不需要在此处进行通信。并且因为您的TabControl绑定到viewmodel中的模型对象集合,所以您可以在tabcontrol中添加或删除项目,只需添加或删除绑定集合中的模型对象。

现在,Unti和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中

将视图连接到viewmodel的另一种方法是直接在xaml中设置视图的datacontext。

要执行此操作,您必须向您的MainWindowViewModel添加xmlns,其类似于:xmlns:viewModel="clr-namespace:MVVMDemo.ViewModel",然后您可以在Window-Tag之后添加以下xaml:

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

我希望此示例可以帮助您。如果您对此有任何疑问,请随时提出

答案 1 :(得分: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/上使用工具包找到关于一般模式和一些示例的说明(示例代码可能与某些更新有关,但基础知识应该仍然相关)。

答案 2 :(得分:1)

在您完成答案之前,您需要了解MVVM: Basic MVVM and ICommand Usage Example

回答你的问题:
1. a。视图的DataContext应该是ViewModel。

this.DataContext = new ViewModel();
  1. 湾ViewModel应该始终实现INotifyPropertyChanged。所以,目前通过代码,如果你正在初始化标签,它将不会显示在屏幕上,因为没有通知&amp;也是错误的数据背景。

  2. 您需要对Add按钮使用命令绑定,该按钮应与ViewModel的AddCommand属性(ICommand类型)&amp;绑定。然后将AddItem函数附加到命令(使用构造函数)。将新TabItem添加到选项卡列表&amp;它会自动反映到屏幕上,因为它是可观察的收集和放大实施INPC。

  3. 您可以通过两种方式完成此操作:在删除按钮的Visibity上使用Converter或使用DeleteCommand的CanExecute。

  4. 在ViewModel中将DeleteCommand指向DeleteItem()。

答案 3 :(得分:0)

您正在通过设置MainWindow()MainWindow()的Datacontext指定为this.DataContext = this;,因此它不会绑定视图模型中的内容。所以你必须将viewmodel指定为MainWindow()的dataContext。进行以下更改

替换

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

使用

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