WPF UserControl DependencyProperty do not have BindingExpression?

CodingNinja 96 Reputation points
2022-02-26T10:25:19.543+00:00

I found that some of the Dependency properties of my UserControl do not have BindingExpressions. I was confused all day.

P.S: i use [PostSharp/AOP][1] to auto implement INotifyPropertyChanged and DepencenyProperty Register.
I believe my problem is not caused by PostSharp because I have tried not using it and the problem persists.

   CSharp  
   [NotifyPropertyChanged]  
   public class StockThumbnail : UserControl  
   {  
       public static DependencyProperty StockProviderProperty { get; private set; }  
     
       [DependencyProperty]  
       public string AspectRatio { get; set; }  
     
       [DependencyProperty]  
       public string Description { get; set; }  
     
       public int DownloadProgress { get; private set; }  
     
       [DependencyProperty]  
       public Uri Icon { get; set; }  
     
       [DependencyProperty]  
       public string Id { get; set; }  
     
       [DependencyProperty]  
       public Uri ImageSource { get; set; }  
     
       [DependencyProperty]  
       public bool IsAdded { get; set; }  
     
       [DependencyProperty]  
       public bool IsFavorited { get; set; }  
     
       [SafeForDependencyAnalysis]  
       public Size MaxSize => (Sizes?.Any()).GetValueOrDefault(false) ? Sizes.MaxBy(x => x.Width * x.Height) : Size.Empty;  
     
       [DependencyProperty]  
       public ObservableCollection<Size> Sizes { get; set; }  //⛔ always null.  
     
       [DependencyProperty]  
       public string StockProvider { get; set; }  //⛔ always null.  
     
       [SafeForDependencyAnalysis]  
       public string SupportSizesToolTip => (Sizes?.Any()).GetValueOrDefault(false) ? "Supports Sizes:\n" + String.Join('\n', Sizes.Select(x => $"{x.Width}x{x.Height}")) : null;  
     
       public ImageSource? Thumbnail { get; private set; }  
     
       public event EventHandler<bool>? IsAddedChanged;  
     
       public event EventHandler<bool>? IsFavoritedChanged;  
     
       public StockThumbnail()  
       { }  
     
       protected string GetCacheThumbnailPath()  
       {  
           var dir = Directory.CreateDirectory(".Caches/Thumbnails");  
     
           return Path.Combine(dir.FullName, $"{StockProvider}.{Id}.jpg");  
       }  
     
     
       protected virtual void OnDecodeFailed(object? sender, ExceptionEventArgs e)  
       {  
           throw e.ErrorException;  
       }  
     
       protected virtual void OnDownloadCompleted(object? sender, EventArgs e)  
       {  
           if (File.Exists(GetCacheThumbnailPath()))  
           {  
               return;  
           }  
     
           var image = sender as BitmapImage;  
           BitmapEncoder encoder = new JpegBitmapEncoder();  
           encoder.Frames.Add(BitmapFrame.Create(image));  
           using var fileStream = new FileStream(GetCacheThumbnailPath(), FileMode.Create);  
           encoder.Save(fileStream);  
       }  
     
       protected virtual void OnDownloadProgress(object? sender, DownloadProgressEventArgs e)  
       {  
           DownloadProgress = e.Progress;  
       }  
     
       // OnImageSourceChanged will auto called when dp changed.  
       private void OnImageSourceChanged()  
       {  
           var uri = ImageSource;  
           string path = GetCacheThumbnailPath();  
     
           if (File.Exists(path))  
           {  
               uri = new(path);  
           }  
     
           var img = new BitmapImage();  
           img.DownloadProgress += (sender, e) => OnDownloadProgress(sender, e);  
           img.DownloadCompleted += (sender, e) => OnDownloadCompleted(sender, e);  
           img.DecodeFailed += (sender, e) => OnDecodeFailed(sender, e);  
     
           img.BeginInit();  
           img.CacheOption = BitmapCacheOption.OnLoad;  
           img.UriSource = uri;  
           img.EndInit();  
     
           Thumbnail = img;  
       }  
     
       // OnIsAddedChanged will auto called when dp changed.  
       private void OnIsAddedChanged()  
       {  
           IsAddedChanged?.Invoke(this, IsAdded);  
       }  
     
       // OnIsFavoritedChanged will auto called when dp changed.  
       private void OnIsFavoritedChanged()  
       {  
           IsFavoritedChanged?.Invoke(this, IsFavorited);  
       }  
   }  



   CSharp  
   [NotifyPropertyChanged]  
   public partial class VideoThumbnail : StockThumbnail  
   {  
       [DependencyProperty]  
       public TimeSpan Duration { get; set; }  
     
       public Brush ThumbnailProgressBarForground { get; private set; } = App.Current.Resources["DefaultProgressBarBrush"] as Brush;  
     
       public Visibility ThumbnailProgressBarVisibility { get; private set; }  
     
       public VideoThumbnail()  
       {  
           InitializeComponent();  
       }  
     
       protected override void OnDecodeFailed(object? sender, ExceptionEventArgs e)  
       {  
           ThumbnailProgressBarForground = new SolidColorBrush(Colors.Red);  
       }  
     
       protected override void OnDownloadCompleted(object? sender, EventArgs e)  
       {  
           ThumbnailProgressBarVisibility = Visibility.Collapsed;  
           base.OnDownloadCompleted(sender, e);  
       }  
     
       private void Border_MouseEnter(object sender, System.Windows.Input.MouseEventArgs e)  
       {  
           imgThumbnail.Stretch = Stretch.UniformToFill;  
           vbPlayIndicator.Visibility = Visibility.Visible;  
       }  
     
       private void Border_MouseLeave(object sender, System.Windows.Input.MouseEventArgs e)  
       {  
           imgThumbnail.Stretch = Stretch.Uniform;  
           vbPlayIndicator.Visibility = Visibility.Collapsed;  
       }  
     
       private void BtnAdd_Click(object sender, RoutedEventArgs e)  
       {  
           IsAdded = !IsAdded;  
       }  
     
       private void BtnFavorite_Click(object sender, RoutedEventArgs e)  
       {  
           IsFavorited = !IsFavorited;  
       }  
   }  

