Laden...

Wie kann ich ein ItemsControl aktualisieren?

Erstellt von Davaaron vor 4 Jahren Letzter Beitrag vor 4 Jahren 1.207 Views
D
Davaaron Themenstarter:in
106 Beiträge seit 2016
vor 4 Jahren
Wie kann ich ein ItemsControl aktualisieren?

Hi,

ich probiere mich mal etwas mit Prism aus und habe jetzt Regionen in WPF eingebaut.
Es gibt verschiedene Ansichten in der gleichen Region: Startansicht, Projektübersicht und Projekt erstellen.
Von der Projektübersicht gelangt man in die Ansicht "Projekt erstellen". Das Problem ist nun folgendes: Wenn ich ein Projekt erstelle (über Speicher-Button), dann sehe ich beim Debuggen im ViewModel der Projektübersicht auch das neue Projekt, die UI zeigt aber nur die alten Projekte an.
Ich dachte es liegt eventuell daran, dass Prism jedes mal ein neues VM erzeugt und die UI noch an das alte VM gebunden ist. Aber es wird nur ein Mal der Konstruktor meines VM aufgerufen.
Zudem habe ich das Problem, dass der Inhalt von der ProjectCardView (in XAMl von projectsOverviewView) leer ist.
Hier mal die relevanten Teile:

XAML: ProjectsOverviewView



        <ItemsControl Grid.Row="3" ItemsSource="{Binding Path=ProjectCards}" Margin="20">
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <viewscontrols:ProjectCardView DataContext="{Binding }"  Padding="15"></viewscontrols:ProjectCardView>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <WrapPanel/>
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
        </ItemsControl>


 public class ProjectsOverviewViewModel : ViewModelBase<List<Project>>, IToolbarProvider, INavigationAware
    {
        private ObservableCollection<ProjectCardViewModel> _projectCards;



        private IRegionService _regionService;

        public ProjectsOverviewViewModel(IRegionService regionService)
        {
            _regionService = regionService;
            ProjectCards = new ObservableCollection<ProjectCardViewModel>();

            Init();
        }

        public ObservableCollection<ProjectCardViewModel> ProjectCards
        {
            get => _projectCards;
            set => SetProperty(ref _projectCards, value);
        }
        public ObservableCollection<ToolbarItemViewModel> ToolbarItems { get; private set; }
        public bool IsNavigationTarget(NavigationContext navigationContext)
        {
            return true;
        }

        private void Init()
        {

            InitCards();
        }

        private void InitCards()
        {
            ClientInfo.Workspace.Projects.ForEach(proj =>
            {
                ProjectCards.Add(new ProjectCardViewModel(_regionService)
                {
                    Name = proj.Name,
                    CreatedAt = proj.Created,
                    Modified = proj.Modified,
                    Model = proj
                });
            });
        }



        private void CreateNewProject()
        {
            _regionService.ChangeView<CreateProjectView>(RegionNames.MainContentRegion);
        }
    }


 public class CreateProjectViewModel : ViewModelBase<Project>
    {


        private void CreateProject()
        {
            Project newProject = new Project()
            {
                ID = Guid.NewGuid(),
                Created = DateTime.Now,
                Modified = DateTime.Now,
                Name = Name,
                Description = Description,
                Path = ClientInfo.ProjectsPath
            };
            try
            {
                var filename = Path.Combine(ClientInfo.ProjectsPath, newProject.Name + ".proj");
                newProject.Filename = filename;

                newProject.Workspace = ClientInfo.Workspace;
                ClientInfo.Workspace.Projects.Add(newProject);
                //Update model
                Serialization.SerializeDataContract(newProject, filename);
                Model = newProject;

                _regionService.ChangeView<ProjectsOverviewView>(RegionNames.MainContentRegion);

            }
            catch (Exception e)
            {

            }
        }
    }

