Vydělávej až 160.000 Kč měsíčně! Akreditované rekvalifikační kurzy s garancí práce od 0 Kč. Více informací.
Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.
Avatar
Poggy
Člen
Avatar
Poggy:5.5.2019 17:55

Snažím se pochopit a natrénovat databinding ve WPF. Chci aby změna jedné vlastnosti vyvolala změnu jiné vlastnosti a automaticky se přepočítala a zobrazila. Uživatel může některé vlastnosti měnit. Nedaří se mi dosáhnout uspokojivého funkčního řešení. Některé vlastnosti se při startu aplikace nezobrazí, jiné ano. Při přepínání mezi gridy v okně také nedojde k zobrazení správné hodnoty.

Zkusil jsem: Myslím, že problém je v pochopení módů bindingu x XAML.
Aplikace má dvě třídy:
Byt: vlastnosti zobrazuje aplikace, v mainWindow mám vytvořenou aktivní instanci bytu a datacontex na tuto instanci
Spravce bytů: observablecollec­tion s byty, ukládání /načítání dat do XML, vyhledávání v kolekci

Celé zdrojové kódy:
XAML:

<Window x:Class="WpfInotifyPropertyChange_test.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:WpfInotifyPropertyChange_test"
        mc:Ignorable="d"
        Title="MainWindow" Height="250" Width="400" WindowStartupLocation="CenterScreen">
    <Grid Margin="10">
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="45"/>
            <RowDefinition/>
        </Grid.RowDefinitions>

        <!--Cislo bytu-->
        <StackPanel Orientation="Horizontal" VerticalAlignment="Center" Margin="5">
            <Label Content="Byt číslo: "/>
            <Label Content="{Binding CisloBytu, Mode=TwoWay}"/>
            <Button x:Name="btnPredchozi"   Content="_Predchozi"   Height="23" Width="75" Margin="10,0,0,0" Click="btnPredchozi_Click"/>
            <Button x:Name="btnNasledujici" Content="_Nasledujici" Height="23" Width="75" Margin="10,0,0,0" Click="btnNasledujici_Click"/>
            <Button x:Name="btnUloz"        Content="_Ulož změny"  Height="23" Width="75" Margin="10,0,0,0" Click="btnUloz_Click"/>
        </StackPanel>

        <!--Grid prvniho bytu-->
        <Grid x:Name="grid1A" Grid.Row="1" Visibility="Visible" Margin="10" Background="Beige">
            <Grid.ColumnDefinitions>
                <ColumnDefinition/>
                <ColumnDefinition/>
            </Grid.ColumnDefinitions>

            <!--Labely-->
            <StackPanel Orientation="Vertical" Grid.Column="0" Grid.Row="1" Margin="5">
                <Label Content="Sazba DPH (%):"  Target="{Binding ElementName=txtDPH}"           Margin="5"/>
                <Label Content="Cena bez DPH"    Target="{Binding ElementName=txtCenaBezDPH}"    Margin="5"/>
                <Label Content="Cena včetně DPH" Target="{Binding ElementName=txtCenaVcetneDPH}" Margin="5"/>
            </StackPanel>

            <!--Text boxy-->
            <StackPanel Orientation="Vertical" Grid.Column="1" Grid.Row="1" Margin="5">
                <TextBox x:Name="txtDPH" Text="{Binding Path=DPH, Mode=OneWayToSource}" Height="25" Margin="5"
                         TabIndex="0" TextChanged="textBoxy_TextChanged"/>
                <TextBox x:Name="txtCenaBezDPH" Text="{Binding Path=CenaBezDPH, Mode=OneWayToSource}" Height="25" Margin="5"
                         TabIndex="1" TextChanged="textBoxy_TextChanged"/>
                <TextBox x:Name="txtCenaVcetneDPH" Text="{Binding Path=CenaVcetneDPH, Mode=OneWay}" Height="25" Margin="5"
                         IsReadOnly="True"/>
            </StackPanel>
        </Grid>

        <!--Grid druheho bytu-->
        <Grid x:Name="grid2A" Grid.Row="1" Visibility="Hidden" Margin="10" Background="Azure">
            <Grid.ColumnDefinitions>
                <ColumnDefinition/>
                <ColumnDefinition/>
            </Grid.ColumnDefinitions>

            <!--Labely-->
            <StackPanel Orientation="Vertical" Grid.Column="0" Grid.Row="1" Margin="5">
                <Label Content="Sazba DPH (%):"  Target="{Binding ElementName=txtDPH2A}"           Margin="5"/>
                <Label Content="Cena bez DPH"    Target="{Binding ElementName=txtCenaBezDPH2A}"    Margin="5"/>
                <Label Content="Cena včetně DPH" Target="{Binding ElementName=txtCenaVcetneDPH2A}" Margin="5"/>
            </StackPanel>

            <!--Text boxy-->
            <StackPanel Orientation="Vertical" Grid.Column="1" Grid.Row="1" Margin="5">
                <TextBox x:Name="txtDPH2A" Text="{Binding Path=DPH, Mode=OneWayToSource}" Height="25" Margin="5"
                         TabIndex="0" TextChanged="textBoxy_TextChanged"/>
                <TextBox x:Name="txtCenaBezDPH2A" Text="{Binding Path=CenaBezDPH, Mode=OneWayToSource}" Height="25" Margin="5"
                         TabIndex="1" TextChanged="textBoxy_TextChanged"/>
                <TextBox x:Name="txtCenaVcetneDPH2A" Text="{Binding Path=CenaVcetneDPH, Mode=OneWay}" Height="25" Margin="5"
                         IsReadOnly="True"/>
            </StackPanel>
        </Grid>
    </Grid>