178091-%E5%BE%AE%E4%BF%A1%E6%88%AA%E5%9B%BE-20220226181903.png

I found StockProvider and Sizes is not set BindingExpression:

177909-%E5%BE%AE%E4%BF%A1%E6%88%AA%E5%9B%BE-20220226181427.png

And What confuses me is that all other dependency properties work fine(For example Description):

178016-%E5%BE%AE%E4%BF%A1%E6%88%AA%E5%9B%BE-20220226181617.png

P.S: If I add UI for StockProvider and Sizes, then they bind successfully! But I don't need UI for these two properties.
178017-%E5%BE%AE%E4%BF%A1%E6%88%AA%E5%9B%BE-20220226182135.png

Why did such a strange thing happen?

----------

Update: i change public ObservableCollection<Size> Sizes { get; set; } -> public IEnumerable<Size> Sizes { get; set; }, it work fine!!!!!!!!!!!

And i add Debug.WriteLine($">>> {StockProvider}.{Id}.jpg"); in GetCacheThumbnailPath():
It will be called multiple times, but finally the StockProvider property will be fetched.
178065-%E5%BE%AE%E4%BF%A1%E6%88%AA%E5%9B%BE-20220226185036.png

The first StockProvider is null, so File.Exists(path) always returns false.

Developer technologies | Windows Presentation Foundation
Developer technologies | XAML
Developer technologies | XAML
A language based on Extensible Markup Language (XML) that enables developers to specify a hierarchy of objects with a set of properties and logic.
0 comments No comments
{count} votes

1 answer

Sort by: Most helpful
  1. CodingNinja 96 Reputation points
    2022-02-26T12:18:40.807+00:00

    Finally I found the reason why StockProvider starts as null, the initialization of C# properties is related to their relative position in the source code, I write StockProvider at the beginning of the class and depend on it to write later.

    0 comments No comments

Your answer

Answers can be marked as 'Accepted' by the question author and 'Recommended' by moderators, which helps users know the answer solved the author's problem.