Witam!
Chciałem Was prosić o poradę bo utknąłem w martwym punkcie. Mianowicie chciałbym napisać pewną aplikację bazodanową. Zacząłem od przygotowania panelu do dodawania danych. Wcześniej programowałem trochę w technologii Windows Form, teraz próbuję ugryźć WPF. Utworzyłem w Visual Studio źródła danych, stworzyłem adapter, wypełniłem danymi kontrolkę DataGrid. Chciałbym aby dodawanie danych, akutalizacja i usuwanie odbywały się poprzez tą kontrolkę, bez zbędnych formularzy. Wszystkie pola w przypadku DataGridTextColumn działają prawidłowo. Natomiast mam potrzebę by w kolumnie, w której przechowywana jest ścieżka do pliku, jej dodanie odbywało się poprzez kliknięcie w komórkę co spowoduje otwarcie okna dialogowego w celu wybrania pliku. Udało mi się to zrealizować połowicznie przy użyciu DataGridTemplateColumn. Połowicznie, gdyż po wczytaniu pliku ścieżka jest widoczna w komórce DataGrid, ale nie jest zapisywana w bazie danych. Próbowałem wczoraj z tym sobie poradzić, jednak bezskutecznie.
Zastanawiam się także czy dobrą drogę obrałem. Datagrid jest wypełniany bezpośrednio z adaptera pracującego na źródle danych. Może powinien być połączony z jakąś strukturą (np. obiekt klasy lista). Szukając materiałów na ten temat trafiłem na kurs w którym kontrolka DataGrid połączona jest z obiektem klasy lista i poszczególne tabele mają swoją reprezentację w postaci klas. Czy można w ten sposób zaprojektować aplikację a potem "jakoś w prosty sposób połączyć z bazą danych"? Trafiłem także na materiały związane z Entity Framework i w sumie od natłoku koncepcji rozbolała mnie głowa. Czy w związku z tym mogę Was prosić o poradę w jaki sposób najlepiej zaprojektować taką aplikację?
Pracuję na UWP ze względu na dostęp do MS Store i osobiście nie wiem, czy będę w stanie pomóc, bo nie wiem czy dobrze zrozumiałem, więc proszę o jakiś fragment, bym to sobie mógł zwizualizować. W szczególności fragment axml z DataGrid i logikę jego wypełniania. Jak tworzysz procedurę zdarzeń typu SelectionChanged dla GridView, możesz dla konkretnego indeksu kontrolek wewnątrz DataGrid wykonać jakieś zadanie, np. aktualizację danych w bazie. Po związaniu kontrolek z objektami dane w obie strony będą wymieniane automatycznie gdy edytujesz kontrolkę, bądź prześlesz z programu dane do związanego obiektu. Być może chodzi Ci o INotifyPropertyChanged, ale nie wiem, bo bez przykładu nie skumam o co Ci chodzi..
namespace HB_test
{
public partial class Dzwiek_uc : UserControl
{
DB_HBDataSetTableAdapters.DzwiekTableAdapter adapter = new DB_HBDataSetTableAdapters.DzwiekTableAdapter();
DB_HBDataSet.DzwiekDataTable table = new DB_HBDataSet.DzwiekDataTable();
public Dzwiek_uc()
{
InitializeComponent();
adapter.Fill(table);
Dzwiek_dg.ItemsSource = table.DefaultView;
Dzwiek_dg.Items.Refresh();
}
private void aktualizacja_bazy ()
{
try
{
adapter.Update(table);
}
catch (Exception ex)
{
if (ex.Message.Contains("UNIQUE")) MessageBox.Show("Już istnieje w bazie danych!", "Alert", MessageBoxButton.OK, MessageBoxImage.Error);
if (ex.Message.Contains("DELETE")) MessageBox.Show("Nie można usunąć", "Alert", MessageBoxButton.OK, MessageBoxImage.Error);
adapter.Fill(table);
}
}
private void Dzwiek_dg_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
aktualizacja_bazy();
}
private void TextBlock_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
TextBlock tb = sender as TextBlock;
tb.Text = "To jest test";
aktualizacja_bazy();
}
}
}
XAML:
<DataGridTemplateColumn Header="Nazwa dźwięku">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock PreviewMouseLeftButtonUp="TextBlock_PreviewMouseLeftButtonUp">
<TextBlock.Text>
<Binding Mode="TwoWay" Path="Nazwa_dzwieku"></Binding>
</TextBlock.Text>
</TextBlock>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
Przyznam, że trochę nabałaganiłem w kodzie, więc żeby przesłać przykład musiałem jeszcze raz się pochylić i przypadkiem połowicznie zaczęło mi działać. Problem polega na tym iż gdy komórka dostaje tekst, to baza jest aktualizowana metodą Dzwiek_dg_SelectionChanged - czyli po zmianie komórki w datagrid, a nie w momencie przypisana tekstu do komórki. Przykład jest poglądowy, zamiast przypisywania tekstu zrobię sobie OpenDialog.
Nie widzę w ogóle elementu, do którego podpiąłeś kontrolkę i myślę, że niepotrzebnie też wydłużasz kod interfejsu. Akurat z ogólnie z axml zacząłem dopiero niedawno, ale fajnie się go pisze.
<TextBlock PreviewMouseLeftButtonUp="TextBlock_PreviewMouseLeftButtonUp">
<TextBlock.Text>
<Binding Mode="TwoWay" Path="Nazwa_dzwieku"></Binding>
</TextBlock.Text>
Tam gdzie wystarczy Ci podstawowa struktura spokojnie możesz napisać prościej:
<TextBlock Text="{Bingding Nazwa_dzwieku}" Mode="TwoWay"/>
Takie coś jest bardziej czytelne. Co innego np. jakbyś chciał coś w kontrolce zrobić inaczej, np. miałbyś kontrolkę z wartością Icon, ale chciałbyś użyć niestandardowej ikony. Taki myślę przykład kiedy warto rozbijać podstawową konstrukcję kontrolki.
Np. Podstawowa jest taka:
<NavigationViewItem x:Name="NVIBing" Content="Bing" Tag="Bing" Icon="Nie ma ikony bing"/>
Ale ponieważ nie ma ikony bing rozbijam:
<NavigationViewItem x:Name="NVIBing" Content="Bing wallpapers" Tag="Bing">
<NavigationViewItem.Icon>
<PathIcon x:Uid="PathIconBing" Data="{Binding}"/>
</NavigationViewItem.Icon>
</NavigationViewItem>
Podłączyłem Data, czyli wartość kodu Path ikony SVG do klucza PathIconBing.Data w pliku zasobów Resources.resw Dzięki temu mam niestandardową ikonę i nie muszę zawartości trzymać bezpośrednio w Data. Oczywiście przykład taki trochę z d*, ale myślę, że wiesz o co mi chodzi. Potem jest porządek w tym i łatwiej się połapać. Oczywiście w sumie jestem w tym nowy, więc tylko taka sugestia, pewnie można jeszcze prościej.
Jeśli chodzi o stronę z kodem, to tutaj masz wytłumaczone jak wymieniać dane i łapać zmiany:
Dokumentacja:
https://docs.microsoft.com/pl-pl/dotnet/framework/wpf/data/how-to-implement-property-change-notification
Jakiś wideo-poradnik:
Jedyny problem, który kiedykolwiek z tą metodą aktualizacji danych się może pojawić, to np. przy obrazach, czy plikach, gdzie ścieżki do źródła się nie zmieniają, ale pliki tak. Wówczas event nie wykryje zmiany, a nawet jak mu powiesz, że zmiana zaszła, to nie przeładuje danych. np. obrazy w interfejsie się nie zmienią, bo ścieżki się nie zmieniły i z wymuszaniem odświeżania miałem problem, bo nie działało, ale to jakoś udało mi się obejść.
Może trochę namieszałem i nie do końca się zrozumieliśmy. Zamiast tekstu który wstawiam będzie okno dialogowe do wskazania pliku. Baza ma przechowywać ścieżkę do pliku. Dlatego w XAML kontrolka jest rozbudowana. Po prostu po kliknięciu w komórkę kolumny powinno pojawić się okno dialogowe i ścieżka do wskazanego pliku powinna być zapisana w bazie po jego wybraniu z okna dialogowego. Udało mi się zrealizować mniej więcej tą funkcjonalność, ale zamiana wartości komórki datagrid z poziomu code-behind
tb.Text=dlg.Filname;
albo nawet
tb.Text="jakiś tam takst"
nie aktualizuje bazy. Może po prostu czegoś brakuje w kodzie. Może nie do końca rozumiem co napisałeś bo staram się dopiero jakoś ogarniać ten WPF :)