</Window>

Třída Byt:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ComponentModel;

namespace WpfInotifyPropertyChange_test
{
    public class Byt : INotifyPropertyChanged
    {
        private double cenaBezDPH;
        private double cenaVcetneDPH;
        private double dph;
        private string cisloBytu;

        public Byt()
        {

        }

        public Byt(string cisloBytu)
        {
            CisloBytu = cisloBytu;
        }

        public double CenaBezDPH
        {
            get
            {
                return cenaBezDPH;
            }

            set
            {
                if (cenaBezDPH != value)
                {
                    cenaBezDPH = value;
                    OnPropertyChanged("CenaVcetneDPH");
                }
            }
        }

        public double CenaVcetneDPH
        {
            get
            {
                return cenaBezDPH * ((dph/100) + 1);
            }

            set
            {
                if (cenaVcetneDPH != value)
                {
                    cenaVcetneDPH = value;
                    OnPropertyChanged("CenaBezDPH");
                }
            }
        }

        public double DPH
        {
            get
            {
                return dph;
            }

            set
            {
                if (dph != value)
                {
                    dph = value;
                    OnPropertyChanged("CenaVcetneDPH");
                }
            }
        }

        public string CisloBytu
        {
            get
            {
                return cisloBytu;
            }

            set
            {
                if (cisloBytu != value)
                    cisloBytu = value;
                OnPropertyChanged("CisloBytu");
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        private void OnPropertyChanged(string parameter)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(parameter));
            }
        }

        public override string ToString()
        {
            return string.Format("{0} {1} {2} {3}", CisloBytu, CenaBezDPH, CenaVcetneDPH, DPH);
        }
    }
}

Třída Spávce bytů:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ComponentModel;
using System.Collections.ObjectModel;
using System.IO;
using System.Xml.Serialization;

namespace WpfInotifyPropertyChange_test
{
    class SpravceBytu
    {
        public ObservableCollection<Byt> Byty { get; set; }

        public Byt byt;

        private string cesta = "data.xml";

        public SpravceBytu()
        {
            Byty = new ObservableCollection<Byt>();
        }

        public void AktualizujByt(Byt bytFormular)
        {
            // Najdi byt dle cisla bytu
            byt = Byty.FirstOrDefault(b => b.CisloBytu == bytFormular.CisloBytu);

            if (byt == null)
            {
                Byty.Add(bytFormular);
            }

            // aktualizuj byt v kolekci shodne s formularem
            byt = bytFormular;

            // Uloz zmeny do xml
            UlozData();
        }

        public object NactiByt(string cisloBytu)
        {
            byt = Byty.FirstOrDefault(b => b.CisloBytu == cisloBytu);

            if (byt == null)
            {
                try
                {
                    byt.CisloBytu = cisloBytu;
                    Byty.Add(byt);
                }
                catch (Exception)
                {

                    throw new ArgumentException("Načtení bytu se nezdařilo.");
                }
            }

            return byt;
        }

        public void NactiData()
        {
            XmlSerializer s = new XmlSerializer(Byty.GetType());

            using (StreamReader sr = new StreamReader(cesta))
            {
                Byty = (ObservableCollection<Byt>)s.Deserialize(sr);
            }

        }

        public void UlozData()
        {
            XmlSerializer s = new XmlSerializer(Byty.GetType());

            using (StreamWriter sw = new StreamWriter(cesta))
            {
                s.Serialize(sw, Byty);
            }
        }

    }
}

Data ve zdrojovém XML:

<?xml version="1.0" encoding="utf-8"?>
<ArrayOfByt xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Byt>
    <CenaBezDPH>5000000</CenaBezDPH>
    <CenaVcetneDPH>6050000</CenaVcetneDPH>
    <DPH>21</DPH>
    <CisloBytu>1A</CisloBytu>
  </Byt>
  <Byt>
    <CenaBezDPH>2000000</CenaBezDPH>
    <CenaVcetneDPH>2420000</CenaVcetneDPH>
    <DPH>21</DPH>
    <CisloBytu>2A</CisloBytu>
  </Byt>
