Diskuze: EntityFramework, listbox WPF - zobrazení dat do textboxů
V předchozím kvízu, Test znalostí C# .NET online, jsme si ověřili nabyté zkušenosti z kurzu.


Ahoj,
využij Binding v XAML. Tvůj ListBox bude mít jako DataSource něco ve stylu {Binding = JmenoListu} a potom si buď můžeš udělat šablonu a zobrazovat v jednotlivých položkách více informací, nebo si přepsat ToString() na jméno jídla. Bylo by dobré vytvořit view model třídu, která bude mít seznam těch jídel a view model nastavíš jako DataContext pro tvoje okno, aby fungoval Binding. Pokud chceš přes Binding upravovat, tak nastav Mode na TwoWay, aby se to zpětně přepsalo.
Entity Framework si změny objektů hlídá sám, takže potom stačí zavolat Save() na tom contextu, přes který se dostáváš k datům.
Ještě musíš implementovat INotifyPropertyChanged na tvé třídě
Jidlo, aby se UI měnilo se změnami properties. Stejně tak použij
ObservableCollection pro seznam jídel, aby jejich mazání/přidání bylo
hned promítáno do UI a nemusel jsi to manuálně nastavovat sám
Poggy:24.3.2018 17:37
Ahoj,
děkuji za odpověď. Bohužel prvnímu odstavci prakticky nerozumím. Nevím co mám udělat. Zatím jsem si hrál jen ve WF. Tzn., že mám dopsat do MainWindow.xaml do definice listboxu něco jako toto:
<!--ListBox s výsledky hledání-->
<StackPanel Grid.Column="0" Grid.Row="2" Grid.ColumnSpan="2" >
<ListBox x:Name="lbVysledkyU" Height="100" Margin="0 5" SelectionChanged="lbVysledkyU_SelectionChanged"
DataSource="{Binding=upravovanaJidla}" />
</StackPanel>
To mi VS nedovolí, celý DataSource je podržený. V MainWindow.xaml.cs jsem změnil kolekci List na ObservableCollection. Rozhraní se mi povedlo implementovat.
public partial class MainWindow : MetroWindow
{
// slouží k uchování obejktů zvoleného jídla pro úpravu nebo smazání jídla
ObservableCollection<Jidlo> upravovanaJidla = new ObservableCollection<Jidlo>();
ObservableCollection<Jidlo> mazanaJidla = new ObservableCollection<Jidlo>();
public MainWindow()
{
InitializeComponent();
}
#region Tlačítko Hledej, naplnění listboxu v gridu uprav jídlo
private void btnHledejU_Click(object sender, RoutedEventArgs e)
{
try
{
DateTime hledaneDatum = dtpDatumU.SelectedDate.Value;
string hledanaKategorie = cbDruhJidlaU.SelectedItem.ToString();
lbVysledkyU.Items.Clear();
using (JidelnicekEntities context = new JidelnicekEntities())
{
var jidlo = (from Jidlo in context.Jidloes where Jidlo.Datum == hledaneDatum orderby Jidlo.Nazev select Jidlo).ToList();
foreach (var j in jidlo)
{
lbVysledkyU.Items.Add(j);
upravovanaJidla.Add(j);
}
}
}
catch (Exception)
{
MessageBox.Show("Něco se nepovedlo.");
}
}
#endregion
}
Co je model view třída již bohužel netuším vůbec.
Filip Němeček:24.3.2018 18:00
Nevadí, já právě nevěděl, kolik toho už z WPF znáš...
Na Binding mrkni třeba sem - https://www.itnetwork.cz/…nin-bindingy Prakticky bych bez toho WPF aplikaci nedělal.
ListBox má ItemsSource místo DataSource, moje chyba
View model je součástí návrhového vzoru MVVM (Model-View-ViewModel), což je takové MVC ve WPF světě. Prakticky jde pouze o to, že složitější logiku máš oddělenou v jiné tříd, aby se ti nepletla v tříde pro okno, kde máš události tlačítek, konstruktor okna, Load metodu a tak podobně. Právě tomu se říká View Model. Model jsou potom data, která získáváš z databáze a View je tvoje okno.
Takže vlasně všechny vlastnosti (v tvém případě ty ObservableCollection<Jidlo>) a další bys dal do třídy MainWindowVM (jako view model) a v konstruktoru okna vytvořil instanci právě tohoto VM. Pak už právě stačí nastavit DataContext okna na tvoji vytvořenou třídu.
public partial class MainWindow : Window
{
private MainWindowVM _vm;
public MainWindow()
{
InitializeComponent();
_vm = new MainWindowVM();
}
}
Potom už můžeš mít logiku v MainWindowVM a všechny změny dat se ti přes nastavený Binding budou přepisovat do uživatelského rozhraní.
Mám za to, že ty ObservableCollection musí být deklarované jako properties, aby fungoval Binding. Což znamená něco takového:
public ObservableCollection<Jidlo> Jidla { get; set; } = new ObservableCollection<Jidlo>();
A Binding by pak vypadal asi nějak takhle:
<ListBox ItemsSource="{Binding Jidla, Mode=TwoWay} " />
+20 Zkušeností
+2,50 Kč

