Jamie Marshall
Jamie Marshall

Reputation: 2304

WPF DisplayMemberPath Only Works On Launch When Nesting Views/VMs

I have a DisplayMemberPath value that is failing.

The set up of my project is that I have a ParentView which contains a ChildView (this child view I have simplified down to the single control that is failing for this question). The DataContext of the Parent view is set a Parent VM in the VM and a DataTemplate is used to set the context of the ChildView. That DataTemplate is set in the Parent View.

The Problem: I need to display the FileName, not the FullFileName property value of the FileInfo objects in my ListBox. I've set DisplayMemberName, which works partly when I set as binding in the ChildView/UserControl. That is it only works on start up of the application. If I add a FileInfo Object to my ChildVM - it shows up in the ListBox, but doesn't show up as FileName. Instead I just get the FullFileName.

It would stand to reason that I'm missing an event triggering the binding, but I'm not entirely sure I should be binding this way in the first place.

This is the functional code. Please note i have simplified it for the purpose of this question. You can ignore naming conventions, etc.

Here is the usercontrol. It is a list box containing FileInfo objects for which I want to set the DisplayMemberName to be the FileInfo.FileName value.

<UserControl x:Class="CatalogInterface.ctlDirFilesListBox"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:CatalogInterface"
             xmlns:vm="clr-namespace:CatalogInterface.ViewModels"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">

    <Grid x:Name="MainControlGrid">
        <ListBox x:Name="FileListBox" 
                 DisplayMemberPath="{Binding ElementName=Files, Path=Files.FileName}"
                 <!-- Also tried DisplayMemberPath="FileName" -->
                 ItemsSource="{Binding Files}"
                 SelectedItem="{Binding Path=SelectedFiles}"
                 SelectionChanged="ListBoxItem_SelectionChanged" 
                 HorizontalAlignment="Stretch" 
                 VerticalAlignment="Stretch" 
                 Background="#FFFFFF"
                 Grid.Row="2" 
                 Grid.Column="1" 
                 Grid.ColumnSpan="3" 
                 BorderThickness="0">
            <ListBox.ItemContainerStyle>
                <Style TargetType="{x:Type ListBoxItem}" BasedOn="{StaticResource {x:Type ListBoxItem}}">
                    <EventSetter Event="MouseDoubleClick" Handler="ListBoxItem_MouseDoubleClick"/>
                    <EventSetter Event="KeyDown" Handler="ListBoxItem_KeyDown"/>
                </Style>
            </ListBox.ItemContainerStyle>
        </ListBox>
    </Grid>
</UserControl>

This is my MainWindo View which takes in the UserControl:

<Window x:Name="FCTWindow" x:Class="CatalogInterface.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:CatalogInterface"
        xmlns:vm="clr-namespace:CatalogInterface.ViewModels"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="532">

    <Window.DataContext>
        <vm:MainWindowViewModel />
    </Window.DataContext>

    <!--#region Body Left Side Grid-->
    <Grid x:Name="BodyGridLeft" Grid.Row="0" Grid.Column="0">
        <UserControl Content="{Binding DirFilesViewModel}">
            <UserControl.ContentTemplate>
                <DataTemplate>
                    <local:ctlDirFilesListBox />
                </DataTemplate>
            </UserControl.ContentTemplate>
        </UserControl>
    </Grid>
    <!--#endregion Body Left Side-->
</Window>

This is the VM for the UserControl (The usercontrol is actually part of a more complicated child view I have simplified for this quesiton) In the VM, the listbox is updated when OnPublishDirFiles() is called:

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;

namespace CatalogInterface.ViewModels
{
    public class DirFilesViewModel : ViewModelBase<DirFilesModel>
    {
        private object _selectedFiles;
        private Messenger _messenger;

        public object SelectedFiles
        {
            get { return _selectedFiles; }
            set {
                SetProperty<object>(ref _selectedFiles, value);
                _messenger.SendMessage(this, "DirFilesListBox_SelectedDocumentChanged", _selectedFiles);
            }
        }

        public ObservableCollection<FileInfo> Files { get; set; }
        private DirFilesModel _model;

        public DirFilesViewModel()
        {
            _model = new DirFilesModel();
            Files = new ObservableCollection<FileInfo>();
            this.OnPublishDirFiles(this, new MessageEventArgs("s", "o"));
            _messenger = Messenger.Set_Messenger();
            _messenger.Register(OnPublishDirFiles, "PublishDirFiles");
        }
        protected virtual void OnPublishDirFiles(object source, MessageEventArgs e)
        {
            PublishDirFiles();
        }
        private void PublishDirFiles()
        {
            if (Files == null) { } //raise NullArgumentException
            Files.Clear();
            foreach (FileInfo f in _model.Files) Files.Add(f);
            OnPropertyChanged("Files.FileName");
        }
    }
}

Upvotes: 1

Views: 43

Answers (1)

Laith
Laith

Reputation: 6091

Wouldn't it be DisplayMemberPath="Name" ? I don't see an actual "FileName" property. There's Name and FullName.

Upvotes: 2

Related Questions