Prism4学习笔记(七):State-Based Navigation QuickStart

 

   本节学习了Navigation的一些基本知识,觉得这节比较难。这里讲学习和理解点的东西记录下来。觉得本节应该弄清楚的问题的关键词

  (1)CallMethodAction用于事件和行为的绑定。

(2)InteractionRequest<T> 在交互请求时协调ViewModel和View

(3)Notification用于交互式单向通知用户,所以ViewModel不能预测用户对Notification中Title和Content的更改

(4)学会自定义行为和操作

(一)在ChatView.xaml,代码如下

View Code
< Grid x:Name ="LayoutRoot" >
<!-- 虚拟状态过渡 -->
<!-- Blend中DataStateBehavior 行为
利用 DataStateBehavior 行为,您可以比较两个值。一个值来自绑定。
您可以显式地声明另一个值。如果两个值相等,则会激活为 True 指定的可视状态。
如果两个值不相等,则会激活为 False 指定的可视状态。
-->
< i:Interaction.Behaviors >
< ei:DataStateBehavior Binding =" {Binding ShowDetails} "
Value
="True"
TrueState
="ShowDetails" FalseState ="ShowContacts" />
< ei:DataStateBehavior Binding =" {Binding IsChecked, ElementName=ShowAsListButton} "
Value
="True"
TrueState
="ShowAsList" FalseState ="ShowAsIcons" />
< ei:DataStateBehavior Binding =" {Binding ConnectionStatus} "
Value
="Available"
TrueState
="Available" FalseState ="Unavailable" />
< ei:DataStateBehavior Binding =" {Binding SendingMessage} "
Value
="True"
TrueState
="SendingMessage" FalseState ="NotSendingMessage" />
</ i:Interaction.Behaviors >

< i:Interaction.Triggers >
<!-- 点击SendMessage按钮后弹出子窗口 -->
< prism:InteractionRequestTrigger SourceObject =" {Binding SendMessageRequest} " >
< prism:PopupChildWindowAction >
< prism:PopupChildWindowAction.ChildWindow >
< vs:SendMessageChildWindow />
</ prism:PopupChildWindowAction.ChildWindow >
</ prism:PopupChildWindowAction >
</ prism:InteractionRequestTrigger >
<!-- 调用自定义行为ShowNotificationAction -->
< prism:InteractionRequestTrigger SourceObject =" {Binding ShowReceivedMessageRequest} " >
< cb:ShowNotificationAction TargetName ="NotificationPopup" />
</ prism:InteractionRequestTrigger >

</ i:Interaction.Triggers >
< Grid Grid.Row ="0" Grid.Column ="1" Grid.RowSpan ="2" >
< Grid.RowDefinitions >
< RowDefinition Height ="Auto" />
< RowDefinition Height ="Auto" />
</ Grid.RowDefinitions >
< Grid.ColumnDefinitions >
< ColumnDefinition Width ="*" />
< ColumnDefinition Width ="Auto" />
</ Grid.ColumnDefinitions >
<!-- Send Message按钮行为 在View中直接调用方法(InvokeCommandAction)使用CallMethodAction实现
动作响应事件或者触发器,有两种方式获取ViewModel的响应在松散耦合的风格中。
(1)你可以用Command来时实现操作
(2)你可用行为附加到元素上(附加属性机制或使用Blend SDK中behavior基本类)Interaction.Triggers和Interaction.Behaviors.
行为能与事件挂钩
CallMethodAction一般用到下面的3个属性
(1)TargetObject
(2)MethodName
(3)IsEnabled
Prism中 DelegateCommand
Blend SDK中ActonCommand

-->
< Button x:Name ="SendMessageButton" Grid.Column ="0" Grid.Row ="0" Margin ="4" VerticalAlignment ="Center"
AutomationProperties.AutomationId
="SendMessageButton" >
< i:Interaction.Triggers >
< i:EventTrigger EventName ="Click" >
< ei:CallMethodAction TargetObject =" {Binding DataContext, ElementName=userControl} "
MethodName
="SendMessage" />
</ i:EventTrigger >
</ i:Interaction.Triggers >
Send Message
</ Button >
< GuidanceTools:InfoTipToggleButton VerticalAlignment ="Center"
Grid.Column
="1" Grid.Row ="0" Margin ="4" >
< StackPanel MaxWidth ="400" >
< TextBlock TextWrapping ="Wrap" >
This button executes the SendMessage method on the view model,
and is only enabled when no other message is being sent. Executing
the method causes a child window to be opened to capture the
message to send. Accepting the message causes the view model
to send the message to the chat service, which in turns
causes an update on the view model that is represented by showing
a progress bar until a confirmation that the message has been sent
is issued by the chat service.
</ TextBlock >
</ StackPanel >
</ GuidanceTools:InfoTipToggleButton >

