0

I am stumped. I have a user control that contains an ObservableCollection in it. That OC holds instances of a read status class. The read status class has 3 properties, a string, a Progress Bar and a string. The user control should show a list of files to be loaded. Each line having the filename, a progress indicator and a status. The MainWindow VM also has a OC of the same name. If I put instances of the read status class into the VM's OC, they show up in the user control's view. If I chance one of the object in the OC, only the progress bar changes. I cannot change either of the strings. It has to be a binding problem of some sort. Here is a link WPF Test App to a small test app that contains the DLLs needed for the progress bar.

This is the read status class:

public class FileLoadClass
{
    public string FileName { get; set; }
    public ProgressBarEdit ProgressBar { get; set; }
    public string Status { get; set; }
}

The user control:

<UserControl
x:Class="WpfApp4.MultiFileReadControl"
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:dxe="http://schemas.devexpress.com/winfx/2008/xaml/editors"
xmlns:local="clr-namespace:WpfApp4"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
d:DesignHeight="450"
d:DesignWidth="800"
mc:Ignorable="d">
<Grid>
    <ListView
        Name="LogListView"
        Height="220"
        HorizontalContentAlignment="Center"
        FontSize="10"
        ItemsSource="{Binding FilesToProcess}"
        ScrollViewer.VerticalScrollBarVisibility="Auto">
        <ListView.View>
            <GridView>
                <GridViewColumn                        
                    DisplayMemberBinding="{Binding FileName}"
                    Header="File Name" />
                <GridViewColumn Header="Progress">
                    <GridViewColumn.CellTemplate>
                        <DataTemplate>
                            <dxe:ProgressBarEdit
                                Width="300"
                                Height="30"
                                HorizontalAlignment="Center"
                                VerticalAlignment="Center"
                                Background="Red"
                                ContentDisplayMode="Value"
                                DisplayFormatString="{}{0}%"
                                Foreground="#FFFCF5F5"
                                Maximum="100"
                                Minimum="0"
                                ProgressBrush="DarkGreen"
                                Value="{Binding ProgressBar.Value}" />
                        </DataTemplate>
                    </GridViewColumn.CellTemplate>
                </GridViewColumn>
                <GridViewColumn
                    DisplayMemberBinding="{Binding Status}"
                    Header="Status" />
            </GridView>
        </ListView.View>
    </ListView>
</Grid>

The VM:

namespace WpfApp4

{ internal class MainWindowVM {

    private Dispatcher _dispatcher;
    private ObservableCollection<FileLoadClass> _filesToProcess;
    private MultiFileReadControl _multiFileReadControl;
    private MainWindow _parent;
    private Random _rnd;

    public ObservableCollection<FileLoadClass> FilesToProcess
    {
        get => _filesToProcess;
        set { _filesToProcess = value;}
    }

    public MainWindowVM(MainWindow parent, Dispatcher disp)
    {
        _parent = parent;
        _dispatcher = disp;
        _rnd = new Random();
    }

    internal void RndClick(object sender, RoutedEventArgs e)
    {
        FileLoadClass x = FilesToProcess[1];

        x.ProgressBar.Value = _rnd.Next(100);
        x.Status = "Processing";

        FilesToProcess[1] = x;
    }

    internal void StartClick(object sender, RoutedEventArgs e)
    {
        _multiFileReadControl = new MultiFileReadControl();

        DockPanel dp = _parent.dpMasterPanel;
        DockPanel.SetDock(_multiFileReadControl, Dock.Top);
        dp.Children.Add(_multiFileReadControl);

        FilesToProcess = new ObservableCollection<FileLoadClass>();

        FileLoadClass fileLoadClass = new FileLoadClass()
        {
            FileName = "File #1",
            ProgressBar = new DevExpress.Xpf.Editors.ProgressBarEdit() { Value = 0 },
            Status = "Waiting"
        };
        FilesToProcess.Add(fileLoadClass);

        fileLoadClass = new FileLoadClass()
        {
            FileName = "File #2",
            ProgressBar = new DevExpress.Xpf.Editors.ProgressBarEdit() { Value = 0 },
            Status = "Waiting"
        };

        FilesToProcess.Add(fileLoadClass);

        fileLoadClass = new FileLoadClass()
        {
            FileName = "File #3",
            ProgressBar = new DevExpress.Xpf.Editors.ProgressBarEdit() { Value = 0 },
            Status = "Waiting"
        };

        FilesToProcess.Add(fileLoadClass);

    }

}

} The Main Window xaml and cs

