So, I have a button that upon clicking will display a ProgressBar with its value bound to the parent element's attached property called ProgressValueProperty.

This is the code I've used for the ProgressBar (this is in a separate xaml file)

    <!-- Regular Button -->
    <Style TargetType="{x:Type Button}" BasedOn="{StaticResource BaseStyle}">
        <Setter Property="Background" Value="{StaticResource WordOrangeBrush}"/>
        <Setter Property="Foreground" Value="{StaticResource ForegroundLightBrush}"/>
        <Setter Property="BorderThickness" Value="0"/>
        <Setter Property="FontSize" Value="{StaticResource FontSizeLarge}"/>
        <Setter Property="FontFamily" Value="{StaticResource LatoRegular}"/>
        <Setter Property="Padding" Value="50 10"/>
        <Setter Property="Margin" Value="0 10"/>
        <Setter Property="localap:IsBusyProperty.Value" Value="False"/>

        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type ButtonBase}">
                    <Border
                        x:Name="border" 
                        BorderBrush="{TemplateBinding BorderBrush}" 
                        BorderThickness="{TemplateBinding BorderThickness}" 
                        Background="{TemplateBinding Background}" 
                        SnapsToDevicePixels="True"
                        CornerRadius="10"
                        
                        >
                        <Grid>
                            <!-- Login Text -->
                            <TextBlock
                            Text="{TemplateBinding Content}"  
            
                                
                            Focusable="False" 
                            FontFamily="{TemplateBinding FontFamily}"
                            HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" 
                            Margin="{TemplateBinding Padding}" 
                            SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" 
                            VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
                            Visibility="{TemplateBinding localap:IsBusyProperty.Value,Converter={localvc:AttachedPropertyValueConverter}}"
                                />

                            <ProgressBar 
                                Minimum="0" 
                                Maximum="100" 
                                Value="{Binding Path=(localap:ProgressValueProperty.Value),RelativeSource={RelativeSource TemplatedParent},Converter={localvc:StringIntValueConverter}}"
                                Visibility="{TemplateBinding localap:IsBusyProperty.Value,Converter={localvc:AttachedPropertyValueConverter},ConverterParameter=True}"
                                >
                            </ProgressBar>

                        </Grid>
                    </Border>
                   
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>              Visibility="{TemplateBinding localap:IsBusyProperty.Value,Converter={localvc:AttachedPropertyValueConverter}}"
                                />
                            <!--<ProgressBar 
                                Minimum="0" 
                                Maximum="100" 
                                Value="{Binding Path=(localap:ProgressValueProperty.Value),RelativeSource={RelativeSource TemplatedParent},Converter={localvc:StringIntValueConverter}}"
                                Visibility="{TemplateBinding localap:IsBusyProperty.Value,Converter={localvc:AttachedPropertyValueConverter},ConverterParameter=True}"
                                >
                            </ProgressBar>-->

                        </Grid>
                    </Border>
                  
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

However, this creates an error during design time Failed to create a 'Path' from the text '(p6:BaseAttachedProperty`2.Value)'.

I tried to bind the value of the ProgressBar using this method

Value="{TemplateBinding localap:ProgressValueProperty.Value,Converter=localvc:StringIntValueConverter}}"

and while this does not create the previous error, runtime does not seem to change the ProgressBar's progress in the UI though creating a breakpoint at the StringIntValueConverter seems to show that the value is being passed in, at least to the converter.

Wondering if anyone knows how a workaround for the above?

Edit :

  1. Updated the code above for a more complete show of using the progress bar within a button.
  2. The Button code within the window file itself:
<Button 
    Content="Next" 
    localap:IsBusyProperty.Value="{Binding LoginIsRunning}"
    Command="{Binding LoginCommand}" 
    CommandParameter="{Binding ElementName=Page}" 
    HorizontalAlignment="Center" 
    localap:ProgressValueProperty.Value="{Binding ProgressValue}"
    Tag="50"
    >
</Button>
  1. The attached properties are all classes inheriting from the below base class:
  public abstract class BaseAttachedProperty<Parent, Property>
        where Parent : BaseAttachedProperty<Parent, Property>, new()
    {

        public event Action<DependencyObject, DependencyPropertyChangedEventArgs> ValueChanged = (sender, e) =>
        {
            Debug.WriteLine($"{sender.ToString()} value changed : {e.NewValue}");
        };

        public static Parent Instance { get; private set; } = new Parent();

        public static readonly DependencyProperty ValueProperty = DependencyProperty.RegisterAttached("Value", typeof(Property), typeof(BaseAttachedProperty<Parent, Property>), new PropertyMetadata(new PropertyChangedCallback(OnValuePropertyChanged)));

        private static void OnValuePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            // Call the parent function
            Instance.OnValueChanged(d, e);
            // Call event listeners
            Instance.ValueChanged(d, e);
        }


        public static Property GetValue(DependencyObject d) {

            var p = (Property)d.GetValue(ValueProperty);

            Debug.WriteLine($"Getting Value of {d.ToString()} : {Instance.ToString()} - {p.ToString()}");

            return p; }


        public static void SetValue(DependencyObject d, Property value) {
            Debug.WriteLine($"Setting Value of {d.ToString()} : {Instance.ToString()} - {value.ToString()}");
            d.SetValue(ValueProperty, value); 
        }


        public virtual void OnValueChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            Debug.WriteLine($"{sender.ToString()} On value changed : {e.NewValue}");

        }

    }
  1. The value converter :
 public class StringIntValueConverter : BaseValueConverter<StringIntValueConverter>
    {
        public override object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value == null) return 90;
            try
            {
                return int.Parse(value.ToString());
            }
            catch
            {
                return 40;
            }
        }

        public override object ConvertBack(object value, Type targetType, object parameter, CultureInfo cultue)
        {
            throw new NotImplementedException();
        }
    }
public abstract class BaseValueConverter<T> : MarkupExtension, IValueConverter
        where T:class,new()
{

        private static T _converter = null;
       
        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            return _converter ?? (_converter = new T());
        }
     
        public abstract object Convert(object value, Type targetType, object parameter, CultureInfo culture);
       
        public abstract object ConvertBack(object value, Type targetType, object parameter, CultureInfo cultue);
}

Thanks.

0

There are 0 answers