3

Im creating some items in my UI in the following way

Buttons.cs

[Browsable(true)]
[Display(Order = 1, Name = "Object1", GroupName = "Objects", ResourceType typeof(Resources.DisplayNames) , AutoGenerateField =false)]

public ButtonViewModel Button1{ get; set; }

[Browsable(true)]
[Display(Order = 2, Name = "Object2", GroupName = "Objects", ResourceType typeof(Resources.DisplayNames) , AutoGenerateField =false)]

public ButtonViewModel Button2{ get; set; }

//etc

What im trying to do is show or hide these UI elements based on some condition. I saw that if i set AutoGenerateField to true/false i get the desired result, is there some way to set that value true/false at runtime? (if its even possible)

Is there another way to do this alltogether? Like adding/removing the display attribute each time.

EDIT

PropertyGridView.xaml

<Grid>       
        <telerik:RadPropertyGrid telerik:StyleManager.Theme="Fluent"  
                                 x:Name="PropertyGrid"
                                 IsGrouped="True"
                                 Item="{Binding SelectedItem}"
                                 PropertySetMode="Union"
                                 RenderMode="Flat"                                                               
                                 SortAndGroupButtonsVisibility="Collapsed">           
        </telerik:RadPropertyGrid>
    </Grid>

PropertyGridViewModel.cs


    public class PropertyGridViewModel : Screen, IPropertyGridViewModel, IHandle<ItemSelectionMessage>
    {
        private IEventAggregator _eventAggregator;

        private IItem _item;

        public PropertyGridViewModel(IEventAggregator eventAggregator)
        {
            _eventAggregator = eventAggregator;

        }

        protected override void OnActivate()
        {
            base.OnActivate();
            _eventAggregator.Subscribe(this);
        }

        protected override void OnDeactivate(bool close)
        {
            base.OnDeactivate(close);
            _eventAggregator.Unsubscribe(this);
        }

        public IItem SelectedItem
        {
            get
            {
                return _item;
            }
            set
            {
                _item = value;
                NotifyOfPropertyChange(() => SelectedItem);
            }
        }

        public void Handle(ItemSelectionMessage message)
        {
            SelectedItem = message.Item;
        }  

    }


