Binding. Dwie klasy, dwie kontrolki.

Binding. Dwie klasy, dwie kontrolki.
HI
  • Rejestracja:prawie 13 lat
  • Ostatnio:2 dni
  • Postów:1855
0

Witam.

Stawiam pierwsze kroczki w WPF i nie bardzo mogę zrozumieć DataContext.

Co powinienem zrobić, zakładając że chce:

  1. Wrzucić na formę dla komponenty Label
  2. Stworzyć dwie niezależne klasy np:
Kopiuj
    class Person
    {
        public string Name { get; set; }

        public Person(string name)
        {
            Name = name;
        }       
    }

    class Adress
    {
        public string PostCode { get; set; }

        public Adress(string postCode)
        {
            PostCode = postCode;
        }        
    }
  1. Stworzyć w klasie głównej obiekty powyższych klas., np
Kopiuj
Person p = new Person("Mateo");
Adress a = new Adress("00-001");
  1. Stworzyć binding tak aby Label1 prezentował to co jest we właściwości name obiektu p, a Label2 to co jest we właściwości PostCode obiektu a.

Z tego co znalazłem w sieci w przypadku jednego obiektu można go użyć jako DataContext i później wykorzystać w bindingu, ale nie bardzo mogę pojąć jak to zrobić w tym wypadku...

Z góry dziękuję za pomoc.


EDIT:

Udało mi się to zrobić nadając właściwość Name do poszczególnych Labeli i przypisując DataContext do poszczególnych kontrolek.

Jednak z tego co czytałem konieczność wstawienia Name do kontrolki oznacza, że prawdopodobnie coś robimy nie tak...

Czy w tym przypadku jest to prawidłowe rozwiązanie?

edytowany 3x, ostatnio: hipekk
Ktos
Moderator
  • Rejestracja:prawie 23 lata
  • Ostatnio:około 10 godzin
1

Musisz mieć kontrolkę, która zgrupuje ci te dwa labele - DataContext jest dziedziczony, więc jeżeli go nadasz rodzicowi (np. całemu oknu) to wszystkie kontrolki wewnątrz również go będą miały.

gswidwa
  • Rejestracja:ponad 13 lat
  • Ostatnio:około 5 lat
  • Postów:839
0

DataContext służy do wiązania danych. Jest to jakby wskaźnik na klasę z której chcesz wyciągać jakieś dane. Możesz użyć bindingu aby aktualizować dane w klasie, kiedy właściwość kontrolki się zmienia (BindingMode = OneWayToSource) lub aktualizować dane w kontrolce kiedy właściwość w klasie sie zmienia (BindingMode = OneWay) lub oba naraz (BindingMode = TwoWay).

Dobrą cechą jest to, że każda kontrolka-dziecko będzie dziedziczyć DataContext po rodzicach.

Ale tu nachodzi nas myśl: Kiedy kontrolka wie, że ma się zaktualizować? DataContext korzysta z interfejsu INotifyPropertyChanged. Za pomocą eventu w nim zawartego możemy poinformować wszystkie obiekty obserwujące naszą właściwość o tym, że wartość jaką reprezentuje zmieniła się.

Na początku stwórzmy sobie projekt MainWindow.xaml, MainWindow.xaml.cs.
Dodajmy teraz pustą klasę MainWindowView. ta klasa będzie reprezentowała nasz DataContext i z niej będziemy tworzyć wszystkie właściwości do Bindingu.
screenshot-20180704015424.png

Mamy więc naszą klasę do której będziemy bindować. Zwróć uwagę, na przestrzeń nazw w jakiej znajduje się Twój MainWindowView

Kopiuj
namespace testowa
{
    class MainWindowView
    {
    }
}

Kolejnym krokiem będzie zastąpienie obecnej wartości DataContext na naszą klasę MainWindowView. Otwórz MainWindow.xaml. Jeżeli twoja klasa znajduje się w innej przestrzeni nazw niż kontrolka możesz dodać nową przestrzeń nazw w ten sposób:

Kopiuj
xmlns:NowyTagDlaPrzestrzeniNazw="clr-namespace:testowa.MynameSpace"

Gdy już wiemy, że kod XAML będzie widział naszą klasę ustawmy wartość dla DataContext:

Kopiuj
<Grid>
        <Grid.DataContext>
            <local:MainWindowView x:Name="hMainWindowView"/>
        </Grid.DataContext>
    </Grid>

Przy okazji dodajmy od razu 2 TextBoxy:

Kopiuj
<StackPanel>
            <TextBox />
            <TextBox />
</StackPanel>

Teraz chcielibyśmy zrobić tak:
Pierwszy **TextBox **będzie zapisywał we właściwości to, co do niego wpiszemy.
Drugi **TextBox **będzie odczytywał zmiany we właściwości.