<Window
x:Class="WpfApp4.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:local="clr-namespace:WpfApp4"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="MainWindow"
Width="800"
Height="450"
mc:Ignorable="d">
<DockPanel Name="dpMasterPanel">
    <StackPanel
        HorizontalAlignment="Center"
        VerticalAlignment="Center"
        DockPanel.Dock="Bottom"
        Orientation="Horizontal">
        <Button
            Width="65"
            Height="50"
            Margin="0,0,20,0"
            HorizontalAlignment="Left"
            Background="Firebrick"
            Click="Start_Click"
            Foreground="AntiqueWhite">
            <TextBlock
                Width="50"
                HorizontalAlignment="Center"
                VerticalAlignment="Center"
                Text="Start"
                TextAlignment="Center"
                TextWrapping="Wrap" />
        </Button>
        <Button
            Width="70"
            Height="50"
            HorizontalAlignment="Left"
            Background="Firebrick"
            Click="Rnd_Click"
            Foreground="AntiqueWhite">
            <TextBlock
                Width="50"
                HorizontalAlignment="Center"
                VerticalAlignment="Center"
                Text="Set Random Number"
                TextAlignment="Center"
                TextWrapping="Wrap" />
        </Button>
    </StackPanel>
</DockPanel>
namespace WpfApp4
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
    private MainWindowVM _vm;
    public MainWindow()
    {
        InitializeComponent();
        _vm  = new MainWindowVM(this, Dispatcher);
        this.DataContext = _vm;
    }

    private void Start_Click(object sender, RoutedEventArgs e)
    {
        _vm.StartClick(sender, e);
    }

    private void Rnd_Click(object sender, RoutedEventArgs e)
    {
        _vm.RndClick(sender, e);
    }

}

}

What is suspicious is the user control dependency property is never called. The data context is set in the MainWindow cs file to be the VM. Clicking the Start button creates the control and fills in data in the VM's OC.

Clicked Start and the OC is shown

Clicking the Set Random Number button should change the progress bar and the status text. Only the status bar changes: enter image description here

Thanks.

AeroClassics
  • 1,074
  • 9
  • 19
  • too much code here. try post a minimal code. sometimes you can find the cause yourself when minimize the code. – Lei Yang Feb 25 '22 at 01:27
  • 2
    You will need to implement `INotifyPropertyChanged` on `FileLoadClass`. – rfmodulator Feb 25 '22 at 02:21
  • @rfmodulator, that certainly did the trick. But I still don't understand why none of the Dependency Properties in the UserControl are not being called? – AeroClassics Feb 25 '22 at 03:15
  • The `UserControl`'s `ListView` is only responsible to showing one instance of your `ItemTemplate` for each item that in your backing list. That's it. It is not responsible for updating the status of each individual control within the template for each item.. That is the job of the controls inside your `ItemTemplate`. And your `dxe:ProgressBar` would have happily updated for you but since you did not implement `INotifyPropertyChanged` on the `FileLoadClass`, it had no way of knowing that the progress of any particular `FileLoadClass` object had actually changed – Joe Feb 25 '22 at 03:26
  • 1
    You may also want to take a look at this: https://stackoverflow.com/a/15023687/1136211 – Clemens Feb 25 '22 at 06:57
  • @Joe Thanks for the assist. The interesting things is, the Progress bar is updating. Adding the INotifyPropertyChanged only made the string fields update. – AeroClassics Feb 25 '22 at 15:31
  • @Clemens Thanks for the reference to the other post. I thought over the last few days I had read every post concerning OC's and UC's. Missed that one. – AeroClassics Feb 25 '22 at 15:32

0 Answers0