Poggy:25.3.2018 12:03
Nebyla by jíná cesta bez Bindingu, něco jako jsem myslel původně. Bindingy nedokáži rozchodit ani dle toho tutoriálu zde na upomínači. Některé pole mi fungují a některé ne... To už si to raději budu hlídat ručně jako ve WF.
jozef_i:25.3.2018 13:53
Ak máš v ListBoxu na Value property naviazaní Id objektu Jidlo, tak potom
po zvolení položky v ComboBoxe z ListBoxItem vytiahneš Id (ako?, stačí
ListBoxItem
pretypovať na triedu, s dvoma poliami Id, Nazev, ktorú si vytvoríš). A potom
cez Id v tabuľke Jidloes nášdeš potrebné Jidlo.
Michal Štěpánek:25.3.2018 20:40
Binding je právě ta jedna z "přidaných hodnot" WPF oproti WF. Umožní ti
bez problémů změnu hodnot v kontrolkách při nějaké události. Projdi si
ještě jednou ten tutoriál na WPF, je to tam krásně vysvětleno. Pochopil
jsem to z toho i já hňup a to je co říct...
Děkuji za obě odpovědi. Bohužel dnes jsem neměl moc času. Čím víc nad tím přemýšlím, tím víc by to bylo lepší udělat přes binding. Dle toho tutoriálu mí funguje jen něco. Např. dnešní datum a zobrazení narozenin. Ostatní ne. Ale asi mám něco špatně. Jen to najít. Tomu se pověnuji zítra.
<Window x:Class="WpfUpominacnarozenin.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:WpfUpominacnarozenin"
mc:Ignorable="d"
Title="Upominac narozenin" Height="350" Width="525" Background="WhiteSmoke" WindowStartupLocation="CenterScreen">
<Grid Margin="10" >
<!--Hlavní rozložení gridu-->
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="200"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="20"/>
<RowDefinition Height="30"/>
<RowDefinition Height="*"/>
<RowDefinition Height="30"/>
</Grid.RowDefinitions>
<!--Dnešní datum-->
<StackPanel Grid.Column="0" Grid.Row="0" Grid.ColumnSpan="2" Orientation="Horizontal">
<TextBlock Text="Dnes je "/>
<TextBlock Text="{Binding DnesniDatum, StringFormat=dd.MM.yyyy}"/>
</StackPanel>
<!--Nejbližší narozeni-->
<StackPanel Grid.Column="0" Grid.Row="1" Grid.ColumnSpan="2" Orientation="Horizontal">
<TextBlock Text="Nejbližší narozeniny má "/>
<TextBlock Text="{Binding NejblizsiOsoba.Jmeno}" />
<TextBlock Text=" za " />
<TextBlock Text="{Binding NejblizsiOsoba.ZbyvaDni}"/>
<TextBlock Text=" dní"/>
</StackPanel>
<!--ListBox osoby-->
<ListBox Name="lbOsoby" Grid.Column="0" Grid.Row="2" Margin="0,0,0,10"
ItemsSource="{Binding Osoby}"/>
<!--Kalendář + popisky na ním-->
<StackPanel Grid.Column="1" Grid.Row="2" Margin="10,0,0,0">
<!--TextBloky nad kalendářem-->
<StackPanel Orientation="Horizontal" DataContext="{Binding ElementName=lbOsoby, Path=SelectedItem}">
<TextBlock Text="Narozeniny: " />
<TextBlock Text="{Binding Narozeniny, StringFormat=dd.MM.yyyy}" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Vek: " />
<TextBlock Text="{Binding Vek}" />
</StackPanel>
<!--Kalendář-->
<Calendar x:Name="kalendar" SelectedDate="{Binding Narozeniny, Mode=OneTime}"
DisplayDate="{Binding Narozeniny, Mode=OneTime}"/>
</StackPanel>
<!--Spodní tlačítka-->
<StackPanel Grid.Column="0" Grid.Row="3" Grid.ColumnSpan="2" Orientation="Horizontal"
HorizontalAlignment="Center">
<Button x:Name="btnPridat" Content="_Přidat" Width="100" Margin="20 2" Click="btnPridat_Click" />
<Button x:Name="btnOdebrat" Content="_Odebrat" Width="100" Margin="20 2" Click="btnOdebrat_Click" />
</StackPanel>
</Grid>
</Window>
Ale na to snad přijdu.
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace WpfUpominacnarozenin
{
public class SpravceOsob
{
public ObservableCollection<Osoba> Osoby { get; set; }
public SpravceOsob()
{
Osoby = new ObservableCollection<Osoba>();
}
public Osoba NejblizsiOsoba { get; set; }
public DateTime DnesniDatum
{
get
{
return DateTime.Now;
}
}
private void NajdiNejblizsi()
{
var serazeneOsoby = Osoby.OrderBy(o => o.ZbyvaDni);
if (serazeneOsoby.Count() > 0)
NejblizsiOsoba = serazeneOsoby.First();
else
NejblizsiOsoba = null;
}
public void Pridej(string jmeno, DateTime? datumNarozeni)
{
if (jmeno.Length < 3)
throw new ArgumentException("Jméno je příliš krátké");
if (datumNarozeni == null)
throw new ArgumentException("Nebylo zadané datum narození");
if (datumNarozeni.Value.Date > DateTime.Today)
throw new ArgumentException("Datum narození nesmí být v budoucnosti");
Osoba osoba = new Osoba(jmeno, datumNarozeni.Value.Date);
Osoby.Add(osoba);
NajdiNejblizsi();
}
public void Odeber(Osoba osoba)
{
Osoby.Remove(osoba);
NajdiNejblizsi();
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace WpfUpominacnarozenin
{
public class Osoba
{
public string Jmeno { get; set; }
public DateTime Narozeniny { get; set; }
public int Vek
{
get
{
DateTime dnes = DateTime.Today;
int vek = dnes.Year - Narozeniny.Year;
if (dnes < Narozeniny.AddYears(vek))
vek--;
return vek;
}
}
public int ZbyvaDni
{
get
{
DateTime dnes = DateTime.Today;
DateTime dalsiNarozeniny = Narozeniny.AddYears(Vek + 1);
TimeSpan rozdil = dalsiNarozeniny - DateTime.Today;
return Convert.ToInt32(rozdil.TotalDays);
}
}
public override string ToString()
{
return Jmeno;
}
public Osoba(string jmeno, DateTime narozeniny)
{
Jmeno = jmeno;
Narozeniny = narozeniny;
}
}
}
Michal Štěpánek:25.3.2018 21:59
Chybu máš v tom, že nemáš "nabindovaný" ten StackPanel
<StackPanel Grid.Column="1" Grid.Row="2" Margin="10, 0, 0, 0" DataContext="{Binding ElementName=osobyListBox,Path=SelectedItem}">
Zobrazeno 11 zpráv z 11.