Zabierzmy się więc do utworzenia takowej właściwości: Do klasy **MainWindowView **zaimplementuj interfejs INotifyPropertyChanged, za pomocą którego będziesz informował o zmianie wartości właściwości:
screenshot-20180704021506.png

następnie utwórzmy właściwość typu string:
csharp public string Tekst;
I tu powstaje problem, ponieważ kiedy właściwość się zmienia musimy powiadomić o tej zmianie metodą OnPropertyChanged().
Rozwiązanie:

  1. Stworzymy właściwość "text" przechowującą wartość +
  2. Stworzymy właściwość "Text" za pomocą której będziemy odczytywać i zapisywać do poprzedniej właściwości
Kopiuj
        private string tekst;
        public string Tekst
        {
            get
            {
                return tekst;
            }
            set
            {
                tekst = value;
            }
        }

jedyne co nam teraz pozostało to użycie metody z interfejsu** INotifyPropertyChanged **od razu po tym kiedy właściwość zmieniła wartość (pamiętaj, że wartość zmienia się w naszej drugiej właściwości, pierwsza ją tylko przechowuje)

csharp OnPropertyChanged("Tekst");

teraz możemy zbindować **Property ** do naszego DataContext. Składnia jest następująca:

Kopiuj
<TextBox Text="{Binding Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"/>

Efekt jest następujący:
screenshot-20180704023346.png

Ale my przecież chcemy użyć właściwości z tej klasy, a nie tej klasy. Aby odwołać się do właściwości musimy użyć ścieżki tzn:

Kopiuj
<TextBox Text="{Binding Path=Tekst, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"/>

Od teraz kiedy zmieni się wartość właściwości "Tekst" zmieni się właściwość "Text" w kontrolce

Teraz kiedy wiesz jak to działa możemy zająć się twoim problemem:

  1. Stwórz swoje klasy w MainWindowView jako właściwości
  2. Pamiętaj, że Twoje klasy też muszą implementować interfejs INotifyPropertyChanged
  3. Aby odnosić się do elementów klasy spójrz na ten przykład i odezwij się jak będziesz miał jakiś problem

MainWindoowView:

Kopiuj
public class MainWindowView : INotifyPropertyChanged
    {
        public MainWindowView()
        {
            MyDog = new Dog("pupilek");
            MyCat = new Cat("kiciuś");
        }

        #region ProperyChanged
        public event PropertyChangedEventHandler PropertyChanged;
        public void OnPropertyChanged(string PropertyName)
        {
            this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(PropertyName));
        }
        #endregion

        private Dog myDog;
        public Dog MyDog
        {
            get
            {
                return myDog;
            }
            private set
            {
                myDog = value;
                OnPropertyChanged("MyDog");
            }
        }

        private Cat myCat;
        public Cat MyCat
        {
            get
            {
                return myCat;
            }
            private set
            {
                myCat = value;
                OnPropertyChanged("MyCat");
            }
        }
    }

    public class Animal : INotifyPropertyChanged
    {
        public Animal(string Name)
        {
            this.Name = Name;
        }

        #region ProperyChanged
        public event PropertyChangedEventHandler PropertyChanged;
        public void OnPropertyChanged(string PropertyName)
        {
            this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(PropertyName));
        }
        #endregion

        private string name;
        public string Name
        {
            get
            {
                return name;
            }
            private set
            {
                name = value;
                OnPropertyChanged("Name");
            }
        }
    }
    public class Dog : Animal, INotifyPropertyChanged
    {
        public Dog(string Name) : base(Name)
        {
            
        }
    }
    public class Cat : Animal, INotifyPropertyChanged
    {
        public Cat(string Name) : base(Name)
        {

        }
    }

Kod MainWindow.xaml

Kopiuj
<TextBox Text="{Binding Path=MyDog.Name, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"/>
<TextBox Text="{Binding Path=MyCat.Name, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"/>

Jeżeli coś nie jasne pytaj

SK
  • Rejestracja:ponad 8 lat
  • Ostatnio:około 2 lata
  • Postów:117
2

@gswidwa: dlaczego klasę reprezentującą viewmodel nazywasz MainWindowView?


grzesiek51114
grzesiek51114
  • Rejestracja:ponad 11 lat
  • Ostatnio:ponad 4 lata
  • Postów:2442
0

Ale przecież tutaj dzieją się jakieś niestworzone rzeczy. Najlepiej zrobić to prostym sposobem czyli po prostu przekazać odpowiednie obiekty do własności w VM i sprawa będzie załatwiona.

Kopiuj
//BindableBase
    public class BindableBase : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        public void RaisePropertyChange(string name)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
        }
    }