And the item that gets passed is Buttons.cs above.

  • 1
    You need to create visibility converter `IValueConverter` to convert your model property to element visibility possibly duplicate https://stackoverflow.com/questions/14692461/bind-visibility-property-to-a-variable – Oleg Bondarenko Nov 05 '19 at 13:08
  • I tried doing that at first, but the visibility property only hides/shows the button, and doesnt affect the name property which is displayed next to the button normally. I dont know if i did something wrong or if im missing something obvious. – Kyriakos Xatzisavvas Nov 05 '19 at 13:50
  • Could you provide your xaml layout, please ? In general you could hide placeholder with button and label. – Oleg Bondarenko Nov 05 '19 at 14:29
  • 1
    You can add/remove attributes at runtime (see https://stackoverflow.com/questions/14663763/how-to-add-an-attribute-to-a-property-at-runtime), however it's not the best way to do what you are trying to achieve. If you want to show/hide multiple controls at the same time, you could simply wrap them in the same StackPanel, and then either adding a Binding to the Visibility parameter with an IValueConverter as @OlegBondarenko said, or adding a style with DataTriggers that set its visibility. – Sasino Nov 05 '19 at 15:18
  • I updated the code above, do you have any examples for how to implement your suggestion? – Kyriakos Xatzisavvas Nov 05 '19 at 15:25
  • I see now is much clearest, I guess you need to separate class to models only with visiable elements (preferable way) or dynamicly set attributes for each property of your class as was mentioned before . – Oleg Bondarenko Nov 05 '19 at 20:45

1 Answers1

2

You could create two classes that are implemented IItem interface for each visibility states and set appropriate one to RadPropertyGrid based on current visibility state. But it will work only for not huge amount of visibility states (you have to create separate class for each of them). Other way is setting dynamically Display attribute for each property with reflexion. But I advice you following the first approach with splitting classes.

            // additional methods for getting appropriate instance of your class  
            public static List<Type> GetInterfaceTypes<Interface>()
            {
                Type serachInterface = typeof(Interface);

                List<Type> findClasses = serachInterface.Assembly.GetTypes().Where
                            (
                                t => t.IsClass && !t.IsAbstract &&
                                serachInterface.IsAssignableFrom(t)
                            ).ToList();

                return findClasses;
            }
            public static List<Interface> GetInstances<Interface>(params object[] paramArray)
            {
                List<Interface> returnInstances = new List<Interface>();
                List<Type> foundTypes = GetInterfaceTypes<Interface>();

                foundTypes.ForEach(x =>
                {
                    returnInstances.Add((Interface)Activator.CreateInstance(x,args:paramArray));
                });
                return returnInstances;
            }
           // your handler from PropertyGridViewModel.cs
            public IItem SelectedItem { get; private set; }
            public  void Handle(ItemSelectionMessage message)
            {
                IItem item = GetInstances<IItem>(_eventAggregator).FirstOrDefault(x => x.Visibility == message.Visibility);
                SelectedItem = item;
            }

            public class ItemSelectionMessage
           {
             public VisibilityStates Visibility { set; get; }

           }

    // enum that  is describe you visibility states
        public enum VisibilityStates
        {
            Button1,
            Button2
        }
    // interface and classes that are implemented visibility interface
        public interface IItem
        {
            VisibilityStates Visibility { get; }
        }

        public class Button1StateClass : IItem
        {
             public VisibilityStates Visibility { get => VisibilityStates.Button1; }

              [Browsable(true)]
              [Display(Order = 1, Name = "Object1", GroupName = "Objects", ResourceType typeof(Resources.DisplayNames), AutoGenerateField = true)]

                public ButtonViewModel Button1 { get; set; }
 public Button1StateClass(IEventAggregator eventAggregator) : base(eventAggregator)
        {

        }
        public Button1StateClass()
        {

        }
        }
        public class Button2StateClass : IItem
        {
              public VisibilityStates Visibility { get => VisibilityStates.Button2; }
              [Browsable(true)]
              [Display(Order = 2, Name = "Object2", GroupName = "Objects", ResourceType typeof(Resources.DisplayNames), AutoGenerateField = true)]

             public ButtonViewModel Button2 { get; set; }
 public Button2StateClass(IEventAggregator eventAggregator) : base(eventAggregator)
        {

        }
        public Button2StateClass()
        {

        }
        }
Oleg Bondarenko
  • 1,694
  • 1
  • 16
  • 19
  • This looks promising, but im getting an error on the returnInstances.Add((Interface)Activator.CreateInstance(x)); command , it says MissingMethodException, from System.Reflection.TargetInvocationException. Also, returnInstances.Count = 0 , and foundTypes.Count = 2. Is returnInstances supposed to be 0 ? – Kyriakos Xatzisavvas Nov 06 '19 at 10:37
  • 1
    did you use some constructors inside classes Button1StateClass? it should be without parameters for creation with reflection. Exceptions means that issue with constructor. – Oleg Bondarenko Nov 06 '19 at 10:51
  • Yea youre right, i was using a constructor to inherit IEventAggregator ,like so `public Button1(IEventAggregator eventAggregator) : base(eventAggregator)` and when i changed it too `public Button1()` it worked. Is there any other way to inherit the eventAggregator though? – Kyriakos Xatzisavvas Nov 06 '19 at 11:45
  • 1
    You could send parameters to instance activator (I have adjusted post with adding IEventAggregator parameter to constructor. – Oleg Bondarenko Nov 06 '19 at 12:08
  • It needed some tweaking here and there (cause apparently i had some other classes 'inherit' my Button1 so i had to make some adjsutments for Button2) but it finally works!! Thank you so much , you deserve all the upvotes! – Kyriakos Xatzisavvas Nov 06 '19 at 13:33