当更改同一视图上的 DataContext 时,Visual Tree 中以前的 DataContext 会发生什么情况?
What happens to previous DataContexts in Visual Tree when DataContext on same View is changed?
我有我的自定义日历控件 - 事件日历。我在某些情况下使用它。
<Controls:EventCalendar Grid.Row="0"
Grid.RowSpan="8"
Grid.Column="2"
Margin="20,50,0,0"
CalendarEvents="{Binding DataContext.CalendarEvents, RelativeSource={RelativeSource TemplatedParent}, UpdateSourceTrigger=PropertyChanged}"
Header="{Binding DataContext.DataSpis.Header, RelativeSource={RelativeSource TemplatedParent}, UpdateSourceTrigger=PropertyChanged}"
ViewModelBase="{Binding DataContext.ViewModel, RelativeSource={RelativeSource TemplatedParent}, UpdateSourceTrigger=PropertyChanged}"
IsFunctionalityVisible="{Binding DataContext.IsFunctionalityVisible, RelativeSource={RelativeSource TemplatedParent}, UpdateSourceTrigger=PropertyChanged}"
IsCaseLoaded="{Binding DataContext.IsLoaded, RelativeSource={RelativeSource TemplatedParent}, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}">
</Controls:EventCalendar>
我检测是否通过 IsCaseLoaded 依赖项 属性 加载案例(相同视图,不同数据)。发生这种情况时,我将新的 DataContext 添加到我的日历控件中。像这样:
private static void LoadPCCallback(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
if (((EventCalendar)source).IsCaseLoaded == true)
{
((EventCalendar)source).DataContext = null;
((EventCalendar)source).DataContext = new EventCalendarViewModel(((EventCalendar)source).Header, ((EventCalendar)source).ViewModelBase, ((EventCalendar)source).CalendarEvents);
}
}
在 EventCalendarViewModel 的构造函数中,我为要显示的会议或任务设置了一些可见性。默认情况下会显示会议并隐藏任务。
当我想显示任务时,我单击此日历控件上的按钮。
现在行为开始变得 意外 :我加载案例,单击任务按钮,它起作用了 - 显示任务,隐藏会议。
我重新加载案例,单击“任务”按钮,它起作用了 - 显示任务,隐藏会议。
但是我第三次重新加载案例(有时是第二次,有时是第四次 - 真的 随机 ),构造函数工作,将会议设置为默认值,但是当我单击任务按钮时,它突然具有以前的值DataContext,所以它认为任务是 true,会议是 false...所以没有任何变化,会议仍然显示。
public void ShowMeetingsButtonClick()
{
this.ShowTasks = false;
NotifyOfPropertyChange(() => ShowTasks);
this.ShowMeetings = true;
NotifyOfPropertyChange(() => ShowMeetings);
}
Show Tasks也是这样:
public void ShowTasksButtonClick()
{
this.ShowMeetings = false;
NotifyOfPropertyChange(() => ShowMeetings);
this.ShowTasks = true;
NotifyOfPropertyChange(() => ShowTasks);
}
所以我想到的一件事是,这个日历视图以某种方式在 Visual Tree 中找到了以前的 DataContext 并采用旧值从那里。因为在 new DataContext 的构造函数之后一切看起来都很好,但是在点击一个按钮之后它突然有了不同的值。
我还认为我的一些线程正在改变某些东西,但我尝试对其进行调试,但在此期间没有任何线程(只有主线程)处于活动状态。
好的,我试着重建一些东西来模拟你的行为。并想出了这个,它应该非常接近你正在走向的行为。
我添加了一个 InverseToBooleanConverter
以与布尔相反的方式显示可见性(false = 可见)。这有助于切换东西
我为来自整数的 GridLength(您的身高)添加了一个转换器。我冒昧地创建了一个代表 Show
和 Hide
值的枚举。
重要的规则是让你的 ViewModels 保持纯粹,没有以 System.Windows 或任何与视图相关的东西开头的命名空间。
我以某种方式整理了您的属性和 属性Notification-Stuff。目标是尽可能保持紧实和苗条。所以对于这段代码,我只在 属性 本身内部调用了 On属性Changed。
对我来说,TaskListView
是一个 Control
,并且会有自己的 ListOfTaskViewModel
(具有行为)和任务集合(取决于其中的复杂性。这也可以成为 ObservableList<TaskItemViewModel>
)
这同样适用于 MeetingListView
和 MeetingListViewModel
。
现在重要的是在哪里以及如何加载数据。我可以考虑一个至少有 2 个方法 GetTasksForCaseID
和 GetMeetingsForCaseID
的服务,它们可以注入到 ViewModel 中,或者可以传递加载的数据。我更喜欢保持独立,并会使用 EventAggregator
或 Messenger
之类的东西来通知 ViewModel
并将匹配的 ID 作为有效负载。并保持对 ViewModel
获取数据的责任。但这要视情况而定,因为我没有足够的关于您的上下文的信息,所以这超出了示例的范围。但我希望你能明白。
这就是 MainViewModel class
同样的事情也适用于日历中的实际事件和突出显示的内容。它应该在自己的 ViewModel 中与自己的视图控件分开,以保持整洁。
public class MainViewModel:INotifyPropertyChanged
{
public MainViewModel()
{
Init();
}
public enum Calendar{
ShowCalendarMaxLength = 145,
HideCalenderHeight = 325,
}
private MeetingsListViewModel _listOfMeetingsViewModel;
public MeetingsListViewModel ListOfMeetingsViewModel {
get { return _listOfMeetingsViewModel; }
set
{
if (_listOfMeetingsViewModel != value)
{
_listOfMeetingsViewModel = value;
OnPropertyChanged("ListOfMeetings");
}
}
}
public TaskListViewModel _listOfTasksViewModel;
public TaskListViewModel ListOfTasksViewModel {
get{return _listOfTasksViewModel;}
set {
if (_listOfTasksViewModel != value)
{
_listOfTasksViewModel = value;
OnPropertyChanged("ListOfTasks");
}
}
}
private Calendar _calendarEventListBoxHeight;
public Calendar CalendarEventListBoxHeight
{
get { return _calendarEventListBoxHeight; }
set
{
if (_calendarEventListBoxHeight != value)
{
_calendarEventListBoxHeight = value;
OnPropertyChanged("CalendarEventListBoxHeight");
}
}
}
private bool _showCalendar;
public bool ShowCalendar
{
get { return _showCalendar; }
set {
if (_showCalendar != value)
{
_showCalendar = value;
OnPropertyChanged("ShowCalendar");
}
}
}
private bool _showTasks;
public bool ShowTasks
{
get { return _showTasks; }
set
{
if (_showTasks != value)
{
_showTasks = value;
OnPropertyChanged("ShowTasks");
}
}
}
private bool _showMeetings;
public bool ShowMeetings
{
get { return _showMeetings; }
set
{
if (_showMeetings != value)
{
_showMeetings = value; OnPropertyChanged("ShowMeetings");
}
}
}
public void ShowCalendarAction()
{
ShowCalendar = true;
CalendarEventListBoxHeight = Calendar.ShowCalendarMaxLength;
}
public void HideCalendarAction()
{
ShowCalendar = false;
CalendarEventListBoxHeight = Calendar.HideCalenderHeight;
}
public void ShowMeetingsAction()
{
ShowTasks = false;
ShowMeetings = true;
}
public void ShowTasksAction() {
ShowMeetings = false;
ShowTasks = true;
}
private void Init()
{
ShowCalendar = true;
CalendarEventListBoxHeight = Calendar.ShowCalendarMaxLength;
ShowMeetings = true;
ShowTasks = false;
ListOfMeetingsViewModel = new MeetingsListViewModel();
ListOfTasksViewModel = new TaskListViewModel();
}
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
这是 XAML.
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:conv="clr-namespace:WpfApplication1.Converters"
xmlns:vm="clr-namespace:WpfApplication1.ViewModels"
xmlns:cal="http://www.caliburnproject.org"
xmlns:views="clr-namespace:WpfApplication1.Views"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
Title="MainWindow" Height="350" Width="525"
>
<Window.Resources>
<BooleanToVisibilityConverter x:Key="VisibilityConverter"/>
<conv:InverseBooleanConverter x:Key="InverseVisibilityConverter"/>
<conv:GridViewLengthConverter x:Key="LengthConverter" />
</Window.Resources>
<Window.DataContext>
<vm:MainViewModel />
</Window.DataContext>
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid Grid.Column="0" Grid.Row="0">
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="auto"/>
</Grid.ColumnDefinitions>
<Calendar Grid.Row="0" Grid.Column="0"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Margin="0,0,0,0"
Visibility="{Binding Path=ShowCalendar, Mode=TwoWay,Converter={StaticResource VisibilityConverter}}"
>
</Calendar>
<Button Margin="0,12,0,0"
FontSize="15"
Grid.Row="0"
Grid.RowSpan="2"
Grid.Column="1"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Content="Show Calendar"
Visibility="{Binding Path=ShowCalendar,Mode=TwoWay,Converter={StaticResource InverseVisibilityConverter}}"
ToolTip="ShowCalendar">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<cal:ActionMessage MethodName="ShowCalendarAction" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
<Button Margin="0,32,0,0"
FontSize="15"
Grid.Row="0"
Grid.RowSpan="2"
Grid.Column="1"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Visibility="{Binding Path=ShowCalendar,Mode=TwoWay,Converter={StaticResource VisibilityConverter}}"
Content="Hide Calendar"
ToolTip="HideCalendarButtonClick">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<cal:ActionMessage MethodName="HideCalendarAction" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
<Button Margin="0,5,0,0"
Grid.Row="1"
Grid.Column="0"
FontSize="15"
HorizontalAlignment="Left"
Visibility="{Binding Path=ShowMeetings,Mode=TwoWay,Converter={StaticResource InverseVisibilityConverter}}"
Content="Show Meetings"
ToolTip="ShowMeetingsButtonClick">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<cal:ActionMessage MethodName="ShowMeetingsAction" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
<Button Margin="20,5,0,0"
Grid.Row="1"
Grid.Column="0"
FontSize="15"
Grid.ColumnSpan="3"
HorizontalAlignment="Left"
Visibility="{Binding Path=ShowTasks,Mode=TwoWay,Converter={StaticResource InverseVisibilityConverter}}"
Content="Show Tasks;"
ToolTip="ShowTasksButtonClick">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<cal:ActionMessage MethodName="ShowTasksAction" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
<Grid Grid.Row="2"
Grid.RowSpan="3"
Grid.Column="0"
Grid.ColumnSpan="2"
MaxHeight="{Binding Path=CalendarEventListBoxHeight, Mode=TwoWay, Converter={StaticResource LengthConverter }}"
Visibility="{Binding Path=ShowMeetings, Mode=TwoWay,Converter={StaticResource VisibilityConverter}}"
>
<views:MeetingsListView DataContext="{Binding Path=ListOfMeetingsViewModel,Mode=TwoWay}">
</views:MeetingsListView>
</Grid>
<Grid Grid.Row="2"
Grid.RowSpan="3"
Grid.Column="0"
Grid.ColumnSpan="2"
MaxHeight="{Binding Path=CalendarEventListBoxHeight, Converter={StaticResource LengthConverter }}"
Visibility="{Binding Path=ShowTaks,Converter={StaticResource LengthConverter}}"
>
<views:TaskListView DataContext="{Binding Path=ListOfTasksViewModel,Mode=TwoWay}" />
</Grid>
</Grid>
</Grid>
</Window>
为了完整起见,两个转换器:
InverseBooleanToVisibiltyConverter
public class InverseBooleanConverter : IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
if (targetType != typeof(Visibility))
throw new InvalidOperationException("The target must be a boolean");
if (!(bool)value)
{
return Visibility.Visible;
}
return Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
throw new NotSupportedException();
}
#endregion
}
GridViewLengthConverter
class GridViewLengthConverter:IValueConverter{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
double val = (int)value;
GridLength gridLength = new GridLength(val);
return gridLength;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
GridLength val = (GridLength)value;
return val.Value;
}
}
我想您可以通过使用更少的布尔值优化切换行为来删除一些代码 =)...
//编辑: 我坚信问题出在您显示的代码之外。特别是在 ViewMode 的情况下,加载和交换部分或您在评论中描述为 "a lot more complex functionality" 的内容。尽管如此,因为您已经有了一个 IsCaseLoaded-属性 。我假设您正在此处进行一些异步数据提取。 Async/await 也可能对 MVVM 很棘手。特别是在将 UI 相关操作与后台操作混合时。您可以在附件中找到一些有用的链接,了解如何处理异步代码和 MVVM。本系列展示了异步可绑定通知属性、异步 IComannd 实现和异步服务的方法。
异步编程:异步 MVVM 应用程序的模式:数据绑定
https://msdn.microsoft.com/en-us/magazine/dn605875.aspx
异步编程:异步 MVVM 应用程序的模式:命令
https://msdn.microsoft.com/en-us/magazine/dn630647.aspx
异步编程:异步 MVVM 应用程序的模式:服务
https://msdn.microsoft.com/en-us/magazine/dn683795.aspx
希望对您有所帮助...
我有我的自定义日历控件 - 事件日历。我在某些情况下使用它。
<Controls:EventCalendar Grid.Row="0"
Grid.RowSpan="8"
Grid.Column="2"
Margin="20,50,0,0"
CalendarEvents="{Binding DataContext.CalendarEvents, RelativeSource={RelativeSource TemplatedParent}, UpdateSourceTrigger=PropertyChanged}"
Header="{Binding DataContext.DataSpis.Header, RelativeSource={RelativeSource TemplatedParent}, UpdateSourceTrigger=PropertyChanged}"
ViewModelBase="{Binding DataContext.ViewModel, RelativeSource={RelativeSource TemplatedParent}, UpdateSourceTrigger=PropertyChanged}"
IsFunctionalityVisible="{Binding DataContext.IsFunctionalityVisible, RelativeSource={RelativeSource TemplatedParent}, UpdateSourceTrigger=PropertyChanged}"
IsCaseLoaded="{Binding DataContext.IsLoaded, RelativeSource={RelativeSource TemplatedParent}, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}">
</Controls:EventCalendar>
我检测是否通过 IsCaseLoaded 依赖项 属性 加载案例(相同视图,不同数据)。发生这种情况时,我将新的 DataContext 添加到我的日历控件中。像这样:
private static void LoadPCCallback(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
if (((EventCalendar)source).IsCaseLoaded == true)
{
((EventCalendar)source).DataContext = null;
((EventCalendar)source).DataContext = new EventCalendarViewModel(((EventCalendar)source).Header, ((EventCalendar)source).ViewModelBase, ((EventCalendar)source).CalendarEvents);
}
}
在 EventCalendarViewModel 的构造函数中,我为要显示的会议或任务设置了一些可见性。默认情况下会显示会议并隐藏任务。
当我想显示任务时,我单击此日历控件上的按钮。
现在行为开始变得 意外 :我加载案例,单击任务按钮,它起作用了 - 显示任务,隐藏会议。
我重新加载案例,单击“任务”按钮,它起作用了 - 显示任务,隐藏会议。
但是我第三次重新加载案例(有时是第二次,有时是第四次 - 真的 随机 ),构造函数工作,将会议设置为默认值,但是当我单击任务按钮时,它突然具有以前的值DataContext,所以它认为任务是 true,会议是 false...所以没有任何变化,会议仍然显示。
public void ShowMeetingsButtonClick()
{
this.ShowTasks = false;
NotifyOfPropertyChange(() => ShowTasks);
this.ShowMeetings = true;
NotifyOfPropertyChange(() => ShowMeetings);
}
Show Tasks也是这样:
public void ShowTasksButtonClick()
{
this.ShowMeetings = false;
NotifyOfPropertyChange(() => ShowMeetings);
this.ShowTasks = true;
NotifyOfPropertyChange(() => ShowTasks);
}
所以我想到的一件事是,这个日历视图以某种方式在 Visual Tree 中找到了以前的 DataContext 并采用旧值从那里。因为在 new DataContext 的构造函数之后一切看起来都很好,但是在点击一个按钮之后它突然有了不同的值。
我还认为我的一些线程正在改变某些东西,但我尝试对其进行调试,但在此期间没有任何线程(只有主线程)处于活动状态。
好的,我试着重建一些东西来模拟你的行为。并想出了这个,它应该非常接近你正在走向的行为。
我添加了一个 InverseToBooleanConverter
以与布尔相反的方式显示可见性(false = 可见)。这有助于切换东西
我为来自整数的 GridLength(您的身高)添加了一个转换器。我冒昧地创建了一个代表 Show
和 Hide
值的枚举。
重要的规则是让你的 ViewModels 保持纯粹,没有以 System.Windows 或任何与视图相关的东西开头的命名空间。
我以某种方式整理了您的属性和 属性Notification-Stuff。目标是尽可能保持紧实和苗条。所以对于这段代码,我只在 属性 本身内部调用了 On属性Changed。
对我来说,TaskListView
是一个 Control
,并且会有自己的 ListOfTaskViewModel
(具有行为)和任务集合(取决于其中的复杂性。这也可以成为 ObservableList<TaskItemViewModel>
)
这同样适用于 MeetingListView
和 MeetingListViewModel
。
现在重要的是在哪里以及如何加载数据。我可以考虑一个至少有 2 个方法 GetTasksForCaseID
和 GetMeetingsForCaseID
的服务,它们可以注入到 ViewModel 中,或者可以传递加载的数据。我更喜欢保持独立,并会使用 EventAggregator
或 Messenger
之类的东西来通知 ViewModel
并将匹配的 ID 作为有效负载。并保持对 ViewModel
获取数据的责任。但这要视情况而定,因为我没有足够的关于您的上下文的信息,所以这超出了示例的范围。但我希望你能明白。
这就是 MainViewModel class
同样的事情也适用于日历中的实际事件和突出显示的内容。它应该在自己的 ViewModel 中与自己的视图控件分开,以保持整洁。
public class MainViewModel:INotifyPropertyChanged
{
public MainViewModel()
{
Init();
}
public enum Calendar{
ShowCalendarMaxLength = 145,
HideCalenderHeight = 325,
}
private MeetingsListViewModel _listOfMeetingsViewModel;
public MeetingsListViewModel ListOfMeetingsViewModel {
get { return _listOfMeetingsViewModel; }
set
{
if (_listOfMeetingsViewModel != value)
{
_listOfMeetingsViewModel = value;
OnPropertyChanged("ListOfMeetings");
}
}
}
public TaskListViewModel _listOfTasksViewModel;
public TaskListViewModel ListOfTasksViewModel {
get{return _listOfTasksViewModel;}
set {
if (_listOfTasksViewModel != value)
{
_listOfTasksViewModel = value;
OnPropertyChanged("ListOfTasks");
}
}
}
private Calendar _calendarEventListBoxHeight;
public Calendar CalendarEventListBoxHeight
{
get { return _calendarEventListBoxHeight; }
set
{
if (_calendarEventListBoxHeight != value)
{
_calendarEventListBoxHeight = value;
OnPropertyChanged("CalendarEventListBoxHeight");
}
}
}
private bool _showCalendar;
public bool ShowCalendar
{
get { return _showCalendar; }
set {
if (_showCalendar != value)
{
_showCalendar = value;
OnPropertyChanged("ShowCalendar");
}
}
}
private bool _showTasks;
public bool ShowTasks
{
get { return _showTasks; }
set
{
if (_showTasks != value)
{
_showTasks = value;
OnPropertyChanged("ShowTasks");
}
}
}
private bool _showMeetings;
public bool ShowMeetings
{
get { return _showMeetings; }
set
{
if (_showMeetings != value)
{
_showMeetings = value; OnPropertyChanged("ShowMeetings");
}
}
}
public void ShowCalendarAction()
{
ShowCalendar = true;
CalendarEventListBoxHeight = Calendar.ShowCalendarMaxLength;
}
public void HideCalendarAction()
{
ShowCalendar = false;
CalendarEventListBoxHeight = Calendar.HideCalenderHeight;
}
public void ShowMeetingsAction()
{
ShowTasks = false;
ShowMeetings = true;
}
public void ShowTasksAction() {
ShowMeetings = false;
ShowTasks = true;
}
private void Init()
{
ShowCalendar = true;
CalendarEventListBoxHeight = Calendar.ShowCalendarMaxLength;
ShowMeetings = true;
ShowTasks = false;
ListOfMeetingsViewModel = new MeetingsListViewModel();
ListOfTasksViewModel = new TaskListViewModel();
}
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
这是 XAML.
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:conv="clr-namespace:WpfApplication1.Converters"
xmlns:vm="clr-namespace:WpfApplication1.ViewModels"
xmlns:cal="http://www.caliburnproject.org"
xmlns:views="clr-namespace:WpfApplication1.Views"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
Title="MainWindow" Height="350" Width="525"
>
<Window.Resources>
<BooleanToVisibilityConverter x:Key="VisibilityConverter"/>
<conv:InverseBooleanConverter x:Key="InverseVisibilityConverter"/>
<conv:GridViewLengthConverter x:Key="LengthConverter" />
</Window.Resources>
<Window.DataContext>
<vm:MainViewModel />
</Window.DataContext>
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid Grid.Column="0" Grid.Row="0">
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="auto"/>
</Grid.ColumnDefinitions>
<Calendar Grid.Row="0" Grid.Column="0"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Margin="0,0,0,0"
Visibility="{Binding Path=ShowCalendar, Mode=TwoWay,Converter={StaticResource VisibilityConverter}}"
>
</Calendar>
<Button Margin="0,12,0,0"
FontSize="15"
Grid.Row="0"
Grid.RowSpan="2"
Grid.Column="1"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Content="Show Calendar"
Visibility="{Binding Path=ShowCalendar,Mode=TwoWay,Converter={StaticResource InverseVisibilityConverter}}"
ToolTip="ShowCalendar">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<cal:ActionMessage MethodName="ShowCalendarAction" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
<Button Margin="0,32,0,0"
FontSize="15"
Grid.Row="0"
Grid.RowSpan="2"
Grid.Column="1"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Visibility="{Binding Path=ShowCalendar,Mode=TwoWay,Converter={StaticResource VisibilityConverter}}"
Content="Hide Calendar"
ToolTip="HideCalendarButtonClick">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<cal:ActionMessage MethodName="HideCalendarAction" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
<Button Margin="0,5,0,0"
Grid.Row="1"
Grid.Column="0"
FontSize="15"
HorizontalAlignment="Left"
Visibility="{Binding Path=ShowMeetings,Mode=TwoWay,Converter={StaticResource InverseVisibilityConverter}}"
Content="Show Meetings"
ToolTip="ShowMeetingsButtonClick">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<cal:ActionMessage MethodName="ShowMeetingsAction" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
<Button Margin="20,5,0,0"
Grid.Row="1"
Grid.Column="0"
FontSize="15"
Grid.ColumnSpan="3"
HorizontalAlignment="Left"
Visibility="{Binding Path=ShowTasks,Mode=TwoWay,Converter={StaticResource InverseVisibilityConverter}}"
Content="Show Tasks;"
ToolTip="ShowTasksButtonClick">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<cal:ActionMessage MethodName="ShowTasksAction" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
<Grid Grid.Row="2"
Grid.RowSpan="3"
Grid.Column="0"
Grid.ColumnSpan="2"
MaxHeight="{Binding Path=CalendarEventListBoxHeight, Mode=TwoWay, Converter={StaticResource LengthConverter }}"
Visibility="{Binding Path=ShowMeetings, Mode=TwoWay,Converter={StaticResource VisibilityConverter}}"
>
<views:MeetingsListView DataContext="{Binding Path=ListOfMeetingsViewModel,Mode=TwoWay}">
</views:MeetingsListView>
</Grid>
<Grid Grid.Row="2"
Grid.RowSpan="3"
Grid.Column="0"
Grid.ColumnSpan="2"
MaxHeight="{Binding Path=CalendarEventListBoxHeight, Converter={StaticResource LengthConverter }}"
Visibility="{Binding Path=ShowTaks,Converter={StaticResource LengthConverter}}"
>
<views:TaskListView DataContext="{Binding Path=ListOfTasksViewModel,Mode=TwoWay}" />
</Grid>
</Grid>
</Grid>
</Window>
为了完整起见,两个转换器:
InverseBooleanToVisibiltyConverter
public class InverseBooleanConverter : IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
if (targetType != typeof(Visibility))
throw new InvalidOperationException("The target must be a boolean");
if (!(bool)value)
{
return Visibility.Visible;
}
return Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
throw new NotSupportedException();
}
#endregion
}
GridViewLengthConverter
class GridViewLengthConverter:IValueConverter{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
double val = (int)value;
GridLength gridLength = new GridLength(val);
return gridLength;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
GridLength val = (GridLength)value;
return val.Value;
}
}
我想您可以通过使用更少的布尔值优化切换行为来删除一些代码 =)...
//编辑: 我坚信问题出在您显示的代码之外。特别是在 ViewMode 的情况下,加载和交换部分或您在评论中描述为 "a lot more complex functionality" 的内容。尽管如此,因为您已经有了一个 IsCaseLoaded-属性 。我假设您正在此处进行一些异步数据提取。 Async/await 也可能对 MVVM 很棘手。特别是在将 UI 相关操作与后台操作混合时。您可以在附件中找到一些有用的链接,了解如何处理异步代码和 MVVM。本系列展示了异步可绑定通知属性、异步 IComannd 实现和异步服务的方法。
异步编程:异步 MVVM 应用程序的模式:数据绑定 https://msdn.microsoft.com/en-us/magazine/dn605875.aspx
异步编程:异步 MVVM 应用程序的模式:命令 https://msdn.microsoft.com/en-us/magazine/dn630647.aspx
异步编程:异步 MVVM 应用程序的模式:服务 https://msdn.microsoft.com/en-us/magazine/dn683795.aspx
希望对您有所帮助...