< Button Grid.Column ="0" Grid.Row ="1" Margin ="4" Command =" {Binding ShowDetailsCommand} " VerticalAlignment ="Center" >
< Button.CommandParameter >
< sys:Boolean > False </ sys:Boolean >
</ Button.CommandParameter >
Go Back
</ Button >
< GuidanceTools:InfoTipToggleButton VerticalAlignment ="Center"
Grid.Column
="1" Grid.Row ="1" Margin ="4" >
< StackPanel MaxWidth ="400" >
< TextBlock TextWrapping ="Wrap" >
This button uses the ShowDetailsCommand, which is only available
when a contact has been selected. Executing the command sets
one of the 'ShowDetails' and 'ShowContacts' visual states.
Transitions to this state are implemented with a flip visual effect.
</ TextBlock >
< TextBlock TextWrapping ="Wrap" >
Visual state transitions for the detail visualization mode are
triggered by changes in the view model.
</ TextBlock >
</ StackPanel >
</ GuidanceTools:InfoTipToggleButton >
</ Grid >

(二)在ChatViewModel.cs

ublic class ChatViewModel : ViewModel
{
private readonly IChatService chatService;
// InteractionRequest<T> 在交互请求时协调ViewModel和View
// (1)Raise方法允许ViewModel初始化交互和指定一个对象上下文(类型为T),并且回调方法在交互完成后被调用
// 上下文对象允许ViewModel传递数据和状态到View上在用交互期间
// (2)如果回调方法被指定,上下文对象将被传递回ViewModel,this允许交互期间用户的任何改变都可以回传到ViewModel
private readonly InteractionRequest < SendMessageViewModel > sendMessageRequest;
private readonly InteractionRequest < ReceivedMessage > showReceivedMessageRequest;
private readonly ObservableCollection < Contact > contacts;
private readonly PagedCollectionView contactsView;
private readonly ShowDetailsCommandImplementation showDetailsCommand;
private bool showDetails;
private bool sendingMessage;

public ChatViewModel(IChatService chatService)
{
this .contacts = new ObservableCollection < Contact > ();
this .contactsView = new PagedCollectionView( this .contacts);
this .sendMessageRequest = new InteractionRequest < SendMessageViewModel > ();
this .showReceivedMessageRequest = new InteractionRequest < ReceivedMessage > ();
this .showDetailsCommand = new ShowDetailsCommandImplementation( this );

this .contactsView.CurrentChanged += this .OnCurrentContactChanged;

this .chatService = chatService;
this .chatService.Connected = true ;
this .chatService.ConnectionStatusChanged += (s, e) => this .RaisePropertyChanged(() => this .ConnectionStatus);
this .chatService.MessageReceived += this .OnMessageReceived;

this .chatService.GetContacts(
result
=>
{
if (result.Error == null )
{
foreach (var item in result.Result)
{
this .contacts.Add(item);
}
}
});
}

public ObservableCollection < Contact > Contacts
{
get { return this .contacts; }
}

public ICollectionView ContactsView
{
get { return this .contactsView; }
}

public IInteractionRequest SendMessageRequest
{
get { return this .sendMessageRequest; }
}

public IInteractionRequest ShowReceivedMessageRequest
{
get { return this .showReceivedMessageRequest; }
}

public string ConnectionStatus
{
get
{
return this .chatService.Connected ? " Available " : " Unavailable " ;
}

set
{
this .chatService.Connected = value == " Available " ;
}
}

public Contact CurrentContact
{
get
{
return this .contactsView.CurrentItem as Contact;
}
}

public bool ShowDetails
{
get
{
return this .showDetails;
}

set
{
if ( this .showDetails != value)
{
this .showDetails = value;
this .RaisePropertyChanged(() => this .ShowDetails);
}
}
}

public bool SendingMessage
{
get
{
return this .sendingMessage;
}

private set
{
if ( this .sendingMessage != value)
{
this .sendingMessage = value;
this .RaisePropertyChanged(() => this .SendingMessage);
}
}
}

public ICommand ShowDetailsCommand
{
get { return this .showDetailsCommand; }
}

// SendMessage绑定到Send Message按钮
public void SendMessage()
{
var contact
= this .CurrentContact;
this .sendMessageRequest.Raise(
new SendMessageViewModel(contact, this ),
sendMessage
=>
{
if (sendMessage.Result.HasValue && sendMessage.Result.Value)
{
this .SendingMessage = true ;

this .chatService.SendMessage(
contact,
sendMessage.Message,
result
=>
{
this .SendingMessage = false ;
});
}
});
}

private void OnCurrentContactChanged( object sender, EventArgs a)
{
this .RaisePropertyChanged(() => this .CurrentContact);
this .showDetailsCommand.RaiseCanExecuteChanged();
}

private void OnMessageReceived( object sender, MessageReceivedEventArgs a)
{
this .showReceivedMessageRequest.Raise(a.Message);
}

#region 类ShowDetailsCommandImplementation
private class ShowDetailsCommandImplementation : ICommand
{
private readonly ChatViewModel owner;

public ShowDetailsCommandImplementation(ChatViewModel owner)
{
this .owner = owner;
}

public event EventHandler CanExecuteChanged;

public bool CanExecute( object parameter)
{
return this .owner.ContactsView.CurrentItem != null ;
}

public void Execute( object parameter)
{
this .owner.ShowDetails = ( bool )parameter;
}

public void RaiseCanExecuteChanged()
{
var handler
= this .CanExecuteChanged;
if (handler != null )
{
handler(
this , EventArgs.Empty);
}
}
}
#endregion
}

