Entra

Visualizza la versione completa : Alcune domande sulle WPF


ciucciatiilcalzino
27-01-2017, 19:25
Ciao a tutti,
ho da poco iniziato a dare uno sguardo al ai WPF.
Venendo da dal web non sono in questo momento abbituato a creare applicazioni desktop, anche se oltre 15 anni fa ho usato visualbasic.
Quindi volevo chiedervi, anche se forse più avanti lo trovo nella guida, quando creo il primo progetto ottengo una finestra mainwindow.xaml, in cui carico le mie griglie con i miei elementi.
Ho visto che posso aggiungere altre finestre e ovviamnete chiuderne poi una e aprirne altre con lo show.
Ma non ho ancora capito e trovato a cosa servono le page

Inoltre seguendo questo tutorial su msdn
https://msdn.microsoft.com/it-it/library/mt270964.aspx
non ho capito perchè va a sostituire
<Window x:Class="ExpenseIt.MainWindow" con
<NavigationWindow x:Class="ExpenseIt.MainWindow"

cosa che per ora non ho trovato in altri esempi o guide.


Grazie in anticipo

Marsh
30-01-2017, 11:36
Window è la finestra di base, la quale fornisce le implementazioni classiche di un applicazione desktop,

la NavigationWindow invece ti offre già alcune possibilità in più, ossia la navigazione tra pagine (es: esplora risorse, browser), se utilizzi quest'ultima tipologia puoi fare uso della Page per determinare il contenuto e quindi spostarti tra pagina a pagina con metodi già implementati del NavigationService (deriva molto dalle tematiche Silverlight)

sintatticamente parlando, nell'esempio viene cambiata la classe da cui si eredita (sia dal lato XAML che da quello C#)

ciucciatiilcalzino
30-01-2017, 12:53
Window � la finestra di base, la quale fornisce le implementazioni classiche di un applicazione desktop,

la NavigationWindow invece ti offre gi� alcune possibilit� in pi�, ossia la navigazione tra pagine (es: esplora risorse, browser), se utilizzi quest'ultima tipologia puoi fare uso della Page per determinare il contenuto e quindi spostarti tra pagina a pagina con metodi gi� implementati del NavigationService (deriva molto dalle tematiche Silverlight)

sintatticamente parlando, nell'esempio viene cambiata la classe da cui si eredita (sia dal lato XAML che da quello C#)

Grazie Marsh per la risposta.
Io ho provato l'utilizzo delle page (per capire cosa facevano) e le ho messe all'interno di un frame ma sempre in un contesto Window


<Window x:Name="MyWin" x:Class="Tutorial1.WindowFrame" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:Tutorial1" mc:Ignorable="d" Title="WindowFrame" Height="300" Width="498.376">
<Grid>
<Frame x:Name="frame" Content="Frame" NavigationUIVisibility="Hidden"/>
</Grid>
</Window>

Questo perchèil tutorial seguito , così mi faceva fare.
Ho sbagliato?
Puoi indicarmi gentilmente un link dove capire quello che stai dicendo tu per avere maggiori informazioni, o se vuoi puoi spiegarmelo tu. Grazie
Nell'esempio postato da msdn non facevo nessuna navigazione tra pagine o cosa di particolare che non andasse bene in una window/grid anche per questo non ho capito perchè fa fare quel passaggio

Marsh
30-01-2017, 13:40
se usare Window o NavigationWindow dipende da cosa vuoi ottenere

window ti permette una customizzazione totale, mentre navigationwindow ti implementa in modo automatico la barra di navigazione (quella del browser)

esempio banale per navigare con quest'ultima

Finestra di navigazione, ove Source -> pagina iniziale


<NavigationWindow x:Class="WpfPage.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfPage"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525" Source="Page1.xaml">
</NavigationWindow>


Al lancio singolare di quanto scritto sopra, ti accorgi subito della presenza della barra di navigazione, questa viene aggiornata in base alle pagine su cui hai eseguito la navigazione, e ti permette lo spostamento
Pagina esempio 1