Kopiuj
//MainWindowViewModel oraz reszta klas
    class Person
    {
        public string Name { get; set; }

        public Person(string name)
        {
            Name = name;
        }
    }

    class Address
    {
        public string PostCode { get; set; }

        public Address(string postCode)
        {
            PostCode = postCode;
        }
    }

    public class MainWindowViewModel : BindableBase
    {
        public string PersonName
        {
            get => person.Name;
            set
            {
                person.Name = value;
                RaisePropertyChange(nameof(PersonName));
            }
        }

        public string PostalCode
        {
            get => address.PostCode;
            set
            {
                address.PostCode = value;
                RaisePropertyChange(nameof(PostalCode));
            }
        }

        private Person person;
        private Address address;

        public MainWindowViewModel()
        {
            person = new Person("Grzegorz");
            address = new Address("99-111");
        }
    }
Kopiuj
//code-behind
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            var vm = new MainWindowViewModel();
            DataContext = vm;
        }
    }

@gswidwa implementowanie klas przedstawionych przez autora wątku jako viewmodele jest kompletnie bez sensu. W ogóle łamiesz z premedytacją DRY implementując za każdym razem interfejs miast zrzucić go do jakiejś klasy i po niej dziedziczyć.

Aha, jeszcze XAML:

Kopiuj
<Window
    x:Class="App4P.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:local="clr-namespace:App4P"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    Title="MainWindow"
    SizeToContent="WidthAndHeight"
    mc:Ignorable="d">
    <StackPanel>
        <Label Content="{Binding PersonName, UpdateSourceTrigger=PropertyChanged}" />
        <Label Content="{Binding PostalCode, UpdateSourceTrigger=PropertyChanged}" />
    </StackPanel>
</Window>

Rzecz jasna zawsze można utworzyć UserControl, przypiąć do niego ww viewmodel i wrzucić to jako kontrolka do okna.

edytowany 7x, ostatnio: grzesiek51114
gswidwa
w swoim programie mam interfejs w innej klasie i dziedziczę ;)
gswidwa
  • Rejestracja:ponad 13 lat
  • Ostatnio:około 5 lat
  • Postów:839
0

Czasami się boję tu wchodzić i o coś pytać bo wychodzi tyle błędów wtedy w mojej aplikacji i złych przyzwyczajeń, że znów swoją apkę będę pisac chyba od nowa xD ( i tak od 6 lat)

edytowany 1x, ostatnio: gswidwa
grzesiek51114
grzesiek51114
Trening czyni mistrza, jak to mówią.
Kliknij, aby dodać treść...

Pomoc 1.18.8

Typografia

Edytor obsługuje składnie Markdown, w której pojedynczy akcent *kursywa* oraz _kursywa_ to pochylenie. Z kolei podwójny akcent **pogrubienie** oraz __pogrubienie__ to pogrubienie. Dodanie znaczników ~~strike~~ to przekreślenie.

Możesz dodać formatowanie komendami , , oraz .

Ponieważ dekoracja podkreślenia jest przeznaczona na linki, markdown nie zawiera specjalnej składni dla podkreślenia. Dlatego by dodać podkreślenie, użyj <u>underline</u>.

Komendy formatujące reagują na skróty klawiszowe: Ctrl+B, Ctrl+I, Ctrl+U oraz Ctrl+S.

Linki

By dodać link w edytorze użyj komendy lub użyj składni [title](link). URL umieszczony w linku lub nawet URL umieszczony bezpośrednio w tekście będzie aktywny i klikalny.

Jeżeli chcesz, możesz samodzielnie dodać link: <a href="link">title</a>.

Wewnętrzne odnośniki

Możesz umieścić odnośnik do wewnętrznej podstrony, używając następującej składni: [[Delphi/Kompendium]] lub [[Delphi/Kompendium|kliknij, aby przejść do kompendium]]. Odnośniki mogą prowadzić do Forum 4programmers.net lub np. do Kompendium.

Wspomnienia użytkowników

By wspomnieć użytkownika forum, wpisz w formularzu znak @. Zobaczysz okienko samouzupełniające nazwy użytkowników. Samouzupełnienie dobierze odpowiedni format wspomnienia, zależnie od tego czy w nazwie użytkownika znajduje się spacja.

Znaczniki HTML

Dozwolone jest używanie niektórych znaczników HTML: <a>, <b>, <i>, <kbd>, <del>, <strong>, <dfn>, <pre>, <blockquote>, <hr/>, <sub>, <sup> oraz <img/>.

Skróty klawiszowe

Dodaj kombinację klawiszy komendą notacji klawiszy lub skrótem klawiszowym Alt+K.

Reprezentuj kombinacje klawiszowe używając taga <kbd>. Oddziel od siebie klawisze znakiem plus, np <kbd>Alt+Tab</kbd>.

