在 Caliburn Micro 中重新排列父子激活顺序
Rearranging parent-child activation order in Caliburn Micro
在我的视图模型中覆盖 OnActivate() 期间,我需要调用 GetView() 以聚焦元素。当我在之前激活我的视图后执行此操作时,没问题。但是当我称之为第一次激活时,它失败了。
我可以通过在 ConductorBaseWithActiveItem.ChangeActiveItem 中交换几行来让它工作。原文如下:
protected virtual void ChangeActiveItem(T newItem, bool closePrevious) {
ScreenExtensions.TryDeactivate(activeItem, closePrevious);
newItem = EnsureItem(newItem);
if(IsActive)
ScreenExtensions.TryActivate(newItem);
activeItem = newItem;
NotifyOfPropertyChange("ActiveItem");
OnActivationProcessed(activeItem, true);
}
以及我的更改:
protected virtual void ChangeActiveItem(T newItem, bool closePrevious) {
ScreenExtensions.TryDeactivate(activeItem, closePrevious);
newItem = EnsureItem(newItem);
activeItem = newItem;
NotifyOfPropertyChange("ActiveItem");
if (IsActive)
ScreenExtensions.TryActivate(newItem);
OnActivationProcessed(activeItem, true);
}
这似乎有效。通知 "ActiveItem" 已更改会触发代码加载和缓存视图。然后 ScreenExtensions.TryActivate 调用我的 OnActivate 覆盖。
问题:我没有注意到这样做有任何问题,但我很好奇是否有人比我更了解此更改可能产生的影响?
谢谢!
您可以尝试的一件事是覆盖 Caliburn 的 OnViewAttached
方法并尝试将其集中在那里。话虽这么说,在 MVVM 中,焦点更多的是视图关注点,因此如果可能的话,应该将该逻辑从 ViewModel 移动到 View。
解决此问题的一种方法是创建附加行为(您需要引用 Microsoft.Expression.Interactions
程序集):
public class FocusWhenVisibleBehavior : Behavior<FrameworkElement>
{
protected override void OnAttached()
{
this.AssociatedObject.Loaded += this.Loaded;
this.AssociatedObject.IsVisibleChanged += this.VisibleChanged;
}
protected override void OnDetaching()
{
this.AssociatedObject.Loaded -= this.Loaded;
this.AssociatedObject.IsVisibleChanged -= this.VisibleChanged;
}
private void Loaded(object sender, RoutedEventArgs e)
{
this.TryFocus();
}
private void VisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
{
this.TryFocus();
}
private void TryFocus()
{
if (this.AssociatedObject.IsLoaded && this.AssociatedObject.IsVisible)
{
// Focus the control
this.AssociatedObject.Focus();
}
}
}
然后将该行为附加到您想要关注的任何控件:
<Button>
<i:Interaction.Behaviors>
<b:FocusWhenVisibleBehavior/>
</i:Interaction.Behaviors>
</Button>
在我的视图模型中覆盖 OnActivate() 期间,我需要调用 GetView() 以聚焦元素。当我在之前激活我的视图后执行此操作时,没问题。但是当我称之为第一次激活时,它失败了。
我可以通过在 ConductorBaseWithActiveItem.ChangeActiveItem 中交换几行来让它工作。原文如下:
protected virtual void ChangeActiveItem(T newItem, bool closePrevious) {
ScreenExtensions.TryDeactivate(activeItem, closePrevious);
newItem = EnsureItem(newItem);
if(IsActive)
ScreenExtensions.TryActivate(newItem);
activeItem = newItem;
NotifyOfPropertyChange("ActiveItem");
OnActivationProcessed(activeItem, true);
}
以及我的更改:
protected virtual void ChangeActiveItem(T newItem, bool closePrevious) {
ScreenExtensions.TryDeactivate(activeItem, closePrevious);
newItem = EnsureItem(newItem);
activeItem = newItem;
NotifyOfPropertyChange("ActiveItem");
if (IsActive)
ScreenExtensions.TryActivate(newItem);
OnActivationProcessed(activeItem, true);
}
这似乎有效。通知 "ActiveItem" 已更改会触发代码加载和缓存视图。然后 ScreenExtensions.TryActivate 调用我的 OnActivate 覆盖。
问题:我没有注意到这样做有任何问题,但我很好奇是否有人比我更了解此更改可能产生的影响?
谢谢!
您可以尝试的一件事是覆盖 Caliburn 的 OnViewAttached
方法并尝试将其集中在那里。话虽这么说,在 MVVM 中,焦点更多的是视图关注点,因此如果可能的话,应该将该逻辑从 ViewModel 移动到 View。
解决此问题的一种方法是创建附加行为(您需要引用 Microsoft.Expression.Interactions
程序集):
public class FocusWhenVisibleBehavior : Behavior<FrameworkElement>
{
protected override void OnAttached()
{
this.AssociatedObject.Loaded += this.Loaded;
this.AssociatedObject.IsVisibleChanged += this.VisibleChanged;
}
protected override void OnDetaching()
{
this.AssociatedObject.Loaded -= this.Loaded;
this.AssociatedObject.IsVisibleChanged -= this.VisibleChanged;
}
private void Loaded(object sender, RoutedEventArgs e)
{
this.TryFocus();
}
private void VisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
{
this.TryFocus();
}
private void TryFocus()
{
if (this.AssociatedObject.IsLoaded && this.AssociatedObject.IsVisible)
{
// Focus the control
this.AssociatedObject.Focus();
}
}
}
然后将该行为附加到您想要关注的任何控件:
<Button>
<i:Interaction.Behaviors>
<b:FocusWhenVisibleBehavior/>
</i:Interaction.Behaviors>
</Button>