Tech Off Thread

2 posts

Help with "Improving perceived WPF app startup performance with MEF and a Splash Screen".

Back to Forum: Tech Off
  • User profile image
    VcDeveloper

    Hi,

    Need help trying to get my custom splash screen working based on this example from Channel 9 "Improving perceived WPF app startup performance with MEF and a Splash Screen".

    I can't get any of the Storyboards to work nor display text in a TextBlock, creating the splash screen during InitializeModules() in my MefBootstrapper:

    public class NatsarBootstrapper : MefBootstrapper
    {
        #region Private Properties
        private IEventAggregator EventAggregator
        {
            get { return ServiceLocator.Current.GetInstance<IEventAggregator>(); }
        }
        #endregion
    
        protected override void InitializeModules()
        {
            IModuleManager manager = ServiceLocator.Current.GetInstance<IModuleManager>();
    
            Loading loading = this.Container.GetExportedValue<Loading>();
            Shell shellMain = this.Container.GetExportedValue<Shell>();
    
            loading.WindowStartupLocation = WindowStartupLocation.CenterScreen;
            loading.WindowState = WindowState.Normal;
    
            Application.Current.MainWindow = loading;
            Application.Current.MainWindow.Show();
    
            EventAggregator.GetEvent<MessageUpdateEvent>().Publish(new MessageUpdateEvent { Message = "Loading TurahStudyUIModule" });
            manager.LoadModule("TurahStudyUIModule");
    
            Application.Current.MainWindow.Hide();
    
            TurahStudyUIView view = ServiceLocator.Current.GetInstance<TurahStudyUIView>();
    
            shellMain.Height = 768;
            shellMain.Width = 1024;
            shellMain.WindowStartupLocation = WindowStartupLocation.CenterScreen;
            shellMain.WindowState = WindowState.Maximized;
            shellMain.Activate();
    
            Application.Current.MainWindow = shellMain;
            Application.Current.MainWindow.Content = view;
            Application.Current.MainWindow.Show();
        }
    
        protected override void ConfigureAggregateCatalog()
        {
            try
            {
                this.AggregateCatalog.Catalogs.Add(new AssemblyCatalog(typeof(NatsarBootstrapper).Assembly));
                this.AggregateCatalog.Catalogs.Add(new AssemblyCatalog(typeof(NatsarCommonModule).Assembly));
                this.AggregateCatalog.Catalogs.Add(new AssemblyCatalog(typeof(TurahStudyUIModule).Assembly));
            }
            catch (Exception msg)
            {
                .....
            }
        }
    
        protected override DependencyObject CreateShell()
        {
            DependencyObject shell = null;
    
            return shell;
        }
    }

    Loading.xaml Window:

    <Window 
        x:Class="NatsarTurahStudyMaster.Views.Loading"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        AllowsTransparency="True" WindowStyle="None" >
        <Window.Background>
            <ImageBrush ImageSource="habakkuk-paleo-hebrew.png"  Stretch="UniformToFill"/>
        </Window.Background>
        <Window.Resources>
            <Storyboard  x:Key="InTransition">
    
                    <!-- Create an animation that repeats indefinitely. -->
                    <DoubleAnimation 
                      Storyboard.TargetName="foreverRepeatingRectangle" Storyboard.TargetProperty="(Rectangle.Width)" 
                      From="50" To="300" Duration="0:0:2" RepeatBehavior="Forever" />
    
                    <!-- Create an animation that repeats for four seconds. As a result, the
                         animation repeats twice. -->
                    <DoubleAnimation Storyboard.TargetName="fourSecondsRepeatingRectangle" Storyboard.TargetProperty="(Rectangle.Width)"
                      From="50" To="300" Duration="0:0:2" RepeatBehavior="0:0:4" />
    
                    <!-- Create an animation that repeats twice. -->
                    <DoubleAnimation Storyboard.TargetName="twiceRepeatingRectangle" Storyboard.TargetProperty="(Rectangle.Width)" 
                      From="50" To="300" Duration="0:0:2" RepeatBehavior="2x" />
    
                    <!-- Create an animation that repeats 0.5 times. The resulting animation
                         plays for one second, half of its Duration. It animates from 50 to 150. -->
                    <DoubleAnimation Storyboard.TargetName="halfRepeatingRectangle" Storyboard.TargetProperty="(Rectangle.Width)" 
                      From="50" To="300" Duration="0:0:2" RepeatBehavior="0.5x" />
    
                    <!-- Create an animation that repeats for one second. The resulting animation
                         plays for one second, half of its Duration. It animates from 50 to 150. -->
                    <DoubleAnimation Storyboard.TargetName="oneSecondRepeatingRectangle" Storyboard.TargetProperty="(Rectangle.Width)" 
                      From="50" To="300" Duration="0:0:2" RepeatBehavior="0:0:1" />
                </Storyboard>
        </Window.Resources>
    
        <Window.Triggers>
            <EventTrigger RoutedEvent="FrameworkElement.Loaded">
                <BeginStoryboard Storyboard="{StaticResource InTransition}"/>
            </EventTrigger>
        </Window.Triggers>
        <Grid>
            <Border HorizontalAlignment="Stretch">
                <StackPanel Margin="20" >
                    <TextBlock>RepeatBehavior="Forever"</TextBlock>
                    <Rectangle Name="foreverRepeatingRectangle" Fill="#AA3333FF" Width="50" Height="20" HorizontalAlignment="Left" />
                    <TextBlock Margin="0,20,0,0">RepeatBehavior="0:0:4"</TextBlock>
                    <Rectangle Name="fourSecondsRepeatingRectangle" Fill="#AA3333FF" Width="50" Height="20" HorizontalAlignment="Left" />
                    <TextBlock Margin="0,20,0,0">RepeatBehavior="2x"</TextBlock>
                    <Rectangle Name="twiceRepeatingRectangle" Fill="#AA3333FF" Width="50" Height="20" HorizontalAlignment="Left" />
                    <TextBlock Margin="0,20,0,0">RepeatBehavior="0.5x"</TextBlock>
                    <Rectangle Name="halfRepeatingRectangle" Fill="#AA3333FF" Width="50" Height="20" HorizontalAlignment="Left" />
                    <TextBlock Margin="0,20,0,0">RepeatBehavior="0:0:1"</TextBlock>
                    <Rectangle Name="oneSecondRepeatingRectangle" Fill="#AA3333FF" Width="50" Height="20" HorizontalAlignment="Left" />
                </StackPanel>
            </Border>
    
            <TextBlock Text="{Binding Status}" Margin="10,0" Grid.Row="2" Grid.ColumnSpan="2" 
                       TextAlignment="Right">
                <TextBlock.Effect>
                    <DropShadowEffect ShadowDepth="1" Color="#99ffffff"/>
                </TextBlock.Effect>
            </TextBlock>
        </Grid>
    </Window>
    

    Loading.xaml.cs:

    [Export(typeof(Loading))]
    [PartCreationPolicy(CreationPolicy.NonShared)]
    public partial class Loading : Window, IPartImportsSatisfiedNotification
    {
        public Loading()
        {
            InitializeComponent();
        }
    
        [Import]
        public LoadingModel ViewModel
        {
            set
            {
                this.DataContext = value;
            }
            get
            {
                return DataContext as LoadingModel;
            }
        }
    
        /// <summary>
        /// Called when a part's imports have been satisfied and it is safe to use.
        /// </summary>
        public void OnImportsSatisfied()
        {
            // IPartImportsSatisfiedNotification is useful when you want to coordinate doing some work
            // with imported parts independent of when the UI is visible.
            Debug.WriteLine("Loading OnImportsSatisfied instantiation");
        }
    }
    

    LoadingModel.cs is where I'm just sending test text:

    [Export(typeof(LoadingModel))]
    [PartCreationPolicy(CreationPolicy.NonShared)]
    public class LoadingModel : NotificationObject
    {
        [ImportingConstructor]
        public LoadingModel(IEventAggregator eventAggregator)
        {
            Debug.WriteLine("LoadingModel instantiation");
            eventAggregator.GetEvent<MessageUpdateEvent>().Subscribe(e => UpdateMessage(e.Message));
        }
    
        private string _status;
        public string Status
        {
            get { return _status; }
            set
            { 
                _status = value; 
                RaisePropertyChanged(() => this.Status); 
            }
        }
    
        private void UpdateMessage(string message)
        {
            if (string.IsNullOrEmpty(message))
            {
                return;
            }
    
            int maxRecords = 1000;
            for (int x = 1; x < maxRecords; x++)
            {
                System.Threading.Thread.Sleep(10);
                Status = string.Format("{0}: {1} {2}",message ,Convert.ToInt32( ((decimal)x / (decimal)maxRecords) * 100), "...");
            }
        }
    }

    But, nothing is working?

  • User profile image
    VcDeveloper

    Well, I couldn't get it to work as I wanted, so I had to result to doing it this way:

    protected override void InitializeModules()
    {
        Shell shellMain = this.Container.GetExportedValue<Shell>();
    
        shellMain.InitializeMainWindow();
    
        TurahStudyUIView view = ServiceLocator.Current.GetInstance<TurahStudyUIView>();
    
        shellMain.Height = 768;
        shellMain.Width = 1024;
        shellMain.WindowStartupLocation = WindowStartupLocation.CenterScreen;
        shellMain.WindowState = WindowState.Maximized;
        shellMain.Activate();
    
        Application.Current.MainWindow = shellMain;
        Application.Current.MainWindow.Content = view;
        Application.Current.MainWindow.Activate();
    }
    

    and put the splash screen in my main window Shell.xaml.cs:

    [Export(typeof(Shell))]
    [PartCreationPolicy(CreationPolicy.NonShared)]
    public partial class Shell : Window, IPartImportsSatisfiedNotification
    {
        private readonly BackgroundWorker _compositionBackgroundWorker = new BackgroundWorker();
        Loading loading;
    
        public Shell()
        {
            InitializeComponent();
    
            _compositionBackgroundWorker.DoWork += CompositionBackgroundWorker_DoWork;
            _compositionBackgroundWorker.RunWorkerCompleted += CompositionBackgroundWorkerRunWorker_Completed;
        }
    
        public void InitializeMainWindow()
        {
            // Set the Window.Content to the "Loading UI" UserControl
            loading = ServiceLocator.Current.GetInstance<Loading>();
            loading.Show();
    
            _compositionBackgroundWorker.RunWorkerAsync();
        }
    
        private void CompositionBackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            this.InitializeModules();
        }
    
        private void InitializeModules()
        {
            IModuleManager manager = ServiceLocator.Current.GetInstance<IModuleManager>();
    
            int maxRecords = 1000;
            for (int x = 1; x < maxRecords; x++)
            {
                System.Threading.Thread.Sleep(5);
                System.Windows.Application.Current.Dispatcher.Invoke((Action)(() => { loading.ViewModel.Status = string.Format("{0}: {1} {2}", "Loading", Convert.ToInt32(((decimal)x / (decimal)maxRecords) * 100), "..."); }));
            }
        }
    
        private void CompositionBackgroundWorkerRunWorker_Completed(object sender, RunWorkerCompletedEventArgs e)
        {
            loading.Hide();
            loading.Close();
            this.Show();
        }
    
        [Import]
        public ShellModel ViewModel
        {
            set
            {
                this.DataContext = value;
            }
            get
            {
                return DataContext as ShellModel;
            }
        }
    
        /// <summary>
        /// Called when a part's imports have been satisfied and it is safe to use.
        /// </summary>
        public void OnImportsSatisfied()
        {
            // IPartImportsSatisfiedNotification is useful when you want to coordinate doing some work
            // with imported parts independent of when the UI is visible.
            Debug.WriteLine("Shell OnImportsSatisfied instantiation");
        }
    }
    

Comments closed

Comments have been closed since this content was published more than 30 days ago, but if you'd like to continue the conversation, please create a new thread in our Forums, or Contact Us and let us know.