【问题标题】:WPF Game editor - Updating properties in UI/codeWPF 游戏编辑器 - 更新 UI/代码中的属性
【发布时间】:2015-08-18 20:41:48
【问题描述】:

我正在尝试在 C#/WPF 中创建游戏编辑器。编辑器由一个显示场景的用户控件(使用 SharpGL 在 OpenGL 中渲染)以及许多用于编辑当前场景和对象的控件组成。对象由可以在编辑器中编辑属性的组件组成(有点像 Unity 游戏引擎中的)。我已经有一个“组件编辑器”视图,它使用反射来查找组件上的所有属性,并为每个属性创建一个属性编辑器(例如,一个滑块)。但是,我不确定如何绑定 UI 和代码之间的属性。

问题是,我希望这些属性在代码更改时在 UI 中更新,并在 UI 中更改时在代码中更新。如果我想将编辑器控件(例如更改属性的滑块)绑定到组件属性,则必须实现 NotifyPropertyChanged,这将非常麻烦。我想另一种方法是进行脏检查,但我也不确定这是否是个好主意。

谁能告诉我应该如何处理 UI/代码之间的这个属性更新?我希望它像在 Unity 中一样工作,您无需在组件类中编写任何额外内容即可使属性可编辑。

编辑:为了更清楚地说明我想要实现的目标和已经拥有的目标,这里是“组件编辑器”用户控件的一部分。它的 datacontext 是一个组件实例(模型)。 PropertiesConverter 返回它的属性(通过 component.GetType().GetProperties())。 ComponentPropertyTemplateSelector 决定属性编辑器用户控件(例如,对于双属性,它将选择具有用于编辑值的文本框的“数字编辑器”)。我有兴趣解决的问题是如何将组件的属性双向绑定到编辑器控件。

<ItemsControl x:Name="ComponentProperties" Grid.Row="1" ItemTemplateSelector="{StaticResource ComponentPropertyTemplateSelector}">
    <ItemsControl.ItemsSource>
        <Binding Converter="{StaticResource PropertiesConverter}"/>
    </ItemsControl.ItemsSource>
</ItemsControl>

【问题讨论】:

标签: c# wpf


【解决方案1】:

我会说您可能想要遵循使用 INotifyPropertyChanged 接口的 MVVM 模式。如果你在 MVVM 上进行谷歌搜索,就会立即出现一些好文章。还有一些现有的工具可以帮助您入门。根据您在问题中的描述,MVVM 模式基本上就是这样工作的。它将 UI 和代码解耦,但仍保持这种连接。真正的快速版本是在一个类上实现 INotifyPropertyChanged,然后将该类的一个实例设置为要为其设置绑定的控件的 DataContext。可能更容易看到一个例子:

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>

我创建了一个视图模型库以节省一些代码编写:

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));
            }
        }
    }

一个示例视图模型类:

 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();
            }
        }

    }

如何挂钩绑定(在本例中为控件背后的代码):

  public MainWindow()
        {
            InitializeComponent();

            this.DataContext = new MyViewModel();
        }

如您所见,设置视图模型和 xaml 涉及一些工作。与其他解决方案相比,我认为就您必须投入的“工作”量而言,这非常好。我不知道是否有任何方法可以解决它并让它像“魔术”一样工作。可能值得检查一下存在哪些 MVVM 工具,可能有一些东西可以让事情变得更加简单。

【讨论】:

  • INPC 的完美例子。您还可以在任何子控件中使用 DependecyProperties,这使您可以在设计时在属性编辑器中查看属性。这是快速控制您的设计表面的一种非常酷的方式。我更喜欢 DP 而不是 INPC,因为他们的能力更“丰富”。
  • @JohnPeters 不完全确定为什么有人会将依赖属性放入 视图模型stackoverflow.com/questions/1500669/…stackoverflow.com/questions/291518/…
  • 你是对的,除非 Viewmodel 从 DepenecyObject 继承,否则它确实无法完成,它在视图模型中没有意义,因为它们在设计时从未用于连接属性值。我并没有暗示它,因为我提到了在代码中包含它们的子控件“用户控件”。 DP 的优势是您可以获得 INPC 无法实现的设计时属性曝光。
  • @JohnPeters 同意,但在撰写本文时,MisterXero 的代码没有发布任何属性的控制代码源,所以我不清楚您为什么提到 DP。干杯
  • 我喜欢向人们介绍未知的、离题但在需要时具有高价值的事物。谢谢!
【解决方案2】:

您可以使用Castle Dynamic Proxy(将类包装在代理中)或Fody(在构建后步骤中修改 IL)自动添加 IPropertyChangeNotification 支持。

【讨论】:

  • 是的,INPC 是一个不错的选择,只要记住您必须设置 datacontext 和/或 itemsource 值。您可以绑定到复杂的类并在每个类中设置单独的属性,并且可以触发 INPC。这会让你有很大的“竞争环境”,但要记住“优先组合而不是继承”,换句话说,尝试包含类和属性而不是继承它们。
  • 这似乎是一个有趣的解决方案,因为它不需要手动将 INPC 添加到组件的属性中(这正是我想要的),但仍然具有该功能。我想我得试试看。
  • 如果您将它与 EF 或 NHibernate 等 ORM 一起使用,请小心。如果您在将成员加载到存储库后将其替换为代理,那么 ORM 会认为您已对其进行了更改并再次将其序列化,这将导致您很快遇到性能问题。他们中的大多数人都可以在创建代理时注入代理以避免这种情况。
猜你喜欢
  • 2021-09-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-09-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多