On launch of my Win8 Store App I want to pass argument parameters.
I was looking at the following method that is called in the App class. Looks like it is passing arguments along, so my question is how do I pass arguments to "OnLaunched" ? Essentially when I run the app straight away from the Win8 Menu
protected override void OnLaunched(LaunchActivatedEventArgs e)
{
if (rootFrame.Content == null)
{
// When the navigation stack isn't restored navigate to the first page,
// configuring the new page by passing required information as a navigation
// parameter
rootFrame.Navigate(typeof(MainPage), e.Arguments);
}
}
These arguments can be passed in via a secondary tile that could be used to launch the app to a specified page during application launch.
I typically use a ViewManager to manage the view to be displayed based on these arguments.
App.cs
protected override async void OnLaunched(LaunchActivatedEventArgs args)
{
ApplicationData.Current.LocalSettings.Values[Constants.APP_PARAMETERS] = args.Arguments;
// Do not repeat app initialization when already running, just ensure that
// the window is active
if (args.PreviousExecutionState == ApplicationExecutionState.Running)
{
Window.Current.Activate();
await ViewManager.Instance.LaunchView();
return;
}
ViewManager:
public class ViewManager
{
#region Singleton
private ViewManager()
{
}
static ViewManager _viewManager = null;
public static ViewManager Instance
{
get
{
if (_viewManager == null)
{
_viewManager = new ViewManager();
}
return _viewManager;
}
}
#endregion
public async Task LaunchView()
{
bool displaySubheader = false;
var displayBackbutton = false;
var arguments = ApplicationData.Current.LocalSettings.Values[Constants.APP_PARAMETERS] as string;
var argumentsExist = !string.IsNullOrEmpty(arguments);
if (!argumentsExist)
{
await UIServices.Instance.Load(typeof(HomePage), null, displaySubheader, displayBackbutton);
}
else
{
displaySubheader = true;
displayBackbutton = false;
await UIServices.Instance.Load(typeof(GroupPage), arguments, displaySubheader, displayBackbutton);
var groupId = new Guid(arguments);
await ReadPost(groupId);
}
}
Related
I've got a WinForm app that parents Windows of other processes (ex. Google Chrome). I'm using the following code to parent a Windows to my Form, using the Handle returned by [Process].MainWindowHandle.
I'm trying to find the MainWindowTitle of all the Windows that are parented to my Form, so I can display their name on a Label.
When the Window of a WebBrowser is embedded, the Title will change when a different Web Page is selected, switching Tabs.
The code I have for starting the program does work as it should:
ProcessStartInfo ps1 = new ProcessStartInfo(#"C:/Users/Jacob/AppData/Roaming/Spotify/Spotify.exe");
ps1.WindowStyle = ProcessWindowStyle.Minimized;
Process p1 = Process.Start(ps1);
// Allow the process to open it's window
Thread.Sleep(1000);
appWin1 = p1.MainWindowHandle;
spotify = p1;
// Put it into this form
SetParent(appWin1, this.Handle);
// Move the window to overlay it on this window
MoveWindow(appWin1, 0, 70, this.Width / 2, this.Height/2, true);
Since you're willing to use UIAutomation to handle this parenting affair, I propose to handle this using Automation methods entirely. Almost, SetParent still required :).
The class shown here uses the WindowPatter.WindowOpenedEvent to detect and notify when a new Window is opened in the System.
It can be any Window, Console included (still a Window).
This method allows to identify a Window when it's handle is already created, so you don't need an arbitrary time-out or try to use Process.WaitForInputIdle(), which may not have the desired result.
You can pass a list of names of processes to the ProcessNames Property of the class: when any Window that belongs to one of these Processes is opened, UIAutomation detects it and a public event is raised. It notifies the subscribers that one of the Processes in the list has opened a Window, which is the ProcessId of the Owner and the handle of the Windows.
These values are passed in a custom EventArgs class, ProcessStartedArgs when the ProcessStarted event is raised.
Since the Automation Event is raised in a Thread other than the UI Thread, the class captures the SynchronizationContext where the class is created (the UI Thread, since you're probably creating this class in a Form) and marshals the event to that Thread, calling its Post() method passing a SendOrPostCallback delegate.
This way, you can safely pass the Handle of your Form and the Handle of the Window to SetParent().
To retrieve the current Title (Caption) of the parented Window, pass the Handle previously returned in the event argument to the GetCurrentWindowTitle() method. If the Window contains tabbed child Windows, as a Web Browser, this method will return the Title related to the Tab currently selected.
▶ The class is disposable and you need to call its public Dispose() method. This removes the Automation event handler and also all the events in the Invocation List of the public event you have subscribed to. This way, you can use a Lambda to subscribe to the event.
Use a Field to store an instance of this class. Create the instance when needed, passing a List of Process Names you're interested in.
Subscribe to the ProcessStarted event.
When on of these Processes opens a new Window, you'll get a notification and the parenting thing can be performed:
public partial class SomeForm : Form
{
private WindowWatcher watcher = null;
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
watcher = new WindowWatcher();
watcher.ProcessNames.AddRange(new[] { "msedge", "firefox", "chrome", "notepad" });
watcher.ProcessStarted += (o, ev) => {
SetParent(ev.WindowHandle, this.Handle);
MoveWindow(ev.WindowHandle, 0, 70, this.Width / 2, this.Height / 2, true);
string windowTitle = WindowWatcher.GetCurrentWindowTitle(ev.WindowHandle);
};
}
protected override void OnFormClosed(FormClosedEventArgs e)
{
watcher.Dispose();
base.OnFormClosed(e);
}
}
WindowWatcher class:
NOTE: UI Automation assemblies are part of Windows Presentation Framework.
When one of these assemblies is referenced in a WinForms application, the WinForms application will become DpiAware (SystemAware), if it's not already DpiAware.
This can have an impact on the Layout of one or more Forms that is not designed to handle Dpi Awareness changes and notifications.
Requires a Project Reference to:
UIAutomationClient
UIAutomationTypes
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows.Automation;
public class WindowWatcher : IDisposable
{
private SynchronizationContext context = null;
private readonly SendOrPostCallback eventCallback;
public event EventHandler<ProcessStartedArgs> ProcessStarted;
private AutomationElement uiaWindow;
private AutomationEventHandler WindowOpenedHandler;
public WindowWatcher() {
context = SynchronizationContext.Current;
eventCallback = new SendOrPostCallback(EventHandlersInvoker);
InitializeWatcher();
}
public List<string> ProcessNames { get; set; } = new List<string>();
private void InitializeWatcher()
{
Automation.AddAutomationEventHandler(
WindowPattern.WindowOpenedEvent, AutomationElement.RootElement,
TreeScope.Children, WindowOpenedHandler = new AutomationEventHandler(OnWindowOpenedEvent));
}
public static string GetCurrentWindowTitle(IntPtr handle)
{
if (handle == IntPtr.Zero) return string.Empty;
var element = AutomationElement.FromHandle(handle);
if (element != null) {
return element.Current.Name;
}
return string.Empty;
}
private void OnWindowOpenedEvent(object uiaElement, AutomationEventArgs e)
{
uiaWindow = uiaElement as AutomationElement;
if (uiaWindow == null || uiaWindow.Current.ProcessId == Process.GetCurrentProcess().Id) return;
var window = uiaWindow.Current;
var procName = string.Empty;
using (var proc = Process.GetProcessById(window.ProcessId)) {
if (proc == null) throw new InvalidOperationException("Invalid Process");
procName = proc.ProcessName;
}
if (ProcessNames.IndexOf(procName) >= 0) {
var args = new ProcessStartedArgs(procName, window.ProcessId, (IntPtr)window.NativeWindowHandle);
context.Post(eventCallback, args);
}
}
public class ProcessStartedArgs : EventArgs
{
public ProcessStartedArgs(string procName, int procId, IntPtr windowHandle)
{
ProcessName = procName;
ProcessId = procId;
WindowHandle = windowHandle;
}
public string ProcessName { get; }
public int ProcessId { get; }
public IntPtr WindowHandle { get; }
}
private void EventHandlersInvoker(object state)
{
if (!(state is ProcessStartedArgs args)) return;
ProcessStarted?.Invoke(this, args);
}
~WindowWatcher() { Dispose(false); }
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected void Dispose(bool disposing)
{
if (uiaWindow != null && WindowOpenedHandler != null) {
Automation.RemoveAutomationEventHandler(
WindowPattern.WindowOpenedEvent, uiaWindow, WindowOpenedHandler);
}
if (ProcessStarted != null) {
var invList = ProcessStarted.GetInvocationList();
if (invList != null && invList.Length > 0) {
for (int i = invList.Length - 1; i >= 0; i--) {
ProcessStarted -= (EventHandler<ProcessStartedArgs>)invList[i];
}
}
}
}
}
I make a method called Instance that allow me to have a single instance of the Settings window, like this:
public static async Task<Settings> Instance()
{
if (AppWindow == null)
{
AppWindow = new Settings();
AppWindow.Closing += async (x, y) =>
{
bool close = await AppWindow.CheckSettings();
y.cancel = (close) ? true : false;
AppWindow = null;
};
}
return AppWindow;
}
the CheckSettings have this structure:
private async Task<bool> CheckSettings()
{
//just as example
return true;
}
the method Instance() tell me that there is no await operator inside. Why happen this?
I need to ask also other questions:
Can this logic used inside a property instead of Instance method? How?
Is possible close the window without implement a Task<bool>
UPDATE
based on the helpful answer and comments on this great community I have edited the method as this (now is a property):
public static Settings Instance
{
get
{
if (AppWindow == null)
{
AppWindow = new Settings();
AppWindow.Closing += async (x, y) =>
{
bool close = await AppWindow.CheckSettings();
y.Cancel = close;
//AppWindow.Close();
//AppWindow = null;
};
}
return AppWindow;
}
}
the problem is that the Cancel does not await the CheckSettings()
Set the Cancel property to true before you call your async method:
public static Settings Instance
{
get
{
if (AppWindow == null)
{
AppWindow = new Settings();
//attach the event handler
AppWindow.Closing += AppWindow_Closing;
}
return AppWindow;
}
}
private static async void AppWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
e.Cancel = true;
//call the async method
bool close = await AppWindow.CheckSettings();
if (close)
{
AppWindow win = (AppWindow)sender;
//detach the event handler
AppWindow.Closing -= AppWindow_Closing;
//...and close the window immediately
win.Close();
AppWindow = null;
}
}
I'm attempting to create an object which will hold any values the application will need globally during its runtime. I thought i'd use App.xaml.cs as this is the heart of the application if I understand correctly as that code is run first and kept in memory.
Getting this error on the .inProgress part of the code at the bottom of this post:
'App' does not contain a definition for 'inProgress' and no extension
method 'inProgress' accepting a first argument of type 'App' could be
found (are you missing a using directive or an assembly reference?)
App.xaml.cs
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
//Startup
Window main = new MainWindow();
main.Show();
//Bind Commands
Classes.MyCommands.BindCommandsToWindow(main);
//Create runtime objects
var runtime = new runtimeObject();
}
public static explicit operator App(Application v)
{
throw new NotImplementedException();
}
}
/// <summary>
/// Global values for use during application runtime
/// </summary>
public class runtimeObject
{
private bool _inProgress = false;
public bool inProgress
{
get { return _inProgress; }
set { _inProgress = value; }
}
}
Here i'm trying to access the runtime object so that I can see if the application can be closes, bare in mind this may not be needed, but I would need to do similar tasks like this other than close the window.
Classes > Commands.cs
bool inProgress = (System.Windows.Application.Current as App).inProgress;
It looks like you need to add a property to access the runtime object. Currently you're just creating an instance in OnStartup method. Assign that instance to a property:
public partial class App : Application
{
public static runtimeObject runtime { get; set; };
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
//Startup
Window main = new MainWindow();
main.Show();
//Bind Commands
Classes.MyCommands.BindCommandsToWindow(main);
// Create runtime objects
// Assign to accessible property.
runtime = new runtimeObject();
}
public static explicit operator App(Application v)
{
throw new NotImplementedException();
}
}
Then access the property from your commands logic:
public static void CloseWindow_CanExecute(object sender,
CanExecuteRoutedEventArgs e)
{
if (App.runtime.inProgress == true)
{
e.CanExecute = false;
}
else
{
e.CanExecute = true;
}
}
I am working on a Windows Phone 7 app and trying to implement the features of a Windows Phone 8 like wide tiles into it. I achieved it using reflection but when i wanted to update the tile using a ScheduledAgent for a Periodic Task the tile is not getting created.
The scheduled agent OnInvoke code looks like
protected override void OnInvoke(ScheduledTask task)
{
//TODO: Add code to perform your task in background
if (task is PeriodicTask)
{
//Update the tile using Scheduled Task
CreateTileForWindowsPhone.CreateWideTile();
}
NotifyComplete();
}
I created the tile using this code
public class CreateTileForWindowsPhone
{
private static Version TargetedVersion = new Version(7, 10, 8858);
public static bool IsTargetedVersion { get { return Environment.OSVersion.Version >= TargetedVersion; } }
public static void CreateWideTile()
{
if (IsTargetedVersion)
{
try
{
// Get the new FlipTileData type.
Type flipTileDataType = Type.GetType("Microsoft.Phone.Shell.FlipTileData, Microsoft.Phone");
// Get the ShellTile type so we can call the new version of "Update" that takes the new Tile templates.
Type shellTileType = Type.GetType("Microsoft.Phone.Shell.ShellTile, Microsoft.Phone");
// Loop through any existing Tiles that are pinned to Start.
QuotesCollection aq = new QuotesCollection();
Random rand = new Random();
int randNum = rand.Next(0, 163);
//String wideBackStr = "Dont be the same, Be Better.";
String wideBackStr = aq.quotes[randNum];
foreach (var tileToUpdate in ShellTile.ActiveTiles)
{
// Get the constructor for the new FlipTileData class and assign it to our variable to hold the Tile properties.
var UpdateTileData = flipTileDataType.GetConstructor(new Type[] { }).Invoke(null);
// Set the properties.
SetProperty(UpdateTileData, "WideBackgroundImage", new Uri("/images/QuottedWideTile.png", UriKind.Relative));
SetProperty(UpdateTileData, "WideBackContent", wideBackStr);
// Invoke the new version of ShellTile.Update.
shellTileType.GetMethod("Update").Invoke(tileToUpdate, new Object[] { UpdateTileData });
break;
}
}
catch
{
MessageBox.Show("Tile Error Caught");
}
}
}
private static void SetProperty(object instance, string name, object value)
{
var setMethod = instance.GetType().GetProperty(name).GetSetMethod();
setMethod.Invoke(instance, new object[] { value });
}
}
i made a register agent method to register the Periodic Task
private void RegisterAgent()
{
string taskName = "MyTask";
try
{
if (ScheduledActionService.Find(taskName) != null)
{
//if the agent exists, remove and then add it to ensure
//the agent's schedule is updated to avoid expiration
ScheduledActionService.Remove(taskName);
}
PeriodicTask periodicTask = new PeriodicTask(taskName);
periodicTask.Description = "Random Quote Update On Tile";
ScheduledActionService.Add(periodicTask);
}
catch (InvalidOperationException exception)
{
MessageBox.Show(exception.Message);
}
catch (SchedulerServiceException schedulerException)
{
MessageBox.Show(schedulerException.Message);
}
}
and called the register in the app launching
private void Application_Launching(object sender, LaunchingEventArgs e)
{
RegisterAgent();
}
But the wide tile is not being created when i run the app.
Wide tile creation used to work before i used scheduled agents to update the tile. I used to create a wide tile just by calling that function in application launching.
private void Application_Launching(object sender, LaunchingEventArgs e)
{
CreateTileForWindowsPhone.CreateWideTile();
}
WHy is the tile not being created. Am i doing something wrong?
Deployment.Current.Dispatcher.BeginInvoke(
CreateTileForWindowsPhone.CreateWideTile);
Using VS 2013, C#, Windows Store App
I need to send one object from main Frame to new one and then work with it.
So i have main Frame, second Frame (for work with sended object) and DataModel.
Idea - is to display all data that i have at main frame, than choose one object, press on it, after pressing new Frame will appear and you can work with selected items in new frame.
Problem - how to send object from one Frame to another.
Currently i made next: create additional static class that with static property:
public static class GetCurrentEvent
{
public static Event CurrentEvent { get; set; }
}
So, at first i call to property of this class at main Frame, and save required object using it:
private void ItemView_ItemClick(object sender, ItemClickEventArgs e)
{
var clickedItems = (Event)e.ClickedItem;
GetCurrentEvent.CurrentEvent = new Event(
clickedItems.UniqueId,
clickedItems.Name,
clickedItems.Place,
clickedItems.Description,
clickedItems.Start,
clickedItems.End,
clickedItems.ImagePath
);
if (this.Frame != null)
{
this.Frame.Navigate(typeof(ChangeEvent));
}
}
After that i use this property in new Frame:
private void navigationHelper_LoadState(object sender, LoadStateEventArgs e)
{
this.DataContext = GetCurrentEvent.CurrentEvent;
...
}
All works, but i think that it's not the perfect method.
So,the quesion how i can change code abowe for improving methods, or how i can send object from one class to another?
EDIT
Choosed varinat to send object from Frame to Frame - use Parameter:
var clickedItems = (Event)e.ClickedItem;
this.Frame.Navigate(typeof(ChangeEvent), clickedItems);
and then convert to required type in new Frame:
this.DataContext = (Event)e.NavigationParameter;
There are many many ways that this accomplished and this tends be an opinionated debate.
I typically opt for a simple solution, such as saving a state/session variable in a global accessible singleton. I call the singleton Global and keep it in the root of the namespace.
Example:
public sealed class Global
{
#region Singlton Contructor
Global() { }
static readonly Global instance = new Global();
public static Global Default
{
get { return instance; }
}
#endregion
#region Global Settings
public Settings Settings {get;set;}
private AuthenticatedUser _authenticatedUser;
public AuthenticatedUser AuthenticatedUser
{
get
{
return _authenticatedUser;
}
set { _authenticatedUser = value; }
}
private UserSession _currentSession;
public UserSession CurrentSession
{
get
{
if (_currentSession == null) _currentSession = UserSessionService.UserSessionFactoy();
return _currentSession;
}
private set { _currentSession = value; }
}
#endregion
}
CurrentSession in this case keeps track of the objects I want to pass frame to frame. And its easily accessed by using
Global.CurrentSession.SomePropertyOrObject