1
votes

I have set up a MVVM design pattern to the WPF application and I'm trying to think of the best way to append listbox items to a listbox and am assuming this should be done in the viewmodel. If this is the case, then i'm stuck. My mainwindow holds a contentcontrol that pulls in the appropriate view as a usercontrol. I'm assuming I would have to bind the itemssource of the listbox to a property in the viewmodel but I'm not sure how to attempt this as I don't have access to the listbox in the camera view.

Should the dynamic data just pull in the items in the user controls constuctor (this seems wrong but would work).

Any thoughts?

No frameworks are used, custom MVVM pattern.

User Control

<ListBox x:Name="CameraList" Background="#ff4c4c4c" BorderThickness="0" 
 ScrollViewer.CanContentScroll="False" TouchEnter="CameraList_TouchEnter" 
 TouchLeave="CameraList_TouchLeave" 
 ManipulationBoundaryFeedback="CameraList_ManipulationBoundaryFeedback"
 ItemContainerStyle="{DynamicResource ResourceKey=ListBoxItemStyle}" 
 PreviewTouchDown="CameraList_PreviewTouchDown" 
 PreviewTouchMove="CameraList_PreviewTouchMove" 
 PreviewTouchUp="CameraList_PreviewTouchUp" 
 HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
    <ListBox.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="/ResourceLibrary;component/User 
                 Controls/Slider.xaml">
                </ResourceDictionary>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </ListBox.Resources>
</ListBox>

ViewModel

class CameraListViewModel : WorkspaceViewModel
{
    #region Fields
    private readonly CameraRepository cameraRepository;
    #endregion


    #region Properties
    /// <summary>
    /// Gets and sets the cameras in the system.
    /// </summary>
    public ObservableCollection<CameraViewModel> Cameras { get; private set; }
    #endregion


    #region Constructors
    public CameraListViewModel(CameraRepository cameraRepository)
    {
        if (cameraRepository == null)
        {
            throw new ArgumentNullException("cameraRepository");
        }

        base.DisplayName = "CameraList";

        this.cameraRepository = cameraRepository;

        // Populate the CameraList collection with CameraViewModel.
        this.CreateCameras();
    }
    #endregion


    #region Internal Members
    /// <summary>
    /// Create all the cameras in the system.
    /// </summary>
    private void CreateCameras()
    {
        List<CameraViewModel> all =
            (from cam in cameraRepository.GetCameras()
             select new CameraViewModel(cam, cameraRepository)).ToList();

        foreach (CameraViewModel cvm in all)
        {
            cvm.PropertyChanged += this.OnCameraViewModelPropertyChanged;
        }

        this.Cameras = new ObservableCollection<CameraViewModel>(all);
        this.Cameras.CollectionChanged += this.OnCollectionChanged;
    }
    #endregion


    #region Events
    /// <summary>
    /// Handle changed collections.
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        if (e.NewItems != null && e.NewItems.Count != 0)
        {
            foreach (CameraViewModel cvm in e.NewItems)
            {
                cvm.PropertyChanged += this.OnCameraViewModelPropertyChanged;
            }
        }

        if (e.OldItems != null && e.OldItems.Count != 0)
        {
            foreach (CameraViewModel cvm in e.OldItems)
            {
                cvm.PropertyChanged -= this.OnCameraViewModelPropertyChanged;
            }
        }
    }

    /// <summary>
    /// Handle property changes.
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void OnCameraViewModelPropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        string isSelected = "IsSelected";

        // Make sure that the property name we're referencing is valid.
        // This is a debugging technique, and does not execute in a Release build.
        (sender as CameraViewModel).VerifyPropertyName(isSelected);

        // When a camera is selected or unselected, we must let the system know
        // that properties have changed, so that it will be queried again for a new value.
        if (e.PropertyName == isSelected)
        {
            this.OnPropertyChanged("IsSelected");
        }
    }
    #endregion
}

Currently this works and will display the listbox, however need a good way of merging what is in the camerarepository into the listbox.

1
You are correct, this should be done in the view model, but you haven't provided enough information to answer your question. There are a lot of ways to accomplish this, but it's very dependent on how you have your system set up, whether you're using any MVVM frameworks, etc.CodingGorilla
I added some logic above to help clarify.bl4kh4k
MVVM is all about Context... DataContext, in this case. Assuming that your parent View has your ViewModel as it's DataContext, then any child controls (even UserControls) will also have the same DataContext (unless you specifically set it to something different). You should be able to simply bind your ListBox ItemsSource to a collection of strings to show.EtherDragon
Regarding EtherDragon's response, so if my mainwindow has the usercontrol inside of it I should be able to retrieve the CameraList inside the usercontrol. So then I should be able to bind the itemssource from the cameralist to that of whats in the mainwindow view model, or the cameralistviewmodel?bl4kh4k

1 Answers

1
votes

Based on the code samples you provided you need to add the following to your <ListBox>:

<ListBox ItemsSource="{Binding Path=Cameras}" ...>

This assumes that your CameraListViewModel is the UserControl's data context, or perhaps the Window's context. If not, you will need to set the Source attribute of the binding.

If you're wondering if you've coded your ViewModel properly (with the constructor loading the cameras), I would say that it looks fine assuming it works.

I'm still not 100% sure what you're asking, so I hope this answers your question.