C# WPF Brak wyświetlenia danych z klasy w kontrolkach użyciem binding

0

Witam tworzę nowy obiekt którego dane chce wyświetlić w kontrolce, przypisałem przy użyciu binding odpowiednią listę ale po uruchomieniu programu nic się nie wyświetla.

Kod:
MainWindow.cs


    public partial class MainWindow : Window
    {
        public List<Movie> CurrentMovie = new List<Movie>();

        public MainWindow()
        {
            InitializeComponent();
    
            Movie Movie1 = new Movie();
            Movie1.Name = "Movie Test";
            Movie1.Year = 1999;
            Movie1.HoursWatched = 2000;
            Movie1.Cast = new List<string>() { "Actor 1", "Actor 2" };

            CurrentMovie.Add(Movie1);

            DataContext = this;

        }
    }

Movie.cs

    public class Movie
    {
        public string Name { get; set; }
        public short Year { get; set; }
        public List<string> Cast { get; set; }
        public long HoursWatched { get; set; }
    }

XAML:

    <Grid>
        <ListBox ItemsSource="{Binding CurrentMovie}" Margin="28,36,505,209">
            <Label Content="{Binding Name}"></Label>
        </ListBox>
    </Grid>
0

CurrentMovie powinno być właściwością {get;set;}, a nie polem.
Kolejna kwestia jest taka, że powinieneś rzucić event PropertyChanged aby odświeżyć to w xamlu

0

Zrobiłem tak jak napisałeś i program wysypuje mi się z komunikatem "Kolekcja elementów musi być pusta zanim zostanie użyte źródło ItemsSource.” i po skasowaniu

  <Label Content="{Binding Name}"></Label>

Problem się rozwiązuje.

0

Mam to samo. Nie wiem co Ci powiedzieć ¯_(ツ)_/¯ Spróbuj wdrożyć model MVVM bo code behind ( plik .xaml.cs) to zła praktyka.

0

Jeżeli chcesz aby dla każdego elementu w Twojej kolekcji: CurrentMovie wyświetlał się w liście tylko element Name musisz edytować wzór elementu. Domyślnie jest to TextBlock z tekstem Item.ToString(). Są dwie opcje rozwiązania problemu:

  1. Tworzymy pliki .cs o nazwie MainWindowViewModel oraz Movie.

MainWindowViewModel

public class MainWindowViewModel
    {
        public IList<Movie> Movies { get; private set; } = new ObservableCollection<Movie>();
    }

Movie

public class Movie
    {
        public string Name { get; set; }
        public short Year { get; set; }
        public List<string> Cast { get; set; }
        public long HoursWatched { get; set; }
    }

Celowo użyłem ObservableCollection, ponieważ podczas przy bindingu do zwykłej listy widok nie otrzyma powiadomienia o odświeżeniu np. gdy dodasz kolejny element. ObservableCollection sam powiadomi kontrolki powiązane. Tyczy się to tylko zmiany rozmiaru listy, jeżeli będziesz zmieniał wartości we właściwościach elementów musisz zaimplementować interfejs INotifyPropertyChanged.

  1. Bindujemy główny grid widoku z naszym ViewModelem i tworzymy listę. Następnie bindujemy ItemsSource z właściwością Movies
<Grid>
        <Grid.DataContext>
            <local:MainWindowViewModel x:Name="hViewModel"/>
        </Grid.DataContext>
        <ListBox ItemsSource="{Binding Path=Movies}">
            
        </ListBox>
    </Grid>
  1. Teraz aby wyświetlić właściwość Name dla każdego elementu możemy albo nadpisać metodę ToString() albo edytować wzór:
    a) Nadpisanie metody ToString()
public class Movie
    {
        public string Name { get; set; }
        public short Year { get; set; }
        public List<string> Cast { get; set; }
        public long HoursWatched { get; set; }

        public override string ToString()
        {
            return Name;
        }
    }

b) Edycja wzoru

<ListBox ItemsSource="{Binding Path=Movies}">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <Grid>
                        <TextBlock Text="{Binding Path=Name}"/>
                    </Grid>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>

Dodawanie elementów odbywa się następująco:

public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            Movie movie1 = new Movie() { Name = "MyFirstMovie" };
            Movie movie2 = new Movie() { Name = "MySecondMovie" };
            hViewModel.Movies.Add(movie1);
            hViewModel.Movies.Add(movie2);
        }
    }

Oraz efekt końcowy:
screenshot-20210314114139.png

0

Mam pytanie jak poprawnie INotifyPropertyChanged ponieważ ja chyba źle coś zrobiłem ponieważ gdy zmieniałem wartość to w klasie owszem zmienia się ale w UI się nie aktualizuje.

public class Movie : INotifyPropertyChanged
{
    public string Name { get; set; }
    public short Year { get; set; }
    public List<string> Cast { get; set; }
    public long HoursWatched { get; set; }

public event PropertyChangedEventHandler PropertyChanged;

    public string Name
    {
        get
        {
            return Name ;
        }
        set
        {
            if (value != Name )
            {

                Name  = value;
                OnPropertyChanged();
            }   
        }
    }

void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
    if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}

}
0