(三)在SendMessageModel.cs

// Notification介绍
// (1)Notification类支持普通交互请求服务
// (2)Notification类是作为对象上下文最基本得类
// (3)用于交互请求时通知用户。
// (4)提供了两个属性Title和Content被显示给用户
// (5)通常,通知是单向的,所以Notification在交互期间不能预测用户将会改变Title和Content
// (6)Confirmation类继承自Notification类和添加了第三个属性Cofirmed--被用于表示用户已经确认或者拒绝操作
// (7)Confirmation类用于实现MessageBox样式交互,当用户想获取yes/no相应从呼呼那里。
// (8)你自定一个上下文类继承自Notification封装成无论是支持交互的数据还是状态你需要
public class SendMessageViewModel : Notification, INotifyPropertyChanged
{
private readonly Contact contact;
private readonly ChatViewModel parent;
private bool ? result;
private string message;

public SendMessageViewModel(Contact contact, ChatViewModel parent)
{
this .contact = contact;
this .parent = parent;
}

public Contact Contact
{
get { return this .contact; }
}

public string Message
{
get
{
return this .message;
}

set
{
if (value != this .message)
{
this .message = value;
RaisePropertyChanged(()
=> this .Message);
}
}
}

public bool ? Result
{
get
{
return this .result;
}

set
{
if (value != this .result)
{
this .result = value;
RaisePropertyChanged(()
=> this .Result);
}
}
}

public event PropertyChangedEventHandler PropertyChanged;

protected virtual void OnPropertyChanged( string propertyName)
{
PropertyChangedEventHandler handler
= PropertyChanged;
if (handler != null ) handler( this , new PropertyChangedEventArgs(propertyName));
}

private void RaisePropertyChanged < T > (Expression < Func < T >> lambda)
{
var name
= PropertySupport.ExtractPropertyName < T > (lambda);
OnPropertyChanged(name);
}

}

(四)在Infrastructure文件夹下的ViewModel.cs代码如下:

/// <summary>
/// Base class for view models.
/// </summary>
/// <remarks>
/// This class provides basic support for implementing the <see cref="INotifyPropertyChanged"/> interface.
/// </remarks>
public class ViewModel : INotifyPropertyChanged
{
/// <summary>
/// Raised when a property on this object has a new value.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;

/// <summary>
/// Raises this object's PropertyChanged event.
/// </summary>
/// <typeparam name="T"> The type of the property that has a new value </typeparam>
/// <param name="propertyExpresssion"> A Lambda expression representing the property that has a new value. </param>
protected void RaisePropertyChanged < T > (Expression < Func < T >> propertyExpresssion)
{
var propertyName
= PropertySupport.ExtractPropertyName(propertyExpresssion);
this .RaisePropertyChanged(propertyName);
}

/// <summary>
/// Raises this object's PropertyChanged event.
/// </summary>
/// <param name="propertyName"> The property that has a new value. </param>
protected virtual void RaisePropertyChanged( string propertyName)
{
var handler
= this .PropertyChanged;
if (handler != null )
{
handler(
this , new PropertyChangedEventArgs(propertyName));
}
}

protected void ExecuteOnUIThread(Action action)
{
var dispatcher
= Deployment.Current.Dispatcher;

if (dispatcher.CheckAccess())
{
action();
}
else
{
dispatcher.BeginInvoke(action);
}
}
}

(五)在Infrastructure文件夹下的Behavior文件夹下,有自己定义行为(Behavior)和操作(Action)

(1) RelocatePopupBehavior行为类