Die Methode ChangeView<T>(string regionName) macht nichts besonderes:


 public void ChangeView<T>(string regionName, NavigationParameters parameters)
        {

            if (string.IsNullOrEmpty(regionName))
            {
                Trace.WriteLine("Cannot change view!");
                return;
            }
            var view = Provider.Resolve<T>();
            Debug.Assert(view.GetType().Name.EndsWith("View"));
            Navigate(regionName, typeof(T).Name, view, parameters);
        }

        public void Navigate(string regionName, string source, object view = null, NavigationParameters parameters = null)
        {
            if (parameters == null)
                RegionManager.RequestNavigate(regionName, source);
            else
                RegionManager.RequestNavigate(regionName, source, parameters);

            Trace.WriteLine($"Navigated {regionName} - {source}");

            if (view != null)
                OnChangedView?.Invoke(this, new ChangeViewEventArgs(regionName, view));
        }

Mit dem Tool Snoop sehe ich auch, dass die Daten eigentlich da sind... woran könnte es liegen?
Habe auch mal ein Bild hochgeladen mit den Snoop-Werten und der App. Diese Kacheln sind die ProjectCardsViews(Models).

W
955 Beiträge seit 2010
vor 4 Jahren

Implementiert ProjectCardViewModel INotifyPropertyChanged?

D
Davaaron Themenstarter:in
106 Beiträge seit 2016
vor 4 Jahren

Ja, bzw. erbt BindableBas (von Prism, welches INotifyPropertyChanged implementiert).
Hab eben den ContentPresenter mal aufgeklappt und sehe, dass die Werte von der ProjectCardViewModel tatsächlich leer sind. Es scheint also einen Fehler beim Übertragen des Datenkontextes zu geben.
Ich gehe davon aus, dass die Zeile im XAML
"<viewscontrols:ProjectCardView DataContext="{Binding }" Padding="15"></viewscontrols:ProjectCardView>"
der ausschlaggebende Punkt ist.

Wenn es euch hilft, kann ich das Projekt auch gerne per .zip bereitstellen.

Edit: Ok, das Problem liegt doch daran, dass mir Prism jedes mal eine neue Instanz des ViewModels zurückgibt, wenn ich container.Resolve<T> aufrufe. Mal sehen, ob ich dies umgehen kann.
Denn das ist natürlich etwas doof: Zum Navigieren benutze ich den DI Container, um meine View zu bekommen. Dadurch wird dann aber ein ViewModel erzeugt. Die UI/View scheint aber an das alte ViewModel gebunden zu sein.

Edit2: Nein, daran liegt es nicht. Habe das ProjectOverviewModel nun als Singleton dem Container hinzugefügt (ich weiß, sollte man nicht machen): Selbes Verhalten.
Mein XAML für die ProjectOverviewView sieht nun so aus:

 <UserControl.Resources>
        <DataTemplate DataType="{x:Type vm:ProjectCardViewModel}">
            <viewscontrols:ProjectCardView DataContext="{Binding }" Padding="15"></viewscontrols:ProjectCardView>
        </DataTemplate>
    </UserControl.Resources>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="50"></RowDefinition>
            <RowDefinition Height="50"></RowDefinition>
            <RowDefinition Height="50"></RowDefinition>
            <RowDefinition Height="*"></RowDefinition>
        </Grid.RowDefinitions>
        <Label>Projects</Label>
        <Label Grid.Row="1" Content="{Binding }"></Label>
        <Label Grid.Row="2" Content="{Binding ProjectCards.Count, UpdateSourceTrigger=PropertyChanged}"></Label>

        <ItemsControl Grid.Row="3" ItemsSource="{Binding ProjectCards}" Margin="20">
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <WrapPanel/>
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
        </ItemsControl>
    </Grid>

Wenn ich für die ProjectCardView das DataContext entferne während der Laufzeit und wieder hinzufüge, dann werden die Cards mit Inhalt gefüllt (der Name, etc sind nun da).

D
Davaaron Themenstarter:in
106 Beiträge seit 2016
vor 4 Jahren

Habe es gelöst. Letzten Endes war es nur eine Kleinigkeit: Ich habe jedes mal eine neue Liste erstellt in der Init Methode. Jetzt prüfe ich, ob die Liste null ist oder nicht. Demnach erstelle ich eine Liste oder füge die Elemente der bestehenden Liste hinzu.
Nun funktioniert alles wie erwartet und ich brauche auch keine Singletons mehr für die View, VM, etc.