Musisz podać nazwę property. Wprost "Name" albo przez operator nameof.

OnPropertyChanged(nameof(Name))

0

To w ogóle się kompiluje? Masz dwa razy public string Name, w dodatku to drugie jest rekurencyjne.

Musisz mieć prywatne pole oraz publiczną (powiadamiającą właściwość).

Tak powinno zadziałać (tylko dla Name oczywiście).

public class Movie : INotifyPropertyChanged
{    
    public short Year { get; set; }
    public List<string> Cast { get; set; }
    public long HoursWatched { get; set; }

    public event PropertyChangedEventHandler PropertyChanged;

    private string name;
    public string Name
    {
        get
        {
            return name;
        }
        set
        {
            if (value != name)
            {

                name = value;
                OnPropertyChanged();
            }   
        }
    }

    private void OnPropertyChanged([CallerMemberName] string propertyName = "")
    {
        if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }

}
0

Mam teraz inny problem bo chcę użyć kontrolki TabControl i by zakładki do niej były podpięte do czegoś. To akurat mam zrobione tylko mam wrażenie że przełączanie się między zakładkami nie działa poprawnie. Problem inny mam taki że dziedziczone są 2 klasy po sobie ponieważ Visual Studio wyświetlał mi taki komunikat w konsoli: BindingExpression path error: 'languageTexts' property not found on 'object' ''TabItemViewModel'. Zakładki tworzą się poprawnie ale zawartość wyświetla tylko się AppName.TabItem.ViewModel zamiast mojego labela z zawartością.

XAML

    <Grid>
    <Grid.DataContext>
        <local:TabControlViewModel/>
    </Grid.DataContext>
    <TabControl ItemsSource="{Binding Items}">
        <TabControl.ItemTemplate>
            <DataTemplate>
                <TabItem Header="{Binding Name}" IsSelected="{Binding IsSelected}" MouseLeftButtonUp="TabItem_MouseLeftButtonUp">
                    <TabControl ItemsSource="{Binding Path=languageTexts}">
                        <TabControl.DataContext>
                            <local:LanguageTextViewModel/>
                        </TabControl.DataContext>
                        <TabControl.ItemTemplate>
                            <DataTemplate>
                                <StackPanel>
                                    <Label Content="{Binding Path=ownedVersion}" HorizontalAlignment="Left" Margin="10,5,0,0" VerticalAlignment="Top" FontSize="18"/>
                                </StackPanel>
                            </DataTemplate>
                        </TabControl.ItemTemplate>
                    </TabControl> 
                </TabItem>
            </DataTemplate>
        </TabControl.ItemTemplate>
    </TabControl>

</Grid>

C# TabControlViewModel

public class TabControlViewModel
{
    public ObservableCollection<TabItemViewModel> Items { get; set; } = new ObservableCollection<TabItemViewModel>
     {
                new TabItemViewModel {Name="Tab 1", IsSelected = true },
                new TabItemViewModel {Name="Tab 2" },
                new TabItemViewModel {Name="Tab 3" },
                new TabItemViewModel {Name="Tab 4" },
     };
}

C# TabItemViewModel

 public class TabItemViewModel : LanguageTextViewModel
{
    public string Name { get; set; }
    private bool isSelected;

    public bool IsSelected
    {
        get { return isSelected; }
        set
        {
            isSelected = value;
            DoSomethingWhenSelected();
        }
    }
    private void DoSomethingWhenSelected()
    {
        if (isSelected)
            Debug.WriteLine("You selected " + Name);
    }
}

C# LanguageTextViewModel

public class LanguageTextViewModel
{
    public ObservableCollection<LanguageTexts> languageTexts { get; private set; } = new ObservableCollection<LanguageTexts>();
}

C# LanguageTexts

 public class LanguageTexts : INotifyPropertyChanged
{
    private string _ownedVersion;
    public string ownedVersion
    {
        get
        {
            return _ownedVersion;
        }
        set
        {
            if (value != _ownedVersion)
            {

                _ownedVersion= value;
                OnPropertyChanged();
            }
        }
    }
0

Tłumaczenie błędu: Nie udało się utworzyć powiązania, ponieważ w TabItemViewModel nie znaleziono właściwości languageTexts.
Tutaj:

<TabControl ItemsSource="{Binding Path=languageTexts}">

Powinieneś w TabControlViewModel utworzyć IList<LanguageTexts>. Wtedy nastąpi binding do tej właściwości. Automatycznie każdy element przybierze element z listy jako swój DataContext, więc to można wtedy wywalić, a nawet trzeba, ponieważ jak tego nie zrobisz to nadpiszesz DataContext nowoutworzonym LanguageTextViewModel

<TabControl.DataContext>
     <local:LanguageTextViewModel/>
</TabControl.DataContext>
0

No dobrze tylko czemu nadal wyświetla mi się AppName.TabItem.ViewModel zamiast mojego labela z zawartością?

1 użytkowników online, w tym zalogowanych: 0, gości: 1