// 该自定义行为:确保弹出串口定位在父视图的右下角
/// <summary>
/// Behavior that ensures a popup is located at the bottom-right corner of its parent.
/// </summary>
public class RelocatePopupBehavior : Behavior < Popup >
{
protected override void OnAttached()
{
base .OnAttached();

this .AssociatedObject.Opened += this .OnPopupOpened;
this .AssociatedObject.Closed += this .OnPopupClosed;
}

protected override void OnDetaching()
{
this .AssociatedObject.Opened -= this .OnPopupOpened;
this .AssociatedObject.Closed -= this .OnPopupClosed;

this .DetachSizeChangeHandlers();

base .OnDetaching();
}

private void OnPopupOpened( object sender, EventArgs e)
{
this .UpdatePopupOffsets();
this .AttachSizeChangeHandlers();
}

private void OnPopupClosed( object sender, EventArgs e)
{
this .DetachSizeChangeHandlers();
}

private void AttachSizeChangeHandlers()
{
var child
= this .AssociatedObject.Child as FrameworkElement;
if (child != null )
{
child.SizeChanged
+= this .OnChildSizeChanged;
}

var parent
= this .AssociatedObject.Parent as FrameworkElement;
if (parent != null )
{
parent.SizeChanged
+= this .OnParentSizeChanged;
}
}

private void DetachSizeChangeHandlers()
{
var child
= this .AssociatedObject.Child as FrameworkElement;
if (child != null )
{
child.SizeChanged
-= this .OnChildSizeChanged;
}

var parent
= this .AssociatedObject.Parent as FrameworkElement;
if (parent != null )
{
parent.SizeChanged
-= this .OnParentSizeChanged;
}
}

private void OnChildSizeChanged( object sender, EventArgs e)
{
this .UpdatePopupOffsets();
}

private void OnParentSizeChanged( object sender, EventArgs e)
{
this .UpdatePopupOffsets();
}

private void UpdatePopupOffsets()
{
if ( this .AssociatedObject != null )
{
var child
= this .AssociatedObject.Child as FrameworkElement;
var parent
= this .AssociatedObject.Parent as FrameworkElement;

if (child != null && parent != null )
{
var anchor
= new Point(parent.ActualWidth, parent.ActualHeight);

this .AssociatedObject.HorizontalOffset = anchor.X - child.ActualWidth;
this .AssociatedObject.VerticalOffset = anchor.Y - child.ActualHeight;
}
}
}
}

(2) ShowNotificationAction.cs定义了行为

/ 行为是功能单元容器。有两种类型的行为
// (1)不具有调用概念的行为,以附加的方式添加到对象上
// (2)触发器和动作更接近调用模型
// 你可以重复利用事件的句柄或者粗发起到UI上。
namespace StateBasedNavigation.Infrastructure.Behaviors
{

// 这个自定义行为允许ViewModel推一个通知到目标元素UI上,通过用户接受在在右下角处
public class ShowNotificationAction : TargetedTriggerAction < FrameworkElement >
{
// 注册依赖属性NotificationTimeoutProperty
public static readonly DependencyProperty NotificationTimeoutProperty =
DependencyProperty.Register(
" NotificationTimeout " , typeof (TimeSpan), typeof (ShowNotificationAction), new PropertyMetadata( new TimeSpan( 0 , 0 , 5 )));

private ObservableCollection < object > notifications;

public ShowNotificationAction()
{
this .notifications = new ObservableCollection < object > ();
}

public TimeSpan NotificationTimeout
{
get { return (TimeSpan)GetValue(NotificationTimeoutProperty); }

set { SetValue(NotificationTimeoutProperty, value); }
}

protected override void OnTargetChanged(FrameworkElement oldTarget, FrameworkElement newTarget)
{
base .OnTargetChanged(oldTarget, newTarget);

if (oldTarget != null )
{
this .Target.ClearValue(FrameworkElement.DataContextProperty);
}

if (newTarget != null )
{
this .Target.DataContext = this .notifications;
}
}

protected override void Invoke( object parameter)
{
var args
= parameter as InteractionRequestedEventArgs;
if (args == null )
{
return ;
}

var notification
= args.Context;

this .notifications.Insert( 0 , notification);

var timer
= new DispatcherTimer { Interval = this .NotificationTimeout };
EventHandler timerCallback
= null ;
timerCallback
=
(o, e)
=>
{
timer.Stop();
timer.Tick
-= timerCallback;
this .notifications.Remove(notification);
};
timer.Tick
+= timerCallback;
timer.Start();

args.Callback();
}
}
}

(七 )项目组织结构及运行截图

   

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值