Problem z odświeżaniem ComboBoxa

0

Witam, mam taki mały problemik z którym sobie nie umie poradzić. Mam główne okno w którym mam buttona który otwiera mi okno konfiguracji. Gdy okno się otwiera to combobox wywyołuje geta w którym jest metoda która pobiera do niego dane z xmla i prasuje do sringa. Tu wszystko ładnie śmiga problem zaczyna się jeżeli z poziomu okna dodaje nowy element do xmla. Nowe dane dopisują się do xmla oraz dodaję do listy nowy element i wyświetla mi go pod debbugerem ale już nie przesyła go do kontrolki comboboxa, dopiero pojawia się jak wyłączę i włączę okno od nowa.

propertis związane z lista przesyłana do comboboxa

private List<string> _categoryList;

        public List<string> CategoryList
        {
            get
            {
                return _categoryList = PRSS_Logika.Methods.ConfigWindowMethods.XmlToListUpdate();
            }
            set
            {
                _categoryList = value;
                OnPropertyChanged(nameof(CategoryList));
            }
        }

Tak to binduje w xamlu

<ComboBox x:Name="cmbKategorie" ItemsSource="{Binding CategoryList, UpdateSourceTrigger=PropertyChanged}" SelectedValue="{Binding CategorySelect}" Height="25" Margin="5,5,5,5" />
1

Zamiast lisy zastosuj ObservableCollection.

0

Chciałbym zrobic coś takiego:
title
I powiedz mi jak zrobić ObservableCollection żeby dało się edytować kategorię i kanały? Model bazy danych wygląda tak:

public class RSSCategory
    {
        [Key]
        public int RSSCategoryID { get; set; }
        public string Name { get; set; }
        public virtual ICollection<RSSChannel> RSSChannels { get; set; }
    }
public class RSSChannel
    {
        [Key]
        public int RSSChannelID { get; set; }
        public string Name { get; set; }
        public string Link { get; set; }
        public int RSSCategoryID { get; set; }
        public virtual ICollection<RSSFeed> RSSFeeds { get; set; }

        public virtual RSSCategory RSSCategory { get; set; }
    }
0

No ale co rozumiesz przez edytować? Ma się pojawić nowe okno czy co ma się robić? Opisz dokładnie.

0

Chce zrobić to w ten sposób, gdy wpiszę nazwę kategorii i kliknę "Dodaj kategorię" to żeby dodawała się do comboboxa "Kategoria", następnie jeżeli wybierzemy w comboboxie "Kategoria" jakąś kategorię to chcę żeby wszystkie kanały przypisane do tej kategorii wyświetlały się w comboboxie "Kanały dla wybranej kategorii" i jeżeli np dodamy nowy kanał to ma się pojawiać w comboboxie od razu po naciśnięciu przycisku "Dodaj kanał" a nie dopiero po otwarciu okna na nowo. Mam nadzieję że w miarę jasno to napisałem ;)

1

Dobrze, najlepiej utwórz sobie nowy projekt, ponieważ napisałem coś takiego:

Najpierw XAML

<Window x:Class="_4programmersWPF.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:_4programmersWPF"
        xmlns:vm="clr-namespace:_4programmersWPF"
        mc:Ignorable="d"
        Name="Window" WindowStartupLocation="CenterScreen" 
        SizeToContent="Height" Width="230.674">
  <Window.DataContext>
    <vm:MainWindowVM/>
  </Window.DataContext>
  <StackPanel>
    <Label Margin="2" FontSize="18" Padding="2"
           Content="Categories:"/>
    <TextBox Margin="2" FontSize="18" Padding="2"
             Text="{Binding CategoryName,UpdateSourceTrigger=PropertyChanged}">
      <TextBox.InputBindings>
        <KeyBinding Key="Return" Command="{Binding AddCategory}"/>
      </TextBox.InputBindings>
    </TextBox>
    <Button Margin="2" FontSize="18" Padding="2"
            Content="Add Category"
            Command="{Binding AddCategory}"/>
    <ComboBox FontSize="18" Padding="2" Margin="2"
              DisplayMemberPath="Name"
              ItemsSource="{Binding Categories,UpdateSourceTrigger=PropertyChanged}"
              SelectedItem="{Binding SelectedCategory,UpdateSourceTrigger=PropertyChanged}"/>
    <Label Margin="2" FontSize="18" Padding="2"
           Content="Channels:"/>
    <TextBox Margin="2" FontSize="18" Padding="2"
             Text="{Binding ChannelName,UpdateSourceTrigger=PropertyChanged}">
      <TextBox.InputBindings>
        <KeyBinding Key="Return" Command="{Binding AddChannel}"/>
      </TextBox.InputBindings>
    </TextBox>
    <Button Margin="2" FontSize="18" Padding="2"
            Content="Add Channel"
            Command="{Binding AddChannel}"/>
    <ComboBox FontSize="18" Padding="2" Margin="2"
              ItemsSource="{Binding SelectedCategory.Channels,UpdateSourceTrigger=PropertyChanged}"
              SelectedItem="{Binding SelectedCategory.SelectedChannel,UpdateSourceTrigger=PropertyChanged}"/>
  </StackPanel>
