我一直在做一个小样本Wpf Mvvm项目,用于试验INotifyPropertyChanged接口。该项目实际上正常工作,但我遇到的问题是,只有在MainWindow.xaml后面的代码中设置DataContext时,项目才能正常工作。如果我尝试在xaml标记中设置DataContext,那么该项目的某些功能不起作用。用户界面包含一个文本块,文本框(用于输入要显示在文本块OnPropertyChanged中的文本)和提交按钮(除了提供从文本框中丢失焦点的地方,它实际上什么也不做)以及3个用于改变背景颜色的其他按钮(颜色按钮) UI。 UI的默认颜色是橙色 - 直到通过单击任何颜色按钮更改颜色为止为什么我的WPF项目中的datacontext从后台代码正确运行,但不能从xaml运行?
有3个viewModels,PersonViewModel(文本框绑定到),BackgroundViewModel(用于颜色按钮)和一个MainViewModel它结合了另外两个viewModels。 viewModels驻留在项目的viewModels文件夹中。还有一个实现INotifyPropertyChanged接口并由PersonViewModel和BackgroundViewModel继承的ObservableObject类(基本上是ViewModelBase类)。 ObservableObject.cs驻留在项目的根文件夹中。
该项目不是纯粹的Mvvm。颜色按钮在MainWindow.xaml后面的代码中使用click事件。如果我在MainWindow.xaml后面的代码中设置了DataContext,那么一切正常。如果我在xaml标记中设置DataContext - 文本框/文本块功能可以工作,但颜色按钮不会更改UI的背景颜色。当我遍历代码时,它会正确运行所有代码,但UI背景颜色不会更改。我猜这是一件有约束力的事情。
示例项目可以下载here
的代码如下。如果我在xaml标记中设置DataContext,我怎样才能使这个项目的功能正确?我尝试了以下将在其上设置了用户界面的默认橙色网格绑定,但颜色按钮不起作用:
<Grid Background="{Binding Background.Color}" DataContext="{StaticResource bc}">
--MainWindow.xaml
<Window x:Class="NotifyChangeExample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:NotifyChangeExample"
xmlns:VM="clr-namespace:NotifyChangeExample.ViewModels"
mc:Ignorable="d"
Title="MainWindow" Height="550" Width="525">
<!--<Window.DataContext>
<VM:MainViewModel />
</Window.DataContext>-->
<Window.Resources>
<VM:MainViewModel x:Key="bc" />
</Window.Resources>
<Grid Background="{Binding Background.Color}" DataContext="{StaticResource bc}">
<!--<Grid Background="{Binding Background.Color}">-->
<DockPanel LastChildFill="False" Margin="0,82,0,0">
<StackPanel Width="150" DockPanel.Dock="Top">
<TextBlock Text="{Binding Person.Name, StringFormat=Welcome (0)}" />
<TextBox Text="{Binding Person.Name, Mode=OneWayToSource, UpdateSourceTrigger=PropertyChanged}" />
<Button>Submit</Button>
</StackPanel>
<StackPanel HorizontalAlignment="Center" Orientation="Horizontal" DockPanel.Dock="Bottom" >
<Button Click="Red_Clicked">Red Background</Button>
<Button Click="Blue_Clicked">Blue Background</Button>
<Button Click="Yellow_Clicked">Yellow Background</Button>
</StackPanel>
</DockPanel>
</Grid>
</Window>
--MainWindow .xaml.cs
using NotifyChangeExample.ViewModels;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace NotifyChangeExample
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
MainViewModel _main = new MainViewModel();
public MainWindow()
{
InitializeComponent();
//DataContext = _main;
}
private void Red_Clicked(object sender, RoutedEventArgs e)
{
_main.SetBackground(Brushes.Red);
}
private void Blue_Clicked(object sender, RoutedEventArgs e)
{
_main.SetBackground(Brushes.Blue);
}
private void Yellow_Clicked(object sender, RoutedEventArgs e)
{
_main.SetBackground(Brushes.Yellow);
}
}
}
--ObservableObject.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace NotifyChangeExample
{
public class ObservableObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
}
}
--PersonViewModel.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace NotifyChangeExample.ViewModels
{
public class PersonViewModel : ObservableObject
{
private string _name;
public string Name
{
get
{
if (string.IsNullOrEmpty(_name))
return "Unknown";
return _name;
}
set
{
_name = value;
OnPropertyChanged("Name");
}
}
}
}
--BackgroundViewModel.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Media;
namespace NotifyChangeExample.ViewModels
{
public class BackgroundViewModel : ObservableObject
{
private Brush _color;
public Brush Color
{
get
{
if (_color == null)
return Brushes.Orange;
return _color;
}
set
{
_color = value;
OnPropertyChanged("Color");
}
}
}
}
--MainViewModel.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Media;
namespace NotifyChangeExample.ViewModels
{
public class MainViewModel
{
public PersonViewModel Person { get; private set; }
public BackgroundViewModel Background { get; private set; }
public MainViewModel()
{
Person = new PersonViewModel();
Background = new BackgroundViewModel();
}
public void SetBackground(Brush brushColor)
{
Background.Color = brushColor;
}
}
}
with' '你有** 2 ** MainViewModel对象。目前按钮始终与代码隐藏中创建的按钮一起工作,即使您将“bc”设置为DataContext。 –
ASh
请参阅:https://stackoverflow.com/a/34617048/1136211。我即将关闭此作为重复,虽然问题*听起来*完全不同。 – Clemens
如果你真的想遵循MVVM,你应该将这些点击事件移出代码隐藏,并将它们转换为viewmodel上的'ICommand'实现。 –