Nota
O acesso a esta página requer autorização. Podes tentar iniciar sessão ou mudar de diretório.
O acesso a esta página requer autorização. Podes tentar mudar de diretório.
Observação
Se você estiver executando o Visual Studio 2022, certifique-se de usar a versão 17.3 ou posterior para este tutorial.
Este tutorial mostra como criar uma aplicação básica de formulários sobre dados no Visual Studio. O aplicativo usa o SQL Server LocalDB, o banco de dados Northwind, o Entity Framework 6 (não o Entity Framework Core) e o Windows Presentation Foundation (WPF) para .NET Framework (não .NET Core ou .NET 5 ou posterior). Ele mostra como fazer a vinculação básica de dados com uma exibição de detalhes mestre e inclui um controle BindingNavigator personalizado com botões que fazem o seguinte: mover para primeiro, mover anterior, mover em seguida, mover para último, excluir, adicionar, definir nova ordem, atualizar e cancelar.
Este tutorial se concentra no uso de ferramentas de dados no Visual Studio e não tenta explicar as tecnologias subjacentes em profundidade. Ele pressupõe que você tenha uma familiaridade básica com Extensible Application Markup Language (XAML), Entity Framework e SQL. Embora o código neste tutorial não demonstre a arquitetura Model-View-ViewModel (MVVM), que é padrão para aplicativos WPF, você pode copiar o código com algumas modificações em seu próprio aplicativo MVVM.
Para exibir o código final deste tutorial, consulte Exemplos de tutorial do Visual Studio - EF6.
Neste tutorial, você:
- Instalar e conectar-se ao Northwind
- Configurar o projeto do aplicativo WPF
- Criar o modelo de dados de entidade ADO.NET
- Os dados associam o modelo à página XAML
- Ajustar o design da página e adicionar grades
- Adicionar botões para navegar, adicionar, atualizar e excluir
- Execute o aplicativo WPF
Pré-requisitos
Visual Studio com carga de trabalho de desenvolvimento de área de trabalho .NET instalada e o componente Windows Communication Foundation instalado. Para instalá-lo:
- Abra o aplicativo Visual Studio Installer ou selecione Ferramentas>Obter ferramentas e recursos no menu Visual Studio.
- No Visual Studio Installer, escolha Modificar ao lado da versão do Visual Studio que você deseja modificar.
- Selecione a guia Componentes individuais e, em seguida, escolha Windows Communication Foundation em Atividades de desenvolvimento.
- Selecione e Modifique.
SQL Server Express LocalDB. Se você não tiver o SQL Server Express LocalDB, poderá instalá-lo a partir da página de download do SQL Server. Ou, você pode instalá-lo com o aplicativo Visual Studio Installer como um componente individual.
Pesquisador de Objetos do SQL Server. Para instalá-lo, instale a carga de trabalho de armazenamento e processamento de dados no aplicativo Visual Studio Installer .
Ferramentas do Entity Framework 6. Isso geralmente é instalado quando se instala a carga de trabalho de desenvolvimento do .NET Desktop.
Instalar e conectar-se ao Northwind
O exemplo a seguir usa o SQL Server Express LocalDB e o banco de dados de exemplo Northwind. Se o provedor de dados ADO.NET desse produto oferecer suporte ao Entity Framework, ele também deverá funcionar com outros produtos de banco de dados SQL.
Instale o banco de dados de exemplo Northwind seguindo estas etapas:
No Visual Studio, abra a janela do Pesquisador de Objetos do SQL Server no menu Exibir . Expanda o nó SQL Server . Clique com o botão direito do mouse em sua instância LocalDB e selecione Nova consulta.
Uma janela do editor de consultas é aberta.
Copie o script Northwind Transact-SQL (T-SQL) para a área de transferência.
Cole o script T-SQL no editor de consultas e escolha Executar.
A consulta de script T-SQL cria o banco de dados Northwind e o preenche com dados.
Adicione novas conexões para o banco de dados Northwind.
Configurar o projeto do aplicativo WPF
Para configurar o projeto de aplicativo WPF, execute estas etapas:
No Visual Studio, crie um novo projeto C# WPF App (.NET Framework).
Adicione o pacote NuGet para o Entity Framework 6. No Explorador de Soluções , selecione a árvore do projeto. No menu principal, escolha Project>Manage NuGet Packages.
No Gestor de Pacotes NuGet, selecione a ligação Procurar. Procure e selecione o pacote EntityFramework . Selecione Instalar no painel direito e siga as instruções.
A janela Saída exibe o progresso e notifica quando a instalação for concluída.
Agora você pode usar o Visual Studio para criar um modelo baseado no banco de dados Northwind.
Criar o modelo de dados de entidade ADO.NET
Para criar o modelo de dados de entidade ADO.NET, siga estas etapas:
Clique com o botão direito do mouse no nó do projeto WPF App no Gerenciador de Soluções e escolha Adicionar>Novo Item. No painel esquerdo, sob o nó C#, escolha Data e, no painel central, escolha ADO.NET Entity Data Model.
Introduza Northwind_model para o Nome e, em seguida, selecione Adicionar.
No Assistente de Modelo de Dados de Entidade, escolha EF Designer no banco de dados e selecione Avançar.
Em Choose Your Data Connection, selecione sua conexão LocalDB Northwind (por exemplo, (localdb)\MSSQLLocalDB) e selecione Next.
Se não vir uma ligação:
Escolha Nova conexão. Se o Microsoft SQL Server não estiver selecionado como fonte de dados na caixa de diálogo Propriedades da conexão , selecione Alterar. Na caixa de diálogo Escolher Fonte de Dados, escolha Microsoft SQL Server e selecione OK.
Na caixa de diálogo Propriedades da Conexão , digite (localdb)\MSSQLLocalDB como o nome do servidor.
Para Selecionar ou inserir um nome de banco de dados, selecione Northwind e, em seguida, selecione OK.
Em Choose Your Data Connection, selecione sua conexão LocalDB Northwind e selecione Next.
Se solicitado, escolha a versão do Entity Framework que você está usando e selecione Avançar.
Na próxima página do assistente, escolha quais tabelas, procedimentos armazenados e outros objetos de banco de dados devem ser incluídos no modelo do Entity Framework. Expanda o nó dbo sob o nó Tabelas na visualização em árvore. Selecione Clientes, Detalhes do pedido e Pedidos. Deixe os padrões marcados e selecione Concluir.
O assistente gera as classes C# que representam o modelo do Entity Framework e são o que os dados do Visual Studio vinculam à interface do usuário do WPF. Ele cria os seguintes arquivos em seu projeto:
O arquivo
.edmxdescreve as relações e outros metadados que associam as classes a objetos no banco de dados.Os arquivos
.ttsão modelos T4 que geram o código que opera no modelo e salva as alterações no banco de dados.
Esses arquivos são visíveis no Gerenciador de Soluções sob o nó Northwind_model :
O Designer de Formulários para o
.edmxarquivo não é usado neste tutorial, mas você pode usá-lo para modificar determinadas propriedades e relações no modelo.
Os .tt arquivos são de uso geral e você deve editar um deles para trabalhar com a vinculação de dados WPF, que requer ObservableCollection objetos. Siga estes passos:
No Gerenciador de Soluções, expanda o nó Northwind_model até encontrar Northwind_model.tt. Clique duas vezes neste arquivo e faça as seguintes edições:
Substitua as duas ocorrências de ICollection por ObservableCollection<T>.
Substitua a primeira ocorrência de HashSet<T> por ObservableCollection<T> perto da linha 51. Não substitua a segunda ocorrência de HashSet.
Substitua a única ocorrência de System.Collections.Generic perto da linha 431 por System.Collections.ObjectModel.
Pressione F5 para criar e executar o projeto. Quando o aplicativo é executado pela primeira vez, as classes de modelo ficam visíveis para o assistente de fontes de dados.
Agora você está pronto para conectar esse modelo à página XAML para que possa exibir, navegar e modificar os dados.
Os dados associam o modelo à página XAML
Embora seja possível escrever seu próprio código de vinculação de dados, é mais fácil permitir que o Visual Studio faça isso por você. Para fazer isso, execute as seguintes etapas:
No menu principal, escolha Project>Add new data source para exibir o Assistente de Configuração da Fonte de Dados. Como você está vinculando às classes de modelo, não ao banco de dados, escolha Objeto. Selecione Seguinte.
Expanda o nó do seu projeto, selecione o objeto Customer e, em seguida, selecione Concluir. As fontes dos objetos Order são geradas automaticamente a partir da propriedade de navegação Orders em Customer.
No Gerenciador de Soluções, clique duas vezes em MainWindow.xaml em seu projeto para editar o XAML. Altere o
Titlede MainWindow para algo mais descritivo e aumenteHeighteWidthpara 600 e 800 (pode alterar esses valores mais tarde, se necessário).Adicione estas três definições de linha à grelha principal, uma linha para os botões de navegação, uma para os detalhes do cliente e uma para a grelha que mostra as suas encomendas:
<Grid.RowDefinitions> <RowDefinition Height="auto"/> <RowDefinition Height="auto"/> <RowDefinition Height="*"/> </Grid.RowDefinitions>
Em seguida, você exibe cada propriedade na Customers classe em sua própria caixa de texto individual. Siga estes passos:
No Gerenciador de Soluções, clique duas vezes em MainWindow.xaml para abri-lo no designer.
A guia Fontes de Dados aparece no painel esquerdo do Visual Studio perto da Caixa de Ferramentas.
Para abrir a janela Fontes de Dados, selecione a guia Fontes de Dados ou escolha Exibir> OutrasFontes de Dados do> no menu.
Em Fontes de Dados, selecione Clientes e, em seguida, selecione Detalhes na lista suspensa.
Arraste o nó para a linha do meio da área de design. Caso a perca, poderá especificar a linha de código manualmente no XAML, selecionando
Grid.Row="1".Por padrão, os controles são colocados verticalmente em um elemento de grade, mas você pode organizá-los como quiser no formulário. Por exemplo, você pode colocar a caixa de texto Nome acima do endereço. O aplicativo de exemplo para este tutorial reordena os campos e os reorganiza em duas colunas.
Na vista XAML, agora é possível ver um novo elemento
Gridna linha 1 (que é a linha do meio) da grelha principal. A grelha principal tem um atributoDataContextque se refere a um CollectionViewSource que pertence ao elementoWindows.Resources. Dado esse contexto de dados, quando a primeira caixa de texto se vincula a Address, esse nome é mapeado na propriedadeAddressdo objeto correnteCustomeremCollectionViewSource.<Grid DataContext="{StaticResource customerViewSource}">Arraste a
Orderpropriedade do objeto daCustomersclasse para a metade inferior do formulário, para que o designer a coloque na linha 2.Quando um cliente está visível na metade superior do formulário, você deseja ver seus pedidos na metade inferior. Você apresenta os pedidos num único controle de visualização em grade. Para que a vinculação de dados mestre-detalhe funcione conforme o esperado, é importante que você vincule à propriedade
Ordersna classeCustomers, não ao nó separadoOrders.
O Visual Studio agora gera todo o código de associação que conecta os controles da interface do usuário a eventos no modelo.
Para ver alguns dados, escreva código para preencher o modelo. Navegue até
MainWindow.xaml.cse adicione um membro de dados àMainWindowclasse para o contexto de dados.Esse objeto, que foi gerado para você, age como um controle que rastreia alterações e eventos no modelo.
Adicione
CollectionViewSourcemembros de dados para clientes e pedidos, bem como a lógica de inicialização associada ao construtorMainWindow()existente. A primeira parte da aula deve ter esta aparência:public partial class MainWindow : Window { NorthwindEntities context = new NorthwindEntities(); CollectionViewSource custViewSource; CollectionViewSource ordViewSource; public MainWindow() { InitializeComponent(); custViewSource = ((CollectionViewSource)(FindResource("customerViewSource"))); ordViewSource = ((CollectionViewSource)(FindResource("customerOrdersViewSource"))); DataContext = this; }Se não existir, adicione uma diretiva
usingparaSystem.Data.Entitytrazer o método de extensãoLoadpara o escopo:using System.Data.Entity;Role para baixo e localize o manipulador de
Window_Loadedeventos. Observe que o Visual Studio adicionou umCollectionViewSourceobjeto. Este objeto representa oNorthwindEntitiesobjeto que você selecionou quando criou o modelo. Porque você já o adicionou, você não precisa dele aqui. Substitua o código emWindow_Loadedde modo que o método fique com esta aparência:private void Window_Loaded(object sender, RoutedEventArgs e) { // Load is an extension method on IQueryable, // defined in the System.Data.Entity namespace. // This method enumerates the results of the query, // similar to ToList but without creating a list. // When used with Linq to Entities, this method // creates entity objects and adds them to the context. context.Customers.Load(); // After the data is loaded, call the DbSet<T>.Local property // to use the DbSet<T> as a binding source. custViewSource.Source = context.Customers.Local; }Pressione F5.
Deverá ver os detalhes do primeiro cliente que foi recuperado no
CollectionViewSourcee os seus pedidos na grelha de dados. Você corrigirá a formatação na próxima seção. Você também pode criar uma maneira de exibir os outros registros e fazer operações básicas de criação, leitura, atualização e exclusão (CRUD).
Ajuste o design da página e adicione grades para novos clientes e pedidos
A disposição padrão produzida pelo Visual Studio não é ideal para seu aplicativo, por isso fornecemos o XAML final aqui para copiar em seu código. Você também precisa de algumas grades para permitir que o usuário adicione um novo cliente ou pedido.
Para adicionar um novo cliente e um pedido, crie um conjunto separado de caixas de texto que não estejam vinculadas a dados ao CollectionViewSource. Você controla qual grelha o usuário vê a qualquer momento definindo a propriedade Visível nos métodos do manipulador. Finalmente, você adiciona um botão Excluir a cada linha na grade Pedidos para permitir que o usuário exclua um pedido individual.
Abra
MainWindow.xamle adicione os seguintes estilos aoWindows.Resourceselemento :<Style x:Key="Label" TargetType="{x:Type Label}" BasedOn="{x:Null}"> <Setter Property="HorizontalAlignment" Value="Left"/> <Setter Property="VerticalAlignment" Value="Center"/> <Setter Property="Margin" Value="3"/> <Setter Property="Height" Value="23"/> </Style> <Style x:Key="CustTextBox" TargetType="{x:Type TextBox}" BasedOn="{x:Null}"> <Setter Property="HorizontalAlignment" Value="Right"/> <Setter Property="VerticalAlignment" Value="Center"/> <Setter Property="Margin" Value="3"/> <Setter Property="Height" Value="26"/> <Setter Property="Width" Value="120"/> </Style>Substitua toda a grade externa por esta marcação:
<Grid> <Grid.RowDefinitions> <RowDefinition Height="auto"/> <RowDefinition Height="auto"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <Grid x:Name="existingCustomerGrid" Grid.Row="1" HorizontalAlignment="Left" Margin="5" Visibility="Visible" VerticalAlignment="Top" Background="AntiqueWhite" DataContext="{StaticResource customerViewSource}"> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto" MinWidth="233"/> <ColumnDefinition Width="Auto" MinWidth="397"/> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> </Grid.RowDefinitions> <Label Content="Customer ID:" Grid.Row="0" Style="{StaticResource Label}"/> <TextBox x:Name="customerIDTextBox" Grid.Row="0" Style="{StaticResource CustTextBox}" Text="{Binding CustomerID, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}"/> <Label Content="Company Name:" Grid.Row="1" Style="{StaticResource Label}"/> <TextBox x:Name="companyNameTextBox" Grid.Row="1" Style="{StaticResource CustTextBox}" Text="{Binding CompanyName, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}"/> <Label Content="Contact Name:" Grid.Row="2" Style="{StaticResource Label}"/> <TextBox x:Name="contactNameTextBox" Grid.Row="2" Style="{StaticResource CustTextBox}" Text="{Binding ContactName, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}"/> <Label Content="Contact title:" Grid.Row="3" Style="{StaticResource Label}"/> <TextBox x:Name="contactTitleTextBox" Grid.Row="3" Style="{StaticResource CustTextBox}" Text="{Binding ContactTitle, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}"/> <Label Content="Address:" Grid.Row="4" Style="{StaticResource Label}"/> <TextBox x:Name="addressTextBox" Grid.Row="4" Style="{StaticResource CustTextBox}" Text="{Binding Address, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}"/> <Label Content="City:" Grid.Column="1" Grid.Row="0" Style="{StaticResource Label}"/> <TextBox x:Name="cityTextBox" Grid.Column="1" Grid.Row="0" Style="{StaticResource CustTextBox}" Text="{Binding City, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}"/> <Label Content="Country:" Grid.Column="1" Grid.Row="1" Style="{StaticResource Label}"/> <TextBox x:Name="countryTextBox" Grid.Column="1" Grid.Row="1" Style="{StaticResource CustTextBox}" Text="{Binding Country, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}"/> <Label Content="Fax:" Grid.Column="1" Grid.Row="2" Style="{StaticResource Label}"/> <TextBox x:Name="faxTextBox" Grid.Column="1" Grid.Row="2" Style="{StaticResource CustTextBox}" Text="{Binding Fax, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}"/> <Label Content="Phone:" Grid.Column="1" Grid.Row="3" Style="{StaticResource Label}"/> <TextBox x:Name="phoneTextBox" Grid.Column="1" Grid.Row="3" Style="{StaticResource CustTextBox}" Text="{Binding Phone, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}"/> <Label Content="Postal Code:" Grid.Column="1" Grid.Row="4" VerticalAlignment="Center" Style="{StaticResource Label}"/> <TextBox x:Name="postalCodeTextBox" Grid.Column="1" Grid.Row="4" Style="{StaticResource CustTextBox}" Text="{Binding PostalCode, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}"/> <Label Content="Region:" Grid.Column="1" Grid.Row="5" Style="{StaticResource Label}"/> <TextBox x:Name="regionTextBox" Grid.Column="1" Grid.Row="5" Style="{StaticResource CustTextBox}" Text="{Binding Region, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}"/> </Grid> <Grid x:Name="newCustomerGrid" Grid.Row="1" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="5" DataContext="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=newCustomer, UpdateSourceTrigger=Explicit}" Visibility="Collapsed" Background="CornflowerBlue"> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto" MinWidth="233"/> <ColumnDefinition Width="Auto" MinWidth="397"/> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> </Grid.RowDefinitions> <Label Content="Customer ID:" Grid.Row="0" Style="{StaticResource Label}"/> <TextBox x:Name="add_customerIDTextBox" Grid.Row="0" Style="{StaticResource CustTextBox}" Text="{Binding CustomerID, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}"/> <Label Content="Company Name:" Grid.Row="1" Style="{StaticResource Label}"/> <TextBox x:Name="add_companyNameTextBox" Grid.Row="1" Style="{StaticResource CustTextBox}" Text="{Binding CompanyName, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true }"/> <Label Content="Contact Name:" Grid.Row="2" Style="{StaticResource Label}"/> <TextBox x:Name="add_contactNameTextBox" Grid.Row="2" Style="{StaticResource CustTextBox}" Text="{Binding ContactName, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}"/> <Label Content="Contact title:" Grid.Row="3" Style="{StaticResource Label}"/> <TextBox x:Name="add_contactTitleTextBox" Grid.Row="3" Style="{StaticResource CustTextBox}" Text="{Binding ContactTitle, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}"/> <Label Content="Address:" Grid.Row="4" Style="{StaticResource Label}"/> <TextBox x:Name="add_addressTextBox" Grid.Row="4" Style="{StaticResource CustTextBox}" Text="{Binding Address, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}"/> <Label Content="City:" Grid.Column="1" Grid.Row="0" Style="{StaticResource Label}"/> <TextBox x:Name="add_cityTextBox" Grid.Column="1" Grid.Row="0" Style="{StaticResource CustTextBox}" Text="{Binding City, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}"/> <Label Content="Country:" Grid.Column="1" Grid.Row="1" Style="{StaticResource Label}"/> <TextBox x:Name="add_countryTextBox" Grid.Column="1" Grid.Row="1" Style="{StaticResource CustTextBox}" Text="{Binding Country, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}"/> <Label Content="Fax:" Grid.Column="1" Grid.Row="2" Style="{StaticResource Label}"/> <TextBox x:Name="add_faxTextBox" Grid.Column="1" Grid.Row="2" Style="{StaticResource CustTextBox}" Text="{Binding Fax, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}"/> <Label Content="Phone:" Grid.Column="1" Grid.Row="3" Style="{StaticResource Label}"/> <TextBox x:Name="add_phoneTextBox" Grid.Column="1" Grid.Row="3" Style="{StaticResource CustTextBox}" Text="{Binding Phone, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}"/> <Label Content="Postal Code:" Grid.Column="1" Grid.Row="4" VerticalAlignment="Center" Style="{StaticResource Label}"/> <TextBox x:Name="add_postalCodeTextBox" Grid.Column="1" Grid.Row="4" Style="{StaticResource CustTextBox}" Text="{Binding PostalCode, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}"/> <Label Content="Region:" Grid.Column="1" Grid.Row="5" Style="{StaticResource Label}"/> <TextBox x:Name="add_regionTextBox" Grid.Column="1" Grid.Row="5" Style="{StaticResource CustTextBox}" Text="{Binding Region, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}"/> </Grid> <Grid x:Name="newOrderGrid" Grid.Row="1" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="5" DataContext="{Binding Path=newOrder, Mode=TwoWay}" Visibility="Collapsed" Background="LightGreen"> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto" MinWidth="233"/> <ColumnDefinition Width="Auto" MinWidth="397"/> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> </Grid.RowDefinitions> <Label Content="New Order Form" FontWeight="Bold"/> <Label Content="Employee ID:" Grid.Row="1" Style="{StaticResource Label}"/> <TextBox x:Name="add_employeeIDTextBox" Grid.Row="1" Style="{StaticResource CustTextBox}" Text="{Binding EmployeeID, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}"/> <Label Content="Order Date:" Grid.Row="2" Style="{StaticResource Label}"/> <DatePicker x:Name="add_orderDatePicker" Grid.Row="2" HorizontalAlignment="Right" Width="120" SelectedDate="{Binding OrderDate, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true, UpdateSourceTrigger=PropertyChanged}"/> <Label Content="Required Date:" Grid.Row="3" Style="{StaticResource Label}"/> <DatePicker x:Name="add_requiredDatePicker" Grid.Row="3" HorizontalAlignment="Right" Width="120" SelectedDate="{Binding RequiredDate, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true, UpdateSourceTrigger=PropertyChanged}"/> <Label Content="Shipped Date:" Grid.Row="4" Style="{StaticResource Label}"/> <DatePicker x:Name="add_shippedDatePicker" Grid.Row="4" HorizontalAlignment="Right" Width="120" SelectedDate="{Binding ShippedDate, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true, UpdateSourceTrigger=PropertyChanged}"/> <Label Content="Ship Via:" Grid.Row="5" Style="{StaticResource Label}"/> <TextBox x:Name="add_ShipViaTextBox" Grid.Row="5" Style="{StaticResource CustTextBox}" Text="{Binding ShipVia, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}"/> <Label Content="Freight" Grid.Row="6" Style="{StaticResource Label}"/> <TextBox x:Name="add_freightTextBox" Grid.Row="6" Style="{StaticResource CustTextBox}" Text="{Binding Freight, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}"/> </Grid> <DataGrid x:Name="ordersDataGrid" SelectionUnit="Cell" SelectionMode="Single" AutoGenerateColumns="False" CanUserAddRows="false" IsEnabled="True" EnableRowVirtualization="True" Width="auto" ItemsSource="{Binding Source={StaticResource customerOrdersViewSource}}" Margin="10,10,10,10" Grid.Row="2" RowDetailsVisibilityMode="VisibleWhenSelected"> <DataGrid.Columns> <DataGridTemplateColumn> <DataGridTemplateColumn.CellTemplate> <DataTemplate> <Button Content="Delete" Command="{StaticResource DeleteOrderCommand}" CommandParameter="{Binding}"/> </DataTemplate> </DataGridTemplateColumn.CellTemplate> </DataGridTemplateColumn> <DataGridTextColumn x:Name="customerIDColumn" Binding="{Binding CustomerID}" Header="Customer ID" Width="SizeToHeader"/> <DataGridTextColumn x:Name="employeeIDColumn" Binding="{Binding EmployeeID}" Header="Employee ID" Width="SizeToHeader"/> <DataGridTextColumn x:Name="freightColumn" Binding="{Binding Freight}" Header="Freight" Width="SizeToHeader"/> <DataGridTemplateColumn x:Name="orderDateColumn" Header="Order Date" Width="SizeToHeader"> <DataGridTemplateColumn.CellTemplate> <DataTemplate> <DatePicker SelectedDate="{Binding OrderDate, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true, UpdateSourceTrigger=PropertyChanged}"/> </DataTemplate> </DataGridTemplateColumn.CellTemplate> </DataGridTemplateColumn> <DataGridTextColumn x:Name="orderIDColumn" Binding="{Binding OrderID}" Header="Order ID" Width="SizeToHeader"/> <DataGridTemplateColumn x:Name="requiredDateColumn" Header="Required Date" Width="SizeToHeader"> <DataGridTemplateColumn.CellTemplate> <DataTemplate> <DatePicker SelectedDate="{Binding RequiredDate, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true, UpdateSourceTrigger=PropertyChanged}"/> </DataTemplate> </DataGridTemplateColumn.CellTemplate> </DataGridTemplateColumn> <DataGridTextColumn x:Name="shipAddressColumn" Binding="{Binding ShipAddress}" Header="Ship Address" Width="SizeToHeader"/> <DataGridTextColumn x:Name="shipCityColumn" Binding="{Binding ShipCity}" Header="Ship City" Width="SizeToHeader"/> <DataGridTextColumn x:Name="shipCountryColumn" Binding="{Binding ShipCountry}" Header="Ship Country" Width="SizeToHeader"/> <DataGridTextColumn x:Name="shipNameColumn" Binding="{Binding ShipName}" Header="Ship Name" Width="SizeToHeader"/> <DataGridTemplateColumn x:Name="shippedDateColumn" Header="Shipped Date" Width="SizeToHeader"> <DataGridTemplateColumn.CellTemplate> <DataTemplate> <DatePicker SelectedDate="{Binding ShippedDate, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true, UpdateSourceTrigger=PropertyChanged}"/> </DataTemplate> </DataGridTemplateColumn.CellTemplate> </DataGridTemplateColumn> <DataGridTextColumn x:Name="shipPostalCodeColumn" Binding="{Binding ShipPostalCode}" Header="Ship Postal Code" Width="SizeToHeader"/> <DataGridTextColumn x:Name="shipRegionColumn" Binding="{Binding ShipRegion}" Header="Ship Region" Width="SizeToHeader"/> <DataGridTextColumn x:Name="shipViaColumn" Binding="{Binding ShipVia}" Header="Ship Via" Width="SizeToHeader"/> </DataGrid.Columns> </DataGrid> </Grid>
Adicionar botões para navegar, adicionar, atualizar e excluir
Em um aplicativo do Windows Forms, você recebe um BindingNavigator objeto com botões para navegar pelas linhas de um banco de dados e fazer operações CRUD básicas. Embora o WPF não forneça um BindingNavigator, é fácil criar um criando botões dentro de um horizontal StackPanele associando os botões a comandos que estão vinculados a métodos no arquivo code-behind.
Há quatro partes na lógica de comando:
- Comandos
- Vínculos
- Botões
- Manipuladores de comando no code-behind
Adicionar comandos, associações e botões em XAML
No arquivo
MainWindow.xaml, adicione os comandos dentro do elementoWindows.Resourcesda seguinte forma:<RoutedUICommand x:Key="FirstCommand" Text="First"/> <RoutedUICommand x:Key="LastCommand" Text="Last"/> <RoutedUICommand x:Key="NextCommand" Text="Next"/> <RoutedUICommand x:Key="PreviousCommand" Text="Previous"/> <RoutedUICommand x:Key="DeleteCustomerCommand" Text="Delete Customer"/> <RoutedUICommand x:Key="DeleteOrderCommand" Text="Delete Order"/> <RoutedUICommand x:Key="UpdateCommand" Text="Update"/> <RoutedUICommand x:Key="AddCommand" Text="Add"/> <RoutedUICommand x:Key="CancelCommand" Text="Cancel"/>A
CommandBindingmapeia umRoutedUICommandevento para um método no code-behind. Adicione este elementoCommandBindingsapós a tag de fechamentoWindows.Resourcesda seguinte maneira:<Window.CommandBindings> <CommandBinding Command="{StaticResource FirstCommand}" Executed="FirstCommandHandler"/> <CommandBinding Command="{StaticResource LastCommand}" Executed="LastCommandHandler"/> <CommandBinding Command="{StaticResource NextCommand}" Executed="NextCommandHandler"/> <CommandBinding Command="{StaticResource PreviousCommand}" Executed="PreviousCommandHandler"/> <CommandBinding Command="{StaticResource DeleteCustomerCommand}" Executed="DeleteCustomerCommandHandler"/> <CommandBinding Command="{StaticResource DeleteOrderCommand}" Executed="DeleteOrderCommandHandler"/> <CommandBinding Command="{StaticResource UpdateCommand}" Executed="UpdateCommandHandler"/> <CommandBinding Command="{StaticResource AddCommand}" Executed="AddCommandHandler"/> <CommandBinding Command="{StaticResource CancelCommand}" Executed="CancelCommandHandler"/> </Window.CommandBindings>Adicione o
StackPanelcom os botões de navegação, adicionar, excluir e atualizar. Adicione este estilo aWindows.Resources:<Style x:Key="NavButton" TargetType="{x:Type Button}" BasedOn="{x:Null}"> <Setter Property="FontSize" Value="24"/> <Setter Property="FontFamily" Value="Segoe UI Symbol"/> <Setter Property="Margin" Value="2,2,2,0"/> <Setter Property="Width" Value="40"/> <Setter Property="Height" Value="auto"/> </Style>Cole este código logo após o
RowDefinitionspara o elemento externoGrid, na parte superior da página XAML:<StackPanel Orientation="Horizontal" Margin="2,2,2,0" Height="36" VerticalAlignment="Top" Background="Gainsboro" DataContext="{StaticResource customerViewSource}" d:LayoutOverrides="LeftMargin, RightMargin, TopMargin, BottomMargin"> <Button Name="btnFirst" Content="|◄" Command="{StaticResource FirstCommand}" Style="{StaticResource NavButton}"/> <Button Name="btnPrev" Content="◄" Command="{StaticResource PreviousCommand}" Style="{StaticResource NavButton}"/> <Button Name="btnNext" Content="►" Command="{StaticResource NextCommand}" Style="{StaticResource NavButton}"/> <Button Name="btnLast" Content="►|" Command="{StaticResource LastCommand}" Style="{StaticResource NavButton}"/> <Button Name="btnDelete" Content="Delete Customer" Command="{StaticResource DeleteCustomerCommand}" FontSize="11" Width="120" Style="{StaticResource NavButton}"/> <Button Name="btnAdd" Content="New Customer" Command="{StaticResource AddCommand}" FontSize="11" Width="80" Style="{StaticResource NavButton}"/> <Button Content="New Order" Name="btnNewOrder" FontSize="11" Width="80" Style="{StaticResource NavButton}" Click="NewOrder_click"/> <Button Name="btnUpdate" Content="Commit" Command="{StaticResource UpdateCommand}" FontSize="11" Width="80" Style="{StaticResource NavButton}"/> <Button Content="Cancel" Name="btnCancel" Command="{StaticResource CancelCommand}" FontSize="11" Width="80" Style="{StaticResource NavButton}"/> </StackPanel>
Adicionar manipuladores de comando à classe MainWindow
O código subjacente em MainWindow.xaml.cs é minimalista, exceto para os métodos adicionar e excluir.
Para navegar, chame os métodos na propriedade
ViewdoCollectionViewSource.Para executar uma exclusão em cascata em uma ordem, use o
DeleteOrderCommandHandlercomo mostrado no exemplo de código. No entanto, deve primeiro eliminar oOrder_Detailsassociado ao pedido.Use o
UpdateCommandHandlerpara adicionar um cliente ou pedido à coleção, ou atualizar um cliente ou pedido existente com as alterações que o usuário faz nas caixas de texto.Adicione esses métodos manipuladores à
MainWindowclasse emMainWindow.xaml.cs.CollectionViewSourceSe a tabela for the Customers tiver um nome diferente, você deverá ajustar o nome em cada um dos métodos:private void LastCommandHandler(object sender, ExecutedRoutedEventArgs e) { custViewSource.View.MoveCurrentToLast(); } private void PreviousCommandHandler(object sender, ExecutedRoutedEventArgs e) { custViewSource.View.MoveCurrentToPrevious(); } private void NextCommandHandler(object sender, ExecutedRoutedEventArgs e) { custViewSource.View.MoveCurrentToNext(); } private void FirstCommandHandler(object sender, ExecutedRoutedEventArgs e) { custViewSource.View.MoveCurrentToFirst(); } private void DeleteCustomerCommandHandler(object sender, ExecutedRoutedEventArgs e) { // If existing window is visible, delete the customer and all their orders. // In a real application, you should add warnings and allow the user to cancel the operation. var cur = custViewSource.View.CurrentItem as Customer; var cust = (from c in context.Customers where c.CustomerID == cur.CustomerID select c).FirstOrDefault(); if (cust != null) { foreach (var ord in cust.Orders.ToList()) { Delete_Order(ord); } context.Customers.Remove(cust); } context.SaveChanges(); custViewSource.View.Refresh(); } // Commit changes from the new customer form, the new order form, // or edits made to the existing customer form. private void UpdateCommandHandler(object sender, ExecutedRoutedEventArgs e) { if (newCustomerGrid.IsVisible) { // Create a new object because the old one // is being tracked by EF now. Customer newCustomer = new Customer { Address = add_addressTextBox.Text, City = add_cityTextBox.Text, CompanyName = add_companyNameTextBox.Text, ContactName = add_contactNameTextBox.Text, ContactTitle = add_contactTitleTextBox.Text, Country = add_countryTextBox.Text, CustomerID = add_customerIDTextBox.Text, Fax = add_faxTextBox.Text, Phone = add_phoneTextBox.Text, PostalCode = add_postalCodeTextBox.Text, Region = add_regionTextBox.Text }; // Perform very basic validation if (newCustomer.CustomerID.Length == 5) { // Insert the new customer at correct position: int len = context.Customers.Local.Count(); int pos = len; for (int i = 0; i < len; ++i) { if (String.CompareOrdinal(newCustomer.CustomerID, context.Customers.Local[i].CustomerID) < 0) { pos = i; break; } } context.Customers.Local.Insert(pos, newCustomer); custViewSource.View.Refresh(); custViewSource.View.MoveCurrentTo(newCustomer); } else { MessageBox.Show("CustomerID must have 5 characters."); } newCustomerGrid.Visibility = Visibility.Collapsed; existingCustomerGrid.Visibility = Visibility.Visible; } else if (newOrderGrid.IsVisible) { // Order ID is auto-generated so we don't set it here. // For CustomerID, address, etc we use the values from current customer. // User can modify these in the datagrid after the order is entered. Customer currentCustomer = (Customer)custViewSource.View.CurrentItem; Order newOrder = new Order() { OrderDate = add_orderDatePicker.SelectedDate, RequiredDate = add_requiredDatePicker.SelectedDate, ShippedDate = add_shippedDatePicker.SelectedDate, CustomerID = currentCustomer.CustomerID, ShipAddress = currentCustomer.Address, ShipCity = currentCustomer.City, ShipCountry = currentCustomer.Country, ShipName = currentCustomer.CompanyName, ShipPostalCode = currentCustomer.PostalCode, ShipRegion = currentCustomer.Region }; try { newOrder.EmployeeID = Int32.Parse(add_employeeIDTextBox.Text); } catch { MessageBox.Show("EmployeeID must be a valid integer value."); return; } try { // Exercise for the reader if you are using Northwind: // Add the Northwind Shippers table to the model. // Acceptable ShipperID values are 1, 2, or 3. if (add_ShipViaTextBox.Text == "1" || add_ShipViaTextBox.Text == "2" || add_ShipViaTextBox.Text == "3") { newOrder.ShipVia = Convert.ToInt32(add_ShipViaTextBox.Text); } else { MessageBox.Show("Shipper ID must be 1, 2, or 3 in Northwind."); return; } } catch { MessageBox.Show("Ship Via must be convertible to int"); return; } try { newOrder.Freight = Convert.ToDecimal(add_freightTextBox.Text); } catch { MessageBox.Show("Freight must be convertible to decimal."); return; } // Add the order into the EF model context.Orders.Add(newOrder); ordViewSource.View.Refresh(); } // Save the changes, either for a new customer, a new order // or an edit to an existing customer or order. context.SaveChanges(); } // Sets up the form so that user can enter data. Data is later // saved when user clicks Commit. private void AddCommandHandler(object sender, ExecutedRoutedEventArgs e) { existingCustomerGrid.Visibility = Visibility.Collapsed; newOrderGrid.Visibility = Visibility.Collapsed; newCustomerGrid.Visibility = Visibility.Visible; // Clear all the text boxes before adding a new customer. foreach (var child in newCustomerGrid.Children) { var tb = child as TextBox; if (tb != null) { tb.Text = ""; } } } private void NewOrder_click(object sender, RoutedEventArgs e) { var cust = custViewSource.View.CurrentItem as Customer; if (cust == null) { MessageBox.Show("No customer selected."); return; } existingCustomerGrid.Visibility = Visibility.Collapsed; newCustomerGrid.Visibility = Visibility.Collapsed; newOrderGrid.UpdateLayout(); newOrderGrid.Visibility = Visibility.Visible; } // Cancels any input into the new customer form private void CancelCommandHandler(object sender, ExecutedRoutedEventArgs e) { add_addressTextBox.Text = ""; add_cityTextBox.Text = ""; add_companyNameTextBox.Text = ""; add_contactNameTextBox.Text = ""; add_contactTitleTextBox.Text = ""; add_countryTextBox.Text = ""; add_customerIDTextBox.Text = ""; add_faxTextBox.Text = ""; add_phoneTextBox.Text = ""; add_postalCodeTextBox.Text = ""; add_regionTextBox.Text = ""; existingCustomerGrid.Visibility = Visibility.Visible; newCustomerGrid.Visibility = Visibility.Collapsed; newOrderGrid.Visibility = Visibility.Collapsed; } private void Delete_Order(Order order) { // Find the order in the EF model. var ord = (from o in context.Orders.Local where o.OrderID == order.OrderID select o).FirstOrDefault(); // Delete all the order_details that have // this Order as a foreign key foreach (var detail in ord.Order_Details.ToList()) { context.Order_Details.Remove(detail); } // Now it's safe to delete the order. context.Orders.Remove(ord); context.SaveChanges(); // Update the data grid. ordViewSource.View.Refresh(); } private void DeleteOrderCommandHandler(object sender, ExecutedRoutedEventArgs e) { // Get the Order in the row in which the Delete button was clicked. Order obj = e.Parameter as Order; Delete_Order(obj); }
Execute o aplicativo WPF
Para iniciar a depuração, pressione F5. Verifique se os dados do cliente e do pedido estão preenchidos na grade e se os botões de navegação funcionam conforme o esperado.
Selecione Confirmar para adicionar um novo cliente ou pedido ao modelo depois de inserir os dados.
Selecione Cancelar para sair de um novo cliente ou de um novo formulário de pedido sem salvar os dados.
Para fazer edições diretamente em clientes e pedidos existentes, use as caixas de texto, que gravam essas alterações no modelo automaticamente.