</Window>

Teraz viewmodel wybranej kategorii

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

namespace _4programmersWPF
{
    public class CategoryVM : ViewModelBase
    {
        public string Name { get; set; }

        private IList<string> channels;
        public IList<string> Channels
        {
            get { return this.channels; }
            set
            {
                this.channels = value;
                this.OnPropertyChanged(nameof(this.Channels));
            }
        }

        private string selectecChannel;
        public string SelectedChannel
        {
            get { return this.selectecChannel; }
            set
            {
                this.selectecChannel = value;
                this.OnPropertyChanged(nameof(this.SelectedChannel));
            }
        }

        public CategoryVM()
        {
            this.Channels = new ObservableCollection<string>();
        }
    }
}

Viewmodel okna głownego

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;

namespace _4programmersWPF
{
    public class MainWindowVM : ViewModelBase
    {
        private string categoryName;
        public string CategoryName
        {
            get { return this.categoryName; }
            set
            {
                this.categoryName = value;
                this.OnPropertyChanged(nameof(this.CategoryName));
            }
        }

        private string channelName;
        public string ChannelName
        {
            get { return this.channelName; }
            set
            {
                this.channelName = value;
                this.OnPropertyChanged(nameof(this.ChannelName));
            }
        }

        private IList<CategoryVM> categories;
        public IList<CategoryVM> Categories
        {
            get { return this.categories; }
            set
            {
                this.categories = value;
                this.OnPropertyChanged(nameof(this.Categories));
            }
        }

        private CategoryVM selectedCategory;
        public CategoryVM SelectedCategory
        {
            get { return this.selectedCategory; }
            set
            {
                this.selectedCategory = value;
                this.OnPropertyChanged(nameof(this.SelectedCategory));

                if (this.SelectedCategory != null)
                {
                    this.SelectedCategory.SelectedChannel = null;
                }
            }
        }

        public MainWindowVM()
        {
            this.Categories = new ObservableCollection<CategoryVM>();
        }

        public ICommand AddCategory
        {
            get
            {
                return new RelayCommand(() =>
                {
                    this.Categories.Add(new CategoryVM { Name = this.CategoryName });
                    this.SelectedCategory = this.Categories.SingleOrDefault(c => c.Name == this.CategoryName);
                    this.SelectedCategory.SelectedChannel = null;
                    this.CategoryName = "";
                },

                () => !string.IsNullOrWhiteSpace(this.CategoryName));
            }
        }

        public ICommand AddChannel
        {
            get
            {
                return new RelayCommand(() =>
                {
                    this.SelectedCategory.Channels.Add(this.ChannelName);
                    this.SelectedCategory.SelectedChannel = this.SelectedCategory.Channels.SingleOrDefault(c => c == this.ChannelName);
                    this.ChannelName = "";
                },

                () => !string.IsNullOrWhiteSpace(this.ChannelName) && this.SelectedCategory != null);
            }
        }
    }
}

I standardowa klasa ViewModelBase

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

namespace _4programmersWPF
{
    public class ViewModelBase : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        public void OnPropertyChanged(string property)
        {
            this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
        }
    }
}

Działa jak chciałeś. Poskładaj, skompiluj, a się przekonasz ;)

0

No to działa tak jak należy, a nawet i lepiej ;) Ale teraz mam pytanie, skoro chce pobrać dane z bazy danych to dla RSSCategory i RSSChannel mam zrobic osobne View Modele i je przemapować, zrobić osobne propercje i do nich przypisać dane? Coś takiego?

public class RSSCategoryViewModel : ViewModelBase
    {
        private int _categoryID;

        public int CategoryID
        {
            get { return _categoryID; }
            set
            {
                _categoryID = value;
                OnPropertyChanged(nameof(this.CategoryID));
            }
        }

        private string _categoryName;

        public string CategoryName
        {
            get { return _categoryName; }
            set
            {
                _categoryName = value;
                OnPropertyChanged(nameof(this.CategoryName));
            }
        }

        private RSSChannelViewModel _selectedChannel;
        public RSSChannelViewModel SelectedChannel
        {
            get { return this._selectedChannel; }
            set
            {
                this._selectedChannel = value;
                this.OnPropertyChanged(nameof(this.SelectedChannel));
            }
        }



        private IList<RSSChannelViewModel> _channelLists;

        public IList<RSSChannelViewModel> ChannelLists
        {
            get { return _channelLists; }
            set
            {
                _channelLists = value;
                OnPropertyChanged(nameof(ChannelLists));
            }
        }


    }
