我的嵌套绑定没有刷新我的主页

时间:2019-04-09 09:03:07

标签: c# xaml uwp

我正在做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在我的代码各处正确更改,我的问题是我的应用程序窗口未更改此值:/有人可以向我解释为什么这行不通:)

在此先感谢您的回答,对我的英语水平也很抱歉^^

1 个答案:

答案 0 :(得分:0)

您的DisableButton()方法应设置属性 C anConnect而不是字段 c anConnect来调用OnPropertyChanged方法,并设置{ {1}}引发的事件:

PropertyChanged