</ArrayOfByt>

Chci docílit: Každý byt má přiřazen vlastní grid, gridy skrývám a odkrývám dle aktivního bytu. Gridy přepínají dvě tlačítka, ale bohužel se mi nezobrazují korektní informace o správném bytu.
Poradíte někdo kde mám chybu. Pole DPH a Cena bez DPH na přepínání reagují, gridy se mění (mám jiný backgroud), ale hodnota cena s DPH zůstává stejná a nedojde ani k uložení změn. Případně jak to udělat jinak a lépe?
Za případnou radu děkuji.

 
Odpovědět
5.5.2019 17:55
Avatar
Poggy
Člen
Avatar
Poggy:5.5.2019 20:12

Zapomněl jsem doplnit kód MainWindow:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace WpfInotifyPropertyChange_test
{
    /// <summary>
    /// Interakční logika pro MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        Byt aktivniByt { get; set; }
        SpravceBytu spravceBytu = new SpravceBytu();

        public MainWindow()
        {
            InitializeComponent();
            try
            {
                spravceBytu.NactiData();
                aktivniByt = new Byt();
                aktivniByt.CisloBytu = "1A";
                aktivniByt = (Byt)spravceBytu.NactiByt(aktivniByt.CisloBytu);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "Chyba", MessageBoxButton.OK, MessageBoxImage.Exclamation);
            }
            this.DataContext = aktivniByt;
        }

        private void btnPredchozi_Click(object sender, RoutedEventArgs e)
        {
            aktivniByt.CisloBytu = "1A";
            grid2A.Visibility = Visibility.Hidden;
            grid1A.Visibility = Visibility.Visible;
            aktivniByt = (Byt)spravceBytu.NactiByt("1A");
        }

        private void btnNasledujici_Click(object sender, RoutedEventArgs e)
        {
            aktivniByt.CisloBytu = "2A";
            grid1A.Visibility = Visibility.Hidden;
            grid2A.Visibility = Visibility.Visible;
            aktivniByt = (Byt)spravceBytu.NactiByt("2A");
        }

        private void textBoxy_TextChanged(object sender, TextChangedEventArgs e)
        {
            //spravceBytu.AktualizujByt(aktivniByt); Chci potom doplnit automaticke ukladani na textchanged
        }

        private void btnUloz_Click(object sender, RoutedEventArgs e)
        {
            spravceBytu.AktualizujByt(aktivniByt);
        }
    }
}
 
Nahoru Odpovědět
5.5.2019 20:12
Avatar
Poggy
Člen
Avatar
Poggy:5.5.2019 21:12

Tak už jsem zjistil kde byla chyba. Respektive dvě chyby.

Mód v XAML byl špatně, jak jsem trochu tušil.
Funční je to takto:

<!--Text boxy-->
<StackPanel Orientation="Vertical" Grid.Column="1" Grid.Row="1" Margin="5">
    <TextBox x:Name="txtDPH" Text="{Binding Path=DPH, Mode=TwoWay}" Height="25" Margin="5"
             TabIndex="0" TextChanged="textBoxy_TextChanged"/>
    <TextBox x:Name="txtCenaBezDPH" Text="{Binding Path=CenaBezDPH, Mode=TwoWay}" Height="25" Margin="5"
             TabIndex="1" TextChanged="textBoxy_TextChanged"/>
    <TextBox x:Name="txtCenaVcetneDPH" Text="{Binding Path=CenaVcetneDPH, Mode=OneWay}" Height="25" Margin="5"
             IsReadOnly="True"/>
</StackPanel>

Druhá a zásadnější chyba byla v mainwindow. Na každou událost click musím založit instanci bytu a navést na něj datacontext. Viz. zde:

private void btnPredchozi_Click(object sender, RoutedEventArgs e)
{
    grid2A.Visibility = Visibility.Hidden;
    grid1A.Visibility = Visibility.Visible;
    Byt aktivniByt = new Byt();  // zde musim vytvorit instanci
    aktivniByt = (Byt)spravceBytu.NactiByt("1A");
    this.DataContext = aktivniByt;  // zde na ni navest datacontet
}

private void btnNasledujici_Click(object sender, RoutedEventArgs e)
{
    //aktivniByt = null;
    grid1A.Visibility = Visibility.Hidden;
    grid2A.Visibility = Visibility.Visible;
    Byt aktivniByt = new Byt();
    aktivniByt = (Byt)spravceBytu.NactiByt("2A");
    this.DataContext = aktivniByt;
}

Nyní mi to funguje dle představ.

 
Nahoru Odpovědět
5.5.2019 21:12
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 3 zpráv z 3.