none
Creare ed eliminare la stessa interfaccia grafica WPF con codice C#

    Domanda

  • Buonasera a tutti,

    cerco di spiegare meglio quello che nel titolo ha fatto fatica a sintetizzare. In una finestra XAML inserisco uno StackPanel:

    <StackPanel Name="nomeStackPanel"/>

    All'interno di questo StackPanel vorrei inserire un CheckBox. Quando questo viene cliccato (Checked), vorrei che al di sotto venissero aggiunti altri elementi (Label, TextBox, TextBlock, un altro StackPanel, ...). Nel momento in cui, però, l'utente clicca nuovamente sul CheckBox (Unchecked), vorrei che tutto quanto sia stato aggiunto venga tolto e, al suo posto, vengano aggiunti altri elementi. Se l'utente clicca ancora il CheckBox (Checked), deve ripresentarsi il layout grafico iniziale.

    Insomma, il layout grafico all'interno dello StackPanel deve alternarsi tra due gruppi diversi di elementi a seconda che il CheckBox sia Checked o Unchecked.

    Come posso fare questo?

    Grazie mille e buona serata,

    Marco.


    • Modificato Mrc87 giovedì 12 dicembre 2013 19:56
    • Spostato Marco MinervaMVP venerdì 13 dicembre 2013 14:29 Messaggio spostato perché la domanda riguarda WPF
    giovedì 12 dicembre 2013 19:56

Risposte

  • Buonasera a tutti,

    cerco di spiegare meglio quello che nel titolo ha fatto fatica a sintetizzare. In una finestra XAML inserisco uno StackPanel:

    <StackPanel Name="nomeStackPanel"/>

    All'interno di questo StackPanel vorrei inserire un CheckBox. Quando questo viene cliccato (Checked), vorrei che al di sotto venissero aggiunti altri elementi (Label, TextBox, TextBlock, un altro StackPanel, ...). Nel momento in cui, però, l'utente clicca nuovamente sul CheckBox (Unchecked), vorrei che tutto quanto sia stato aggiunto venga tolto e, al suo posto, vengano aggiunti altri elementi. Se l'utente clicca ancora il CheckBox (Checked), deve ripresentarsi il layout grafico iniziale.

    Insomma, il layout grafico all'interno dello StackPanel deve alternarsi tra due gruppi diversi di elementi a seconda che il CheckBox sia Checked o Unchecked.

    Come posso fare questo?

    Grazie mille e buona serata,

    Marco.


    La checkbox "switcha" i due sotto gruppi:

    XAML:

        <StackPanel Name="sp" Margin="12, 12, 12, 12" >
            
            <CheckBox Name="selector" Content="choose" Click="CheckBox_Click" Margin="2, 2, 2, 2"/>
            
            <!-- sotto gruppo 1 -->
            <StackPanel Name="sp1" Margin="0, 6, 0, 0">
                <TextBox Text="Text 1"/>
                <TextBox Text="Text 2"/>
                <TextBox Text="Text 3"/>
            </StackPanel>
            
            <!-- sotto gruppo 2 -->
            <StackPanel Name="sp2" Margin="0, 6, 0, 0">
                <ListBox>
                    <ListBoxItem>Item 1</ListBoxItem>
                    <ListBoxItem>Item 2</ListBoxItem>
                    <ListBoxItem>Item 3</ListBoxItem>
                </ListBox> 
                
            </StackPanel>
    
        </StackPanel>            
    
    Code:
        public partial class Window1 : Window
        {
            public Window1()
            {
                InitializeComponent();
                sp.Children.Remove(sp2);  
            }
    
            private void CheckBox_Click(object sender, RoutedEventArgs e)
            {
                if (selector.IsChecked == true)
                {
                    sp.Children.Remove(sp1);
                    sp.Children.Add(sp2);                 
                }
                else
                {
                    sp.Children.Remove(sp2);
                    sp.Children.Add(sp1);
                }
    
            }
        }
    

    • Proposto come risposta ASimonassi venerdì 13 dicembre 2013 13:52
    • Contrassegnato come risposta Mrc87 venerdì 13 dicembre 2013 14:12
    venerdì 13 dicembre 2013 08:39
  • Ciao,

    ho provato a usare il codice proposto da BlueLed, ma con l'evento SelectionChanged di un ListBox:

    private void listBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
       {
          if (condizione == true)
          {
             sp.Children.Remove(sp1);
             sp.Children.Add(sp2);
          }
          else
          {
             sp.Children.Remove(sp2);
             sp.Children.Add(sp1);
          }
       }

    ma quando cambia la selezione del ListBox, mi viene dato quest'errore:

    Eccezione di tipo 'System.ArgumentException' in PresentationCore.dll non gestita nel codice utente

    Ulteriori informazioni: El Visual especificado ya es un objeto secundario de otro Visual o la raíz de un CompositionTarget.

    Ho verificato che non ci sono problemi nella verifica della condizione. Sapete dirmi cosa sbaglio? C'è bisogno di maggiori informazioni?

    Ciao e grazie,

    Marco.

    Se intendi una cosa del genere

        <StackPanel Name="sp" Margin="12, 12, 12, 12" >
    
            <ListBox Name="list" Margin="2, 2, 2, 2" SelectionChanged="list_SelectionChanged" >
                <ListBoxItem>Panel 1</ListBoxItem>
                <ListBoxItem>Panel 2</ListBoxItem>
            </ListBox> 
    
            <!-- sotto gruppo 1 -->
            <StackPanel Name="sp1" Margin="0, 6, 0, 0" >
                <TextBox Text="Text 1"/>
                <TextBox Text="Text 2"/>
                <TextBox Text="Text 3"/>
            </StackPanel>
    
            <!-- sotto gruppo 2 -->
            <StackPanel Name="sp2" Margin="0, 6, 0, 0" Visibility="Collapsed" >
                <ListBox>
                    <ListBoxItem>Item 1</ListBoxItem>
                    <ListBoxItem>Item 2</ListBoxItem>
                    <ListBoxItem>Item 3</ListBoxItem>
                </ListBox>
    
            </StackPanel>
    
        </StackPanel>
    

    allora

            private void list_SelectionChanged(object sender, SelectionChangedEventArgs e)
            {
                if (list.SelectedIndex == 1)
                {
                    sp1.Visibility = Visibility.Collapsed;
                    sp2.Visibility = Visibility.Visible;
                }
                else 
                {
                    sp2.Visibility = Visibility.Collapsed;
                    sp1.Visibility = Visibility.Visible;
                }
            }

    • Contrassegnato come risposta Mrc87 sabato 14 dicembre 2013 09:46
    venerdì 13 dicembre 2013 22:14

