@@ -434,6 +434,20 @@ protected void DetachChild(Visual child)
434434 /// <param name="value">The style value.</param>
435435 public void SetStyle < T > ( T value ) where T : IStyle < T > => SetStyle ( T . Key , value ) ;
436436
437+ /// <summary>
438+ /// Sets a style value factory in the environment of this visual and returns it by type.
439+ /// </summary>
440+ /// <typeparam name="T">The style type.</typeparam>
441+ /// <param name="value">The factory used to resolve the style value.</param>
442+ public void SetStyle < T > ( Func < T > value ) where T : IStyle < T > => SetStyle ( T . Key , value ) ;
443+
444+ /// <summary>
445+ /// Sets a style binding in the environment of this visual and returns it by type.
446+ /// </summary>
447+ /// <typeparam name="T">The style type.</typeparam>
448+ /// <param name="value">The binding used to resolve the style value.</param>
449+ public void SetStyle < T > ( Binding < T > value ) where T : IStyle < T > => SetStyle ( T . Key , value ) ;
450+
437451 /// <summary>
438452 /// Sets a style value in the environment of this visual.
439453 /// </summary>
@@ -444,9 +458,51 @@ public void SetStyle<T>(StyleKey<T> key, T value)
444458 {
445459 VerifyAccess ( ) ;
446460 ArgumentNullException . ThrowIfNull ( key ) ;
461+
462+ var oldSource = ResolveStyleSourceBeforeSet ( key ) ;
463+
447464 StyleEnvironment ??= new Dictionary < object , object ? > ( ) ;
448465 StyleEnvironment [ key ] = value ;
449- BindingManager . Current . NotifyValueChanged ( this , key . BindingAccessor ) ;
466+
467+ NotifyStyleSourceChange ( key , oldSource ) ;
468+ }
469+
470+ /// <summary>
471+ /// Sets a style value factory in the environment of this visual.
472+ /// </summary>
473+ /// <typeparam name="T">The style type.</typeparam>
474+ /// <param name="key">The style key.</param>
475+ /// <param name="value">The factory used to resolve the style value.</param>
476+ public void SetStyle < T > ( StyleKey < T > key , Func < T > value )
477+ {
478+ VerifyAccess ( ) ;
479+ ArgumentNullException . ThrowIfNull ( key ) ;
480+ ArgumentNullException . ThrowIfNull ( value ) ;
481+
482+ var oldSource = ResolveStyleSourceBeforeSet ( key ) ;
483+
484+ StyleEnvironment ??= new Dictionary < object , object ? > ( ) ;
485+ StyleEnvironment [ key ] = value ;
486+
487+ NotifyStyleSourceChange ( key , oldSource ) ;
488+ }
489+
490+ /// <summary>
491+ /// Sets a style binding in the environment of this visual.
492+ /// </summary>
493+ /// <typeparam name="T">The style type.</typeparam>
494+ /// <param name="key">The style key.</param>
495+ /// <param name="value">The binding used to resolve the style value.</param>
496+ public void SetStyle < T > ( StyleKey < T > key , Binding < T > value )
497+ {
498+ VerifyAccess ( ) ;
499+ ArgumentNullException . ThrowIfNull ( key ) ;
500+ if ( value . IsEmpty )
501+ {
502+ throw new ArgumentException ( "The binding cannot be empty." , nameof ( value ) ) ;
503+ }
504+
505+ SetStyle ( key , value . GetValue ) ;
450506 }
451507
452508 /// <summary>
@@ -475,7 +531,7 @@ public T GetStyle<T>(StyleKey<T> key)
475531 if ( v . StyleEnvironment is not null && v . StyleEnvironment . TryGetValue ( key , out var boxed ) )
476532 {
477533 BindingManager . Current . RegisterRead ( v , key . BindingAccessor ) ;
478- return boxed is T typed ? typed : key . DefaultValue ;
534+ return ResolveStyleValue ( key , boxed ) ;
479535 }
480536 }
481537
@@ -501,6 +557,47 @@ public bool HasLocalStyle<T>(StyleKey<T> key)
501557 /// </summary>
502558 public Theme GetTheme ( ) => GetStyle < Theme > ( ) ;
503559
560+ private void NotifyStyleSourceChange < T > ( StyleKey < T > key , Visual oldSource )
561+ {
562+ if ( ! ReferenceEquals ( oldSource , this ) )
563+ {
564+ BindingManager . Current . NotifyValueChanged ( oldSource , key . BindingAccessor ) ;
565+ }
566+
567+ BindingManager . Current . NotifyValueChanged ( this , key . BindingAccessor ) ;
568+ }
569+
570+ private Visual ResolveStyleSourceBeforeSet < T > ( StyleKey < T > key )
571+ {
572+ Visual ? root = null ;
573+ for ( var v = this ; v is not null ; v = v . Parent )
574+ {
575+ root = v ;
576+ if ( v . StyleEnvironment is not null && v . StyleEnvironment . ContainsKey ( key ) )
577+ {
578+ return v ;
579+ }
580+ }
581+
582+ return root ?? this ;
583+ }
584+
585+ private static T ResolveStyleValue < T > ( StyleKey < T > key , object ? boxed )
586+ {
587+ if ( boxed is T typed )
588+ {
589+ return typed ;
590+ }
591+
592+ if ( boxed is Func < T > factory )
593+ {
594+ var resolved = factory ( ) ;
595+ return resolved is null ? key . DefaultValue : resolved ;
596+ }
597+
598+ return key . DefaultValue ;
599+ }
600+
504601 /// <summary>
505602 /// Gets the absolute bounds of this visual in the coordinate space of the visual tree root.
506603 /// </summary>
0 commit comments