c# - WPF 数据绑定(bind)不填充选项卡?

标签 c# wpf xaml

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

  1. [完成] 为什么即使在设置绑定(bind)后选项卡也没有出现? (主要问题)
  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>

最佳答案

好的。首先,不要将您的 DataContext 放在 View 的代码隐藏中。

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

  • 转换器
  • helper
  • 模型
  • 查看
  • View 模型

View 模型

在这个文件夹中有您的类,其中包含您的逻辑。 View 模型对任何 View 对象(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 包含您要在 View 中显示为 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 的类似实现)在您要单击的 View 中的对象(MenuItem、Buttons...)和相应的 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 绑定(bind)到 ViewModel 中的 ICommand-Properties。所以你不需要 Event 在这里进行通信。并且因为您的 TabControl 绑定(bind)到 View 模型中的模型对象集合,您可以在 tabcontrol 中添加或删除项目,只需从绑定(bind)集合中添加或删除模型对象。

到目前为止,View 和 ViewModel 之间没有任何联系。如果您现在运行代码,TabControl 中将没有条目。

有几种方法可以在 View 和 View 模型之间建立连接。

在 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 中

另一种将 View 连接到 View 模型的方法是直接在 xaml 中设置 View 的数据上下文。

为此,您必须向 MainWindowViewModel 添加一个 xmlns-,如下所示:xmlns:viewModel="clr-namespace:MVVMDemo.ViewModel" 然后您可以在之后添加以下 xaml窗口标签:

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

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

关于c# - WPF 数据绑定(bind)不填充选项卡?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32431814/

相关文章:

c# - 使用 GraphicsPath 正确绘制文本

c# - 如何使用 simple.odata.client 扩展分层数据

c# - 如何用不同的 stub 调用替换已声明的 stub 调用?

WPF 列表框和键盘导航

c# - WPF 将控件的 zindex 绑定(bind)到属性不起作用

wpf - 如何用椭圆类绘制圆扇形?

wpf - WPF中的数据模板中的x:Key,x:Name和x:UID有什么区别?

c# - 如何对 Path 对象进行分组?

c# - 有关 WPF 及其 XAML 的一般信息

c# - 有没有办法在毛伊岛放置一个按钮来打开弹出窗口