3

I'm trying to create a game editor in C#/WPF. The editor consists of a user control that shows the scene (rendered in OpenGL using SharpGL) as well as many controls for editing the current scene and objects. Objects consist of components whose properties can be edited in the editor (kind of like in Unity game engine). I already have a "component editor" view which uses reflection to find all properties on the component and creates a property editor (for example, a slider) per each property. However, I'm not sure how to bind the properties between UI and code.

The problem is, I want these properties to be updated in the UI when they change in code, as well as updated in code when they're changed in the UI. If I want to bind the editor controls (such as a slider that changes a property) to the component properties, they would have to implement NotifyPropertyChanged, which would be quite cumbersome. I guess the other way is doing dirty-checking, but I'm not sure if that's a good idea either.

Can anybody give me pointers on how this property updating between UI/Code should be handled? I want it to work pretty much like it does in Unity, where you don't need to write anything extra into your component class to make properties editable.

EDIT: To make more clear what I'm trying to achieve and already have, here is a part of the "component editor" user control. It's datacontext is a Component instance (model). PropertiesConverter returns it's properties (through component.GetType().GetProperties()). ComponentPropertyTemplateSelector decides on the property editor user control (for example, for a double property it would select a "number editor" that has a textbox for editing the value). The problem that I'm interested in solving is how to two-way bind a Component's property to an editor control.

<ItemsControl x:Name="ComponentProperties" Grid.Row="1" ItemTemplateSelector="{StaticResource ComponentPropertyTemplateSelector}">
    <ItemsControl.ItemsSource>
        <Binding Converter="{StaticResource PropertiesConverter}"/>
    </ItemsControl.ItemsSource>
</ItemsControl>
ROMANIA_engineer
  • 54,432
  • 29
  • 203
  • 199
  • http://www.codeproject.com/Articles/87715/Native-WPF-PropertyGrid. Remember, `INotifyPropertyChanged` was designed for this sort of thing and actually predates WPF and MVVM in .NET –  Aug 19 '15 at 03:23

2 Answers2

3

I would say you probably want to follow the MVVM pattern which does use the INotifyPropertyChanged interface. If you do a Google search on MVVM there are some good articles that come up right away. There are also some existing tools out there already to help get you started. From what you describe in your question the MVVM pattern essentially works that way. It decouples the UI and the code but still maintains that connection. The real quick version is that you implement the INotifyPropertyChanged on a class and then you set an instance of that class to the DataContext of the control you want to setup the binding for. Probably easier to see an example:

Xaml:

   <StackPanel>
        <Slider Value="{Binding SliderValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
        <TextBox Text="{Binding MyText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
        <TextBox  Text="{Binding MyText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
        <Slider Value="{Binding SliderValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
    </StackPanel>

I created a view model base to save on some code writing:

class ViewModelBase : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            var handler = this.PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }

An example view model class:

 class MyViewModel : ViewModelBase
    {

        private int sliderValue;
        private string myText;

        public int SliderValue
        {
            get { return this.sliderValue; }
            set
            {
                this.sliderValue = value;
                this.OnPropertyChanged();
            }
        }


        public string MyText
        {
            get { return this.myText; }
            set
            {
                this.myText = value;
                this.OnPropertyChanged();
            }
        }

    }

How to hook up the binding (in this case the code behind of the control):

  public MainWindow()
        {
            InitializeComponent();

            this.DataContext = new MyViewModel();
        }

As you can see there is some work involved in setting up the view models and xaml. Compared to other solutions I think this is pretty good as far as the amount of "work" you have to put in. I don't know if there is any way to get around it though and have it work like "magic". It might be worth checking into what MVVM tools exist, there may be stuff out there that can make things even more simple.

MisterXero
  • 1,098
  • 2
  • 9
  • 17
  • Perfect example of INPC. You also have the ability to use DependecyProperties in any child control which allows you to see the properties in the property editor at design time. It's a very cool way to quickly take control of your design surface. I prefer DPs over INPC because they are much more "rich" in what they can do. – JWP Aug 19 '15 at 03:21
  • @JohnPeters Not entirely sure why someone would put dependency properties into a _view model_. http://stackoverflow.com/questions/1500669/can-somone-give-example-of-dependency-property-in-viewmodel and http://stackoverflow.com/questions/291518/inotifypropertychanged-vs-dependencyproperty-in-viewmodel –  Aug 19 '15 at 03:27
  • You are right, and it really cannot be done unless the Viewmodel inhertis from DepenecyObject, it doesn't make sense in the view model because they are never used at design time to wire up property values. I didn't imply it otherwise, as I mention child controls "user controls" that have them in the code behind. The advantage of DPs is you get design time property exposure not possible with INPC. – JWP Aug 19 '15 at 03:31
  • @JohnPeters Agreed, but at the time of writing, MisterXero's code had no control code source that was publishing any property so it was unclear to me why you mentioned DP. Cheers –  Aug 19 '15 at 03:32
  • I like to introduce folks to things not known, off topic but of high value when needed. Thanks! – JWP Aug 19 '15 at 03:35
  • @MisterXero If I understood correctly, I would have to manually create a viewmodel for each different model (component)? What I'm trying to accomplish is a view that is composed of property editors (sliders etc.) that are automatically generated for each field/property in the component (retrieved through component.GetType().GetProperties()). However, I'm not sure how to generate the two way binding for each property between it's editor control and model. – CodeCommander Aug 19 '15 at 20:13
1

You can add IPropertyChangeNotification support automatically using either Castle Dynamic Proxy (which wraps the classes in a proxy) or Fody (which modifies the IL in a post-build step).

Mark Feldman
  • 15,731
  • 3
  • 31
  • 58
  • Yes INPC is a good choice, just remember that you have to set the datacontext and or itemsource values. You can bind to complex classes and set indiviudal properties within each with can fire INPC. This leaves you will large "playing field" but remember "favor composition over inhertiance" in other words try to contain the classes and properties rather than inherit them. – JWP Aug 19 '15 at 03:19
  • This seems like an interesting solution, since it wouldn't require manually adding the INPC to a component's properties (which is exactly what I wanted), but would still have that functionality. I guess I'll have to try it out. – CodeCommander Aug 19 '15 at 20:32
  • Just be careful if you're using it with an ORM like EF or NHibernate. If you replace members with a proxy after they've been loaded into a repository then the ORM thinks you've changed it and serializes it back out again, which will cause you to quickly run into performance issues. Most of them have the abaility to inject a proxy at the time they're created in order to avoid this. – Mark Feldman Aug 19 '15 at 22:25