<Page x:Class="WpfPage.Page1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WpfPage"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
Title="Page1">
<Grid>
<Button x:Name="button" Content="Button" HorizontalAlignment="Left" Margin="113,138,0,0" VerticalAlignment="Top" Width="75" Click="button_Click"/>
<TextBlock x:Name="textBlock" HorizontalAlignment="Left" Margin="10,10,0,0" TextWrapping="Wrap" Text="Page 1" VerticalAlignment="Top"/>
</Grid>
</Page>


dove Click="button_Click", alla pressione ti fa spostare su Page2,
qui l'utilizzo del navigationservice che ti permette lo spostamento tra pagine (indietro, avanti, nuova, ..)



private void button_Click(object sender, RoutedEventArgs e)
{
NavigationService.Navigate(new Page2());
}


Pagina esempio 2


<Page x:Class="WpfPage.Page2"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WpfPage"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
Title="Page1">
<Grid>
<TextBlock x:Name="textBlock" HorizontalAlignment="Left" Margin="10,10,0,0" TextWrapping="Wrap" Text="Page 2" VerticalAlignment="Top"/>
</Grid>
</Page>


nel caso tu non voglia la barra sopra, e quindi le aggiunte funzionalità della NavigationWindow, ti basta, come hai fatto, utilizzare un componente Frame nella Window principale, l'utilizzo delle pagine nel frame ti garantiscono la possibilità d'utilizzo del NavigationService.

ciucciatiilcalzino
01-02-2017, 15:30
Ciao @Marsh e ciao a tutti, grazie per la risposta, mi è un po più chiaro ora grazie alla tua spiegazione e grazie a dei test fatti.
Sono andato avanti e sto provando a riempire una GridView con un data entity model.
Ho una domanda da farvi.
Ho inserito una colonna DataGridHyperlinkColumn perchè vorrei che cliccando su un link o id si acceda ad un'altra finestra/pagina magari per vedere il dettaglio di quello cliccato o fare in update dei dati

La datagrid l'ho creata così



<DataGrid x:Name="MiaDataGrid" HorizontalAlignment="Left" AutoGenerateColumns="False" VerticalAlignment="Top">
<DataGrid.Columns>
<DataGridHyperlinkColumn Header="ID" Binding="{Binding id_Album}">
<DataGridHyperlinkColumn.EditingElementStyle>
<Style TargetType="Hyperlink" >
<Setter Property="Tag" Value="{Binding id_Album}" />
<EventSetter Event="Hyperlink.Click" Handler="DG_Hyperlink_Click"></EventSetter>
</Style>

</DataGridHyperlinkColumn.EditingElementStyle>

</DataGridHyperlinkColumn>
<DataGridTextColumn Binding="{Binding album1}" Header="Album"></DataGridTextColumn>
<DataGridTextColumn Binding="{Binding Anno}" Header="Anno "></DataGridTextColumn>

</DataGrid.Columns>
<DataGrid.Resources>
<Style TargetType="Hyperlink">
<EventSetter Event="Click" Handler="DG_Hyperlink_Click"/>
</Style>
</DataGrid.Resources>
</DataGrid>


mentre sul click vado a scrivere questo



private void DG_Hyperlink_Click(object sender, RoutedEventArgs e)
{

var dc = ((Hyperlink)sender).DataContext;


}


come si può vedere dallo screen intercetto i dati, ma non riesco a tirare fuori l'id
quello che ho fatto io è dopo questa variabile dc è stato crare una int ID = dv.Id_Album
ma mi da errore già andandolo a scrivere

28140


Infine vi vorrei chiedere un suggerimento su come posso passare l'id ad un'altra finestra, non penso che qui si possa fare un request...è una cosa che ancora non ho studiato, se mi date qualche dritta.
Grazie

ciucciatiilcalzino
02-02-2017, 11:33
Ciao a tutti,
sono riuscito a prendere il valore/id della colonna cliccata in questo modo

Hyperlink hpl = sender as Hyperlink;


Album alb = hpl.DataContext as Album;
object idAlbum = alb.id_Album;

quello che ancora non mi è proprio chiaro è il passaggio dei valori in un altro window o page


Dettaglio det = new Dettaglio();
det.idScelto.Text = idAlbum.ToString();


det.Show();


in questo momento ho fatto questo,
inserito un textbox nell'altra finestra e gli passo l'id
anche se in questo caso è un solo valore

