Why does binding of my ObservableCollection

2019-07-23 11:41发布

问题:

When I update a ObservableCollection<string> and call RaisePropertyChanged(..) somehow its content is not shown within my Listbox in WPF. I have no idea what I am doing wrong.

The critical part is where I update the FileNames property:

public class HistoricalDataViewRawDataViewModel : ViewModelBase
{
    private string _currentDirectory;
    private ObservableCollection<string> _fileNames;
    private List<string> _rawData;

    public ICommand ChangeDirectoryCommand { get; private set; }
    public string CurrentDirectory
    {
        get { return _currentDirectory; }
        set
        {
            if (_currentDirectory != value)
            {
                _currentDirectory = value;
                RaisePropertyChanged("CurrentDirectory");
            }
        }
    }
    public ObservableCollection<string> FileNames
    {
        get { return _fileNames; }
        set
        {
            if (_fileNames != value)
            {
                _fileNames = value;
                RaisePropertyChanged("FileNames");
            }
        }
    }

    public List<string> RawData
    {
        get { return _rawData; }
        set
        {
            if (_rawData != value)
            {
                _rawData = value;
                RaisePropertyChanged("RawData");
            }
        }
    }

    public HistoricalDataViewRawDataViewModel()
    {
        ChangeDirectoryCommand = new RelayCommand(ChangeDirectory);
        var fileDirectory = Properties.Settings.Default.HistoricalData_RawDataSourceDirectory;

        //set current directory
        CurrentDirectory = fileDirectory;

        //load all fileNames
        LoadAvailableFileNames(fileDirectory);
    }

    private void ChangeDirectory()
    {
        using (var folderDialog = new FolderBrowserDialog())
        {
            folderDialog.SelectedPath = CurrentDirectory;
            folderDialog.ShowDialog();

            //set current directory
            CurrentDirectory = folderDialog.SelectedPath;

            //save current directory to settings
            Properties.Settings.Default.HistoricalData_RawDataSourceDirectory = CurrentDirectory;
            Properties.Settings.Default.Save();

            //load files in chosen directory
            LoadAvailableFileNames(CurrentDirectory);
        }
    }

    private void LoadAvailableFileNames(string directory)
    {
        FileNames = new ObservableCollection<string>(FileIO.GetFileNamesInDirectory(directory, false, true));
    }
    private async void LoadRawData(string fileName)
    {

    }}

This is the xaml code. It should work as is because I look to display a ObservableCollection<string>. I added couple items to the listbox from code-behind and it displayed just fine:

DataContext="{Binding HistoricalDataViewRawDataViewModel, Source={StaticResource Locator}}">

<Grid>

    <Grid.RowDefinitions>
        <RowDefinition Height="50" />
        <RowDefinition />
    </Grid.RowDefinitions>

    <StackPanel Grid.Row="0" Orientation="Horizontal">
        <ToggleButton Margin="10"  HorizontalAlignment="Left" VerticalAlignment="Center" Content="Choose Directory" FontSize="18" Foreground="White" Command="{Binding ChangeDirectoryCommand}"/>
        <TextBlock 
            Margin="10"
            FontSize="18"
            Foreground="White"
            HorizontalAlignment="Stretch"
            VerticalAlignment="Stretch"
            TextAlignment="Center"
            Text="{Binding CurrentDirectory}"/>
    </StackPanel>

    <dxdo:DockLayoutManager Grid.Row="1">

        <dxdo:LayoutGroup Orientation="Vertical">

            <dxdo:LayoutPanel ItemHeight="7*">
                <dxdo:LayoutControlItem>
                    <ListBox Name="MyListBox" ItemsSource="{Binding FileNames}"/>
                </dxdo:LayoutControlItem>
            </dxdo:LayoutPanel>

            <dxdo:LayoutPanel Caption="Activity Log" ItemHeight="200" >
                <dxdo:LayoutControlItem>
                    <ListBox/>
                </dxdo:LayoutControlItem>
            </dxdo:LayoutPanel>

        </dxdo:LayoutGroup>

    </dxdo:DockLayoutManager>

</Grid>

回答1:

According to this support ticket in DevExpress, simply removing the LayoutControlItem works.

I created a sample project using your XAML and some dummy data and it works fine:

<Window x:Class="WpfApplication12.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:dxdo="http://schemas.devexpress.com/winfx/2008/xaml/docking"
        Title="MainWindow" Height="350" Width="525">
    <dxdo:DockLayoutManager Grid.Row="1">

        <dxdo:LayoutGroup Orientation="Vertical">

            <dxdo:LayoutPanel ItemHeight="7*">
                <!-- notice the removal of LayoutControlItem here -->
                <ListBox ItemsSource="{Binding FileNames}"/>
            </dxdo:LayoutPanel>

            <dxdo:LayoutPanel Caption="Activity Log" ItemHeight="200" >
                <ListBox/>
            </dxdo:LayoutPanel>
        </dxdo:LayoutGroup>
    </dxdo:DockLayoutManager>
</Window>

Code Behind:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        //Dummy data
        DataContext = new
        {
            FileNames = Enumerable.Range(0, 5).Select(x => "File" + x.ToString())
        };
    }
}

Result:



回答2:

I assume these dxdo-controls are non-standard controls? I assume these controls break the inheritance mechanism. The inheritance mechanism applies to all FrameworkElement objects. It means that the property values are inherited along the logical tree. This enables you to set the datacontext property on the outer Grid but use it, for example, on the TextBlock. The value that you set on the Grid is inherited by the StackPanel and then again by the TextBlock. This only works for objects that inherit from FrameworkElement.

Can you set the datacontext directly on the ListBox? . If this works, than this indicates that the inhitance context is broken.