Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
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 formulários básicos por meio de um aplicativo de 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 WPF (Windows Presentation Foundation) para .NET Framework (não .NET Core ou .NET 5 ou posterior). Ele mostra como fazer a vinculação de dados básica com uma exibição mestre-detalhe, e inclui um controle BindingNavigator personalizado com botões que fazem o seguinte: mover para o primeiro, mover para o anterior, mover para o próximo, mover para o último, excluir, adicionar, definir nova sequência, atualizar e cancelar.
Este tutorial se concentra no uso de ferramentas de dados no Visual Studio e não tenta explicar detalhadamente as tecnologias subjacentes. Ele pressupõe que você tenha uma familiaridade básica com XAML (Extensible Application Markup Language), Entity Framework e SQL. Embora o código neste tutorial não demonstre a arquitetura MVVM (Model-View-ViewModel), 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 de 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
- Executar o aplicativo WPF
Pré-requisitos
Visual Studio com a carga de trabalho de Desenvolvimento da Área de Trabalho do .NET instalada e o componente do Windows Communication Foundation instalado. Para instalá-lo:
- Abra o aplicativo Instalador do Visual Studio ou selecione Ferramentas>Para Obter Ferramentas e Recursos no menu do Visual Studio.
- No Instalador do Visual Studio, escolha Modificar ao lado da versão do Visual Studio que você deseja modificar.
- Selecione a guia Componentes individuais e escolha o Windows Communication Foundation em atividades de desenvolvimento.
- Selecione Modificar.
SQL Server Express LocalDB. Se você não tiver o SQL Server Express LocalDB, poderá instalá-lo na página de download do SQL Server. Ou você pode instalá-lo com o aplicativo Instalador do Visual Studio 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 Instalador do Visual Studio .
Ferramentas do Entity Framework 6. Isso geralmente é instalado quando você instala a carga de trabalho de Desenvolvimento .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 ADO.NET provedor de dados desse produto der suporte ao Entity Framework, ele também deverá funcionar com outros produtos do banco de dados SQL.
Instale o banco de dados de exemplo Northwind seguindo estas etapas:
No Visual Studio, abra a janela pesquisador de objetos do SQL Server no menu Exibir . Expanda o nó SQL Server. Clique com o botão direito do mouse na instância do LocalDB e selecione Nova Consulta.
Uma janela do editor de consultas é aberta.
Copie o script Northwind Transact-SQL (T-SQL) para sua á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 de aplicativo WPF
Para configurar o projeto de aplicativo WPF, siga estas etapas:
No Visual Studio, crie um novo projeto de aplicativo WPF de C# (.NET Framework).
Adicione o pacote NuGet para o Entity Framework 6. No Gerenciador de Soluções, selecione o nó do projeto. No menu principal, escolha Projeto>Gerenciar Pacotes NuGet.
No Gerenciador de Pacotes NuGet, selecione o link Procurar . Pesquise e selecione o pacote EntityFramework . Selecione Instalar no painel direito e siga os prompts.
A janela Saída exibe o progresso e notifica você quando a instalação é concluída.
Agora você pode usar o Visual Studio para criar um modelo com base 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 do aplicativo WPF no Gerenciador de Soluções e escolha Adicionar>Novo Item. No painel esquerdo, no nó C#, escolha Dados e, no painel central, escolha Modelo de Dados de Entidade ADO.NET.
Insira Northwind_model para o Nome e escolha Adicionar.
No Assistente de Modelo de Dados de Entidade, escolha Designer EF a partir do banco de dados e selecione Avançar.
Em Escolher Sua Conexão de Dados, selecione sua conexão LocalDB Northwind (por exemplo, (localdb)\MSSQLLocalDB) e selecione Avançar.
Se você não vir uma conexão:
Escolha Nova Conexão. Se o Microsoft SQL Server não estiver selecionado como a 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, em seguida, selecione OK.
Na caixa de diálogo Propriedades da Conexão , insira (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 Escolher Sua Conexão de Dados, selecione sua conexão LocalDB Northwind e selecione Avançar.
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 incluir no modelo do Entity Framework. Expanda o nó dbo no nó Tabelas na exibiçã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 associam à 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 estão visíveis no Gerenciador de Soluções no 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 finalidade geral e você deve editar um deles para trabalhar com a associação de dados do WPF, o que requer ObservableCollection objetos. Siga estas etapas:
No Gerenciador de Soluções, expanda 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) com System.Collections.ObjectModel.
Clique em F5 para compilar 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 associação de dados, é mais fácil permitir que o Visual Studio faça isso por você. Para fazer isso, siga estas etapas:
No menu principal, escolha Projeto>Adicionar nova fonte de dados para exibir o Assistente de Configuração de Fonte de Dados. Como você está associando às classes de modelo, não ao banco de dados, escolha Objeto. Selecione Próximo.
Expanda o nó do projeto, selecione o objeto Cliente e selecione Concluir. As fontes para objetos Order são geradas automaticamente a partir da propriedade de navegação Orders no Cliente.
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 aumente oHeightpara 600 e oWidthpara 800 (você pode alterar esses valores mais tarde, se necessário).Adicione estas três definições de linha à grade principal, uma linha para os botões de navegação, uma para os detalhes do cliente e outra para a grade que mostra seus pedidos:
<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 estas etapas:
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>Outras Fontes de Dados do> no menu.
Em Fontes de Dados, selecione Clientes e selecione Detalhes na lista suspensa.
Arraste o nó até a linha intermediária da área de design. Caso ele desapareça, você poderá especificar manualmente a linha 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 deste tutorial reordena os campos e os reorganiza em duas colunas.
No modo de exibição XAML, agora você pode ver um novo elemento
Gridna linha 1 (a linha intermediária) da grade pai. A grade pai 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 associa ao Endereço, esse nome é mapeado para aAddresspropriedade no objeto atualCustomeremCollectionViewSource.<Grid DataContext="{StaticResource customerViewSource}">Arraste a
Orderpropriedade do objeto daCustomersclasse para a metade inferior do formulário, de modo 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ê mostra os pedidos em um único controle de exibição de grade. Para que o vínculo de dados mestre-detalhe funcione conforme o esperado, é importante que você associe a propriedade
Ordersna classeCustomers, não ao nó separadoOrders.Captura de tela que mostra as classes Orders arrastadas e soltas como uma grade.
O Visual Studio agora gera todo o código de associação que conecta os controles de interface do usuário a eventos no modelo.
Para ver alguns dados, escreva o 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 e a lógica de inicialização do construtor associado ao construtorMainWindow()existente. A primeira parte da classe 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 ele não existir, adicione uma
usingdiretiva paraSystem.Data.Entitytrazer o método de extensãoLoadpara o escopo:using System.Data.Entity;Role para baixo e localize o
Window_Loadedmanipulador de eventos. Observe que o Visual Studio adicionou umCollectionViewSourceobjeto. Esse objeto representa oNorthwindEntitiesobjeto que você selecionou ao criar o modelo. Como você já adicionou isso, não precisa dele aqui. Substitua o códigoWindow_Loadedpara que o método tenha 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.
Você deve ver os detalhes do primeiro cliente que foi recuperado na grade de dados
CollectionViewSourcee seus pedidos. 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).
Ajustar o design da página e adicionar grades para novos clientes e pedidos
A disposição padrão produzida pelo Visual Studio não é ideal para seu aplicativo, portanto, 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 pedido, crie um conjunto separado de caixas de texto que não são associadas a dados ao CollectionViewSource. Você controla qual grade o usuário vê a qualquer momento definindo a propriedade Visible nos métodos do manipulador. Por fim, você adiciona um botão Excluir a cada linha na grade Pedidos para permitir que o usuário exclua uma ordem 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 por linhas em 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 painel horizontal StackPanel, e associando os botões a comandos vinculados a métodos no arquivo code-behind.
Há quatro partes na lógica de comando:
- Commands
- Vinculações
- Buttons
- Manipuladores de comando no code-behind
Adicionar comandos, associações e botões no XAML
No arquivo
MainWindow.xaml, adicione os comandos dentro do elementoWindows.Resourcesda seguinte maneira:<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"/>Um
CommandBindingmapeia um eventoRoutedUICommandpara um método no code-behind. Adicione esteCommandBindingselemento após aWindows.Resourcesmarca de fechamento da 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, adição, exclusão e atualização. 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 esse 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 é mínimo, exceto para os métodos de adição e exclusão:
Para navegar, utilize os métodos na propriedade
ViewdoCollectionViewSource.Para executar uma exclusão em cascata em uma ordem, use o
DeleteOrderCommandHandlerconforme mostrado no exemplo de código. No entanto, primeiro você deve excluir o pedido associadoOrder_Details.Use a
UpdateCommandHandleropção para adicionar um cliente ou pedido à coleção ou atualizar um cliente ou pedido existente com as alterações feitas pelo usuário nas caixas de texto.Adicione esses métodos de manipulador na classe
MainWindowemMainWindow.xaml.cs. Se a sua tabelaCollectionViewSourceClientes 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); }
Executar 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 novo formulário de pedido sem salvar os dados.
Para fazer edições para clientes e pedidos existentes diretamente, use as caixas de texto, que gravam essas alterações no modelo automaticamente.
Conteúdo relacionado
- Ferramentas de dados do Visual Studio para .NET
- documentação do Entity Framework