So I'm writing a WPF application with IronPython. Everything works great if I run the script outside of IronPython REPL via command "ipy.exe wpf.py". However, if the script were run inside IronPython REPL via command "execfile('wpf.py')", the first time it runs OK, the second time it errors out "SystemError: Cannot create more than one System.Windows.Application instance in the same AppDomain."
From my understanding, it's because it'll create a new AppDomain every time you run it outside REPL while it'll share the same domain when running inside REPL, and you can initialize Application twice. The problem is I have to run it inside the same AppDomain many times as it's not a standalone IronPython application. I've tried many things such as change shutdown mode by add app.ShutdownMode = ShutdownMode.OnExplicitShutdown after app=Application(), but that just hang the whole REPL.
Can someone please help shed some light? Thank you very much!
import clr
clr.AddReference("PresentationFramework")
clr.AddReference("PresentationCore")
clr.AddReference("System.Xml")
from System.Xml import XmlReader
from System.IO import StringReader
from System.Windows.Markup import XamlReader
from System.Windows import Application
s = XmlReader.Create(StringReader('''
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="IronPython MVVM Demo2"
Width="450"
SizeToContent="Height">
<Grid Margin="15" x:Name="grid1">
<StackPanel Margin="5">
<Button Margin="5">One</Button>
<Button Margin="5">Two</Button>
<Button Margin="5">Three</Button>
</StackPanel>
</Grid>
</Window>
'''))
win = XamlReader.Load(s)
app = Application()
app.Run(win)
print("The End.")
I believe you need to create a long-running STA thread to host the Applilcation, and communicate with it through the Applciations's Dispatcher. Here's an example in C#:
using System;
using System.IO;
using System.Threading;
using System.Windows;
using System.Xml;
namespace ConsoleApp1
{
class Program
{
static void ShowWindow(string Xaml)
{
var s = XmlReader.Create(new StringReader(Xaml));
var win = (Window)System.Windows.Markup.XamlReader.Load(s);
win.ShowDialog();
}
static void Main(string[] args)
{
Application app = null;
var UIThread = new Thread(() =>
{
app = new Application();
app.ShutdownMode = ShutdownMode.OnExplicitShutdown;
app.Run();
});
UIThread.SetApartmentState(ApartmentState.STA);
UIThread.Start();
while (app == null )
Thread.Sleep(100);
app.Dispatcher.Invoke(() => Console.WriteLine("Started"));
var xaml = #"
<Window
xmlns = ""http://schemas.microsoft.com/winfx/2006/xaml/presentation""
xmlns:x = ""http://schemas.microsoft.com/winfx/2006/xaml""
Title = ""IronPython MVVM Demo2""
Width = ""450""
SizeToContent = ""Height"">
<Grid Margin = ""15"" x:Name = ""grid1"">
<StackPanel Margin = ""5"">
<Button Margin = ""5""> One </Button>
<Button Margin = ""5""> Two </Button>
<Button Margin = ""5""> Three </Button>
</StackPanel>
</Grid>
</Window>";
for (int i = 0; i < 3; i++)
{
Application.Current.Dispatcher.Invoke(() =>
{
ShowWindow(xaml);
});
}
Application.Current.Dispatcher.Invoke(() =>
{
Application.Current.Shutdown();
});
}
}
}
Related
I wonder is there an API for creating custom notifications in Windows 10 style like the one showing when updates are available for example?
I know that Citrix can send messages showing like that and it seems they use the sessionmsg.exe. Unfortunately, I cannot find any help on the parameter the exe supports.
Also, an API would be preferred.
Another thing: How do you call this kind of message? Banner? Message? MessageBox? SystemMessage?
After reading the question, I found it interesting my selves to investigate possibilities and share my founds as the answer.
As you tagged your question with C#, my solution will be based on C#. By and btw I was not able to find the default api to do the job, if other finds a solution with an example I will be happy to vote for it.
I started with the UWP solution and created a very simple information dialog using ContentDialog. Create a UWP project and add the following code:
private async void ShowMessage()
{
ContentDialog dialog = new ContentDialog
{
Title = "Title",
Content = "Content text",
Width = 200,
Height = 600,
Background = new SolidColorBrush(Colors.CornflowerBlue),
Foreground = new SolidColorBrush(Colors.White),
BorderThickness = new Thickness(1),
BorderBrush = new SolidColorBrush(Colors.White),
CloseButtonText = "Close",
};
await dialog.ShowAsync();
}
public MainPage()
{
this.InitializeComponent();
ShowMessage();
}
This will create something like
But that content dialog appears as a part of the application and not the windows system as I tried to solve.
Adding the following lines before the ShowMessage method will maximize application to the whole background of the screen.
ApplicationView.GetForCurrentView().SuppressSystemOverlays = true;
ApplicationView.GetForCurrentView().FullScreenSystemOverlayMode = FullScreenSystemOverlayMode.Minimal;
ApplicationView.GetForCurrentView().TryEnterFullScreenMode();
But IMO it is not the best solution. I thought, there might be another way, I tried with WPF instead.
I created a WPF project, my strategy was to start MainWindow in minimized mode and the content dialog to appear. Hence there is no content dialog in WPF like UWP, I created something similar (look and feel).
Here is the code in my MainWindow
private readonly string _title;
private readonly string _message;
public MainWindow()
{
_title = "Updates are available";
_message = "Required updates need to be downloaded.";
InitializeComponent();
string[]? args = App.Args;
if (args != null && args.Length > 0)
{
_title = args[0];
_message = args[1];
}
MinimizedMainWindow();
ShowContentDialog();
}
protected void MinimizedMainWindow()
{
AllowsTransparency = true;
WindowStyle = WindowStyle.None;
WindowState = WindowState.Maximized;
Background = Brushes.Transparent;
Topmost = true;
}
public void ShowContentDialog()
{
ContentDialog dialog = new ContentDialog
{
Background = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#FF0066CC")),
Foreground = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#FFFFFFFF")),
WindowStartupLocation = WindowStartupLocation.CenterScreen,
SizeToContent = SizeToContent.WidthAndHeight,
WindowStyle = WindowStyle.None,
Padding = new Thickness(20),
Margin = new Thickness(0),
ResizeMode = ResizeMode.NoResize,
Width = 600,
Height = 200,
Title = { Text = _title },
Message = { Text = _message }
};
dialog.Show();
}
And here is my ContentDialog.xaml
<Window x:Class="NotificationSol.ContentDialog"
xmlns:local="clr-namespace:NotificationSol"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:av="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
Title="ContentDialog"
av:DesignWidth="600"
av:DesignHeight="200"
>
<Grid>
<TextBlock x:Name="Title" Margin="30,22,30,118" TextWrapping="Wrap" Text="Title" FontSize="28"/>
<TextBlock x:Name="Message" Margin="30,70,30,70" TextWrapping="Wrap" Text="Content" FontSize="16"/>
<Button x:Name="Button" Click="CloseButton_Click" Content="Close" HorizontalAlignment="Right" Margin="0,0,30,20" VerticalAlignment="Bottom" Width="75" Background="#FF0066CC" BorderBrush="White" Foreground="White" Padding="8,4"/>
</Grid>
</Window>
And ContentDialog.xaml.cs
public ContentDialog()
{
InitializeComponent();
}
private void CloseButton_Click(object sender, RoutedEventArgs e)
{
Close();
base.OnClosed(e);
Application.Current.Shutdown();
}
To let my application take parameters in the command line, I made the following changes to App.xaml.cs:
public static string[]? Args;
void AppStartup(object sender, StartupEventArgs e)
{
if (e.Args.Length > 0)
{
Args = e.Args;
}
}
And to App.xaml adding as the startup
<Application x:Class="NotificationSol.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:NotificationSol"
StartupUri="MainWindow.xaml"
Startup="AppStartup">
<Application.Resources>
</Application.Resources>
</Application>
Running this code from the visual studio I get the:
If you run the software with 2 parameters from the command line, you can pass the title and the message. You have the code, where you can create other method actions or buttons with other features. Right now my button is just closing the dialog box and the application. Here I have put both examples on my repo:
https://github.com/maythamfahmi/BlogExamples/tree/master/Stackoverflow/ContentDialog
You need to create a function using Winforms and WPF assemblies to create the pop up.
Functions
Function New-WPFDialog() {
<#
.SYNOPSIS
This neat little function is based on the one from Brian Posey's Article on Powershell GUIs
.DESCRIPTION
I re-factored a bit to return the resulting XaML Reader and controls as a single, named collection.
.PARAMETER XamlData
XamlData - A string containing valid XaML data
.EXAMPLE
$MyForm = New-WPFDialog -XamlData $XaMLData
$MyForm.Exit.Add_Click({...})
$null = $MyForm.UI.Dispatcher.InvokeAsync{$MyForm.UI.ShowDialog()}.Wait()
.NOTES
Place additional notes here.
.LINK
http://www.windowsnetworking.com/articles-tutorials/netgeneral/building-powershell-gui-part2.html
.INPUTS
XamlData - A string containing valid XaML data
.OUTPUTS
a collection of WPF GUI objects.
#>
Param([Parameter(Mandatory = $True, HelpMessage = 'XaML Data defining a GUI', Position = 1)]
[string]$XamlData)
# Add WPF and Windows Forms assemblies
try {
Add-Type -AssemblyName PresentationCore, PresentationFramework, WindowsBase, system.windows.forms
}
catch {
Throw 'Failed to load Windows Presentation Framework assemblies.'
}
# Create an XML Object with the XaML data in it
[xml]$xmlWPF = $XamlData
# Create the XAML reader using a new XML node reader, UI is the only hard-coded object name here
Set-Variable -Name XaMLReader -Value #{ 'UI' = ([Windows.Markup.XamlReader]::Load((new-object -TypeName System.Xml.XmlNodeReader -ArgumentList $xmlWPF))) }
# Create hooks to each named object in the XAML reader
$Elements = $xmlWPF.SelectNodes('//*[#Name]')
ForEach ( $Element in $Elements ) {
$VarName = $Element.Name
$VarValue = $XaMLReader.UI.FindName($Element.Name)
$XaMLReader.Add($VarName, $VarValue)
}
return $XaMLReader
}
Function New-PopUpWindow () {
param(
[string]
$MessageText = "No Message Supplied")
# This is the XaML that defines the GUI.
$WPFXamL = #'
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Popup" Background="#FF0066CC" Foreground="#FFFFFFFF" ResizeMode="NoResize" WindowStartupLocation="CenterScreen" SizeToContent="WidthAndHeight" WindowStyle="None" Padding="20" Margin="0">
<Grid>
<Button Name="OKButton" Content="OK" HorizontalAlignment="Right" Margin="0,0,30,20" VerticalAlignment="Bottom" Width="75" Background="#FF0066CC" BorderBrush="White" Foreground="White" Padding="8,4"/>
<TextBlock Name="Message" Margin="100,60,100,80" TextWrapping="Wrap" Text="_CONTENT_" FontSize="36"/>
</Grid>
</Window>
'#
# Build Dialog
$WPFGui = New-WPFDialog -XamlData $WPFXaml
$WPFGui.Message.Text = $MessageText
$WPFGui.OKButton.Add_Click( { $WPFGui.UI.Close() })
$null = $WPFGUI.UI.Dispatcher.InvokeAsync{ $WPFGui.UI.ShowDialog() }.Wait()
}
Example call and result
New-PopUpWindow -MessageText "Hey there, I'm a pretty blue form"
Interested in that too.
Finished with simple WinForms C# app though I'm not in code. It covers every screen with 50% transparent black fullscreen form and then opens modal (OnShown TopMost) blue borderless 678x165 form from the primary screen form. Picked almost same colors and fonts (Segoe UI title + Calibri text and bold buttons) so it is pretty simple and similar phoney. And I've added second red button with some special functionality, what is of course impossible via standard API.
Another way is to know that such a message can be called with Send-RDUserMessage powershell cmdlet from RemoteDesktop module. I opened the module source code and understood that it uses wtsapi32.dll. I stopped pushing that direction because of:
RemoteDesktop module requires admin elevation, maybe wtsapi calls in general requires that too; but I want banner to launch in user context from task scheduler;
Cast Send-RDUserMessage on Windows 10 desktop OS results in small "msg.exe"-like (not sessionmsg.exe) messagebox but I want that banner on Windows 10 workstations too, not only on terminal servers. But workstations use the same banner to say about updates and activation so it is definitely not server unique function.
I am not into coding at all. Someone can try to get along with that dll, maybe WTSSendMessageA and WTSSendMessageW methods which are documented at learn.microsoft.com. Maybe it calls another API because such messages are not only for RD/TS purposes. But I got tired dealing with it.
Still interested in oneliner :)
I see some answer about it, suggest use Live Unit Testing feature.
So I don't have Microsoft Enterprise and therefore can't use its Live Unit Testing feature. Tried to create a simple application in order to compile & reload into the WPF container window.
skeleton code look like:
public void RecompileAndReloadPrj ()
{
Grid.Content = null;
ReleaseAsm()
RunMsBuild(targetProject);
Grid.Content = LoadComponetFromAsm(targetASM);
}
Unfortunately getting it to work has turned out to be a bit complicated... Does anyone have ready code that they could post, tips etc, or provide a link?
Yes, before couple years I wrote some code for WPF like you wish. But it's very basic and have a lot of issues around it.
But I suggest you, check the feature of Edit XAML while debugging. It's work well even without breakpoint. Just run project under debugging mode and edit the XAML file, even if your code load it by code behind. Even Save not required.
I list here the code and some comment about it:
MainWindow.xaml
<Window x:Class="RebuildAndReloadWPF.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:RebuildAndReloadWPF"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800" Activated="Window_Activated" >
<Grid >
<Grid.RowDefinitions>
<RowDefinition Height="40"/>
<RowDefinition />
</Grid.RowDefinitions>
<Border CornerRadius="6" BorderBrush="Gray" Background="LightGray" BorderThickness="2" >
<StackPanel x:Name="___No_Name_" Background="LightYellow" Orientation="Horizontal">
<Button Click="Button_Click" >Reload</Button>
<CheckBox x:Name="chkReloadOnFocus" VerticalContentAlignment="Center" VerticalAlignment="Center" Content="Reload on focus" Margin="10,0,0,0"/>
<TextBlock x:Name="txtIndicator" VerticalAlignment="Center" Margin="10,0,0,0"/>
<Border x:Name="elmJustNowIndicator" BorderThickness="1" BorderBrush="Black" Background="Orange" CornerRadius="5" Height="21" Width="76" Visibility="Hidden" Margin="10,0,0,0">
<TextBlock Text="Just now" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</StackPanel>
</Border>
<ScrollViewer x:Name="PlaceHolder" Grid.Row="1"/>
</Grid>
</Window>
MainWindow.xaml.cs
using Microsoft.Build.BuildEngine;
using System;
using System.IO;
using System.Reflection;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Threading;
namespace RebuildAndReloadWPF
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
const string projectPath = #"C:\...\Some-Project.csproj";
const string libFileName = "Some-Lib.dll";
const string ClassName = "NameSpace.UserControlName";
private string projectFileName;
private string projectDirectoryPath;
private string projectBinPath;
private string logFilePath;
private string appDirectoryPath;
private DispatcherTimer indicatorTimer = new DispatcherTimer();
public MainWindow ()
{
projectFileName = Path.GetFileName(projectPath);
projectDirectoryPath = Path.GetDirectoryName(projectPath);
projectBinPath = projectDirectoryPath + #"\bin\Debug";
logFilePath = projectDirectoryPath + #"\bin\Debug\build.log";
appDirectoryPath = AppDomain.CurrentDomain.BaseDirectory;
indicatorTimer = new DispatcherTimer();
indicatorTimer.Interval = TimeSpan.FromMilliseconds(4000);
indicatorTimer.Tick += ( sender, e ) =>
{
elmJustNowIndicator.Visibility = Visibility.Hidden;
indicatorTimer.IsEnabled = false;
};
InitializeComponent();
}
public void ReloadContainer ()
{
PlaceHolder.Content = null;
bool result = RunMsBuild();
if (!result)
{
txtIndicator.Text = "Compile error, see in log file. Compile fail at: " + DateTime.Now.ToLongTimeString();
txtIndicator.Foreground = Brushes.Red;
return;
}
else
{
txtIndicator.Text = "Last build at: " + DateTime.Now.ToLongTimeString();
txtIndicator.Foreground = Brushes.Green;
}
try
{
File.Copy(projectBinPath + #"\" + libFileName, appDirectoryPath + libFileName, true);
}
catch (Exception ex)
{
MessageBox.Show("Can't copy compiled lib file: " + ex.Message);
throw;
}
elmJustNowIndicator.Visibility = Visibility.Visible;
indicatorTimer.IsEnabled = false;
indicatorTimer.IsEnabled = true;
try
{
PlaceHolder.Content = AsmLoad();
}
catch (Exception ex)
{
MessageBox.Show("Laod assembly error" + ex.Message);
}
GC.Collect();
}
public bool RunMsBuild ()
{
Engine eng = new Engine();
FileLogger logger = new FileLogger();
logger.Parameters = "logfile=" + logFilePath;
eng.RegisterLogger(logger);
bool bb = eng.BuildProjectFile(projectPath);
eng.UnregisterAllLoggers();
return bb;
}
public FrameworkElement AsmLoad ()
{
byte[] assemblyFileBUffer = File.ReadAllBytes(appDirectoryPath + #"\" + libFileName);
Assembly asm = AppDomain.CurrentDomain.Load(assemblyFileBUffer);
ContentControl container = (ContentControl)asm.CreateInstance(ClassName);
return (FrameworkElement)container.Content;
}
private void Window_Activated ( object sender, EventArgs e )
{
if (chkReloadOnFocus.IsChecked == true)
ReloadContainer();
}
private void Button_Click ( object sender, RoutedEventArgs e )
{
ReloadContainer();
}
}
}
Comments:
The above code is work only against UserControl class. You can
expand code to support also with Windows and Panel.
The above code make rebuild only when window of our app activated
(focus). You can expand it to respond file save. By use with
FileSystemWatcher. Notice, the watcher run event for every file. So
you need wait after all event burst end (brobebly by timer), and
also Suggest to configure Visual Studio to make always SaveALL for
Ctr+S key combination.
Microsoft replace Microsoft.Build.BuildEngine with newest assembly
and suggest use with new Microsoft.Build. I realize that newest have
problem find the newest Tools (MSBuild.exe) like version 15.0. You
probably will get error that need workaround:
Microsoft.Build.Exceptions.InvalidProjectFileException: 'The tools
version "15.0" is unrecognized. Available tools versions are "12.0",
"14.0", "2.0", "3.5", "4.0".'
Dotnet can't release assembly after loading it dynamically. Work
around not worth the effort. I check the above code and I run it
with loop, the consume RAM is better than I was imagined. And also,
I realize that Core 3.0 have solution for that. So if you like this
code, I suggest you try emigrate it to core 3.0
Performance. I not try it on real project. But if you have spare RAM
and strong CPU I believe that work well for small project, and not
have delay more than half of second. I compare it against start
debugging after code change. The Visual studio have a long delay to
enter debug mode and exit. So may you get significate improvement.
By the way, if you copy the debug info file (.pdb) as well as lib
file, the VS will open the source file when runtime error occur on
target projects. But this file get Lock. And next reload Fail (It's
weird, but according my check, it's not happened if target project
is VB).
If you really want develop with this approach. You need build your
target app as collection of small project. You can Embedded the
below code in your target project and open the container window when
you on develop mode. You can add Cache system for all data read from
files or outside resources like Databases. And Build system that
allow jump the reload directly to some point in the Interface.
I have an MVVMLight multi-page application which has two pages, I can navigate from one page to the other by clicking the respective buttons.
In the second page, I have a loader animation that is triggered every time something is typed in a textBox field. Everything is working fine; from the first page I can go to the second page then you type something in the textBox and the animation starts. The problem is that if I go to the second page, then I go back to the first page and then I go to the second page again and type something in the textBox I get an error that says that the name of the loader doesn't exist, the funny thing is that I don't get this error until I leave the page and come back.
Any idea why the animation stops working after leaving the page and coming back?
EDIT: Here is the link to a complete project.
https://www.dropbox.com/sh/yf87shw5rzxtxen/AAClTesIGpLKl6IzV-6pjfEfa?dl=0
To replicate error do the following...
Download and open application.
Go to page 2.
Type something in the textBox (animation should start).
Go back to page 1, do nothing.
Go to page 2 again and try typing something in the textBox (you should see the error here).
Error:
Additional information: 'rectangleLoader' name cannot be found in the name scope of 'TwoViews.Views.SecondView'.
XAML
<UserControl x:Class="TwoViews.Views.SecondView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
d:DesignHeight="300"
d:DesignWidth="300"
mc:Ignorable="d">
<Grid>
<Rectangle x:Name="rectangleLoader" Fill="#FFB2B2FF" HorizontalAlignment="Left" Height="19" Margin="26,89,0,0" VerticalAlignment="Top" Width="248"/>
<TextBox x:Name="textBoxFileName"
HorizontalAlignment="Left"
Height="35" Width="180"
Margin="26,125,0,0"
VerticalAlignment="Top"
Text="{Binding InputFileNameChanged, UpdateSourceTrigger=PropertyChanged}" FontSize="18"/>
</Grid>
</UserControl>
SecondView.xaml.cs
namespace TwoViews.Views
{
public partial class SecondView : UserControl
{
private Storyboard loaderStoryboard;
public SecondView()
{
InitializeComponent();
Messenger.Default.Register<MessageSearchStatus>(this, messageSearchStatus => ReceivedSearchStatus(messageSearchStatus));
/// Animation for loader
DoubleAnimation myDoubleAnimation = new DoubleAnimation();
myDoubleAnimation.From = 100;
myDoubleAnimation.To = 0;
myDoubleAnimation.Duration = new Duration(TimeSpan.FromSeconds(.1));
myDoubleAnimation.AutoReverse = true;
myDoubleAnimation.RepeatBehavior = RepeatBehavior.Forever;
loaderStoryboard = new Storyboard();
loaderStoryboard.Children.Add(myDoubleAnimation);
Storyboard.SetTargetName(myDoubleAnimation, rectangleLoader.Name);
Storyboard.SetTargetProperty(myDoubleAnimation, new PropertyPath(Rectangle.WidthProperty));
}
private void ReceivedSearchStatus(MessageSearchStatus message)
{
loaderStoryboard.Begin(this, true);
}
/// I manually stop the animation before going to other screens
private void stopAnimation_Click(object sender, System.Windows.RoutedEventArgs e)
{
loaderStoryboard.Stop(this);
}
}
}
ViewModel
namespace TwoViews.ViewModels
{
public class SecondViewModel : ViewModelBase
{
private string _inputFileName;
public string InputFileNameChanged
{
get { return _inputFileName; }
set {
// send message to start animation everytime the textBox changes
Messenger.Default.Send<MessageSearchStatus>(new MessageSearchStatus { isSearchingFile = true });
}
}
}
}
Please note that in my code I'm not showing the code that stops the animation.
Again, the animation works fine until the user leaves the page where the animation is and comes back.
Thanks!
FYI - The issue appeared to be the Storyboard I was using with the animation. Removed the Storyboard and everything work fine.
SecondView.xaml.cs
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media.Animation;
using System;
using System.Windows.Input;
using GalaSoft.MvvmLight.Messaging;
using GalaSoft.MvvmLight.Threading;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Collections.Specialized;
using System.Linq;
using System.Windows.Shapes;
using TwoViews.Models;
using System.Windows.Media;
namespace TwoViews.Views
{
public partial class SecondView : UserControl
{
private DoubleAnimation myDoubleAnimation;
public SecondView()
{
InitializeComponent();
Messenger.Default.Register<MessageSearchStatus>(this, messageSearchStatus => ReceivedSearchStatus(messageSearchStatus));
/// Animation for loader
myDoubleAnimation = new DoubleAnimation();
myDoubleAnimation.From = 100;
myDoubleAnimation.To = 0;
myDoubleAnimation.Duration = new Duration(TimeSpan.FromSeconds(.1));
myDoubleAnimation.AutoReverse = true;
myDoubleAnimation.RepeatBehavior = RepeatBehavior.Forever;
}
private void ReceivedSearchStatus(MessageSearchStatus message)
{
rectangleLoader.BeginAnimation(Rectangle.WidthProperty, myDoubleAnimation);
}
private void stopAnimation_Click(object sender, System.Windows.RoutedEventArgs e)
{
rectangleLoader.BeginAnimation(Rectangle.WidthProperty, null);
}
}
}
I've recently had a go at embedding a PowerPoint file as an XpsDocument in WPF.
It is a simple WPF application in which I embed a DocumentViewer property into my MainWindow.xaml grid:
<Window x:Class="PowerPoint2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:PowerPoint2"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<DocumentViewer
Name="DocumentviewPowerPoint"
VerticalAlignment="Top"
HorizontalAlignment="Left" />
</Grid>
To create the document bound to "DocumentviewPowerPoint" I convert the PowerPoint file that has been opened into Xps format and bind this variable to the XAML property mentioned earlier:
using System;
using System.IO;
using System.Windows;
using System.Windows.Xps.Packaging;
using Microsoft.Office.Core;
using Microsoft.Office.Interop.PowerPoint;
using Application = Microsoft.Office.Interop.PowerPoint.Application;
namespace PowerPoint2
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
const string powerPointFile = #"c:\temp\ppt.pptx";
var xpsFile = Path.GetTempPath() + Guid.NewGuid() + ".pptx";
var xpsDocument = ConvertPowerPointToXps(powerPointFile, xpsFile);
DocumentviewPowerPoint.Document = xpsDocument.GetFixedDocumentSequence();
}
private static XpsDocument ConvertPowerPointToXps(string pptFilename, string xpsFilename)
{
var pptApp = new Application();
var presentation = pptApp.Presentations.Open(pptFilename, MsoTriState.msoTrue, MsoTriState.msoFalse,
MsoTriState.msoFalse);
try
{
presentation.ExportAsFixedFormat(xpsFilename, PpFixedFormatType.ppFixedFormatTypeXPS);
}
catch (Exception ex)
{
MessageBox.Show("Failed to export to XPS format: " + ex);
}
finally
{
presentation.Close();
pptApp.Quit();
}
return new XpsDocument(xpsFilename, FileAccess.Read);
}
}
}
This all works well enough when running the program, showing the Xps document embedded into the WPF:
My question is how can I further modify my code in order to display the PowerPoint not just as a series of scrollable slides as shown, but as an actual slide show? I would like to make further updates to enable the user to navigate to the following slide on each mouse click - like a 'proper' presentation. My problem is that I am unfamiliar with the usage of the XpsDocument Apis - I don't know if it's a case of using these to achieve what I want or is it in the settings properties of the presentation variable that gets converted to the Xps format.
I managed to solve the particular problem I was interested in.
Please refer to this blog posting for a detailed explanation:
Controlling DocumentViewer methods and properties using MVVM
The solution addresses the problem of being able to enable individual PowerPoint slides (converted to xps file format) to occupy the WHOLE of the available windows space by invoking the relevant combination of DocumentViewer methods.
On pressing the screen button to invoke the RelayCommand, the following combination of DocumentViewer API calls in the MainWindowViewModel.cs class was observed to work:
public ICommand Command
{
get
{
return _command ?? (_command = new RelayCommand(
x =>
{
DocumentViewer = MainWindow.GetInstance();
const string powerPointFile = #"c:\temp\ppt.pptx";
var xpsFile = Path.GetTempPath() + Guid.NewGuid() + ".xps";
var xpsDocument = ConvertPowerPointToXps(powerPointFile, xpsFile);
FixedFixedDocumentSequence = xpsDocument.GetFixedDocumentSequence();
DocumentViewer.Document = FixedFixedDocumentSequence;
DocumentViewer.GoToPage(1);
DocumentViewer.FitToMaxPagesAcross(1);
WindowState = WindowState.Maximized;
DocumentViewer.FitToMaxPagesAcross(1);
}));
}
}
And to obtain the DocumentViewer instance itself? I also need to update the MainWindow.xaml.cs to get it to return the instance of the DocumentViewer object:
using System.Windows.Controls;
namespace DocumentView
{
public partial class MainWindow
{
private static DocumentViewer _docViewer;
public MainWindow()
{
InitializeComponent();
_docViewer = DocumentViewPowerPoint;
}
public static DocumentViewer GetInstance()
{
return _docViewer;
}
}
}
Where DocumentViewPowerPoint is the name given to the DocumentViewer in the MainWindow.xaml:
<Window x:Class="DocumentView.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:DocumentView"
mc:Ignorable="d"
WindowState="{Binding WindowState, Mode=TwoWay}"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<local:MainWindowViewModel />
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="50" />
</Grid.RowDefinitions>
<DocumentViewer
Grid.Row="0"
Document="{Binding FixedFixedDocumentSequence}"
Name="DocumentViewPowerPoint"
VerticalAlignment="Top"
HorizontalAlignment="Left" />
<Button
Grid.Row="1"
Command="{Binding Command}"
Width="70" Height="30" Content="Press" />
</Grid>
Since the xaml window stopped showing my wpf form using VS 2012 (after I added some comments to the top of the main .cs form), I reverted back to C# Express 2010.
I copied my xaml and code and pasted them into the new project.
However, I'm getting err msgs such as:
*'duckbilledPlatypusInfoMailer_Express.MainWindow' does not contain a definition for 'MainWindow_Loaded' and no extension method 'MainWindow_Loaded' accepting a first argument of type 'duckbilledPlatypusInfoMailer_Express.MainWindow' could be found (are you missing a using directive or an assembly reference?)*
and:
The name 'InitializeComponent' does not exist in the current context
I get the same err msg about two controls, my label and button (but not the DatePicker!)
So both of my event handlers, and two of my three controls, as well as the 'InitializeComponent' have been rendered in a cloak of invisibility, as far as VC#2010 is concerned...???
Here is my xaml and code (minimal, so p[a,o]sting all of it):
XAML:
<Window x:Class="duckbilledPlatypusInfoMailer_Express.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Duckbilled Platypus Info Mailer" SizeToContent="WidthAndHeight" WindowStartupLocation="CenterScreen" MinHeight="350" MinWidth="525" Loaded="MainWindow_Loaded" >
<StackPanel>
<StackPanel Orientation="Horizontal">
<Button x:Name="btnSelectPDFFile" HorizontalAlignment="Left" Padding="4" Margin="4" Width="120" Click="btnSelectPDFFile_Click" IsDefault="True">Select PDF File
</Button>
<Label x:Name="lblPlatypusSheetFile" Margin="4" >[Selected File]</Label>
</StackPanel>
<StackPanel Orientation="Horizontal">
<DatePicker ></DatePicker>
</StackPanel>
</StackPanel>
</Window>
CODE:
using System;
using System.Windows;
using duckbilledPlatypusInfoMailer;
namespace PlatypusInfo_Express
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
btnSelectPDFFile.Focus();
}
private void btnSelectPDFFile_Click(object sender, RoutedEventArgs e)
{
var dlg = new Microsoft.Win32.OpenFileDialog
{
InitialDirectory = #"C:\Scrooge\McDuckbilledPlatypus\",
DefaultExt = ".pdf",
Filter = "Platypus Data Sheets (sa*.pdf)|sa*.pdf"
};
bool? result = dlg.ShowDialog();
if (result == true)
{
string pdfFilename = dlg.FileName;
// Show just the file name, without the path
string pdfFileNameOnly = System.IO.Path.GetFileName(pdfFilename);
lblReviewSheetFile.Content = pdfFileNameOnly;
string textFilename = String.Format(#"C:\Scrooge\McDuckbilledPlatypus\{0}.txt", pdfFileNameOnly);
var pdfParser = new PDFParser();
if (pdfParser.ExtractText(pdfFilename, textFilename))
{
System.Diagnostics.Process.Start(textFilename);
}
else
{
MessageBox.Show("There was a boo-boo, Yogi!");
}
}
}
}
}
BTW, I did add the necessary 3rd party file to my solution (PDFParser.cs) as well as the two necessary references.
Note: If I right-click the event handlers in the xaml, it DOES take me to those event handlers in the cs file. So it knows where they are, why does it say they don't exist or it can't find them?
UPDATE
Here's the first part of the error I see in the WPF designer:
System.NotSupportedException
An attempt was made to load an assembly from a network location which would have caused the assembly to be sandboxed in previous versions of the .NET Framework. This release of the .NET Framework does not enable CAS policy by default, so this load may be dangerous. If this load is not intended to sandbox the assembly, please enable the loadFromRemoteSources switch. See http://go.microsoft.com/fwlink/?LinkId=155569 for more information.
at Microsoft.Expression.DesignHost.Isolation.Remoting.STAMarshaler.WaitForCompletion(NestedCallContext nestedCallContext, BlockingCall call, WaitHandle timeoutSignal)
at Microsoft.Expression.DesignHost.Isolation.Remoting.STAMarshaler.MarshalOut(Action action, Int32 targetApartmentId, WaitHandle aborted, WaitHandle timeoutSignal)
at Microsoft.Expression.DesignHost.Isolation.Remoting.ThreadMarshaler.MarshalOut[TValue](RemoteHandle1 targetObject, Action action)
at Microsoft.Expression.DesignHost.Isolation.Remoting.ThreadMarshaler.MarshalOut[TResult,TValue](RemoteHandle1 targetObject, Func`2 func)
There is an extra duckbilled in the namespace in the XAML :
<Window x:Class="duckbilledPlatypusInfoMailer_Express.MainWindow"
it has to be in the same namespace as to code behind class.