secondo voi il mio approccio è giusto o mi conviene usare qualche altra tecnica?

Marsh
06-02-2017, 10:52
quando l'evento click dell'hyperlink viene generato, il sender rappresenta l'Hyperlink stesso, per accedere al dato che gestisce usi DataContext, essendo tutto generico devi eseguire il cast appropriato



Hyperlink hl = (Hyperlink)sender; // mandante evento
Disco d = (Disco)hl.DataContext; // hl contenuto
int id = d.IDAlbum; // proprietà


per lo scambio di dati tra finestre non c'è nulla di particolare da fare, esegui come se fosse un passaggio di dati tra due oggetti, ossia quanto istanzi la finestra imposti i dati necessari, in seguito la visualizzi (show).

se ti servono risposte dalla finestra che hai attivato, utilizza gli eventi

ciucciatiilcalzino
07-02-2017, 12:05
quando l'evento click dell'hyperlink viene generato, il sender rappresenta l'Hyperlink stesso, per accedere al dato che gestisce usi DataContext, essendo tutto generico devi eseguire il cast appropriato



Hyperlink hl = (Hyperlink)sender; // mandante evento
Disco d = (Disco)hl.DataContext; // hl contenuto
int id = d.IDAlbum; // proprietà


per lo scambio di dati tra finestre non c'è nulla di particolare da fare, esegui come se fosse un passaggio di dati tra due oggetti, ossia quanto istanzi la finestra imposti i dati necessari, in seguito la visualizzi (show).

se ti servono risposte dalla finestra che hai attivato, utilizza gli eventi

Grazie Marsh per la risposta, per il primo suggerimento ho capito cosa hai fatto e sono riuscito a prendere i valori dal context, per quello che riguarda invece il passaggio all'altro form, onestamente per ignoranza mia non ho capito, sto provando a cercare qualcosa in rete per capire cosa mi hai detto. (hai qualche link da darmi?)
Per ora ho passato il valore in un textBlock nascosto giusto per andare avanti con l'esercizio


Dettaglio det = new Dettaglio(); //dettaglio è l'altra window
det.idScelto.Text = idAlbum.ToString();


Ho provato anche un'altra soluzione ma senza andare a buon fine
Ho creato una classe MyAlbum




class MyAlbum
{


int _IdAlbum;



public int IdAlbum
{
get
{
return _IdAlbum;
}


set
{


_IdAlbum = value;


}
}
}


Poi nella prima finestra imposto l'id
MyAlbum myAlb = new MyAlbum();
myAlb.IdAlbum = Convert.ToInt32( idAlbum.ToString());

e nella seconda (dettaglio) me lo vorrei riprendere
MyAlbum myAlb = new MyAlbum();
txtIdAlbumDaClasse.Text = myAlb.IdAlbum.ToString();

