У меня есть приложение Prism 6.3 / Unity WPF, которое будет искать установленные сканеры после запуска и представлять пользователю сообщение, когда его нет. Для представления сообщения в моем представлении оболочки есть специальный регион, который обычно свернут:
Shell.xaml
<Grid Grid.ColumnSpan="3" Grid.RowSpan="3" Panel.ZIndex="2" Visibility="{Binding MessageVisibility}"
fade:VisibilityAnimation.AnimationType="Fade">
<Grid.RowDefinitions>
<RowDefinition Height="15*"/>
<RowDefinition Height="70*"/>
<RowDefinition Height="15*"/>
</Grid.RowDefinitions>
<Rectangle Grid.Row="0" Fill="{DynamicResource AccentColorBrush2}"/>
<ContentControl Grid.Row="1" prism:RegionManager.RegionName="{x:Static local:RegionNames.PopUpRegionName}"/>
<Rectangle Grid.Row="2" Fill="{DynamicResource AccentColorBrush2}"/>
</Grid>
Когда создается сообщение, агрегатор событий запускает событие, подобное этому
_eventAggregator.GetEvent<ShowMessage>().Publish(message);
, и ShellViewModel прослушивает это событие следующим образом:
ShellViewModel Конструктор и метод
_eventAggregator.GetEvent<ShowMessage>().Subscribe(ReceiveMessage);
public void ReceiveMessage(Models.Message message)
{
_regionManager.RequestNavigate(RegionNames.PopUpRegionName, "MessageView");
MessageVisibility = Visibility.Visible;
}
После этого выскакивает область сообщения и сообщение представляется пользователю.
Загрузчик:
public class Bootstrapper : UnityBootstrapper
{
public override void Run(bool runWithDefaultConfiguration)
{
base.Run(runWithDefaultConfiguration);
(Container.Resolve(typeof(ScanViewModel)) as ScanViewModel).InitializeTwain();
}
protected override System.Windows.DependencyObject CreateShell()
{
return Container.Resolve(typeof(Shell), "Shell") as DependencyObject;
}
protected override void InitializeShell()
{
base.InitializeShell();
var mainWindow = (Shell)this.Shell;
Application.Current.MainWindow = mainWindow;
mainWindow.Show();
}
protected override void ConfigureServiceLocator()
{
base.ConfigureServiceLocator();
Container.RegisterType<IEventAggregator, EventAggregator>(new ContainerControlledLifetimeManager());
Container.RegisterType<IMessageViewModel, MessageViewModel>(new ContainerControlledLifetimeManager());
Container.RegisterType<IFolderSelectionViewModel, FolderSelectionViewModel>(new ContainerControlledLifetimeManager());
Container.RegisterType<IRegionManager, RegionManager>(new ContainerControlledLifetimeManager());
var regionManager = Container.TryResolve<IRegionManager>();
regionManager.RegisterViewWithRegion(RegionNames.PopUpRegionName, typeof(Views.MessageView));
regionManager.RegisterViewWithRegion(RegionNames.PopUpRegionName, typeof(Views.FolderSelectionView));
}
}
ScanViewModel InitializeTwain () метод
public void InitializeTwain()
{
try
{
twain = new Twain(new WpfWindowMessageHook(Application.Current.MainWindow));
SelectedScanner = null;
Scanners = new ObservableCollection<string>();
for (int i = 0; i < twain.SourceNames.Count; i++)
{
Scanners.Add(twain.SourceNames[i]);
}
if (Scanners.Count > 0)
SelectedScanner = Scanners[0];
else
throw new TwainException();
twain.ScanningComplete += Twain_ScanningComplete;
twain.TransferImage += delegate (object sender, TransferImageEventArgs args)
{
if (args.Image != null)
{
resultImage = args.Image;
images.Add(resultImage);
}
};
canScan = true;
((DelegateCommand)ScanCommand).RaiseCanExecuteChanged();
}
catch (TwainException)
{
canScan = false;
((DelegateCommand)ScanCommand).RaiseCanExecuteChanged();
Message = new Models.Message("TwainException", "No TWAIN compatible scanner could be found.");
// _eventAggregator.GetEvent<ShowMessage>().Publish(message) gets called here
}
}
Моя проблема:
Когда я вызываю метод InitializeTwain () в методе Run () программы начальной загрузки и сообщение о том, что нет присутствующий сканер должен отображаться пользователю, PopupRegion НЕ отображается, а лежит в основе ScanView. И я не могу ничего щелкнуть в ScanView, поэтому что-то скрывает это.
Когда я удаляю вызов InitializeTwain () из метода Run () загрузчика, создаю кнопку для его вызова и нажимаю ее после приложения загружено, сообщение отображается правильно.
Чего мне не хватает?
РЕДАКТИРОВАТЬ: кажется, что-то не так с определением fade:VisibilityAnimation.AnimationType="Fade"
в Shell.xaml. Пространство имен исчезновения указывает на VisibilityAnimation.cs, и это следующее:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Data;
using System.Windows.Media.Animation;
namespace Sdadv.CeDis.Wpf.DependencyProperties
{
/// <summary>
/// Code from: http://blogs.microsoft.co.il/arik/2010/02/08/wpf-how-to-animate-visibility-property/
/// </summary>
public class VisibilityAnimation
{
public enum AnimationType
{
/// <summary>
/// No animation
/// </summary>
None,
/// <summary>
/// Fade in / Fade out
/// </summary>
Fade
}
/// <summary>
/// Animation duration
/// </summary>
private const int AnimationDuration = 200;
/// <summary>
/// List of hooked objects
/// </summary>
private static readonly Dictionary<FrameworkElement, bool> _hookedElements =
new Dictionary<FrameworkElement, bool>();
/// <summary>
/// Get AnimationType attached property
/// </summary>
/// <param name=”obj”>Dependency object</param>
/// <returns>AnimationType value</returns>
public static AnimationType GetAnimationType(DependencyObject obj)
{
return (AnimationType)obj.GetValue(AnimationTypeProperty);
}
/// <summary>
/// Set AnimationType attached property
/// </summary>
/// <param name=”obj”>Dependency object</param>
/// <param name=”value”>New value for AnimationType</param>
public static void SetAnimationType(DependencyObject obj, AnimationType value)
{
obj.SetValue(AnimationTypeProperty, value);
}
/// <summary>
/// Using a DependencyProperty as the backing store for AnimationType.
/// This enables animation, styling, binding, etc…
/// </summary>
public static readonly DependencyProperty AnimationTypeProperty =
DependencyProperty.RegisterAttached(
"AnimationType",
typeof(AnimationType),
typeof(VisibilityAnimation),
new FrameworkPropertyMetadata(AnimationType.None,
new PropertyChangedCallback(OnAnimationTypePropertyChanged)));
/// <summary>
/// AnimationType property changed
/// </summary>
/// <param name=”dependencyObject”>Dependency object</param>
/// <param name=”e”>e</param>
private static void OnAnimationTypePropertyChanged(
DependencyObject dependencyObject,
DependencyPropertyChangedEventArgs e)
{
FrameworkElement frameworkElement = dependencyObject as FrameworkElement;
if (frameworkElement == null)
{
return;
}
// If AnimationType is set to True on this framework element,
if (GetAnimationType(frameworkElement) != AnimationType.None)
{
// Add this framework element to hooked list
HookVisibilityChanges(frameworkElement);
}
else
{
// Otherwise, remove it from the hooked list
UnHookVisibilityChanges(frameworkElement);
}
}
/// <summary>
/// Add framework element to list of hooked objects
/// </summary>
/// <param name=”frameworkElement”>Framework element</param>
private static void HookVisibilityChanges(FrameworkElement frameworkElement)
{
_hookedElements.Add(frameworkElement, false);
}
/// <summary>
/// Remove framework element from list of hooked objects
/// </summary>
/// <param name=”frameworkElement”>Framework element</param>
private static void UnHookVisibilityChanges(FrameworkElement frameworkElement)
{
if (_hookedElements.ContainsKey(frameworkElement))
{
_hookedElements.Remove(frameworkElement);
}
}
/// <summary>
/// VisibilityAnimation static ctor
/// </summary>
static VisibilityAnimation()
{
// Here we “register” on Visibility property “before change” event
UIElement.VisibilityProperty.AddOwner(
typeof(FrameworkElement),
new FrameworkPropertyMetadata(
Visibility.Visible,
VisibilityChanged,
CoerceVisibility));
}
/// <summary>
/// Visibility changed
/// </summary>
/// <param name=”dependencyObject”>Dependency object</param>
/// <param name=”e”>e</param>
private static void VisibilityChanged(
DependencyObject dependencyObject,
DependencyPropertyChangedEventArgs e)
{
// Ignore
}
/// <summary>
/// Coerce visibility
/// </summary>
/// <param name=”dependencyObject”>Dependency object</param>
/// <param name=”baseValue”>Base value</param>
/// <returns>Coerced value</returns>
private static object CoerceVisibility(
DependencyObject dependencyObject,
object baseValue)
{
// Make sure object is a framework element
FrameworkElement frameworkElement = dependencyObject as FrameworkElement;
if (frameworkElement == null)
{
return baseValue;
}
// Cast to type safe value
Visibility visibility = (Visibility)baseValue;
// If Visibility value hasn’t change, do nothing.
// This can happen if the Visibility property is set using data binding
// and the binding source has changed but the new visibility value
// hasn’t changed.
if (visibility == frameworkElement.Visibility)
{
return baseValue;
}
// If element is not hooked by our attached property, stop here
if (!IsHookedElement(frameworkElement))
{
return baseValue;
}
// Update animation flag
// If animation already started, don’t restart it (otherwise, infinite loop)
if (UpdateAnimationStartedFlag(frameworkElement))
{
return baseValue;
}
// If we get here, it means we have to start fade in or fade out animation.
// In any case return value of this method will be Visibility.Visible,
// to allow the animation.
DoubleAnimation doubleAnimation = new DoubleAnimation
{
Duration = new Duration(TimeSpan.FromMilliseconds(AnimationDuration))
};
// When animation completes, set the visibility value to the requested
// value (baseValue)
doubleAnimation.Completed += (sender, eventArgs) =>
{
if (visibility == Visibility.Visible)
{
// In case we change into Visibility.Visible, the correct value
// is already set, so just update the animation started flag
UpdateAnimationStartedFlag(frameworkElement);
}
else
{
// This will trigger value coercion again
// but UpdateAnimationStartedFlag() function will reture true
// this time, thus animation will not be triggered.
if (BindingOperations.IsDataBound(frameworkElement,
UIElement.VisibilityProperty))
{
// Set visiblity using bounded value
Binding bindingValue =
BindingOperations.GetBinding(frameworkElement,
UIElement.VisibilityProperty);
BindingOperations.SetBinding(frameworkElement,
UIElement.VisibilityProperty, bindingValue);
}
else
{
// No binding, just assign the value
frameworkElement.Visibility = visibility;
}
}
};
if (visibility == Visibility.Collapsed || visibility == Visibility.Hidden)
{
// Fade out by animating opacity
doubleAnimation.From = 1.0;
doubleAnimation.To = 0.0;
}
else
{
// Fade in by animating opacity
doubleAnimation.From = 0.0;
doubleAnimation.To = 1.0;
}
// Start animation
frameworkElement.BeginAnimation(UIElement.OpacityProperty, doubleAnimation);
// Make sure the element remains visible during the animation
// The original requested value will be set in the completed event of
// the animation
return Visibility.Visible;
}
/// <summary>
/// Check if framework element is hooked with AnimationType property
/// </summary>
/// <param name=”frameworkElement”>Framework element to check</param>
/// <returns>Is the framework element hooked?</returns>
private static bool IsHookedElement(FrameworkElement frameworkElement)
{
return _hookedElements.ContainsKey(frameworkElement);
}
/// <summary>
/// Update animation started flag or a given framework element
/// </summary>
/// <param name=”frameworkElement”>Given framework element</param>
/// <returns>Old value of animation started flag</returns>
private static bool UpdateAnimationStartedFlag(FrameworkElement frameworkElement)
{
bool animationStarted = (bool)_hookedElements[frameworkElement];
_hookedElements[frameworkElement] = !animationStarted;
return animationStarted;
}
}
}