Loading Samples/DeviceLab/DeviceCollectionView.xaml +5 −1 Original line number Diff line number Diff line Loading @@ -96,6 +96,8 @@ </CheckBox> <TextBlock Margin="0,0,4,0" Text="Device Name:" /> <TextBlock Text="{Binding DeviceName}"/> <TextBlock Margin="50,0,4,0" Text="Device Address:" /> <TextBlock Text="{Binding Address}"/> <Button DockPanel.Dock="Right" Height="16" Width="16" Loading Loading @@ -135,9 +137,11 @@ <TextBox Width="100" Margin="0,0,4,0" Text="{Binding DeviceNameEntry, UpdateSourceTrigger=PropertyChanged}" /> <Button Padding="2,1,2,1" Command="{Binding RenameCommand}" Content="Rename" /> </StackPanel> <StackPanel Orientation="Horizontal" HorizontalAlignment="Center"> <StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Height="20" Margin="0,5,0,0"> <Button Content="Reboot" Width="60" Command="{Binding RebootCommand}" /> <Button Content="Reestablish Connection" Width="140" Margin="2,0,0,0" Command="{Binding ReestablishConnectionCommand}" /> <TextBlock Text="Retry Attempts:" Width="86" Margin="15,0,-5,0" /> <local:NumberEntryBox Value="{Binding ConnectionRetryAttempts}" /> </StackPanel> </StackPanel> </TabItem> Loading Samples/DeviceLab/DeviceLab.csproj +7 −0 Original line number Diff line number Diff line Loading @@ -68,6 +68,9 @@ <DependentUpon>DeviceSignInView.xaml</DependentUpon> </Compile> <Compile Include="MainViewModel.cs" /> <Compile Include="NumberEntryBox.xaml.cs"> <DependentUpon>NumberEntryBox.xaml</DependentUpon> </Compile> <Compile Include="SelectionListBox.cs" /> <Compile Include="XboxDevicePortalConnection.cs" /> <Page Include="BindablePassword.xaml"> Loading Loading @@ -99,6 +102,10 @@ <DependentUpon>MainWindow.xaml</DependentUpon> <SubType>Code</SubType> </Compile> <Page Include="NumberEntryBox.xaml"> <SubType>Designer</SubType> <Generator>MSBuild:Compile</Generator> </Page> <Page Include="Themes\CustomStyles.xaml"> <SubType>Designer</SubType> <Generator>MSBuild:Compile</Generator> Loading Samples/DeviceLab/DevicePortalViewModel.cs +36 −4 Original line number Diff line number Diff line Loading @@ -35,6 +35,7 @@ namespace DeviceLab #region Constructors public DevicePortalViewModel(DevicePortal portal, IDiagnosticSink diags) { this.ConnectionRetryAttempts = 5; this.diagnostics = diags; this.portal = portal; this.Ready = true; Loading Loading @@ -160,7 +161,7 @@ namespace DeviceLab } #endregion // PlatformName #region // CPULoad #region CPULoad private int cpuLoad; public string CPULoad { Loading @@ -170,6 +171,22 @@ namespace DeviceLab } } #endregion // CPULoad #region ConnectionRetryAttempts private int connectionRetryAttempts; public int ConnectionRetryAttempts { get { return this.connectionRetryAttempts; } set { SetProperty(ref this.connectionRetryAttempts, value); } } #endregion // ConnectionRetryAttempts #endregion // Properties //------------------------------------------------------------------- Loading Loading @@ -286,6 +303,7 @@ namespace DeviceLab private async Task ExecuteRebootAsync() { await ExecuteStopListeningForSystemPerfAsync(); this.Ready = false; try { Loading @@ -301,6 +319,7 @@ namespace DeviceLab ReportException("Reboot", exn); } await ExecuteReestablishConnectionAsync(); await ExecuteStartListeningForSystemPerfAsync(); } #endregion // Reboot Command Loading Loading @@ -349,7 +368,7 @@ namespace DeviceLab { await this.portal.Connect(); ++numTries; } while (this.portal.ConnectionHttpStatusCode != HttpStatusCode.OK); } while (this.portal.ConnectionHttpStatusCode != HttpStatusCode.OK && numTries < this.ConnectionRetryAttempts); this.Ready = true; } catch (Exception exn) Loading Loading @@ -384,9 +403,15 @@ namespace DeviceLab { this.Ready = false; this.portal.SystemPerfMessageReceived += OnSystemPerfReceived; await this.portal.StartListeningForSystemPerf(); await Task.Run(StartListeningHelper); this.Ready = true; } private async Task StartListeningHelper() { await this.portal.StartListeningForSystemPerf().ConfigureAwait(false); } #endregion // StartListeningForSystemPerfCommand #region StopListeningForSystemPerfCommand Loading @@ -413,9 +438,16 @@ namespace DeviceLab { this.Ready = false; this.portal.SystemPerfMessageReceived -= OnSystemPerfReceived; await this.portal.StopListeningForSystemPerf(); await StopListeningHelper(); //await Task.Run(StopListeningHelper); await StopListeningHelper(); this.Ready = true; } private async Task StopListeningHelper() { await this.portal.StopListeningForSystemPerf(); } #endregion // StopListeningForSystemPerfCommand #endregion // Commands Loading Samples/DeviceLab/DeviceSignInViewModel.cs +50 −41 Original line number Diff line number Diff line Loading @@ -51,15 +51,14 @@ namespace DeviceLab public class DeviceSignInEventArgs : System.EventArgs { internal DeviceSignInEventArgs(HttpStatusCode statusCode, DevicePortal portal) internal DeviceSignInEventArgs(DevicePortal portal, IDevicePortalConnection conn) { this.StatusCode = statusCode; this.Portal = portal; this.Connection = conn; } public HttpStatusCode StatusCode { get; private set; } public DevicePortal Portal { get; private set; } public IDevicePortalConnection Connection { get; private set; } } public class DeviceSignInViewModel : BindableBase Loading Loading @@ -175,7 +174,9 @@ namespace DeviceLab { if (connectCommand == null) { connectCommand = DelegateCommand.FromAsyncHandler(ExecuteConnectAsync, CanExecuteConnect); // TODO //connectCommand = DelegateCommand.FromAsyncHandler(ExecuteConnect, CanExecuteConnect); connectCommand = new DelegateCommand(ExecuteConnect, CanExecuteConnect); connectCommand.ObservesProperty(() => this.DeviceIP); connectCommand.ObservesProperty(() => this.UserName); connectCommand.ObservesProperty(() => this.Password); Loading @@ -186,46 +187,54 @@ namespace DeviceLab } } private async Task ExecuteConnectAsync() private void ExecuteConnect() { this.diagnostics.OutputDiagnosticString("[{0}] Attempting to connect.\n", this.deviceIP); IDevicePortalConnection conn = this.ConnectionTypeSelection.CreateConnection(this.DeviceIP, this.UserName, this.Password); DevicePortal portal = new DevicePortal(conn); DeviceConnectionStatusEventHandler handler = (DevicePortal sender, DeviceConnectionStatusEventArgs args) => { this.diagnostics.OutputDiagnosticString("[{0}] Connection status update: Status: {1}, Phase: {2}\n", portal.Address, args.Status, args.Phase); if (args.Status == DeviceConnectionStatus.Connected) { this.diagnostics.OutputDiagnosticString("[{0}] Language: {1}\n", portal.Address, conn.OsInfo.Language); this.diagnostics.OutputDiagnosticString("[{0}] Name: {1}\n", portal.Address, conn.OsInfo.Name); this.diagnostics.OutputDiagnosticString("[{0}] OsEdition: {1}\n", portal.Address, conn.OsInfo.OsEdition); this.diagnostics.OutputDiagnosticString("[{0}] OsEditionId: {1}\n", portal.Address, conn.OsInfo.OsEditionId); this.diagnostics.OutputDiagnosticString("[{0}] OsVersionString: {1}\n", portal.Address, conn.OsInfo.OsVersionString); this.diagnostics.OutputDiagnosticString("[{0}] Platform: {1}\n", portal.Address, conn.OsInfo.Platform); this.diagnostics.OutputDiagnosticString("[{0}] PlatformName: {1}\n", portal.Address, conn.OsInfo.PlatformName); } else if (args.Status == DeviceConnectionStatus.Failed) { this.diagnostics.OutputDiagnosticString("[{0}] Bummer.\n", portal.Address); this.SignInAttempts?.Invoke(this, new DeviceSignInEventArgs(portal, conn)); } }; portal.ConnectionStatus += handler; try { this.Ready = false; await portal.Connect(); } catch (Exception exn) { this.diagnostics.OutputDiagnosticString("[{0}] Exception when trying to connect:\n[{0}] {1}\nStackTrace: \n[{0}] {2}\n", portal.Address, exn.Message, exn.StackTrace); } this.SignInAttempts?.Invoke(this, new DeviceSignInEventArgs(portal.ConnectionHttpStatusCode, portal)); portal.ConnectionStatus -= handler; this.Ready = true; } // TODO //private async Task OldExecuteConnectAsync() //{ // this.diagnostics.OutputDiagnosticString("[{0}] Attempting to connect.\n", this.deviceIP); // IDevicePortalConnection conn = this.ConnectionTypeSelection.CreateConnection(this.DeviceIP, this.UserName, this.Password); // DevicePortal portal = new DevicePortal(conn); // DeviceConnectionStatusEventHandler handler = (DevicePortal sender, DeviceConnectionStatusEventArgs args) => // { // this.diagnostics.OutputDiagnosticString("[{0}] Connection status update: Status: {1}, Phase: {2}\n", portal.Address, args.Status, args.Phase); // if (args.Status == DeviceConnectionStatus.Connected) // { // this.diagnostics.OutputDiagnosticString("[{0}] Language: {1}\n", portal.Address, conn.OsInfo.Language); // this.diagnostics.OutputDiagnosticString("[{0}] Name: {1}\n", portal.Address, conn.OsInfo.Name); // this.diagnostics.OutputDiagnosticString("[{0}] OsEdition: {1}\n", portal.Address, conn.OsInfo.OsEdition); // this.diagnostics.OutputDiagnosticString("[{0}] OsEditionId: {1}\n", portal.Address, conn.OsInfo.OsEditionId); // this.diagnostics.OutputDiagnosticString("[{0}] OsVersionString: {1}\n", portal.Address, conn.OsInfo.OsVersionString); // this.diagnostics.OutputDiagnosticString("[{0}] Platform: {1}\n", portal.Address, conn.OsInfo.Platform); // this.diagnostics.OutputDiagnosticString("[{0}] PlatformName: {1}\n", portal.Address, conn.OsInfo.PlatformName); // } // else if (args.Status == DeviceConnectionStatus.Failed) // { // this.diagnostics.OutputDiagnosticString("[{0}] Bummer.\n", portal.Address); // } // }; // portal.ConnectionStatus += handler; // try // { // this.Ready = false; // await portal.Connect(); // } // catch (Exception exn) // { // this.diagnostics.OutputDiagnosticString("[{0}] Exception when trying to connect:\n[{0}] {1}\nStackTrace: \n[{0}] {2}\n", portal.Address, exn.Message, exn.StackTrace); // } // this.SignInAttempts?.Invoke(this, new DeviceSignInEventArgs(portal.ConnectionHttpStatusCode, portal)); // portal.ConnectionStatus -= handler; // this.Ready = true; //} private bool CanExecuteConnect() { Loading Samples/DeviceLab/MainViewModel.cs +102 −75 Original line number Diff line number Diff line using Prism.Commands; using Prism.Mvvm; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Input; using System.Collections.Specialized; using System.Collections; using System.ComponentModel; using System; using Microsoft.Tools.WindowsDevicePortal; namespace DeviceLab { public static class CommandExtensions { public class ChainedCommand public class NewDeviceHelper : ICommand { // TODO: Don't close over a DevicePortalViewModel // Instead, should just hook up the CanExecuteChanged events on // the commands private DevicePortalViewModel dpvm; private Queue<ICommand> commandQueue; private bool executionStarted; // TODO: public event EventHandler CanExecuteChanged; private ICommand executeMe; private object arg; public NewDeviceHelper(DevicePortalViewModel dpvm) { this.dpvm = dpvm; private ChainedCommand nextChainedCommand; this.commandQueue = new Queue<ICommand>(); this.executionStarted = false; } public ChainedCommand(ICommand executeMe, object arg, bool start = true) public void Enqueue(ICommand command) { this.executeMe = executeMe; this.arg = arg; this.nextChainedCommand = null; this.Status = State.NotStarted; if(start) if (this.executionStarted) { Start(); throw new InvalidOperationException("Execution already started"); } this.commandQueue.Enqueue(command); } public enum State private void Dpvm_PropertyChanged(object sender, PropertyChangedEventArgs e) { NotStarted, Waiting, Canceled, Completed }; public State Status { get; private set; } public void Start() if(e.PropertyName == "Ready") { if(this.Status != State.NotStarted) if(this.dpvm.Ready) { throw new InvalidOperationException("Must be in the NotStarted state"); DoNextCommand(); } this.Status = State.Waiting; this.executeMe.CanExecuteChanged += ExecuteMe_CanExecuteChanged; TryExecute(); } public State Cancel() { this.executeMe.CanExecuteChanged -= ExecuteMe_CanExecuteChanged; State oldState = this.Status; this.Status = State.Canceled; return oldState; } public ChainedCommand ExecuteNext(ICommand next, object arg) private void DoNextCommand() { this.nextChainedCommand = new ChainedCommand(next, arg, false); TryExecuteNext(); return this.nextChainedCommand; if(this.commandQueue == null) { throw new InvalidOperationException("Attempting to execute commands without a command queue"); } private void ExecuteMe_CanExecuteChanged(object sender, EventArgs e) if(this.commandQueue.Count == 0) { TryExecute(); throw new InvalidOperationException("Attempting to execute command on an empty command queue"); } private void TryExecute() if(this.commandQueue.Peek().CanExecute(null)) { if (this.executeMe.CanExecute(arg)) ICommand nextCommand = this.commandQueue.Dequeue(); if (this.commandQueue.Count == 0) { this.executeMe.Execute(arg); this.executeMe.CanExecuteChanged -= ExecuteMe_CanExecuteChanged; this.Status = State.Completed; TryExecuteNext(); // Executing the next command will probably trigger property changed // events and since this is the last command we want to disconnect from // those events this.dpvm.PropertyChanged -= Dpvm_PropertyChanged; this.executionStarted = false; } nextCommand.Execute(null); } } private void TryExecuteNext() public bool CanExecute(object parameter) { if(this.Status == State.Completed && this.nextChainedCommand != null) if(this.executionStarted) { this.nextChainedCommand.Start(); return false; } if(this.commandQueue.Count > 0) { return this.commandQueue.Peek().CanExecute(null); } return false; } public static ChainedCommand ExecuteWhenReady(this ICommand executeMe, object arg) public void Execute(object parameter) { return new ChainedCommand(executeMe, arg); if(this.executionStarted) { throw new InvalidOperationException("Execution already started"); } dpvm.PropertyChanged += Dpvm_PropertyChanged; DoNextCommand(); } } Loading @@ -124,14 +129,21 @@ namespace DeviceLab this.ConnectedDevices.CollectionChanged += OnConnectedDevicesChanged; } private void OnSignInAttemptCompleted(DeviceSignInViewModel sender, DeviceSignInEventArgs args) { if(args.StatusCode == System.Net.HttpStatusCode.OK) DevicePortal portal = args.Portal; // See if the device is already in the list foreach (DevicePortalViewModel dpvm in this.ConnectedDevices) { this.ConnectedDevices.Add(new DevicePortalViewModel(args.Portal, Diagnostics)); if(dpvm.Address == portal.Address) { this.Diagnostics.OutputDiagnosticString("[!!] The device with address {0} is already in the list\n", portal.Address); return; } } this.ConnectedDevices.Add(new DevicePortalViewModel(args.Portal, Diagnostics)); } #endregion // Constructors //------------------------------------------------------------------- Loading Loading @@ -325,39 +337,44 @@ namespace DeviceLab #endregion // RebootSelectedDevicesCommand #region RemoveDeviceCommand private DelegateCommand<DevicePortalViewModel> removeDeviceCommand; // NOTE: Ideally, the template parameter would be DevicePortalViewMOdel, rather than object. // Unfortunately, this would sporadically throw an invalid cast exception from within the // DelegateCommand<> constructor in the PRISM library. Apparently, the object passed to the // constructor cannot always be cast to something that would rightfully belong to the // collection. private DelegateCommand<object> removeDeviceCommand; public ICommand RemoveDeviceCommand { get { if(this.removeDeviceCommand == null) { this.removeDeviceCommand = new DelegateCommand<DevicePortalViewModel>(ExecuteRemoveDeviceCommand, CanExecuteRemoveDeviceCommand); this.removeDeviceCommand = new DelegateCommand<object>(ExecuteRemoveDeviceCommand, CanExecuteRemoveDeviceCommand); this.removeDeviceCommand.ObservesProperty(() => SomeDevicesReady); } return this.removeDeviceCommand; } } private bool CanExecuteRemoveDeviceCommand(DevicePortalViewModel arg) private bool CanExecuteRemoveDeviceCommand(object arg) { if (arg != null) DevicePortalViewModel dpvm = arg as DevicePortalViewModel; if (dpvm != null) { return arg.Ready; return dpvm.Ready; } return false; } private void ExecuteRemoveDeviceCommand(DevicePortalViewModel obj) private void ExecuteRemoveDeviceCommand(object obj) { if (obj != null) DevicePortalViewModel dpvm = obj as DevicePortalViewModel; if (dpvm != null) { OnDeviceRemoved(obj); this.ConnectedDevices.Remove(obj); this.ConnectedDevices.Remove(dpvm); } } #endregion // RemoveDeviceCommand #endregion // Commands private void OnConnectedDevicesChanged(object sender, NotifyCollectionChangedEventArgs e) Loading @@ -378,7 +395,7 @@ namespace DeviceLab { foreach(DevicePortalViewModel dpvm in e.OldItems) { //OnDeviceRemoved(dpvm); OnDeviceRemoved(dpvm); } } } Loading @@ -387,7 +404,17 @@ namespace DeviceLab private void OnDeviceAdded(DevicePortalViewModel dpvm) { dpvm.PropertyChanged += DevicePropertyChanged; dpvm.RefreshDeviceNameCommand.ExecuteWhenReady(this).ExecuteNext(dpvm.StartListeningForSystemPerfCommand, this); // TODO: //CompositeCommand command = new CompositeCommand(); //command.RegisterCommand(dpvm.RefreshDeviceNameCommand); //command.RegisterCommand(dpvm.StartListeningForSystemPerfCommand); //command.Execute(null); NewDeviceHelper ndh = new NewDeviceHelper(dpvm); ndh.Enqueue(dpvm.ReestablishConnectionCommand); ndh.Enqueue(dpvm.RefreshDeviceNameCommand); ndh.Enqueue(dpvm.StartListeningForSystemPerfCommand); ndh.Execute(null); } private void OnDeviceRemoved(DevicePortalViewModel dpvm) Loading Loading
Samples/DeviceLab/DeviceCollectionView.xaml +5 −1 Original line number Diff line number Diff line Loading @@ -96,6 +96,8 @@ </CheckBox> <TextBlock Margin="0,0,4,0" Text="Device Name:" /> <TextBlock Text="{Binding DeviceName}"/> <TextBlock Margin="50,0,4,0" Text="Device Address:" /> <TextBlock Text="{Binding Address}"/> <Button DockPanel.Dock="Right" Height="16" Width="16" Loading Loading @@ -135,9 +137,11 @@ <TextBox Width="100" Margin="0,0,4,0" Text="{Binding DeviceNameEntry, UpdateSourceTrigger=PropertyChanged}" /> <Button Padding="2,1,2,1" Command="{Binding RenameCommand}" Content="Rename" /> </StackPanel> <StackPanel Orientation="Horizontal" HorizontalAlignment="Center"> <StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Height="20" Margin="0,5,0,0"> <Button Content="Reboot" Width="60" Command="{Binding RebootCommand}" /> <Button Content="Reestablish Connection" Width="140" Margin="2,0,0,0" Command="{Binding ReestablishConnectionCommand}" /> <TextBlock Text="Retry Attempts:" Width="86" Margin="15,0,-5,0" /> <local:NumberEntryBox Value="{Binding ConnectionRetryAttempts}" /> </StackPanel> </StackPanel> </TabItem> Loading
Samples/DeviceLab/DeviceLab.csproj +7 −0 Original line number Diff line number Diff line Loading @@ -68,6 +68,9 @@ <DependentUpon>DeviceSignInView.xaml</DependentUpon> </Compile> <Compile Include="MainViewModel.cs" /> <Compile Include="NumberEntryBox.xaml.cs"> <DependentUpon>NumberEntryBox.xaml</DependentUpon> </Compile> <Compile Include="SelectionListBox.cs" /> <Compile Include="XboxDevicePortalConnection.cs" /> <Page Include="BindablePassword.xaml"> Loading Loading @@ -99,6 +102,10 @@ <DependentUpon>MainWindow.xaml</DependentUpon> <SubType>Code</SubType> </Compile> <Page Include="NumberEntryBox.xaml"> <SubType>Designer</SubType> <Generator>MSBuild:Compile</Generator> </Page> <Page Include="Themes\CustomStyles.xaml"> <SubType>Designer</SubType> <Generator>MSBuild:Compile</Generator> Loading
Samples/DeviceLab/DevicePortalViewModel.cs +36 −4 Original line number Diff line number Diff line Loading @@ -35,6 +35,7 @@ namespace DeviceLab #region Constructors public DevicePortalViewModel(DevicePortal portal, IDiagnosticSink diags) { this.ConnectionRetryAttempts = 5; this.diagnostics = diags; this.portal = portal; this.Ready = true; Loading Loading @@ -160,7 +161,7 @@ namespace DeviceLab } #endregion // PlatformName #region // CPULoad #region CPULoad private int cpuLoad; public string CPULoad { Loading @@ -170,6 +171,22 @@ namespace DeviceLab } } #endregion // CPULoad #region ConnectionRetryAttempts private int connectionRetryAttempts; public int ConnectionRetryAttempts { get { return this.connectionRetryAttempts; } set { SetProperty(ref this.connectionRetryAttempts, value); } } #endregion // ConnectionRetryAttempts #endregion // Properties //------------------------------------------------------------------- Loading Loading @@ -286,6 +303,7 @@ namespace DeviceLab private async Task ExecuteRebootAsync() { await ExecuteStopListeningForSystemPerfAsync(); this.Ready = false; try { Loading @@ -301,6 +319,7 @@ namespace DeviceLab ReportException("Reboot", exn); } await ExecuteReestablishConnectionAsync(); await ExecuteStartListeningForSystemPerfAsync(); } #endregion // Reboot Command Loading Loading @@ -349,7 +368,7 @@ namespace DeviceLab { await this.portal.Connect(); ++numTries; } while (this.portal.ConnectionHttpStatusCode != HttpStatusCode.OK); } while (this.portal.ConnectionHttpStatusCode != HttpStatusCode.OK && numTries < this.ConnectionRetryAttempts); this.Ready = true; } catch (Exception exn) Loading Loading @@ -384,9 +403,15 @@ namespace DeviceLab { this.Ready = false; this.portal.SystemPerfMessageReceived += OnSystemPerfReceived; await this.portal.StartListeningForSystemPerf(); await Task.Run(StartListeningHelper); this.Ready = true; } private async Task StartListeningHelper() { await this.portal.StartListeningForSystemPerf().ConfigureAwait(false); } #endregion // StartListeningForSystemPerfCommand #region StopListeningForSystemPerfCommand Loading @@ -413,9 +438,16 @@ namespace DeviceLab { this.Ready = false; this.portal.SystemPerfMessageReceived -= OnSystemPerfReceived; await this.portal.StopListeningForSystemPerf(); await StopListeningHelper(); //await Task.Run(StopListeningHelper); await StopListeningHelper(); this.Ready = true; } private async Task StopListeningHelper() { await this.portal.StopListeningForSystemPerf(); } #endregion // StopListeningForSystemPerfCommand #endregion // Commands Loading
Samples/DeviceLab/DeviceSignInViewModel.cs +50 −41 Original line number Diff line number Diff line Loading @@ -51,15 +51,14 @@ namespace DeviceLab public class DeviceSignInEventArgs : System.EventArgs { internal DeviceSignInEventArgs(HttpStatusCode statusCode, DevicePortal portal) internal DeviceSignInEventArgs(DevicePortal portal, IDevicePortalConnection conn) { this.StatusCode = statusCode; this.Portal = portal; this.Connection = conn; } public HttpStatusCode StatusCode { get; private set; } public DevicePortal Portal { get; private set; } public IDevicePortalConnection Connection { get; private set; } } public class DeviceSignInViewModel : BindableBase Loading Loading @@ -175,7 +174,9 @@ namespace DeviceLab { if (connectCommand == null) { connectCommand = DelegateCommand.FromAsyncHandler(ExecuteConnectAsync, CanExecuteConnect); // TODO //connectCommand = DelegateCommand.FromAsyncHandler(ExecuteConnect, CanExecuteConnect); connectCommand = new DelegateCommand(ExecuteConnect, CanExecuteConnect); connectCommand.ObservesProperty(() => this.DeviceIP); connectCommand.ObservesProperty(() => this.UserName); connectCommand.ObservesProperty(() => this.Password); Loading @@ -186,46 +187,54 @@ namespace DeviceLab } } private async Task ExecuteConnectAsync() private void ExecuteConnect() { this.diagnostics.OutputDiagnosticString("[{0}] Attempting to connect.\n", this.deviceIP); IDevicePortalConnection conn = this.ConnectionTypeSelection.CreateConnection(this.DeviceIP, this.UserName, this.Password); DevicePortal portal = new DevicePortal(conn); DeviceConnectionStatusEventHandler handler = (DevicePortal sender, DeviceConnectionStatusEventArgs args) => { this.diagnostics.OutputDiagnosticString("[{0}] Connection status update: Status: {1}, Phase: {2}\n", portal.Address, args.Status, args.Phase); if (args.Status == DeviceConnectionStatus.Connected) { this.diagnostics.OutputDiagnosticString("[{0}] Language: {1}\n", portal.Address, conn.OsInfo.Language); this.diagnostics.OutputDiagnosticString("[{0}] Name: {1}\n", portal.Address, conn.OsInfo.Name); this.diagnostics.OutputDiagnosticString("[{0}] OsEdition: {1}\n", portal.Address, conn.OsInfo.OsEdition); this.diagnostics.OutputDiagnosticString("[{0}] OsEditionId: {1}\n", portal.Address, conn.OsInfo.OsEditionId); this.diagnostics.OutputDiagnosticString("[{0}] OsVersionString: {1}\n", portal.Address, conn.OsInfo.OsVersionString); this.diagnostics.OutputDiagnosticString("[{0}] Platform: {1}\n", portal.Address, conn.OsInfo.Platform); this.diagnostics.OutputDiagnosticString("[{0}] PlatformName: {1}\n", portal.Address, conn.OsInfo.PlatformName); } else if (args.Status == DeviceConnectionStatus.Failed) { this.diagnostics.OutputDiagnosticString("[{0}] Bummer.\n", portal.Address); this.SignInAttempts?.Invoke(this, new DeviceSignInEventArgs(portal, conn)); } }; portal.ConnectionStatus += handler; try { this.Ready = false; await portal.Connect(); } catch (Exception exn) { this.diagnostics.OutputDiagnosticString("[{0}] Exception when trying to connect:\n[{0}] {1}\nStackTrace: \n[{0}] {2}\n", portal.Address, exn.Message, exn.StackTrace); } this.SignInAttempts?.Invoke(this, new DeviceSignInEventArgs(portal.ConnectionHttpStatusCode, portal)); portal.ConnectionStatus -= handler; this.Ready = true; } // TODO //private async Task OldExecuteConnectAsync() //{ // this.diagnostics.OutputDiagnosticString("[{0}] Attempting to connect.\n", this.deviceIP); // IDevicePortalConnection conn = this.ConnectionTypeSelection.CreateConnection(this.DeviceIP, this.UserName, this.Password); // DevicePortal portal = new DevicePortal(conn); // DeviceConnectionStatusEventHandler handler = (DevicePortal sender, DeviceConnectionStatusEventArgs args) => // { // this.diagnostics.OutputDiagnosticString("[{0}] Connection status update: Status: {1}, Phase: {2}\n", portal.Address, args.Status, args.Phase); // if (args.Status == DeviceConnectionStatus.Connected) // { // this.diagnostics.OutputDiagnosticString("[{0}] Language: {1}\n", portal.Address, conn.OsInfo.Language); // this.diagnostics.OutputDiagnosticString("[{0}] Name: {1}\n", portal.Address, conn.OsInfo.Name); // this.diagnostics.OutputDiagnosticString("[{0}] OsEdition: {1}\n", portal.Address, conn.OsInfo.OsEdition); // this.diagnostics.OutputDiagnosticString("[{0}] OsEditionId: {1}\n", portal.Address, conn.OsInfo.OsEditionId); // this.diagnostics.OutputDiagnosticString("[{0}] OsVersionString: {1}\n", portal.Address, conn.OsInfo.OsVersionString); // this.diagnostics.OutputDiagnosticString("[{0}] Platform: {1}\n", portal.Address, conn.OsInfo.Platform); // this.diagnostics.OutputDiagnosticString("[{0}] PlatformName: {1}\n", portal.Address, conn.OsInfo.PlatformName); // } // else if (args.Status == DeviceConnectionStatus.Failed) // { // this.diagnostics.OutputDiagnosticString("[{0}] Bummer.\n", portal.Address); // } // }; // portal.ConnectionStatus += handler; // try // { // this.Ready = false; // await portal.Connect(); // } // catch (Exception exn) // { // this.diagnostics.OutputDiagnosticString("[{0}] Exception when trying to connect:\n[{0}] {1}\nStackTrace: \n[{0}] {2}\n", portal.Address, exn.Message, exn.StackTrace); // } // this.SignInAttempts?.Invoke(this, new DeviceSignInEventArgs(portal.ConnectionHttpStatusCode, portal)); // portal.ConnectionStatus -= handler; // this.Ready = true; //} private bool CanExecuteConnect() { Loading
Samples/DeviceLab/MainViewModel.cs +102 −75 Original line number Diff line number Diff line using Prism.Commands; using Prism.Mvvm; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Input; using System.Collections.Specialized; using System.Collections; using System.ComponentModel; using System; using Microsoft.Tools.WindowsDevicePortal; namespace DeviceLab { public static class CommandExtensions { public class ChainedCommand public class NewDeviceHelper : ICommand { // TODO: Don't close over a DevicePortalViewModel // Instead, should just hook up the CanExecuteChanged events on // the commands private DevicePortalViewModel dpvm; private Queue<ICommand> commandQueue; private bool executionStarted; // TODO: public event EventHandler CanExecuteChanged; private ICommand executeMe; private object arg; public NewDeviceHelper(DevicePortalViewModel dpvm) { this.dpvm = dpvm; private ChainedCommand nextChainedCommand; this.commandQueue = new Queue<ICommand>(); this.executionStarted = false; } public ChainedCommand(ICommand executeMe, object arg, bool start = true) public void Enqueue(ICommand command) { this.executeMe = executeMe; this.arg = arg; this.nextChainedCommand = null; this.Status = State.NotStarted; if(start) if (this.executionStarted) { Start(); throw new InvalidOperationException("Execution already started"); } this.commandQueue.Enqueue(command); } public enum State private void Dpvm_PropertyChanged(object sender, PropertyChangedEventArgs e) { NotStarted, Waiting, Canceled, Completed }; public State Status { get; private set; } public void Start() if(e.PropertyName == "Ready") { if(this.Status != State.NotStarted) if(this.dpvm.Ready) { throw new InvalidOperationException("Must be in the NotStarted state"); DoNextCommand(); } this.Status = State.Waiting; this.executeMe.CanExecuteChanged += ExecuteMe_CanExecuteChanged; TryExecute(); } public State Cancel() { this.executeMe.CanExecuteChanged -= ExecuteMe_CanExecuteChanged; State oldState = this.Status; this.Status = State.Canceled; return oldState; } public ChainedCommand ExecuteNext(ICommand next, object arg) private void DoNextCommand() { this.nextChainedCommand = new ChainedCommand(next, arg, false); TryExecuteNext(); return this.nextChainedCommand; if(this.commandQueue == null) { throw new InvalidOperationException("Attempting to execute commands without a command queue"); } private void ExecuteMe_CanExecuteChanged(object sender, EventArgs e) if(this.commandQueue.Count == 0) { TryExecute(); throw new InvalidOperationException("Attempting to execute command on an empty command queue"); } private void TryExecute() if(this.commandQueue.Peek().CanExecute(null)) { if (this.executeMe.CanExecute(arg)) ICommand nextCommand = this.commandQueue.Dequeue(); if (this.commandQueue.Count == 0) { this.executeMe.Execute(arg); this.executeMe.CanExecuteChanged -= ExecuteMe_CanExecuteChanged; this.Status = State.Completed; TryExecuteNext(); // Executing the next command will probably trigger property changed // events and since this is the last command we want to disconnect from // those events this.dpvm.PropertyChanged -= Dpvm_PropertyChanged; this.executionStarted = false; } nextCommand.Execute(null); } } private void TryExecuteNext() public bool CanExecute(object parameter) { if(this.Status == State.Completed && this.nextChainedCommand != null) if(this.executionStarted) { this.nextChainedCommand.Start(); return false; } if(this.commandQueue.Count > 0) { return this.commandQueue.Peek().CanExecute(null); } return false; } public static ChainedCommand ExecuteWhenReady(this ICommand executeMe, object arg) public void Execute(object parameter) { return new ChainedCommand(executeMe, arg); if(this.executionStarted) { throw new InvalidOperationException("Execution already started"); } dpvm.PropertyChanged += Dpvm_PropertyChanged; DoNextCommand(); } } Loading @@ -124,14 +129,21 @@ namespace DeviceLab this.ConnectedDevices.CollectionChanged += OnConnectedDevicesChanged; } private void OnSignInAttemptCompleted(DeviceSignInViewModel sender, DeviceSignInEventArgs args) { if(args.StatusCode == System.Net.HttpStatusCode.OK) DevicePortal portal = args.Portal; // See if the device is already in the list foreach (DevicePortalViewModel dpvm in this.ConnectedDevices) { this.ConnectedDevices.Add(new DevicePortalViewModel(args.Portal, Diagnostics)); if(dpvm.Address == portal.Address) { this.Diagnostics.OutputDiagnosticString("[!!] The device with address {0} is already in the list\n", portal.Address); return; } } this.ConnectedDevices.Add(new DevicePortalViewModel(args.Portal, Diagnostics)); } #endregion // Constructors //------------------------------------------------------------------- Loading Loading @@ -325,39 +337,44 @@ namespace DeviceLab #endregion // RebootSelectedDevicesCommand #region RemoveDeviceCommand private DelegateCommand<DevicePortalViewModel> removeDeviceCommand; // NOTE: Ideally, the template parameter would be DevicePortalViewMOdel, rather than object. // Unfortunately, this would sporadically throw an invalid cast exception from within the // DelegateCommand<> constructor in the PRISM library. Apparently, the object passed to the // constructor cannot always be cast to something that would rightfully belong to the // collection. private DelegateCommand<object> removeDeviceCommand; public ICommand RemoveDeviceCommand { get { if(this.removeDeviceCommand == null) { this.removeDeviceCommand = new DelegateCommand<DevicePortalViewModel>(ExecuteRemoveDeviceCommand, CanExecuteRemoveDeviceCommand); this.removeDeviceCommand = new DelegateCommand<object>(ExecuteRemoveDeviceCommand, CanExecuteRemoveDeviceCommand); this.removeDeviceCommand.ObservesProperty(() => SomeDevicesReady); } return this.removeDeviceCommand; } } private bool CanExecuteRemoveDeviceCommand(DevicePortalViewModel arg) private bool CanExecuteRemoveDeviceCommand(object arg) { if (arg != null) DevicePortalViewModel dpvm = arg as DevicePortalViewModel; if (dpvm != null) { return arg.Ready; return dpvm.Ready; } return false; } private void ExecuteRemoveDeviceCommand(DevicePortalViewModel obj) private void ExecuteRemoveDeviceCommand(object obj) { if (obj != null) DevicePortalViewModel dpvm = obj as DevicePortalViewModel; if (dpvm != null) { OnDeviceRemoved(obj); this.ConnectedDevices.Remove(obj); this.ConnectedDevices.Remove(dpvm); } } #endregion // RemoveDeviceCommand #endregion // Commands private void OnConnectedDevicesChanged(object sender, NotifyCollectionChangedEventArgs e) Loading @@ -378,7 +395,7 @@ namespace DeviceLab { foreach(DevicePortalViewModel dpvm in e.OldItems) { //OnDeviceRemoved(dpvm); OnDeviceRemoved(dpvm); } } } Loading @@ -387,7 +404,17 @@ namespace DeviceLab private void OnDeviceAdded(DevicePortalViewModel dpvm) { dpvm.PropertyChanged += DevicePropertyChanged; dpvm.RefreshDeviceNameCommand.ExecuteWhenReady(this).ExecuteNext(dpvm.StartListeningForSystemPerfCommand, this); // TODO: //CompositeCommand command = new CompositeCommand(); //command.RegisterCommand(dpvm.RefreshDeviceNameCommand); //command.RegisterCommand(dpvm.StartListeningForSystemPerfCommand); //command.Execute(null); NewDeviceHelper ndh = new NewDeviceHelper(dpvm); ndh.Enqueue(dpvm.ReestablishConnectionCommand); ndh.Enqueue(dpvm.RefreshDeviceNameCommand); ndh.Enqueue(dpvm.StartListeningForSystemPerfCommand); ndh.Execute(null); } private void OnDeviceRemoved(DevicePortalViewModel dpvm) Loading