我正在做UWP应用程序。我不知道UWP或XAML,所以我正在学习。
我有我的班级_Viewmodel.cs:
namespace IHM_UWP
{
public class _ViewModel : INotifyPropertyChanged
{
private GestionBras bras= new GestionBras(); //On peut ajouter cet instance dans la classe App.Xaml.cs?
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged([CallerMemberName] string str = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(str));
}
}
/// <summary>
/// Commande pour lancer la connexion au bras
/// </summary>
private ICommand connect;
public ICommand Connect
{
get
{
if (this.connect == null)
this.connect = new RelayCommand(() => this.Bras.ConnectAsync());
return this.connect;
}
}
public GestionBras Bras { get => bras; set => bras = value; }
}
}
MainPage.xaml:
<Page
x:Class="IHM_UWP.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:IHM_UWP"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Page.DataContext>
<local:_ViewModel/>
</Page.DataContext>
<Grid>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<WebView x:Name="web" HorizontalAlignment="Left" VerticalAlignment="Bottom" Grid.Column="1" Grid.Row="1" Source="http://192.168.3.1:8080/stream" Width="600" Height="700" DefaultBackgroundColor="Turquoise" Margin="464,0,0,0"/>
<WebView x:Name="web2" HorizontalAlignment="Left" VerticalAlignment="Top" Grid.Row="1" Source="http://192.168.3.1:8090/stream" Width="600" Height="700" DefaultBackgroundColor="Violet" Grid.ColumnSpan="2" Margin="78,69,0,0"/>
<Button x:Name="connect" Content="{Binding Bras.Com.CanConnect, Mode=TwoWay}" Command="{Binding Connect}" IsEnabled= "true" HorizontalAlignment="Center" Margin="0,0,0,0" VerticalAlignment="Center" Width="auto" Height="auto" Grid.Column="1" Grid.Row="0" />
<Button x:Name="refresh" Content="Réactualisation Video" Click="ReactualisationVideo_Click" IsEnabled= "true" Margin="326,10,0,0" VerticalAlignment="Top" Width="auto" Height="auto" Grid.Column="1" Grid.Row="1" HorizontalAlignment="Left" RenderTransformOrigin="1.269,1.406" />
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Slider x:Name="_0" Grid.Row="0" Grid.Column="0" Margin="15" Orientation="Vertical" Minimum="500" Height="124" Maximum="2500" Value= "{Binding Bras.Moteur[0].Valeur_angle, Mode=TwoWay}" TickFrequency="10"/>
<Slider x:Name="_1" Grid.Row="0" Grid.Column="1" Margin="15" Orientation="Vertical" Minimum="500" Height="124" Maximum="2500" Value= "{Binding Bras.Moteur[1].Valeur_angle, Mode=TwoWay}" TickFrequency="10"/>
<Slider x:Name="_2" Grid.Row="0" Grid.Column="2" Margin="15" Orientation="Vertical" Minimum="500" Height="124" Maximum="2500" Value= "{Binding Bras.Moteur[2].Valeur_angle, Mode=TwoWay}" TickFrequency="10"/>
<Slider x:Name="_3" Grid.Row="0" Grid.Column="3" Margin="15" Orientation="Vertical" Minimum="500" Height="124" Maximum="2500" Value= "{Binding Bras.Moteur[3].Valeur_angle, Mode=TwoWay}" TickFrequency="10"/>
<Slider x:Name="_4" Grid.Row="0" Grid.Column="4" Margin="15" Orientation="Vertical" Minimum="500" Height="124" Maximum="2500" Value= "{Binding Bras.Moteur[4].Valeur_angle, Mode=TwoWay}" TickFrequency="10"/>
<Slider x:Name="_5" Grid.Row="0" Grid.Column="5" Margin="15" Orientation="Vertical" Minimum="500" Height="124" Maximum="2500" Value= "{Binding Bras.Moteur[5].Valeur_angle, Mode=TwoWay}" TickFrequency="10"/>
<TextBlock Text="{Binding ElementName=_0, Path=Value}" Grid.Row="3" Grid.Column="0"/>
<TextBlock Text="{Binding ElementName=_1, Path=Value}" Grid.Row="3" Grid.Column="1"/>
<TextBlock Text="{Binding ElementName=_2, Path=Value}" Grid.Row="3" Grid.Column="2"/>
<TextBlock Text="{Binding ElementName=_3, Path=Value}" Grid.Row="3" Grid.Column="3"/>
<TextBlock Text="{Binding ElementName=_4, Path=Value}" Grid.Row="3" Grid.Column="4"/>
<TextBlock Text="{Binding ElementName=_5, Path=Value}" Grid.Row="3" Grid.Column="5"/>
</Grid>
</Grid>
</Grid>
</Page>
GestionBras.cs:
namespace IHM_bras_robotisé
{
/// <summary>
/// Gère les moteurs du robot et envoie des informations via l'objet communication au bras robotisé
/// </summary>
public class GestionBras
{
private Moteur[] moteur = new Moteur[6];
/// <summary>
/// Gère les 6 moteurs
/// </summary>
public Moteur[] Moteur { get => moteur; set => moteur = value; }
private Communication com = new Communication();
/// <summary>
/// Gère la communication avec le bras robotisé
/// </summary>
public Communication Com { get => com; set => com = value; }
private int[] servoPin = { 1, 2, 23, 4, 22, 21 }; //numéros des moteurs sur la carte de contrôle
public int initialPosition = 1500;
/// <summary>
/// Lance la connection de l'objet communication au serveur
/// </summary>
public async void ConnectAsync()
{
await (Com.StartClient());
for (int i = 0; i < Moteur.Length; i++)
{
InitialPosition(Moteur[i].Num_pin);
}
}
/// <summary>
/// Initialise les moteurs, notamment leur numéro de pin et leur évènement
/// </summary>
public GestionBras()
{
for (int i = 0; i < servoPin.Length; i++)
{
Moteur[i] = new Moteur(servoPin[i]);
Moteur[i].OnAngleChanged += new Moteur.MotorEventHandler(OnMotChanged);
}
// s'inscrire à l'événement declanché pour la connexion
Com.OnCommunication += new Communication.ConnectionEventHandler(OnCom);
}
/// <summary>
/// Désactiver le bouton lors d'une connexion reussie
/// </summary>
/// <param name="sender">la connexion qui a déclenché l'évènement</param>
/// <param name="e"></param>
private void OnCom(Communication sender, EventArgs e)
{
if (Com != null)
{
Com.DisableButton();
}
}
/// <summary>
/// Commande l'envoi d'informations lors du changement d'angle d'un moteur sur le slider IHM
/// </summary>
/// <param name="sender">le moteur qui a déclenché l'évènement</param>
/// <param name="e"></param>
private void OnMotChanged(Moteur sender, EventArgs e)
{
if (Com != null )
{
Com.EnvoiAsync(sender.Num_pin, sender.Valeur_angle);
}
}
/// <summary>
/// Mettre un moteur à sa position initial (90°) lors du lancement de la connexion
/// ou lors de la fermeture de l'IHM
/// </summary>
public void InitialPosition(int numPin)
{
Com.EnvoiAsync(numPin, initialPosition);
}
}
}
Communication.cs:
namespace IHM_bras_robotisé
{
/// <summary>
/// Gère la connection et l'envoi de données vers le bras robotisé
/// Cette classe implemente INotifyPropertyChanged pour traiter les
/// changemens dûs à l'utilisation des liaisons (bindings)
/// quand les elements sont mis à jour de manière dynamique
/// </summary>
public class Communication : INotifyPropertyChanged
{
// Déclarer l'événement
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Le délegué pour stocker les références sur les méthodes
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public delegate void ConnectionEventHandler(Communication sender, EventArgs e = null);
/// <summary>
/// L'événement déclenché lors de la connexion au robot
/// </summary>
public event ConnectionEventHandler OnCommunication;
// Variable indicating if connection has been etablished or not
public bool IsConnectionOk = false;
public string connectionMsg = "Connexion au Bras";
public string disconnectMsg = "Déconnexion";
StreamSocket streamSocket;
byte[] bytes = new byte[1024];
Trame trame = new Trame();
Stream outputStream;
StreamWriter streamWriter;
/// <summary>
/// Constructeur pour la classe communication qui permet d'ajouter le message de connexion au bouton
///
/// </summary>
public Communication()
{
this.canConnect = connectionMsg;
}
/// <summary>
/// Démarre la connexion avec le serveur et initialise le streamSocket d'écriture
/// </summary>
public async Task StartClient()
{
try
{
streamSocket = new StreamSocket();
// The server hostname that we will be establishing a connection to. In this example, the server and client are in the same process.
var hostName = new Windows.Networking.HostName("192.168.3.1");
await streamSocket.ConnectAsync(hostName, "51717");
outputStream = streamSocket.OutputStream.AsStreamForWrite();
IsConnectionOk = true;
}
catch (Exception ex)
{
Windows.Networking.Sockets.SocketErrorStatus webErrorStatus = Windows.Networking.Sockets.SocketError.GetStatus(ex.GetBaseException().HResult);
}
if(IsConnectionOk == true)
{
// Lancer l'evenement pour changer le nom du bouton
OnCommunication(this);
}
//pour faire une méthode que retourne une variable alors: Task<bool> et à la fin return bool
}
/// <summary>
/// Réalise l'envoi d'une trame pour actualiser la position d'un des moteurs du bras robotisé
/// </summary>
/// <param name="num">numéro du moteur sur la carte de controle</param>
/// <param name="angle">angle à atteindre pour le moteur</param>
public async void EnvoiAsync(int num, int angle)
{
// si un autre service est en train de communiquer alors on envoi pas la trame
if (outputStream != null)
{
streamWriter = new StreamWriter(outputStream);
string t = trame.motMove(num, angle);
await streamWriter.WriteLineAsync(t);
await streamWriter.FlushAsync();
}
}
private string canConnect;
public string CanConnect
{
get
{
return canConnect;
}
set
{
if(canConnect != value)
{
canConnect = value;
OnPropertyChanged("CanConnect");
}
}
}
//"{Binding Bras.Com.CanConnect , Mode=OneWay}"
public void DisableButton()
{
this.CanConnect = disconnectMsg;
}
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
}
CommandManager.cs:
namespace IHM_bras_robotisé
{
public static class CommandManager
{
private static List<Action> _raiseCanExecuteChangedActions = new List<Action>();
public static void AddRaiseCanExecuteChangedAction(ref Action raiseCanExecuteChangedAction)
{
_raiseCanExecuteChangedActions.Add(raiseCanExecuteChangedAction);
}
public static void RemoveRaiseCanExecuteChangedAction(Action raiseCanExecuteChangedAction)
{
_raiseCanExecuteChangedActions.Remove(raiseCanExecuteChangedAction);
}
public static void AssignOnPropertyChanged(ref PropertyChangedEventHandler propertyEventHandler)
{
propertyEventHandler += OnPropertyChanged;
}
private static void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
{
// this if clause is to prevent an infinity loop
if (e.PropertyName != "CanExecute")
{
RefreshCommandStates();
}
}
public static void RefreshCommandStates()
{
for (var i = 0; i < _raiseCanExecuteChangedActions.Count; i++)
{
var raiseCanExecuteChangedAction = _raiseCanExecuteChangedActions[i];
if (raiseCanExecuteChangedAction != null)
{
raiseCanExecuteChangedAction.Invoke();
}
}
}
}
}
RelayCommand.cs:
namespace IHM_bras_robotisé
{
/// <summary>
/// A command whose sole purpose is to relay its functionality to other
/// objects by invoking delegates. The default return value for the CanExecute
/// method is 'true'.
/// </summary>
public class RelayCommand : ICommand
{
private readonly Action execute;
private readonly Func<bool> canExecute;
public RelayCommand(Action execute)
: this(execute, null)
{ }
public RelayCommand(Action execute, Func<bool> canExecute)
{
if (execute == null)
throw new ArgumentNullException("execute is null.");
this.execute = execute;
this.canExecute = canExecute;
this.RaiseCanExecuteChangedAction = RaiseCanExecuteChanged;
CommandManager.AddRaiseCanExecuteChangedAction(ref RaiseCanExecuteChangedAction);
}
~RelayCommand()
{
RemoveCommand();
}
public void RemoveCommand()
{
CommandManager.RemoveRaiseCanExecuteChangedAction(RaiseCanExecuteChangedAction);
}
bool ICommand.CanExecute(object parameter)
{
return CanExecute;
}
public void Execute(object parameter)
{
execute();
CommandManager.RefreshCommandStates();
}
public bool CanExecute
{
get { return canExecute == null || canExecute(); }
}
public void RaiseCanExecuteChanged()
{
var handler = CanExecuteChanged;
if (handler != null)
{
handler(this, new EventArgs());
}
}
private readonly Action RaiseCanExecuteChangedAction;
public event EventHandler CanExecuteChanged;
}
}
app.xaml.cs:
namespace IHM_UWP
{
/// <summary>
/// Fournit un comportement spécifique à l'application afin de compléter la classe Application par défaut.
/// </summary>
sealed partial class App : Application
{
private IHM_UWP._ViewModel view = new IHM_UWP._ViewModel();
/// <summary>
/// Initialise l'objet d'application de singleton. Il s'agit de la première ligne du code créé
/// à être exécutée. Elle correspond donc à l'équivalent logique de main() ou WinMain().
/// </summary>
public App()
{
this.InitializeComponent();
this.Suspending += OnSuspending;
}
/// <summary>
/// Invoqué lorsque l'application est lancée normalement par l'utilisateur final. D'autres points d'entrée
/// seront utilisés par exemple au moment du lancement de l'application pour l'ouverture d'un fichier spécifique.
/// </summary>
/// <param name="e">Détails concernant la requête et le processus de lancement.</param>
protected override void OnLaunched(LaunchActivatedEventArgs e)
{
Frame rootFrame = Window.Current.Content as Frame;
// Ne répétez pas l'initialisation de l'application lorsque la fenêtre comporte déjà du contenu,
// assurez-vous juste que la fenêtre est active
if (rootFrame == null)
{
// Créez un Frame utilisable comme contexte de navigation et naviguez jusqu'à la première page
rootFrame = new Frame();
rootFrame.NavigationFailed += OnNavigationFailed;
if (e.PreviousExecutionState == ApplicationExecutionState.Terminated)
{
//TODO: chargez l'état de l'application précédemment suspendue
}
// Placez le frame dans la fenêtre active
Window.Current.Content = rootFrame;
}
if (e.PrelaunchActivated == false)
{
if (rootFrame.Content == null)
{
// Quand la pile de navigation n'est pas restaurée, accédez à la première page,
// puis configurez la nouvelle page en transmettant les informations requises en tant que
// paramètre
rootFrame.Navigate(typeof(MainPage), e.Arguments);
}
// Vérifiez que la fenêtre actuelle est active
Window.Current.Activate();
}
}
/// <summary>
/// Appelé lorsque la navigation vers une page donnée échoue
/// </summary>
/// <param name="sender">Frame à l'origine de l'échec de navigation.</param>
/// <param name="e">Détails relatifs à l'échec de navigation</param>
void OnNavigationFailed(object sender, NavigationFailedEventArgs e)
{
throw new Exception("Failed to load Page " + e.SourcePageType.FullName);
}
/// <summary>
/// Appelé lorsque l'exécution de l'application est suspendue. L'état de l'application est enregistré
/// sans savoir si l'application pourra se fermer ou reprendre sans endommager
/// le contenu de la mémoire.
/// </summary>
/// <param name="sender">Source de la requête de suspension.</param>
/// <param name="e">Détails de la requête de suspension.</param>
private void OnSuspending(object sender, SuspendingEventArgs e)
{
// Remettre tous les moteurs à leur position initial
for (int i = 0; i < view.Bras.Moteur.Length; i++)
{
view.Bras.InitialPosition(view.Bras.Moteur[i].Num_pin);
}
var deferral = e.SuspendingOperation.GetDeferral();
//TODO: enregistrez l'état de l'application et arrêtez toute activité en arrière-plan
deferral.Complete();
}
//public GestionBras Bras { get => bras; set => bras = value; }
}
}
当我单击“连接”按钮时,我启动了一种连接到树莓板的方法,如果连接正常,我想将按钮上的内容更改为“断开手臂”,则在按钮上使用绑定数据:Content =“ {Binding Bras.Com.CanConnect,Mode = TwoWay}”,当我启动该应用程序时,我看到变量CanConnect在我的代码各处正确更改,我的问题是我的应用程序窗口未更改此值:/有人可以向我解释为什么这行不通:)
在此先感谢您的回答,对我的英语水平也很抱歉^^
答案 0 :(得分:0)
您的DisableButton()
方法应设置属性 C anConnect而不是字段 c anConnect来调用OnPropertyChanged
方法,并设置{ {1}}引发的事件:
PropertyChanged