ma il risultato è 0
:(

Marsh
07-02-2017, 17:38
// Poi nella prima finestra imposto l'id
MyAlbum myAlb = new MyAlbum();
myAlb.IdAlbum = Convert.ToInt32( idAlbum.ToString());
// e nella seconda (dettaglio) me lo vorrei riprendere
MyAlbum myAlb = new MyAlbum();
txtIdAlbumDaClasse.Text = myAlb.IdAlbum.ToString();



essendo che dichiari un nuovo oggetto (new), sicuramente non potrà possedere i dati di quello precedente,

il concetto che utilizzi invece nella prima soluzione è più ragionevole chiaramente puoi estenderlo in maniera più efficiente ed efficace

ora come ora hai definito la struttura modello che è (concetto simile) così (*M* v vm):



//Album.cs
/// <summary>
/// Definisce una struttura che rappresenta un oggetto di tipo Album.
/// </summary>
public class Album
{
/// <summary>
/// Ottiene o setta l'identificativo.
/// </summary>
public int ID { get; set; }

/// <summary>
/// Ottiene o setta l nome.
/// </summary>
public string Name { get; set; }

/// <summary>
/// Ottiene o setta il cognome.
/// </summary>
public int Year { get; set; }

/// <summary>
/// Ottiene o setta la durata.
/// </summary>
public TimeSpan Duration { get; set; }

/// <summary>
/// Ottiene o setta il nome dell'immagine.
/// </summary>
public string ImageFileName { get; set; }

/// <summary>
/// Costruttore.
/// </summary>
public Album() { }
}



inoltre possiedi la finestra principale che è rappresentata (concetto simile) cosi (m *V* vm):



//MainWindow.xaml
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApplication1"
mc:Ignorable="d"
Title="Album viewer" Height="350" Width="525" WindowStartupLocation="CenterScreen" Loaded="Window_Loaded">
<Grid>
<DataGrid x:Name="dgAlbums" AutoGenerateColumns="False" Margin="0,20,0,0">
<DataGrid.Columns>
<DataGridHyperlinkColumn Header="ID" Binding="{Binding ID}" Width="*">
<DataGridHyperlinkColumn.ElementStyle>
<Style>
<EventSetter Event="Hyperlink.Click" Handler="Hyperlink_Click"/>
</Style>
</DataGridHyperlinkColumn.ElementStyle>
</DataGridHyperlinkColumn>
<DataGridTextColumn Binding="{Binding Name}" Header="Nome" Width="6*"></DataGridTextColumn>
<DataGridTextColumn Binding="{Binding Year}" Header="Anno" Width="3*"></DataGridTextColumn>
<DataGridTextColumn Binding="{Binding Duration, StringFormat=hh\\:mm\\:ss}" Header="Durata" Width="3*"></DataGridTextColumn>
<DataGridTextColumn Binding="{Binding ImageFileName}" Header="Immagine" Width="6*">
<DataGridTextColumn.CellStyle>
<Style TargetType="DataGridCell">
<Setter Property="ToolTip">
<Setter.Value>
<Image Source="{Binding ImageFileName}" Width="80" Height="80" Stretch="Fill" />
</Setter.Value>
</Setter>
</Style>
</DataGridTextColumn.CellStyle>
</DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>



i tuoi hyperlink associati alla singola riga ti rappresentano il record definito dalla struttura il quale allo scaturire dell'evento click lo gestisci (concetto simile) cosi (m v *VM*):



// MainWindow.cs
/// <summary>
/// Risponde all'evento Hyperlink_Click.
/// </summary>
/// <param name="sender">Mandante dell'evento.</param>
/// <param name="e">Contains state information and event data associated with a routed event.</param>
private void Hyperlink_Click(object sender, RoutedEventArgs e)
{
Hyperlink hyperlink = e.OriginalSource as Hyperlink;
Album album = (Album)hyperlink.DataContext;

AlbumDetailWindow detailWindow = new AlbumDetailWindow(album);
detailWindow.Owner = this;
detailWindow.Show();
}



ora quando attivi la tua nuova finestra (o pagina) che vuoi che ti fornisca i dettagli o quant'altro del preciso oggetto, passa tale oggetto per intero al contesto della finestra (mantieni un VM), prassi molto più comoda di impostare le singole proprietà
quindi la tua nuova finestra (o pagina) di dettaglio (concetto simile) viene definita cosi (m *V* vm):



//AlbumDetailWindow.xaml
<Window x:Class="WpfApplication1.AlbumDetailWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApplication1"
mc:Ignorable="d"
Title="AlbumDetailWindow" Height="170" Width="350" WindowStartupLocation="CenterOwner" ResizeMode="NoResize" Initialized="Window_Initialized">
<Grid x:Name="gContext">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="5"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Separator Grid.Row="1"></Separator>
<TextBlock TextWrapping="Wrap" Text="{Binding Name}" Margin="10, 10, 10, 5" FontSize="14" FontFamily="SketchFlow Print" FontWeight="Bold"/>
<Grid Margin="5" Grid.Row="2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="1" Orientation="Vertical">
<WrapPanel Margin="2" >
<TextBlock TextWrapping="Wrap" Text="ID:" FontWeight="Bold" Margin="0,0,5,0"/>
<TextBlock TextWrapping="Wrap" Text="{Binding ID}"/>
</WrapPanel>
<WrapPanel Margin="2" >
<TextBlock TextWrapping="Wrap" Text="Nome:" FontWeight="Bold" Margin="0,0,5,0"/>
<TextBlock TextWrapping="Wrap" Text="{Binding Name}"/>
</WrapPanel>
<WrapPanel Margin="2" >
<TextBlock TextWrapping="Wrap" Text="Anno:" FontWeight="Bold" Margin="0,0,5,0"/>
<TextBlock TextWrapping="Wrap" Text="{Binding Year}"/>
</WrapPanel>
<WrapPanel Margin="2" >
<TextBlock TextWrapping="Wrap" Text="Durata:" FontWeight="Bold" Margin="0,0,5,0"/>
<TextBlock TextWrapping="Wrap" Text="{Binding Duration, StringFormat=hh\\:mm\\:ss}"/>
</WrapPanel>
</StackPanel>
<Image x:Name="iIcon" Margin="5" HorizontalAlignment="Left" Height="80" VerticalAlignment="Top" Width="80" Source="{Binding ImageFileName}" Stretch="Fill"/>
</Grid>
</Grid>
</Window>



utilizzando la stessa tecnica del data-binding eseguito per la griglia, definisci il contesto della finestra, la quale tramite quanto dettato dallo xaml, ti determinerà i dati visuali seguendo il modello (m v *VM*)



// AlbumDetailWindow.cs
/// <summary>
/// Interaction logic for AlbumDetailWindow.xaml
/// </summary>
public partial class AlbumDetailWindow : Window
{
private Album m_Album;

/// <summary>
/// Ottiene l'album associato.
/// </summary>
public Album Album
{
get { return m_Album; }
}

/// <summary>
/// Costruttore.
/// </summary>
public AlbumDetailWindow()
{
this.InitializeComponent();
}

/// <summary>
/// Costruttore.
/// </summary>
/// <param name="album">Imposta l'album di contesto.</param>
public AlbumDetailWindow(Album album)
{
m_Album = album;
this.InitializeComponent();
}

/// <summary>
/// Risponde all'evento Window_Initialized.
/// </summary>
/// <param name="sender">Mandante dell'evento.</param>
/// <param name="e">Represents the base class for classes that contain event data, and provides a
/// value to use for events that do not include event data.</param>
private void Window_Initialized(object sender, EventArgs e)
{
Title = m_Album.ID + " - " + m_Album.Name;
gContext.DataContext = m_Album;
}
}



da quanto puoi vedere non vi è nulla di particolare da fare a livello logico, inoltre noti che l'oggetto Album dato alla finestra dei dettagli è il medesimo di quello della griglia, infatti al cambio di uno muta anche l'altro (stesso riferimento),

28156
28158

questo è chiaramente un esempio, la personalizzazione è determinata per intero dalle tue esigenze di progetto

ciucciatiilcalzino
07-02-2017, 19:10
Ciao Marsh,
grazie per la tua esaustiente risposta.
Sono riuscito a rifare quello che hai fatto tu, ora riprovo con la stessa cosa a tirare fuori l'elenco dei brani che ho in un' altra tabella
quindi senza fare quella griglia come la riempi tu.
Onestamente non avevo ancora nemmeno usato gContext.DataContext per ora .
Ma sopratutto non avevo passato all'oggetto finestra i dati in questo modo ne usato Owner (che mi sono visto dalla documentazione a che serve)
tra le poche cose che ti vorrei chiedere è perchè nella seconda pagina hai aggiunto questa dichiarazione
public Album Album
{
get { return m_Album; }


}


Vai a richierare praticamente la classe Album? Ma io la classe Album (e mi sembra anche tu) ce l'ho già creata dal Model
Per fare un test ho provato a commentarla e tutto mi funziona ugualmente.


Ti volevo chiedere anche un'ultima cosa irrilevante
vedo che tu hai messo tutti questi commenti
/// <summary>
/// Costruttore.
/// </summary>





/// <summary>
/// Risponde all'evento Window_Initialized.
/// </summary>
/// <param name="sender">Mandante dell'evento.</param>
/// <param name="e">Represents the base class for classes that contain event data, and provides a
/// value to use for events that do not include event data.</param>


Li hai scritti tu, o hai usato qualche sistema/scorciatoia che ti creava i costruttori e i metodi e automaticamente anche il commento

Loading