Tutte le risposte

  • Prova a guardare qui:

    Threestate

    Parla di una proprietà checkbox.threestate con uno stato checkstate.Indeterminate


    dogman77 your best friend

    giovedì 12 dicembre 2013 20:20
  • Buonasera a tutti,

    cerco di spiegare meglio quello che nel titolo ha fatto fatica a sintetizzare. In una finestra XAML inserisco uno StackPanel:

    <StackPanel Name="nomeStackPanel"/>

    All'interno di questo StackPanel vorrei inserire un CheckBox. Quando questo viene cliccato (Checked), vorrei che al di sotto venissero aggiunti altri elementi (Label, TextBox, TextBlock, un altro StackPanel, ...). Nel momento in cui, però, l'utente clicca nuovamente sul CheckBox (Unchecked), vorrei che tutto quanto sia stato aggiunto venga tolto e, al suo posto, vengano aggiunti altri elementi. Se l'utente clicca ancora il CheckBox (Checked), deve ripresentarsi il layout grafico iniziale.

    Insomma, il layout grafico all'interno dello StackPanel deve alternarsi tra due gruppi diversi di elementi a seconda che il CheckBox sia Checked o Unchecked.

    Come posso fare questo?

    Grazie mille e buona serata,

    Marco.


    La checkbox "switcha" i due sotto gruppi:

    XAML:

        <StackPanel Name="sp" Margin="12, 12, 12, 12" >
            
            <CheckBox Name="selector" Content="choose" Click="CheckBox_Click" Margin="2, 2, 2, 2"/>
            
            <!-- sotto gruppo 1 -->
            <StackPanel Name="sp1" Margin="0, 6, 0, 0">
                <TextBox Text="Text 1"/>
                <TextBox Text="Text 2"/>
                <TextBox Text="Text 3"/>
            </StackPanel>
            
            <!-- sotto gruppo 2 -->
            <StackPanel Name="sp2" Margin="0, 6, 0, 0">
                <ListBox>
                    <ListBoxItem>Item 1</ListBoxItem>
                    <ListBoxItem>Item 2</ListBoxItem>
                    <ListBoxItem>Item 3</ListBoxItem>
                </ListBox> 
                
            </StackPanel>
    
        </StackPanel>            
    
    Code:
        public partial class Window1 : Window
        {
            public Window1()
            {
                InitializeComponent();
                sp.Children.Remove(sp2);  
            }
    
            private void CheckBox_Click(object sender, RoutedEventArgs e)
            {
                if (selector.IsChecked == true)
                {
                    sp.Children.Remove(sp1);
                    sp.Children.Add(sp2);                 
                }
                else
                {
                    sp.Children.Remove(sp2);
                    sp.Children.Add(sp1);
                }
    
            }
        }
    

    • Proposto come risposta ASimonassi venerdì 13 dicembre 2013 13:52
    • Contrassegnato come risposta Mrc87 venerdì 13 dicembre 2013 14:12
    venerdì 13 dicembre 2013 08:39
  • Devi bindare la proprietà Visibility dello stackpanel figlio alla proprietà IsChecked della checkbox. ( o sarebbe meglio alla rispettiva proprietà del viewmodel)

    (sostituire MyApplication con tuo namespace)

    <Window x:Class="MyApplication.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:my="clr-namespace:MyApplication"
            Title="MainWindow" Height="350" Width="525">
        <Window.Resources>
            <my:BoolToVisibileConverter x:Key="BoolToVisibleConverter"/>
        </Window.Resources>
        <Grid>
            
            <StackPanel Name="stackPanel1" Margin="10">
                
                <CheckBox Name="cbControl">Visibile</CheckBox>
    
                <StackPanel Margin="8" Visibility="{Binding IsChecked,ElementName=cbControl,Converter={StaticResource BoolToVisibleConverter}}">
                        <TextBox Margin="0,4,0,4">Sono la textbox</TextBox>
                        <TextBox  Margin="0,4,0,4">Sono un altra textbox</TextBox>
                    </StackPanel>
                
                <Border Margin="0, 4, 0, 0"  CornerRadius="6" BorderBrush="Gray" Background="LightGray" BorderThickness="2" >
                    <TextBlock Margin="6">Io sono dopo il pannello nascondibile</TextBlock>
                </Border>
            </StackPanel>
            
        </Grid>
    </Window>
    

    Ora però siccome la proprietà Visibility non accetta valori boolean  hai bisogno di un Converter che converta i valori bool forniti da IsChecked ai valori Visibility necessari per la proprietà IsVisible.

    BoolToVisibleConverter.cs
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Windows.Data;
    using System.Windows;
    using System.Globalization;
    
    namespace MyApplication
    {
        [ValueConversion(typeof(bool), typeof(Visibility))]
        public sealed class BoolToVisibileConverter : IValueConverter
        {
            public object Convert(object value, Type targetType,
                object parameter, CultureInfo culture)
            {
                if (!(value is bool))
                    return null;
                return (bool)value ? Visibility.Visible : Visibility.Collapsed;
            }
    
            public object ConvertBack(object value, Type targetType,
                object parameter, CultureInfo culture)
            {
                if (Equals(value, Visibility.Visible))
                    return true;           
                return false;           
            }
        }
    }
    



    venerdì 13 dicembre 2013 08:43
  • Devi bindare la proprietà Visibility dello stackpanel figlio alla proprietà IsChecked della checkbox. ( o sarebbe meglio alla rispettiva proprietà del viewmodel)

    (sostituire MyApplication con tuo namespace)

    <Window x:Class="MyApplication.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:my="clr-namespace:MyApplication"
            Title="MainWindow" Height="350" Width="525">
        <Window.Resources>
            <my:BoolToVisibileConverter x:Key="BoolToVisibleConverter"/>
        </Window.Resources>
        <Grid>
            
            <StackPanel Name="stackPanel1" Margin="10">
                
                <CheckBox Name="cbControl">Visibile</CheckBox>
    
                <StackPanel Margin="8" Visibility="{Binding IsChecked,ElementName=cbControl,Converter={StaticResource BoolToVisibleConverter}}">
                        <TextBox Margin="0,4,0,4">Sono la textbox</TextBox>
                        <TextBox  Margin="0,4,0,4">Sono un altra textbox</TextBox>
                    </StackPanel>
                
                <Border Margin="0, 4, 0, 0"  CornerRadius="6" BorderBrush="Gray" Background="LightGray" BorderThickness="2" >
                    <TextBlock Margin="6">Io sono dopo il pannello nascondibile</TextBlock>
                </Border>
            </StackPanel>
            
        </Grid>
    </Window>

    Ora però siccome la proprietà Visibility non accetta valori boolean  hai bisogno di un Converter che converta i valori bool forniti da IsChecked ai valori Visibility necessari per la proprietà IsVisible.

    BoolToVisibleConverter.cs
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Windows.Data;
    using System.Windows;
    using System.Globalization;
    
    namespace MyApplication
    {
        [ValueConversion(typeof(bool), typeof(Visibility))]
        public sealed class BoolToVisibileConverter : IValueConverter
        {
            public object Convert(object value, Type targetType,
                object parameter, CultureInfo culture)
            {
                if (!(value is bool))
                    return null;
                return (bool)value ? Visibility.Visible : Visibility.Collapsed;
            }
    
            public object ConvertBack(object value, Type targetType,
                object parameter, CultureInfo culture)
            {
                if (Equals(value, Visibility.Visible))
                    return true;           
                return false;           
            }
        }
    }


    Ma tu guarda, quanto code behind si genera...

    public sealed class BoolToVisibileConverter : IValueConverter .....

    per eliminare il... code behind.

    venerdì 13 dicembre 2013 08:53

  • Ma tu guarda, quanto code behind si genera...

    public sealed class BoolToVisibileConverter : IValueConverter .....

    per eliminare il... code behind.

    Asd.. ma si fa una tantum sola poi si riusa per 4 milioni di volte.

    • Modificato ASimonassi venerdì 13 dicembre 2013 10:00
    venerdì 13 dicembre 2013 09:59

  • Asd.. ma si fa una tantum sola poi si riusa per 4 milioni di volte.

    Una tantum di qua, una tantum di là... se si riusa poi, si deve organizzare una library e portarsela dietro con tanto di gestione (perchè il converter non sarà l'unica eccezione, alla lunga). Problemi, quindi, inerenti a modifiche e rimozioni che ne potrebbero compromettere la backward compatibility. Un secondo progetto "alive and kicking" insomma.

    venerdì 13 dicembre 2013 10:18
  • Milioni di volte all'interno dello stesso progetto intendo.

    Altrimenti per replicare 3 volte l'effetto ottieni questo, con il converter il codebehind non cambia.
    private void CheckBox_Click(object sender, RoutedEventArgs e)
            {
                if (selector.IsChecked == true)
                {
                    sp.Children.Remove(sp1);
                    sp.Children.Add(sp2);                 
                }
                else
                {
                    sp.Children.Remove(sp2);
                    sp.Children.Add(sp1);
                }
    
            }
    
     private void CheckBox2_Click(object sender, RoutedEventArgs e)
            {
                if (selector2.IsChecked == true)
                {
                    sp.Children.Remove(sp3);
                    sp.Children.Add(sp4);                 
                }
                else
                {
                    sp.Children.Remove(sp3);
                    sp.Children.Add(sp4);
                }
    
            }
     private void CheckBox3_Click(object sender, RoutedEventArgs e)
            {
                if (selector3.IsChecked == true)
                {
                    sp.Children.Remove(sp5);
                    sp.Children.Add(sp6);                 
                }
                else
                {
                    sp.Children.Remove(sp5);
                    sp.Children.Add(sp6);
                }
    
            }



    venerdì 13 dicembre 2013 10:33
  • Milioni di volte all'interno dello stesso progetto intendo.

    Altrimenti per replicare 3 volte l'effetto ottieni questo, con il converter il codebehind non cambia.

    Con il converter non cambia?

    Anche nello XAML, con il converter,  replicheresti N volte (da cui la necessità di affrontare il caso in tutt'altro modo).

        <StackPanel Name="sp" Margin="12, 12, 12, 12" >
            <StackPanel Name="panel1">
                <CheckBox Name="selector1" Tag="0" Content="choose 1" Click="CheckBox_Click" Margin="2, 2, 2, 2"/>
                
                <!-- sotto gruppo 1 -->
                <StackPanel Name="sp1" Margin="0, 6, 0, 0">
                    <TextBox Text="Text 1"/>
                    <TextBox Text="Text 2"/>
                    <TextBox Text="Text 3"/>
                </StackPanel>
                
                <!-- sotto gruppo 2 -->
                <StackPanel Name="sp2" Margin="0, 6, 0, 0" Visibility="Collapsed">
                    <ListBox>
                        <ListBoxItem>Item 1</ListBoxItem>
                        <ListBoxItem>Item 2</ListBoxItem>
                        <ListBoxItem>Item 3</ListBoxItem>
                    </ListBox> 
                    
                </StackPanel>
            </StackPanel>
    
    
    
            <StackPanel Name="panel2">
                <CheckBox Name="selector2" Tag="1" Content="choose 2" Click="CheckBox_Click" Margin="2, 2, 2, 2"/>
    
                <!-- sotto gruppo 1 -->
                <StackPanel Name="sp3" Margin="0, 6, 0, 0">
                    <TextBox Text="Text 4"/>
                    <TextBox Text="Text 5"/>
                    <TextBox Text="Text 6"/>
                </StackPanel>
    
                <!-- sotto gruppo 2 -->
                <StackPanel Name="sp4" Margin="0, 6, 0, 0" Visibility="Collapsed">
                    <ListBox>
                        <ListBoxItem>Item 4</ListBoxItem>
                        <ListBoxItem>Item 5</ListBoxItem>
                        <ListBoxItem>Item 6</ListBoxItem>
                    </ListBox>
    
                </StackPanel>
            </StackPanel>
    
        </StackPanel>            

    Ma non cambia neanche con il code-behind (a parte lo xaml)

        public partial class Window1 : Window
        {
            UIElement[] sp_a;
            UIElement[] sp_b;
    
            public Window1()
            {
                InitializeComponent();
    
                sp_a = new UIElement[2];
                sp_b = new UIElement[2];
                sp_a[0] = sp1; sp_a[1] = sp3;
                sp_b[0] = sp2; sp_b[1] = sp4;
            }
    
            private void CheckBox_Click(object sender, RoutedEventArgs e)
            {
                CheckBox selector = (CheckBox)sender;
                int index = Int32.Parse(selector.Tag.ToString());
    
                if (selector.IsChecked == true)
                {
                    sp_a[index].Visibility = Visibility.Collapsed;
                    sp_b[index].Visibility = Visibility.Visible;                  
                }
                else
                {
                    sp_b[index].Visibility = Visibility.Collapsed;
                    sp_a[index].Visibility = Visibility.Visible;  
                }
    
            }
        }



    • Modificato BlueLed venerdì 13 dicembre 2013 11:08
    venerdì 13 dicembre 2013 11:06
  • Bah io dico che è meglio usare il converter, hai gia iniziato a complicare il code behind per gestire il caso particolare e cosa peggiore hai riscritto due volte il codice non avendo potuto riusare il codice da te scritto in precedenza, io ho lavorato una volta sola. Inoltre posso usare lo stesso codice in diverse Window, tu devi fare copia e incolla.

    Lo XAML varia si ma varia anche nel tuo caso, senza contare la scarsa leggibilità di array tag eccetera.

    Comunque rispetto per la tua soluzione, è sicuramente valida e da quanto vedo sei un bravo programmatore.

    Ciao.


    • Modificato ASimonassi venerdì 13 dicembre 2013 11:14
    venerdì 13 dicembre 2013 11:13
  • 1)

    Bah io dico che è meglio usare il converter, hai gia iniziato a complicare il code behind per gestire il caso particolare e cosa peggiore hai riscritto due volte il codice non avendo potuto riusare il codice da te scritto in precedenza, io ho lavorato una volta sola. Inoltre posso usare lo stesso codice in diverse Window, tu devi fare copia e incolla.

    2)

    Lo XAML varia si ma varia anche nel tuo caso,


    2) L'ho detto.

    1) Il codice non è iniziato a complicarsi, è iniziato e finito lì, perchè se aggiungi altri panel allo xaml, non ci sono altre modifiche da farsi (a parte un paio di assegnazioni). E tale codice potrà riusarsi com'è.

    Ma ripeto, per quello che hai prospettato, più che riuso di codice (Cut & Paste), andrebbe usata un'altra strategia, del tipo creare un elemento custom.  Anche quello non dovrai copiarlo ed incollarlo più volte ed avrai lavorato una sola volta.



    • Modificato BlueLed venerdì 13 dicembre 2013 11:30
    venerdì 13 dicembre 2013 11:26
  • Buongiorno a tutti,

    vedo che da quando ho acceduto l'ultima volta si sono aggiunti molti commenti. Grazie mille a tutti!

    Stavo per rispondere a dogman77, in quanto la soluzione da lui proposta non mi pare funzioni. Appena ho provato le altre soluzioni vi faccio sapere.

    Grazie ancora e buona giornata intanto!

    Marco.

    venerdì 13 dicembre 2013 11:34
  • D'accordo, comunque come vedi col converter lavori una volta sola. Non è che in Microsoft sono impazziti quando hanno progettato WPF, IValueConverter ha un suo senso e facilita il binding ed il riuso del codice ed evita l'uso di codebehind.

    Il converter inoltre ha senso soprattutto nel caso di uso di ViewModel, che è il modo migliore di lavorare in WPF (separation of concern) per adattare oggetti di livello applicazione alla UI senza necessità di rivedere lo strato di codice applicativo.

    Comunque l'importante alla fine è che i programmi funzionino e siano facili da manutenere e modificare, usa quel che credi e che ti convince meglio e non curarti di quel che dicono gli altri o che ti posso dire io.

    [edit]Riallacciandomi a questa ultima frase, per esempio io odio gli ORM, mentre amo il paradigma relazionale, pertanto effettuo l'accesso ai dati con mie librerie personali piuttosto che usare ciò che il 95% del mondo adora .. asd [/edit]

    Ciao e buona programmazione.


    • Modificato ASimonassi venerdì 13 dicembre 2013 11:48
    venerdì 13 dicembre 2013 11:41
  • D'accordo, comunque come vedi col converter lavori una volta sola. Non è che in Microsoft sono impazziti quando hanno progettato WPF, IValueConverter ha un suo senso e facilita il binding ed il riuso del codice ed evita l'uso di codebehind.

    Il converter inoltre ha senso soprattutto nel caso di uso di ViewModel, che è il modo migliore di lavorare in WPF (separation of concern) per adattare oggetti di livello applicazione alla UI senza necessità di rivedere lo strato di codice applicativo.

    Comunque l'importante alla fine è che i programmi funzionino e siano facili da manutenere e modificare, usa quel che credi e che ti convince meglio e non curarti di quel che dicono gli altri o che ti posso dire io.

    [edit]Riallacciandomi a questa ultima frase, per esempio io odio gli ORM, mentre amo il paradigma relazionale, pertanto effettuo l'accesso ai dati con mie librerie personali piuttosto che usare ciò che il 95% del mondo adora .. asd [/edit]

    Ciao e buona programmazione.


    Non è che l'uso del converter è sbagliato.

    Ma qui, rispetto alla richiesta fatta, è stato presentato un sistema semplice, semplice, adatto allo scopo.

    Allora, probabilmente l'MVC presisteva anche Noè e chi ha programmato, con cognizione di causa, si sarà sempre attenuto alle sue caratteristiche, ma ora che è diventato tecnologicamente di moda, non è che tutto ciò che è codice è diventato improvvisamente diabolico...

    Nel caso di questo thread, il code-behind faceva egregiamente il suo dovere, senza la necessità di scomodare una over-ingegnerizzazione.

    Ed alla argomentazione che quel codice non sopravvive ad una generalizzazione (non ne aveva l'intenzione ) ho detto che se di generalizzazione si deve trattare, allora neanche il converter è così generico da affrontare un problema che prevederebbe la creazione di un elemento che faccia da container.

    Sempre che non esista già... perchè un Tab Control protrebbe fare le sue veci.

    venerdì 13 dicembre 2013 12:15
  • +1 Sono d'accordo, capisco la tua idea, anche io sono contro l'over-ingegnerizzazione che considero un anti-pattern, sono contro l'ottimizzazione continua ove non necessaria e contro gli eccessi nell'uso dei pattern software.

    Però implementare un IValueConverter ha un costo veramente piccolo, a partirà di costo preferisco implementare un IValueConverter che non del codebehind.

    Detto questo ho già scritto che la tua soluzione per il caso specifico va benissimo, anzi la consiglio all'utente Mrc87, e se l'avessi vista non mi sarei disturbato a dare la mia, nel seguito della discussione mi premeva solo sottolineare che non è vero che per risparmiare codebehind ho fatto più lavoro, al massimo ne ho fatto tanto quanto, utilizzando di più i pattern standard di WPF, in quanto vedo che molti utenti principianti lavorano in WPF come lavoravo io col turbo c++ di Borland quasi venti anni fa col Charles Petzold sotto braccio, ma come dici tu non è un problema usare codice old style (alla fine le cpu capiscono antiquato codice macchina).

    Con stima
    Andrea


    • Modificato ASimonassi venerdì 13 dicembre 2013 13:51
    venerdì 13 dicembre 2013 13:50
  • Ciao,

    ringrazio tutti del loro contributo alla risposta alla mia domanda. Alla fin delle finite ho naturalmente dovuto scegliere una delle due soluzioni prospettate; ho scelto quella di BlueLed, perché è quella che mi pare più semplice e perché è quella che al momento capisco meglio (preferisco usare strumenti di cui ho una migliore comprensione). Molto interessante anche la soluzione di usare un TabControl, a cui non avevo proprio pensato; credo che mi cimenterò al fine di impiegare questa soluzione, perché graficamente mi pare molto efficace ed efficente (cercherò di usare quanto ho appreso dai vostri messaggi per usare però un TabControl. Se mi andasse male, o opterò per l'uso del CheckBox, che già mi funziona, oppure mi ripresenterò con un'altra domanda). Tutto ciò non toglie che la soluzione di ASimonassi mi interessa molto, perché mi da diversi spunti sui quali costruire uno studio mirato, che certamente farò.

    Ho molto da imparare da chi, come voi, ne sa molto ma molto più di me; quindi grazie mille ancora a tutti e buon proseguimento in ogni cosa,

    Marco.




    • Modificato Mrc87 venerdì 13 dicembre 2013 14:14
    venerdì 13 dicembre 2013 14:08
  • Ed alla argomentazione che quel codice non sopravvive ad una generalizzazione (non ne aveva l'intenzione ) ho detto che se di generalizzazione si deve trattare, allora neanche il converter è così generico da affrontare un problema che prevederebbe la creazione di un elemento che faccia da container.

    Sempre che non esista già... perchè un Tab Control protrebbe fare le sue veci.

    Su questo non sono d'accordo, IValueConverter è solo un piccolo aspetto della soluzione che proponevo, che è quella di sfruttare il binding. IValueConverter è genericissimo, converte un qualsiasi valore boolean in Visibility, cosa ha di non generico ? Semplicemente IValueConverter fa il suo lavoro, non devi cambiare il tuo BooleanToVisibilityConverter se devi affrontare il problema di creare un elemento container al volo, semplicemente devi usare altri parametri per il binding, ad esempio si può usare un datatemplate, senza scrivere codebehind, ma il tuo converter sarà sempre buono e convertirà sempre da bool a visibility in tutte le circostanze necessarie, mentre il codebehind... va buttato via.

    Ciao.
    venerdì 13 dicembre 2013 14:36
  • Ciao,

    ho provato a usare il codice proposto da BlueLed, ma con l'evento SelectionChanged di un ListBox:

    private void listBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
       {
          if (condizione == true)
          {
             sp.Children.Remove(sp1);
             sp.Children.Add(sp2);
          }
          else
          {
             sp.Children.Remove(sp2);
             sp.Children.Add(sp1);
          }
       }

    ma quando cambia la selezione del ListBox, mi viene dato quest'errore:

    Eccezione di tipo 'System.ArgumentException' in PresentationCore.dll non gestita nel codice utente

    Ulteriori informazioni: El Visual especificado ya es un objeto secundario de otro Visual o la raíz de un CompositionTarget.

    Ho verificato che non ci sono problemi nella verifica della condizione. Sapete dirmi cosa sbaglio? C'è bisogno di maggiori informazioni?

    Ciao e grazie,

    Marco.

    venerdì 13 dicembre 2013 18:21
  • Ciao,

    interessante discussione... :)

    ci sarebbe una terza via, più semplice : sfruttare quello che Microsoft ci fornisce, ovvero un converter già implementato e nessun codice nel code behind...

    ecco come :

    <Canvas>
                <Canvas.Resources>
                    <BooleanToVisibilityConverter x:Key="converter"/>
                </Canvas.Resources>
                <CheckBox Name="selector" Content="choose" Margin="2, 2, 2, 2"/>
    
                <StackPanel Name="sp1" Margin="0, 20, 0, 0" >
                    <TextBox Text="Text 1"/>
                    <TextBox Text="Text 2"/>
                    <TextBox Text="Text 3"/>
                </StackPanel>
    
                <StackPanel Name="sp2" Margin="0, 20, 0, 0" Visibility="{Binding ElementName=selector, Path=IsChecked, Converter={StaticResource converter}}">
                    <ListBox>
                        <ListBoxItem>Item 1</ListBoxItem>
                        <ListBoxItem>Item 2</ListBoxItem>
                        <ListBoxItem>Item 3</ListBoxItem>
                    </ListBox>
                </StackPanel>
            </Canvas>

    questo codice non fa altro che mostrare lo stackpanel superiore quando si spunta il checbox, viceversa lo nasconde e si vede quello sottostante, proprio come diceva ASimonassi ma senza dover implementare un converter apposito, c'è quello di microsoft.

    Anche io preferisco utilizzare MVVM, ma c'è chi invece vuole stare su un approccio più diretto e meno generico. In questo caso BlueLed (che anche io ritengo un ottimo programmatore) mi pare proponesse una soluzione forse più semplice da comprendere per Mrc87. 

    @Mrc87

    prima o poi se vorrai fare buona programmazione con wpf (a mio parere), dovrai scontrarti con quello che sono i maggiori vantaggi di wpf, quali binding, converters ecc.

    EDIT :

    dimenticavo, si può fare anche senza converter visto che parliamo di wpf :

    <Canvas Name="sp" Margin="12,54,12,275" Grid.RowSpan="2" >
                <CheckBox x:Name="selector" Content="choose" Margin="2, 2, 2, 2"/>
                <StackPanel Name="sp1" Margin="0, 20, 0, 0" >
                    <StackPanel.Style>
                        <Style>
                            <Style.Triggers>
                                <DataTrigger Binding="{Binding ElementName=selector, Path=IsChecked}" Value="False">
                                    <Setter Property="StackPanel.Visibility" Value="Visible" />
                                </DataTrigger>
                            </Style.Triggers>
                        </Style>
                    </StackPanel.Style>                
                    <TextBox Text="Text 1"/>
                    <TextBox Text="Text 2"/>
                    <TextBox Text="Text 3"/>
                </StackPanel>
                <StackPanel Name="sp2" Margin="0, 20, 0, 0" >
                    <StackPanel.Style>
                        <Style>
                            <Style.Triggers>
                                <DataTrigger Binding="{Binding ElementName=selector, Path=IsChecked}" Value="False">
                                    <Setter Property="StackPanel.Visibility" Value="Collapsed" />
                                </DataTrigger>
                            </Style.Triggers>
                        </Style>
                    </StackPanel.Style>
                    <ListBox>
                        <ListBoxItem>Item 1</ListBoxItem>
                        <ListBoxItem>Item 2</ListBoxItem>
                        <ListBoxItem>Item 3</ListBoxItem>
                    </ListBox>
                </StackPanel>
            </Canvas>

    Basta aggiungere allo style un trigger in binding con il checkbox.


    • Modificato U 235Editor venerdì 13 dicembre 2013 20:04
    • Contrassegnato come risposta Mrc87 sabato 14 dicembre 2013 09:50
    • Contrassegno come risposta annullato Mrc87 sabato 14 dicembre 2013 09:51
    venerdì 13 dicembre 2013 19:47
    Postatore
  • Ciao,

    ho provato a usare il codice proposto da BlueLed, ma con l'evento SelectionChanged di un ListBox:

    private void listBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
       {
          if (condizione == true)
          {
             sp.Children.Remove(sp1);
             sp.Children.Add(sp2);
          }
          else
          {
             sp.Children.Remove(sp2);
             sp.Children.Add(sp1);
          }
       }

    ma quando cambia la selezione del ListBox, mi viene dato quest'errore:

    Eccezione di tipo 'System.ArgumentException' in PresentationCore.dll non gestita nel codice utente

    Ulteriori informazioni: El Visual especificado ya es un objeto secundario de otro Visual o la raíz de un CompositionTarget.

    Ho verificato che non ci sono problemi nella verifica della condizione. Sapete dirmi cosa sbaglio? C'è bisogno di maggiori informazioni?

    Ciao e grazie,

    Marco.

    Se intendi una cosa del genere

        <StackPanel Name="sp" Margin="12, 12, 12, 12" >
    
            <ListBox Name="list" Margin="2, 2, 2, 2" SelectionChanged="list_SelectionChanged" >
                <ListBoxItem>Panel 1</ListBoxItem>
                <ListBoxItem>Panel 2</ListBoxItem>
            </ListBox> 
    
            <!-- sotto gruppo 1 -->
            <StackPanel Name="sp1" Margin="0, 6, 0, 0" >
                <TextBox Text="Text 1"/>
                <TextBox Text="Text 2"/>
                <TextBox Text="Text 3"/>
            </StackPanel>
    
            <!-- sotto gruppo 2 -->
            <StackPanel Name="sp2" Margin="0, 6, 0, 0" Visibility="Collapsed" >
                <ListBox>
                    <ListBoxItem>Item 1</ListBoxItem>
                    <ListBoxItem>Item 2</ListBoxItem>
                    <ListBoxItem>Item 3</ListBoxItem>
                </ListBox>
    
            </StackPanel>
    
        </StackPanel>
    

    allora

            private void list_SelectionChanged(object sender, SelectionChangedEventArgs e)
            {
                if (list.SelectedIndex == 1)
                {
                    sp1.Visibility = Visibility.Collapsed;
                    sp2.Visibility = Visibility.Visible;
                }
                else 
                {
                    sp2.Visibility = Visibility.Collapsed;
                    sp1.Visibility = Visibility.Visible;
                }
            }

    • Contrassegnato come risposta Mrc87 sabato 14 dicembre 2013 09:46
    venerdì 13 dicembre 2013 22:14
  • Su questo non sono d'accordo, IValueConverter è solo un piccolo aspetto della soluzione che proponevo, che è quella di sfruttare il binding. IValueConverter è genericissimo, converte un qualsiasi valore boolean in Visibility, cosa ha di non generico ? Semplicemente IValueConverter fa il suo lavoro, non devi cambiare il tuo BooleanToVisibilityConverter se devi affrontare il problema di creare un elemento container al volo, semplicemente devi usare altri parametri per il binding, ad esempio si può usare un datatemplate, senza scrivere codebehind, ma il tuo converter sarà sempre buono e convertirà sempre da bool a visibility in tutte le circostanze necessarie, mentre il codebehind... va buttato via.

    Ciao.

    Non dicevo che il converter non è generico. Parlavo della richiesta generalizzata ad N pannelli.

    venerdì 13 dicembre 2013 22:20
  • Ciao a tutti,

    grazie mille! Con la soluzione di BlueLed ho risolto il problema relativo all'evento SelectionChanged. Comunque rianalizzerò meglio le vostre risposte, o meglio, le studierò. Ho molto da imparare, vedo!

    U 235, hai ragione, e la prima volta che mi si presenta l'occasione intendo appropriarmi delle conoscenze a cui hai fatto riferimento.

    Grazie ancora e buon weekend!

    Marco.

    sabato 14 dicembre 2013 09:49