0
votes

I'm trying to figure out how to bind a property of a custom user control that is placed inside of the cell template of a list view control but it's not working. All of the DisplayMemberBinding fields are working as expected, and I'm getting the correct values, but inside of that custom control, nothing is updating.

WPF LIstView Control

<ListView Margin="10" x:Name="lvHistory">
                <ListView.Resources>
                    <Style TargetType="{x:Type GridViewColumnHeader}">
                        <Setter Property="HorizontalContentAlignment" Value="Left" />
                    </Style>
                </ListView.Resources>
                <ListView.View>
                    <GridView>
                        <GridViewColumn Header="Database" Width="150" DisplayMemberBinding="{Binding ActiveBackup.Database.Name, Mode=TwoWay}"  />
                        <GridViewColumn Header="Start Time" Width="130" DisplayMemberBinding="{Binding ActiveBackup.StartTime, Mode=TwoWay}" />
                        <GridViewColumn Header="Time Elapsed" Width="100" DisplayMemberBinding="{Binding ActiveBackup.TimeElapsed, Mode=TwoWay}" />
                        <GridViewColumn Header="P2" Width="100" DisplayMemberBinding="{Binding Progress, Mode=TwoWay}" />
                        <GridViewColumn x:Name="progressColumn" Header="Progress" Width="150">
                            <GridViewColumn.CellTemplate>
                                <DataTemplate>
                                    <local:cProgressBarSmall x:Name="pr1" Value="{Binding Progress, Mode=TwoWay}" Visibility="Visible" />
                                </DataTemplate>
                            </GridViewColumn.CellTemplate>
                        </GridViewColumn>
                    </GridView>
                </ListView.View>
            </ListView>

Code-Behind in the cProgressBarSmall control.

public partial class cProgressBarSmall : UserControl
    {
        public ActiveBackup ActiveBackup { get; set; }

        public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(decimal), typeof(cProgressBarSmall));

        private decimal _value;
        public decimal Value
        {
            get
            {
                return (decimal) GetValue(ValueProperty);
            }
            set
            {
                _value = value;
                SetValue(ValueProperty, value);
                p1.Value = value.ToDoubleNotNull();
                pLabel.Text = value.ToPercent(0);
                if (value == 0)
                {
                    p1.Visibility = Visibility.Hidden;
                    pLabel.Visibility = Visibility.Hidden;
                }
                else if (value.ToDoubleNotNull() >= p1.Maximum)
                {
                    pLabel.Text = "Finished!";
                    pLabel.Foreground = new SolidColorBrush(Colors.Black);
                }
            }
        }
    }
}

I can't find a way to access the "pr1" because it's in a DataTemplate and therefore not directly accessible from the code-behind. Does binding not work through? The column before it (the "P2" column) is just at test column I put in just to make sure that the value is in fact updating and it is and that displays correctly, however the "progressColumn" always just shows the default value.

Is there anything special to data binding inside of a ListView.View > GridView > GridViewColumn > GridViewColumn.CellTemplate > DataTemplate hierarchy?

1

1 Answers

1
votes

First, if you put a breakpoint in your setter you'll find that it's not hit by the binding. That's because the Binding is setting the dependency property, not the C# property. They're different. The C# property with get/set is an optional wrapper around the dependency property.

The correct way to do this is to have little or no code behind (code behind's not evil; you just don't need any for this one), but use a binding in the usercontrol xaml to update the UI. You can hide and show controls, and update label text, with style triggers in the usercontrol XAML. You don't need any code behind for this.

But here's the simplest way to adapt your existing code to something that works.

    public decimal Value
    {
        get { return (decimal)GetValue(ValueProperty); }
        set { SetValue(ValueProperty, value); }
    }

    public static readonly DependencyProperty ValueProperty = 
        DependencyProperty.Register("Value", typeof(decimal), typeof(cProgressBarSmall), 
            new PropertyMetadata(0m, Value_ChangedCallback));

    //  Has to be static
    private static void Value_ChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        ((cProgressBarSmall)d).OnValueChanged();
    }

    private void OnValueChanged()
    {
        p1.Value = Value.ToDoubleNotNull();
        pLabel.Text = Value.ToPercent(0);
        if (Value == 0)
        {
            p1.Visibility = Visibility.Hidden;
            pLabel.Visibility = Visibility.Hidden;
        }
        else if (Value.ToDoubleNotNull() >= p1.Maximum)
        {
            pLabel.Text = "Finished!";
            pLabel.Foreground = new SolidColorBrush(Colors.Black);
        }
    }