Indeks górny oraz dolny

Przykład: wpisując H<sub>2</sub>O i m<sup>2</sup> otrzymasz: H2O i m2.

Składnia Tex

By precyzyjnie wyrazić działanie matematyczne, użyj składni Tex.

<tex>arcctg(x) = argtan(\frac{1}{x}) = arcsin(\frac{1}{\sqrt{1+x^2}})</tex>

Kod źródłowy

Krótkie fragmenty kodu

Wszelkie jednolinijkowe instrukcje języka programowania powinny być zawarte pomiędzy obróconymi apostrofami: `kod instrukcji` lub ``console.log(`string`);``.

Kod wielolinijkowy

Dodaj fragment kodu komendą . Fragmenty kodu zajmujące całą lub więcej linijek powinny być umieszczone w wielolinijkowym fragmencie kodu. Znaczniki ``` lub ~~~ umożliwiają kolorowanie różnych języków programowania. Możemy nadać nazwę języka programowania używając auto-uzupełnienia, kod został pokolorowany używając konkretnych ustawień kolorowania składni:

```javascript
document.write('Hello World');
```

Możesz zaznaczyć również już wklejony kod w edytorze, i użyć komendy  by zamienić go w kod. Użyj kombinacji Ctrl+`, by dodać fragment kodu bez oznaczników języka.

Tabelki

Dodaj przykładową tabelkę używając komendy . Przykładowa tabelka składa się z dwóch kolumn, nagłówka i jednego wiersza.

Wygeneruj tabelkę na podstawie szablonu. Oddziel komórki separatorem ; lub |, a następnie zaznacz szablonu.

nazwisko;dziedzina;odkrycie
Pitagoras;mathematics;Pythagorean Theorem
Albert Einstein;physics;General Relativity
Marie Curie, Pierre Curie;chemistry;Radium, Polonium

Użyj komendy by zamienić zaznaczony szablon na tabelkę Markdown.

Lista uporządkowana i nieuporządkowana

Możliwe jest tworzenie listy numerowanych oraz wypunktowanych. Wystarczy, że pierwszym znakiem linii będzie * lub - dla listy nieuporządkowanej oraz 1. dla listy uporządkowanej.

Użyj komendy by dodać listę uporządkowaną.

1. Lista numerowana
2. Lista numerowana

Użyj komendy by dodać listę nieuporządkowaną.

* Lista wypunktowana
* Lista wypunktowana
** Lista wypunktowana (drugi poziom)

Składnia Markdown

Edytor obsługuje składnię Markdown, która składa się ze znaków specjalnych. Dostępne komendy, jak formatowanie , dodanie tabelki lub fragmentu kodu są w pewnym sensie świadome otaczającej jej składni, i postarają się unikać uszkodzenia jej.

Dla przykładu, używając tylko dostępnych komend, nie możemy dodać formatowania pogrubienia do kodu wielolinijkowego, albo dodać listy do tabelki - mogłoby to doprowadzić do uszkodzenia składni.

W pewnych odosobnionych przypadkach brak nowej linii przed elementami markdown również mógłby uszkodzić składnie, dlatego edytor dodaje brakujące nowe linie. Dla przykładu, dodanie formatowania pochylenia zaraz po tabelce, mogłoby zostać błędne zinterpretowane, więc edytor doda oddzielającą nową linię pomiędzy tabelką, a pochyleniem.

Skróty klawiszowe

Skróty formatujące, kiedy w edytorze znajduje się pojedynczy kursor, wstawiają sformatowany tekst przykładowy. Jeśli w edytorze znajduje się zaznaczenie (słowo, linijka, paragraf), wtedy zaznaczenie zostaje sformatowane.

  • Ctrl+B - dodaj pogrubienie lub pogrub zaznaczenie
  • Ctrl+I - dodaj pochylenie lub pochyl zaznaczenie
  • Ctrl+U - dodaj podkreślenie lub podkreśl zaznaczenie
  • Ctrl+S - dodaj przekreślenie lub przekreśl zaznaczenie

Notacja Klawiszy

  • Alt+K - dodaj notację klawiszy

Fragment kodu bez oznacznika

  • Alt+C - dodaj pusty fragment kodu

Skróty operujące na kodzie i linijkach:

  • Alt+L - zaznaczenie całej linii
  • Alt+, Alt+ - przeniesienie linijki w której znajduje się kursor w górę/dół.
  • Tab/⌘+] - dodaj wcięcie (wcięcie w prawo)
  • Shit+Tab/⌘+[ - usunięcie wcięcia (wycięcie w lewo)

Dodawanie postów:

  • Ctrl+Enter - dodaj post
  • ⌘+Enter - dodaj post (MacOS)