【发布时间】:2016-04-11 11:46:03
【问题描述】:
最近几天我一直在阅读并尝试从该页面应用导航模式:https://rachel53461.wordpress.com/2011/12/18/navigation-with-mvvm-2/
现在,在我的项目开始工作后,我对绑定在这里的工作方式感到非常困惑。首先,我必须澄清我不想要一个像给定示例中那样始终可见的导航窗格。我只想使用我的 MainView 进行导航,每个“子视图”应该只能返回到它的“父视图”。
这是我得到的:
项目:APP
类:App.xaml.cs
protected override void OnStartup(StartupEventArgs e) {
base.OnStartup(e);
UI.View.Main.MainView app = new UI.View.Main.MainView();
UI.View.Main.MainViewModel viewModel = new UI.View.Main.MainViewModel(some dependencies);
app.DataContext = viewModel;
app.Show();
}
ViewModel 基类
public abstract class BaseViewModel : INotifyPropertyChanged {
public event PropertyChangedEventHandler PropertyChanged;
private string _name;
public string Name {
get {
return _name;
}
set {
if (Name != value) {
_name = value;
OnPropertyChanged("Name");
}
}
}
private BaseViewModel _homePage;
public BaseViewModel HomePage {
get {
return _homePage;
}
set {
if (HomePage != value) {
_homePage = value;
OnPropertyChanged("HomePage");
}
}
}
public void OnPropertyChanged(string propertyName) {
PropertyChangedEventHandler temp = PropertyChanged;
if (temp != null) {
temp(this, new PropertyChangedEventArgs(propertyName));
}
}
}
主视图模型
namespace SGDB.UI.View.Main {
public class MainViewModel : BaseViewModel {
private BaseViewModel _currentPageViewModel;
public BaseViewModel CurrentPageViewModel {
get {
return _currentPageViewModel;
}
set {
if (CurrentPageViewModel != value) {
_currentPageViewModel = value;
OnPropertyChanged("CurrentPageViewModel");
}
}
}
public List<BaseViewModel> PageViewModels { get; private set; }
public RelayCommand ChangePageCommand {
get {
return new RelayCommand(p => ChangeViewModel((BaseViewModel)p), p => p is BaseViewModel);
}
}
//Some Dependencies
public List<BaseViewModel> ViewPages { get; private set; }
public MainViewModel(some dependencies) {
HomePage = new HomeViewModel() { Name = "TEST" };
//assign dependencies
var uavm = new UserAdministration.UserAdministrationViewModel(_userUnitOfWork, _personUnitOfWork) {
Name = Resources.Language.Sys.UserAdministartionTitle
};
PageViewModels = new List<BaseViewModel>();
PageViewModels.Add(uavm);
ChangeViewModel(HomePage);
}
public void ChangeViewModel(BaseViewModel viewModel) {
CurrentPageViewModel = viewModel;
}
}
}
主视图
<Window x:Class="SGDB.UI.View.Main.MainView"
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:SGDB.UI.View.Main"
xmlns:ua="clr-namespace:SGDB.UI.View.UserAdministration"
xmlns:home="clr-namespace:SGDB.UI.View.Home"
mc:Ignorable="d"
Title="MainView" Height="400" Width="800">
<Window.Resources>
<DataTemplate DataType="{x:Type home:HomeViewModel}">
<home:Home/>
</DataTemplate>
<DataTemplate DataType="{x:Type ua:UserAdministrationViewModel}">
<ua:UserAdministration/>
</DataTemplate>
</Window.Resources>
<ContentControl Content="{Binding CurrentPageViewModel}"/>
主视图模型
public class HomeViewModel : BaseViewModel {
public RelayCommand TestCommand {
get {
return new RelayCommand((x) => MessageBox.Show(x.ToString()), (x) => true);
}
}
}
主视图
<UserControl x:Class="SGDB.UI.View.Home.Home"
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:SGDB.UI.View.Home"
xmlns:controls="clr-namespace:SGDB.UI.Controls"
xmlns:resx="clr-namespace:SGDB.UI.Resources.Language"
mc:Ignorable="d"
d:DesignHeight="400" d:DesignWidth="800">
<Grid>
<Grid.Resources>
<Style TargetType="controls:ModernButton">
<Setter Property="Margin" Value="1"/>
<Setter Property="FontFamily" Value="Bosch Office Sans"/>
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="Size" Value="155"/>
</Style>
</Grid.Resources>
<Grid.Background>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<LinearGradientBrush.GradientStops>
<GradientStop Color="#26688B" Offset="1"/>
<GradientStop Color="#11354C" Offset="0"/>
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</Grid.Background>
<Grid.RowDefinitions>
<RowDefinition Height="60"/>
<RowDefinition Height="3*"/>
<RowDefinition Height="2*"/>
<RowDefinition Height="25"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="150"/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Row="0" Grid.Column="0" HorizontalAlignment="Center">
<StackPanel.Resources>
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="White"/>
<Setter Property="FontFamily" Value="Bosch Office Sans"/>
</Style>
</StackPanel.Resources>
<TextBlock Text="{x:Static resx:Sys.ApplicationTitle}" FontSize="20" FontWeight="Bold" Margin="5"/>
<TextBlock Text="{x:Static resx:Sys.ApplicationSubTitle}" FontSize="12" FontWeight="Light"/>
</StackPanel>
<WrapPanel Grid.Row="1"
Grid.Column="0"
FlowDirection="LeftToRight"
HorizontalAlignment="Left"
Width="367">
<ItemsControl ItemsSource="{Binding DataContext.PageViewModels, RelativeSource={RelativeSource AncestorType={x:Type Window}}}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<controls:ModernButton Background="Dark"
Text="{Binding Name}"
Command="{Binding DataContext.ChangePageCommand, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
CommandParameter="{Binding}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<Button Content="{Binding Name}" Command="{Binding DataContext.ChangePageCommand, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
CommandParameter="{Binding HomePage}"/>
// This Button is always disabled although HomePage is of Type HomeViewModel which is based on BaseViewModel.
</WrapPanel>
</Grid>
我的问题是:
- 为什么HomeView会打结说HomeViewModel就是它的ViewModel?我没有在代码中的任何地方定义它。
- 为什么对名称属性的绑定有效,但对主页属性的绑定无效?它们都在 BaseViewModel 类中定义。
更新 1:
RelayCommand 类:
public class RelayCommand : ICommand {
public event EventHandler CanExecuteChanged;
readonly Action<object> _action;
readonly Predicate<object> _predicate;
public RelayCommand(Action<object> action, Predicate<object> predicate) {
_action = action;
_predicate = predicate;
}
public RelayCommand(Action<object> action) {
_action = action;
_predicate = ((x) => true);
}
public bool CanExecute(object parameter) {
return _predicate(parameter);
}
public void Execute(object parameter) {
_action(parameter);
}
}
更新 2:
真正的问题是什么?
<Button Content="{Binding Name}" Command="{Binding DataContext.ChangePageCommand, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
CommandParameter="{Binding HomePage}"/>
内容已正确绑定,但应为 BaseViewModel 类型的 CommandParameter (HomePage) 不会通过 Command 的 CanExecute 进行验证。属性、名称和主页都在 BaseViewModel 中定义。
更新 3:
<Button Content="{Binding Name}" Command="{Binding DataContext.ChangePageCommand, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
CommandParameter="{Binding DataContext.HomePage, ElementName=Test}"/>
【问题讨论】:
-
检查调试输出窗口是否有任何绑定警告或错误。这可能会澄清发生了什么。
-
不幸的是,调试窗口没有说明任何绑定错误(让我想知道)。