1

Nigdy nie pchaj encji z bazy danych do widoku. Model powinien dostarczyć wyższym warstwom obiekty typu DTO. Dopiero takie obiekty powinno się mapować na viewmodele etc...

Czyli... zwyczajnie DTO z bazy danych przepisać na viewmodele z własnościami i OnPropertyChanged gdzie trzeba.

0

Ok zrobiłem tak:

public class ConfigViewModel : ViewModelBase
    {
        private string _categoryName;

        public string CategoryName
        {
            get { return _categoryName; }
            set
            {
                _categoryName = value;
                OnPropertyChanged(nameof(this.CategoryName));
            }
        }

        private string _channelName;

        public string ChannelName
        {
            get { return _channelName; }
            set
            {
                _channelName = value;
                OnPropertyChanged(nameof(this.ChannelName));
            }
        }

        private string _channelLink;

        public string ChannelLink
        {
            get { return _channelLink; }
            set
            {
                _channelLink = value;
                OnPropertyChanged(nameof(this.ChannelLink));
            }
        }

        private RSSCategory _selectedCategory;
        public RSSCategory SelectedCategory
        {
            get { return this._selectedCategory; }
            set
            {
                _selectedCategory = value;
                OnPropertyChanged(nameof(this.SelectedCategory));
            }
        }
        private RSSChannel _selectedChannel;

        public RSSChannel SelectedChannel
        {
            get { return _selectedChannel; }
            set
            {
                _selectedChannel = value;
                OnPropertyChanged(nameof(this.SelectedChannel));
            }
        }


        private IList<RSSCategory> _categoriesLists;

        public IList<RSSCategory> CategoriesLists
        {
            get { return _categoriesLists; }
            set
            {
                _categoriesLists = value;
                OnPropertyChanged(nameof(this.CategoriesLists));
            }
        }

        public ObservableCollection<RSSCategory> RssObservableCollection { get; set; }

        public ConfigViewModel()
        {
            RssObservableCollection = new ObservableCollection<RSSCategory>();
            update(RssObservableCollection);
        }

        public void update(ObservableCollection<RSSCategory> RssObservableCollection)
        {
            var categoryLists = PRSS_Logika.Methods.ConfigWindowMethods.XmlToListUpdate();
            using (RSSContext database = new RSSContext())
            {
                try
                {
                    foreach (var category in categoryLists)
                    {
                        var query = database.RSSCategories.Select(x => new { x.RSSCategoryID, x.Name, x.RSSChannels }).Where(x => x.Name == category).Single();
                        RSSCategory newRssCategory = new RSSCategory() { RSSCategoryID = query.RSSCategoryID, Name = query.Name, RSSChannels = query.RSSChannels };
                        RssObservableCollection.Add(newRssCategory);
                    }
                }
                catch (Exception)
                {

                    Debug.Writeline(Exception);
                }
            }
        }

Wszystko śmiga oprócz jednej rzeczy jak dodaje nowy kanał to nie odświeża comboboxa "Kanaly dla wybranej kategorii" i teraz nie wiem co mam zrobić :/ Mam zrobić dodatkowy ViewModel i tam zrobić nowa kolekcję ObservableCollection<RSSChannel>?

Tu jeszcze XAML i binding dla tych 2 comboboxow:

<ComboBox x:Name="cmbKategorie" 
                      ItemsSource="{Binding RssObservableCollection}"
                      DisplayMemberPath="Name" 
                      SelectedValue="{Binding SelectedCategory}"
                      Height="25" Margin="5,5,5,5" 
                      />
            <Button x:Name="btnDodajKanal" Command="{Binding DodajKanal}" Content="Dodaj kanał" Margin="5" VerticalContentAlignment="Center"/>
            <Label Content="Kanały dla wybranej kategori: " Grid.Row="3" VerticalAlignment="Center" Margin="5,0,5,0"/>
            <ComboBox x:Name="cmbKanalyDlaKategori" 
                      ItemsSource="{Binding SelectedCategory.RSSChannels}"
                      SelectedValue="{Binding SelectedChannel}" 
                      HorizontalContentAlignment="Center"
                      Height="25" Margin="5,5,5,5" >
                <ComboBox.ItemTemplate>
                    <DataTemplate>
                        <TextBlock Text="{Binding Name}"/>
                    </DataTemplate>
                </ComboBox.ItemTemplate>
            </ComboBox>
            <Button x:Name="btnUsunKanal" Command="{Binding DodajKanal}" Content="Usuń kanał" Margin="5" VerticalContentAlignment="Center"/>
0

Jakieś pomysły?

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