我在网上搜索了mvvm设置,并注意到我创建我的示例时,启动应用程序时标签似乎没有生成。但是,我并不完全清楚为什么会这样。除了没有创建标签之外,我还有一些其他问题......
我在网上发现了这些东西的一些例子,但很多都是复杂的或不完整的。感谢您的帮助,谢谢。
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 :(得分: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。
命令AddCommand
和DeleteCommand
是在单击“添加”或“删除”时执行的命令。在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();
湾ViewModel应该始终实现INotifyPropertyChanged
。所以,目前通过代码,如果你正在初始化标签,它将不会显示在屏幕上,因为没有通知&amp;也是错误的数据背景。
您需要对Add按钮使用命令绑定,该按钮应与ViewModel的AddCommand
属性(ICommand类型)&amp;绑定。然后将AddItem函数附加到命令(使用构造函数)。将新TabItem添加到选项卡列表&amp;它会自动反映到屏幕上,因为它是可观察的收集和放大实施INPC。
您可以通过两种方式完成此操作:在删除按钮的Visibity上使用Converter或使用DeleteCommand的CanExecute。
在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();
}