11using System . Collections ;
22using System . ComponentModel ;
33using System . Reflection ;
4+ using System . Windows ;
45using System . Windows . Controls ;
56using System . Windows . Controls . Primitives ;
67using System . Windows . Data ;
@@ -15,11 +16,13 @@ namespace dosymep.WpfCore.MarkupExtensions;
1516/// <summary>
1617/// Конвертирует enum в список значений.
1718/// </summary>
18- [ MarkupExtensionReturnType ( typeof ( string [ ] ) ) ]
19+ [ MarkupExtensionReturnType ( typeof ( MarkupValueObject ) ) ]
1920public sealed class EnumToItemsSourceExtension : MarkupExtension {
2021 private readonly MarkupValueObject _markupValueObject = new ( ) ;
2122 private readonly Binding _binding = new ( nameof ( MarkupValueObject . Value ) ) ;
2223
24+ private IHasLocalization ? _localization ;
25+
2326 /// <summary>
2427 /// Конструирует объект.
2528 /// </summary>
@@ -36,6 +39,8 @@ public EnumToItemsSourceExtension() { }
3639 /// </summary>
3740 public Type ? EnumType { get ; set ; }
3841
42+ internal IReadOnlyCollection < EnumInfo > ? ItemsSource => _markupValueObject . Value as IReadOnlyCollection < EnumInfo > ;
43+
3944 /// <inheritdoc />
4045 public override object ? ProvideValue ( IServiceProvider serviceProvider ) {
4146 if ( EnumType is null ) {
@@ -46,77 +51,80 @@ public EnumToItemsSourceExtension() { }
4651 throw new InvalidOperationException ( "EnumType must be an enum." ) ;
4752 }
4853
49- SetPropertyPaths ( serviceProvider ) ;
50- SetLocalizationStrings ( serviceProvider ) ;
51-
52- return _binding . ProvideValue ( serviceProvider ) ;
53- }
54+ // получаем корневой элемент,
55+ // может быть Window, Page, UserControl
56+ FrameworkElement ? rootObject = serviceProvider . GetRootObject < FrameworkElement > ( ) ;
5457
55- private void SetLocalizationStrings ( IServiceProvider serviceProvider ) {
56- IHasLocalization ? localization = serviceProvider . GetRootObject < IHasLocalization > ( ) ;
57- ILocalizationService ? localizationService = localization ? . LocalizationService ;
58+ // для случая, если окно уже получено
59+ _localization = rootObject as IHasLocalization ;
5860
59- if ( localization is not null ) {
60- localization . LanguageChanged += _ => UpdateDisplayName ( _markupValueObject . Value ) ;
61+ if ( _localization is null && rootObject is not null ) {
62+ // либо ждем его загрузку,
63+ // чтобы можно было получить корневой элемент Window
64+ rootObject . Loaded += RootObjectOnLoaded ;
6165 }
6266
6367 _binding . Source = _markupValueObject ;
64- _markupValueObject . Value = GetEnumValues ( localizationService ) ;
65- }
68+ _markupValueObject . Value = GetEnumValues ( ) ;
6669
67- private static void SetPropertyPaths ( IServiceProvider serviceProvider ) {
68- IProvideValueTarget ? provideValueTarget = serviceProvider . GetService < IProvideValueTarget > ( ) ;
69- if ( provideValueTarget ? . TargetObject is Selector selector ) {
70- selector . SelectedValuePath = nameof ( MarkupDisplayObject . Value ) ;
71- }
70+ // попытка установить значения
71+ // отображаемого имени, контрол может быть уже загружен
72+ TryUpdateDisplayName ( ) ;
7273
73- if ( provideValueTarget ? . TargetObject is ItemsControl itemsControl ) {
74- itemsControl . DisplayMemberPath = nameof ( MarkupDisplayObject . DisplayName ) ;
75- }
74+ // устанавливаем имена свойств обновляемого объекта
75+ UpdateTargetControlProperties ( serviceProvider ) ;
76+
77+ _binding . Mode = BindingMode . OneWay ;
78+ _binding . FallbackValue = Array . Empty < EnumInfo > ( ) ;
79+ _binding . TargetNullValue = Array . Empty < EnumInfo > ( ) ;
80+
81+ return _binding . ProvideValue ( serviceProvider ) ;
7682 }
7783
78- private void UpdateDisplayName ( object ? value ) {
79- if ( value == null ) {
84+ private void RootObjectOnLoaded ( object sender , RoutedEventArgs e ) {
85+ if ( sender is not FrameworkElement frameworkElement ) {
8086 return ;
8187 }
8288
83- IEnumerable < MarkupDisplayObject > list = ( ( IEnumerable ) value )
84- . OfType < MarkupDisplayObject > ( ) ;
89+ // отписываемся от события,
90+ // потому что при смене темы может повторно вызваться
91+ frameworkElement . Loaded -= RootObjectOnLoaded ;
8592
86- foreach ( MarkupDisplayObject magicObject in list ) {
87- magicObject . UpdateDisplayName ( ) ;
93+ _localization = Window . GetWindow ( frameworkElement ) as IHasLocalization ;
94+ if ( _localization is not null ) {
95+ TryUpdateDisplayName ( ) ;
96+ _localization . LanguageChanged += _ => TryUpdateDisplayName ( ) ;
8897 }
8998 }
9099
91- private object [ ] GetEnumValues ( ILocalizationService ? localizationService ) {
92- return EnumType ? . GetFields ( BindingFlags . Static | BindingFlags . Public )
93- . Select ( item => CreateMarkupDisplayObject ( item , localizationService ) )
94- . Cast < object > ( )
95- . ToArray ( ) ?? Array . Empty < object > ( ) ;
96- }
100+ private void TryUpdateDisplayName ( ) {
101+ if ( ItemsSource is null ) {
102+ return ;
103+ }
97104
98- internal static MarkupDisplayObject CreateMarkupDisplayObject ( FieldInfo item ,
99- ILocalizationService ? localizationService ) {
100- return new MarkupDisplayObject ( ( ) => GetEnumName ( item , localizationService ) ) {
101- Value = item . GetValue ( null ) , DisplayName = GetEnumName ( item , localizationService )
102- } ;
105+ foreach ( EnumInfo enumInfo in ItemsSource ) {
106+ enumInfo . UpdateDisplayName ( _localization ? . LocalizationService ) ;
107+ }
103108 }
104109
105- internal static string GetEnumName ( FieldInfo item , ILocalizationService ? localizationService ) {
106- string ? desciption = GetDescription ( item ) ;
110+ private static void UpdateTargetControlProperties ( IServiceProvider serviceProvider ) {
111+ IProvideValueTarget ? provideValueTarget = serviceProvider . GetService < IProvideValueTarget > ( ) ;
112+ if ( provideValueTarget ? . TargetObject is Selector selector ) {
113+ selector . SelectedValuePath = nameof ( EnumInfo . Id ) ;
114+ }
107115
108- if ( string . IsNullOrEmpty ( desciption ) ) {
109- return localizationService ? . GetLocalizedString ( $ "{ item . FieldType . Name } .{ item . Name } ")
110- ?? item . Name ;
116+ if ( provideValueTarget ? . TargetObject is ItemsControl itemsControl ) {
117+ itemsControl . DisplayMemberPath = nameof ( EnumInfo . DisplayName ) ;
111118 }
119+ }
112120
113- return localizationService ? . GetLocalizedString ( desciption )
114- ?? localizationService ? . GetLocalizedString ( $ " { item . FieldType . Name } . { item . Name } " )
115- ?? desciption
116- ?? item . Name ;
121+ private EnumInfo [ ] GetEnumValues ( ) {
122+ return EnumType ? . GetFields ( BindingFlags . Static | BindingFlags . Public )
123+ . Select ( CreateEnumInfo )
124+ . ToArray ( ) ?? [ ] ;
117125 }
118126
119- internal static string ? GetDescription ( FieldInfo fieldInfo ) {
120- return fieldInfo . GetCustomAttribute < DescriptionAttribute > ( ) ? . Description ;
127+ private static EnumInfo CreateEnumInfo ( FieldInfo fieldInfo ) {
128+ return new EnumInfo ( fieldInfo . GetValue ( null ) , fieldInfo ) ;
121129 }
122130}
0 commit comments