HateDread
Topic Author
Posts: 72
Joined: 06 Feb 2020, 10:27

Working with components / ECS?

13 Nov 2021, 04:27

Working in a custom tech stack, and game objects are built out of components.

Right now, my view model setup is very OOP - e.g. every object that can be seen in space has a base class view model
struct SpaceObjectModel : public DeferredNotifyBase
{
public:
	EntityID entityID_ = INVALID_ENTITY;

	virtual void UpdateValues(const Entity& entity);

private:
	uint32_t GetEntityID_Converted() const;
	float GetScreenPosX() const;
	void SetScreenPosX(float screenPosX);
	float GetScreenPosY() const;
	void SetScreenPosY(float screenPosY);
	
	Noesis::String name_;
	float screenPosX_ = 0.0f;
	float screenPosY_ = 0.0f;

	NS_IMPLEMENT_INLINE_REFLECTION(SpaceObjectModel, DeferredNotifyBase, "rtg.SpaceObjectModel")
	{
		NsProp("Name", &SpaceObjectModel::name_);
		NsProp("EntityID", &SpaceObjectModel::GetEntityID_Converted);
		NsProp("ScreenPosX", &SpaceObjectModel::GetScreenPosX);
		NsProp("ScreenPosY", &SpaceObjectModel::GetScreenPosY);
	}
};
And each derived class implements UpdateValues, which allows the view model class to look into the registry of components/entities, using its own Entity to figure out which component(s) it owns, and setting the corresponding values, i.e. mirroring values from components into these VM classes.

My question is; have you seen people work with components differently? I'm quickly running into situations where I might have a derived VM class that does everything I want but has _some_ properties and xaml bindings a particular entity can't support due to lacking components, so it's not a good fit; necessitating yet another derived class and leading to the OOP hell I'm using ECS to escape. Can I have optional elements in XAML that somehow know if the underlying VM has those properties?

Example of a derived class
struct ShipObjectModel : public SpaceObjectModel
{
public:
	float GetIntegrity() const;
	void SetIntegrity(float integrity);

	float GetMaxIntegrity() const;
	void SetMaxIntegrity(float maxIntegrity);

	float GetIntegrityPercent() const;
	
	float GetShield() const;
	void SetShield(float shield);

	float GetMaxShield() const;

	EShieldChargeMode GetShieldMode() const;
	void SetShieldMode(EShieldChargeMode shieldMode);

	bool GetIsSelected() const;
	void SetIsSelected(bool bIsSelected);

	void SetFactionID(FactionID factionID);

	bool GetDidIntegrityDecrease() const;

	virtual void UpdateValues(const Entity& entity) override
	{
		if (entity == INVALID_ENTITY)
		{
			RTG_ERROR("Trying to update ShipObjectModel without a valid associated Entity");
			return;
		}

		SpaceObjectModel::UpdateValues(entity);

		const HealthComponent& healthComponent = entity.GetComponent<const HealthComponent>();
		SetIntegrity(healthComponent.integrity);

		if (const ShieldComponent* pShieldComponent = entity.TryGetComponent<const ShieldComponent>())
		{
			SetShield(pShieldComponent->currShield);
			SetShieldMode(pShieldComponent->chargeMode);
		}

		SetIsSelected(entity.HasComponents<const IsSelectedComponent>());

		if (const FactionComponent* pFactionComponent = entity.TryGetComponent<const FactionComponent>())
		{
			SetFactionID(pFactionComponent->factionID);
		}
		else
		{
			SetFactionID(INVALID_FACTION_ID);
		}
	}

private:
	uint32_t GetFactionID_Converted() const;

	FactionID factionID_ = INVALID_FACTION_ID;
	float integrity_ = 1000.0f;
	float maxIntegrity_ = 1000.0f;
	float shield_ = 1000.0f;
	float maxShield_ = 1000.0f;
	EShieldChargeMode shieldMode_ = EShieldChargeMode::Full;
	bool bIsSelected_ = false;
	bool bDidIntegrityDecrease_ = false;

	NoesisApp::DelegateCommand objectSelected_;

	NS_DECLARE_REFLECTION(ShipObjectModel, SpaceObjectModel)
};
Some of the functionality here like selection would be useful for other objects like planets, but... those won't have shields. You can see the madness it would lead to. I'd love to reuse some user controls for e.g. object overlays in the world but have optional elements (e.g. shield health bar) drawn from some more generic VMs with optional bindings based on the presence of components (which I can check using the ECS reflection system by name - so if I have a binding for "Shield" I can search for the corresponding values. But I don't know if in Noesis you can go in that direction - declaring bindings in xaml and using the binding name to iterate C++ reflection structures?)

Thanks.
 
User avatar
jsantos
Site Admin
Posts: 3918
Joined: 20 Jan 2012, 17:18
Contact:

Re: Working with components / ECS?

17 Nov 2021, 11:57

Your base class view model could implement IDictionaryIndexer to get access to different components. That way you could bind like this:
{Binding enemy[transform].position}
I am not sure if this is what you are trying to achieve.
 
HateDread
Topic Author
Posts: 72
Joined: 06 Feb 2020, 10:27

Re: Working with components / ECS?

19 Nov 2021, 17:40

Your base class view model could implement IDictionaryIndexer to get access to different components. That way you could bind like this:
{Binding enemy[transform].position}
I am not sure if this is what you are trying to achieve.
Totally fair - my post was a mix of "How do I do this?" and "Wait I don't know WPF that well, is this the right way to do it?" so I'll try something more concrete and scope out a little in case I'm starting already barking up the wrong tree.

------------------------

I have a mix of different types of objects in the game that require clickable/hover-able icons over them in UI; spaceships, missiles, planets, and so on. On the game side, they're not represented by any one class; everything is just a collection of components as defined in a json file. As a result, some of these 'objects' have different data associated with them.

Here is my current UI icon (currently "SpaceObjectIcon")

Image

It sits inside an ItemsControl like so, which is responsible for positioning it on the screen according to XY position.
<!-- Ship Icons -->
<ItemsControl ItemsSource="{Binding SpaceObjects}">
	<ItemsControl.ItemsPanel>
		<ItemsPanelTemplate>
			<Canvas/>
		</ItemsPanelTemplate>
	</ItemsControl.ItemsPanel>
	<ItemsControl.ItemTemplate>
		<DataTemplate>
			<controls:SpaceObjectIcon RouteOnClicked="{Binding DataContext.ObjectSelected, ElementName=ShipListItemControl}"
									  RouteOnDoubleClicked="{Binding DataContext.ObjectFocused, ElementName=ShipListItemControl}"
			></controls:SpaceObjectIcon>
		</DataTemplate>
	</ItemsControl.ItemTemplate>
	<ItemsControl.ItemContainerStyle>
		<Style TargetType="{x:Type ContentPresenter}">
			<Setter Property="controls:CenteredCanvas.X" Value="{Binding ScreenPosX}"/>
			<Setter Property="controls:CenteredCanvas.Y" Value="{Binding ScreenPosY}"/>
		</Style>
	</ItemsControl.ItemContainerStyle>
</ItemsControl>
I'm using the term "SpaceObject" to broadly refer to any object in 'space'. "SpaceObjects" is just a "Noesis::Ptr<Noesis::ObservableCollection<SpaceObjectModel>>" on the main game view model.

Herein lies the issue; in the icon shown above, on the left is the health bar for the shield and on the right, for the hull. What if a particular object doesn't have any shield / shield component, just hull? (i.e. missiles). Or doesn't have either hull or shields (i.e. planets). Or if some particular components being present on an entity indicate that said entity has some kind of temporary status/buff/debug that I want to indicate on this icon? Be that via different coloured dots above the square, or diagonal line/double-lines etc through the image - basically different visuals depending on presence of components in C++.

One option is a gigantic, monolithic view model class that can handle everything any object in the game could want bound into the UI. But that seems a bit crazy.

I tried seeing if polymorphism would work - i.e. having a collection of SpaceObjectModel pointers as the base class, then inheriting from it and adding the different properties/bindings, and hoping wpf/Noesis can make the connection when I store those just as base class ptrs in the ObservableCollection. (Also this doesn't actually work - you can see the models I used in the first post - SpaceObjectModel then deriving a ShipObjectModel. If you store just the base class and try to bind, Noesis complains about the bindings mentioned in the xaml even if the base class ptr _is_ actually the right type (polymorphically speaking). WPF handled this without errors, but I don't know what would happen if you start mixing different subclasses together in that collection).


But this leads to the classic OOP issues - what about a missile with shields that can also have some of the aforementioned buffs/debuffs? SpaceObjectModel, SpaceObjectWithShieldsModel, SpaceObjectWithAbilityXModel, SpaceObjectWithAbilityYNoShieldsModel, and so on... would be crazy and something I am using ECS to escape. Looking for a better approach to handling these combinations.

Does this clarify things?
 
User avatar
jsantos
Site Admin
Posts: 3918
Joined: 20 Jan 2012, 17:18
Contact:

Re: Working with components / ECS?

23 Nov 2021, 00:36

Thanks for the detailed explanation!

In the line of my first answer, you could use DataTriggers to enable templates for each component. Something similar to this:
<ei:DataTrigger Binding="{Binding Components[shields]}" Comparison="NotEqual" Value="{x:Null}">
  <ei:ChangePropertyAction PropertyName="Template" Value="{StaticResource ShieldsTemplate}"/>
</ei:DataTrigger>
You could have different controls for each component, and they are only enabled if the corresponding trigger is enabled.

I haven't tried this idea before, please let us know what do you think about it.
 
HateDread
Topic Author
Posts: 72
Joined: 06 Feb 2020, 10:27

Re: Working with components / ECS?

23 Nov 2021, 12:59

Thanks for the detailed explanation!

In the line of my first answer, you could use DataTriggers to enable templates for each component. Something similar to this:
<ei:DataTrigger Binding="{Binding Components[shields]}" Comparison="NotEqual" Value="{x:Null}">
  <ei:ChangePropertyAction PropertyName="Template" Value="{StaticResource ShieldsTemplate}"/>
</ei:DataTrigger>
You could have different controls for each component, and they are only enabled if the corresponding trigger is enabled.

I haven't tried this idea before, please let us know what do you think about it.
That idea seems pretty good - will give it a go! One issue I can foresee; say we use the above DataTrigger to enable the ShieldsTemplate. That ShieldsTemplate will want to bind to a properties like Shield, MaxShield, and so on (for the shield health bar). And that would necessitate exposing those properties through a view model, taking us back to the start. Having said that, your first example using IDictionaryIndexer implies otherwise. How would Noesis/WPF know the type, though? Doesn't it need to be very specific? E.g. when changing your first example:
{Binding entityViewModelThisShieldTemplateIsUsing[shield]Shield}
{Binding entityViewModelThisShieldTemplateIsUsing[shield]MaxShield}
How can you possibly have such generic bindings that don't have Noesis properties in C++ registered using NsProp by name and with a function returning a specific type?
 
User avatar
jsantos
Site Admin
Posts: 3918
Joined: 20 Jan 2012, 17:18
Contact:

Re: Working with components / ECS?

23 Nov 2021, 14:10

That idea seems pretty good - will give it a go! One issue I can foresee; say we use the above DataTrigger to enable the ShieldsTemplate. That ShieldsTemplate will want to bind to a properties like Shield, MaxShield, and so on (for the shield health bar). And that would necessitate exposing those properties through a view model, taking us back to the start. Having said that, your first example using IDictionaryIndexer implies otherwise. How would Noesis/WPF know the type, though? Doesn't it need to be very specific? E.g. when changing your first example:
You need to register those properties using NsProp in each component. So, for example, when using this:
{Binding components[shields].armor}
The property "components" in your VM needs to implement IDictionaryIndexer and must return a different instance for each component. Each of those components must have noesis reflection for the required properties: armor, etc. Note that there is different class for each component, so this is compatible with ECS architectures.

By the way, I forgot to mention
I tried seeing if polymorphism would work - i.e. having a collection of SpaceObjectModel pointers as the base class, then inheriting from it and adding the different properties/bindings, and hoping wpf/Noesis can make the connection when I store those just as base class ptrs in the ObservableCollection. (Also this doesn't actually work - you can see the models I used in the first post - SpaceObjectModel then deriving a ShipObjectModel. If you store just the base class and try to bind, Noesis complains about the bindings mentioned in the xaml even if the base class ptr _is_ actually the right type (polymorphically speaking). WPF handled this without errors, but I don't know what would happen if you start mixing different subclasses together in that collection).
This should work also in Noesis as we use the dynamic type for each instance inside the ObservableCollection. Make sure you are correctly setting the parent type when using NS_DECLARE_REFLECTION.
NS_DECLARE_REFLECTION(ShipObjectModel, SpaceObjectModel )
 
HateDread
Topic Author
Posts: 72
Joined: 06 Feb 2020, 10:27

Re: Working with components / ECS?

23 Nov 2021, 15:22

You need to register those properties using NsProp in each component. So, for example, when using this:
{Binding components[shields].armor}
The property "components" in your VM needs to implement IDictionaryIndexer and must return a different instance for each component. Each of those components must have noesis reflection for the required properties: armor, etc. Note that there is different class for each component, so this is compatible with ECS architectures.
Would this require the components themselves to inherit from NoesisApp::NotifyPropertyChangedBase? The components are flat POD structs and must be trivially copy-able, so that isn't possible. I also don't want to increase the size of components just for Noesis (+ some components are shared between server and client in-process so client-only Noesis members taking up space can't be compiled out).

I suppose one could build a Noesis-compatible type for each, e.g. ShieldComponentModel for ShieldComponent, and link it to the real ShieldComponent under the hood, i.e. it just has pass-through functions for get/set and doesn't store anything itself. Seems a bit redundant to have that 1:1 mapping just for the type system. Will also have to store one VM for each component for each entity.
struct ShieldComponent 
{
	float maxShield = 1000.0f;
	float currShield = 1000.0f;
	float rechargePerSec = 5.0f;
	float delayBeforeRecharge = 0.0f;

	EShieldChargeMode chargeMode = EShieldChargeMode::Full;
};

class ShieldComponentModel : public Noesis::BaseComponent
{
public:
	float GetCurrentShield() const {
		entity.GetComponent<const ShieldComponent>().currShield;
	}

	// Called from ECS to notify Noesis (not exposed to xaml)
	void SetCurrentShield() {
		OnPropertyChanged("CurrentShield");
	}

	float GetMaxShield() const {
		entity.GetComponent<const ShieldComponent>().maxShield;
	}

	// Called from ECS to notify Noesis (not exposed to xaml)
	void SetMaxShield() {
		OnPropertyChanged("MaxShield");
	}

private:
	Entity entity_;

	NS_IMPLEMENT_INLINE_REFLECTION(ShieldComponentModel, Noesis::BaseComponent)
	{
		NsProp("CurrentShield", &ShieldComponentModel::GetCurrentShield);
		NsProp("MaxShield", &ShieldComponentModel::GetMaxShield);
	}
};
So when an entity is created:
  • Create an EntityViewModel instance (inherits from IDictionaryIndexer) that exposes a "Components" property
  • For each component on the entity, create a matching "Something"ComponentModel. Store these somewhere/somehow and make them retrievable via string.
  • Map each Noesis component model instance to its real component and entity
Will also need to mirror add/remove of components at runtime into the Components property.

It seems a bit clunky to have to write an essentially empty forwarding class. But I'm not sure what the alternative is (or even conceptualizing what it could be). For setting/notifying, the components themselves could guard all members with getters/setters, in which case I'd know the name of any property on any component being set. Can I get to the EntityViewModel itself and do something like OnPropertyChanged("ComponentName.PropertyName") or similar? Doesn't remove the need for the Get functions on dedicated component models though.
 
User avatar
jsantos
Site Admin
Posts: 3918
Joined: 20 Jan 2012, 17:18
Contact:

Re: Working with components / ECS?

25 Nov 2021, 11:44

Would this require the components themselves to inherit from NoesisApp::NotifyPropertyChangedBase? The components are flat POD structs and must be trivially copy-able, so that isn't possible. I also don't want to increase the size of components just for Noesis (+ some components are shared between server and client in-process so client-only Noesis members taking up space can't be compiled out).
It is not mandatory inheriting from BaseComponent, you can use Noesis reflection macros without it. But in that case, the instance becomes a "struct" and can't notify changes separately for each property. You can notify a change about the whole struct. Probably acceptable if you don't have a lot of properties.
I suppose one could build a Noesis-compatible type for each, e.g. ShieldComponentModel for ShieldComponent, and link it to the real ShieldComponent under the hood, i.e. it just has pass-through functions for get/set and doesn't store anything itself. Seems a bit redundant to have that 1:1 mapping just for the type system. Will also have to store one VM for each component for each entity.
Yes, this approach is also good. Very similar to the way we are connecting Noesis with Unreal types. In this case, you could also store the Noesis instance as a member of each component.
Can I get to the EntityViewModel itself and do something like OnPropertyChanged("ComponentName.PropertyName") or similar?
No, unfortunately that won't work.

Please let me know how this goes and if you need more help.
 
User avatar
jsantos
Site Admin
Posts: 3918
Joined: 20 Jan 2012, 17:18
Contact:

Re: Working with components / ECS?

25 Nov 2021, 11:58

Another alternative, is building a runtime type for each entity. You inherit your entity from BaseComponent and implement GetClassType with a type created at runtime collecting the properties for all its components ("Shields_Armor", "Transform_Position"). This way you can also notify about property changes as it the properties were in the entity.
 
HateDread
Topic Author
Posts: 72
Joined: 06 Feb 2020, 10:27

Re: Working with components / ECS?

26 Nov 2021, 10:47

Would this require the components themselves to inherit from NoesisApp::NotifyPropertyChangedBase? The components are flat POD structs and must be trivially copy-able, so that isn't possible. I also don't want to increase the size of components just for Noesis (+ some components are shared between server and client in-process so client-only Noesis members taking up space can't be compiled out).
It is not mandatory inheriting from BaseComponent, you can use Noesis reflection macros without it. But in that case, the instance becomes a "struct" and can't notify changes separately for each property. You can notify a change about the whole struct. Probably acceptable if you don't have a lot of properties.
I suppose one could build a Noesis-compatible type for each, e.g. ShieldComponentModel for ShieldComponent, and link it to the real ShieldComponent under the hood, i.e. it just has pass-through functions for get/set and doesn't store anything itself. Seems a bit redundant to have that 1:1 mapping just for the type system. Will also have to store one VM for each component for each entity.
Yes, this approach is also good. Very similar to the way we are connecting Noesis with Unreal types. In this case, you could also store the Noesis instance as a member of each component.
Can I get to the EntityViewModel itself and do something like OnPropertyChanged("ComponentName.PropertyName") or similar?
No, unfortunately that won't work.

Please let me know how this goes and if you need more help.
I'm still experimenting purely in the WPF/Blend mock-up before taking it to C++. I may end up using structs as you mentioned, and marking them as dirtied on the ECS side somehow, then only telling Noesis the struct/component changed once per frame at the end.

I wasn't sure how to use the data template idea you mentioned, so instead went via a style. Here is resources.xaml (where integrity is health/hull):
<sys:Single x:Key="IntegrityLowPercent">0.25</sys:Single>
<Style x:Key="IntegrityBarAnimStyle" TargetType="ProgressBar" BasedOn="{StaticResource {x:Type ProgressBar}}">
	<Style.Triggers>
		<DataTrigger Value="True">
			<DataTrigger.Binding>
				<MultiBinding Converter="{StaticResource IsLessThanPercentageConverter}" Mode="OneWay">
					<Binding Path="[HealthComponent].Integrity"/>
					<Binding Path="[HealthComponent].MaxIntegrity"/>
					<Binding Source="{StaticResource IntegrityLowPercent}"></Binding>
				</MultiBinding>
			</DataTrigger.Binding>
			<DataTrigger.EnterActions>
				<BeginStoryboard Name="LowIntegrityFlash">
					<Storyboard>
						<ColorAnimation Storyboard.TargetProperty="Foreground.Color" To="{StaticResource IntegrityLowColor}" AutoReverse="True" Duration="0:0:0.1" RepeatBehavior="Forever"/>
					</Storyboard>
				</BeginStoryboard>
			</DataTrigger.EnterActions>
			<DataTrigger.ExitActions>
				<StopStoryboard BeginStoryboardName="LowIntegrityFlash"></StopStoryboard>
			</DataTrigger.ExitActions>
		</DataTrigger>
	</Style.Triggers>
</Style>

<Style x:Key="IntegrityBarOptionalStyle" TargetType="ProgressBar" BasedOn="{StaticResource IntegrityBarAnimStyle}">
	<Style.Triggers>
		<DataTrigger Binding="{Binding [HealthComponent], Converter={StaticResource IsNullConverter}}" Value="False">
			<Setter Property="IsEnabled" Value="True"></Setter>
			<Setter Property="Visibility" Value="Visible"></Setter>
		</DataTrigger>
		<DataTrigger Binding="{Binding [HealthComponent], Converter={StaticResource IsNullConverter}}" Value="True">
			<Setter Property="IsEnabled" Value="False"></Setter>
			<Setter Property="Visibility" Value="Hidden"></Setter>
		</DataTrigger>
	</Style.Triggers>
</Style>
And my integrity/health bar is in a control like so:
<ProgressBar x:Name="IntegrityBar" Style="{StaticResource IntegrityBarOptionalStyle}" RenderTransformOrigin="0.5,0.5" VerticalAlignment="Center" Panel.ZIndex="2" Margin="29,0,-29,0" Background="#FF575757" Height="5" Foreground="#FFD4D4D4" Value="{Binding [HealthComponent].Integrity, Mode=OneWay}" BorderBrush="#FFD4D4D4" Maximum="{Binding [HealthComponent].MaxIntegrity, Mode=OneWay}">
	<ProgressBar.RenderTransform>
		<TransformGroup>
			<ScaleTransform/>
			<SkewTransform/>
			<RotateTransform Angle="270"/>
			<TranslateTransform/>
		</TransformGroup>
	</ProgressBar.RenderTransform>
</ProgressBar>
Unfortunately, from what I can tell on StackOverflow, in WPF the data triggers aren't inherited (or something along those lines) - if I use IntegrityBarOptionalStyle, which is meant to be the kind of "guard" style wrapping the animation one, the animations/data triggers never fire - my converters don't execute, and so on. So doing this via styles seems to be a bit janky.

How would you suggest using your data template idea? I have this health/integrity bar idea in a few different controls, and they look similar to the above (hence using a common style - any health bar on screen for a given entity should flash the same way when the data trigger trips). A data trigger needs to be in a style, right? And then I'd use that to activate/use a data template, but then that needs a style to have these re-usable animations... seems like a circle. I wouldn't want to go and build a separate control/xaml file for each optional element - can I do something more inline to null them out?

I tried using just the one style with an attempted "early out" that nulls out the template, but the converter is still called with null/unset values in the later data triggers, which means it's not early-outing (fair enough), although it does disappear from the screen.
<sys:Single x:Key="IntegrityLowPercent">0.25</sys:Single>
<Style x:Key="IntegrityBarAnimStyle" TargetType="ProgressBar" BasedOn="{StaticResource {x:Type ProgressBar}}">
	<Style.Triggers>

		<DataTrigger Binding="{Binding [HealthComponent], Converter={StaticResource IsNullConverter}}" Value="True">
			<Setter Property="Template" Value="{x:Null}"></Setter>
		</DataTrigger>

		<DataTrigger Value="True">
			<DataTrigger.Binding>
				<MultiBinding Converter="{StaticResource IsLessThanPercentageConverter}" Mode="OneWay">
					<Binding Path="[HealthComponent].Integrity"/>
					<Binding Path="[HealthComponent].MaxIntegrity"/>
					<Binding Source="{StaticResource IntegrityLowPercent}"></Binding>
				</MultiBinding>
			</DataTrigger.Binding>
			<DataTrigger.EnterActions>
				<BeginStoryboard Name="LowIntegrityFlash">
					<Storyboard>
						<ColorAnimation Storyboard.TargetProperty="Foreground.Color" To="{StaticResource IntegrityLowColor}" AutoReverse="True" Duration="0:0:0.1" RepeatBehavior="Forever"/>
					</Storyboard>
				</BeginStoryboard>
			</DataTrigger.EnterActions>
			<DataTrigger.ExitActions>
				<StopStoryboard BeginStoryboardName="LowIntegrityFlash"></StopStoryboard>
			</DataTrigger.ExitActions>
		</DataTrigger>
	</Style.Triggers>
</Style>
EDIT: I also noticed that even though I'm using an ObservableCollection for the component view models, I need to use OnPropertyChanged("Item[]") to support adding and removing components at runtime. Apparently that means adding or removing components will refresh all component bindings since you can't specify that e.g. "Components[ShieldComponent]" is the property that changed; only the entire thing, and using the default string (hence "item[]"). Is this correct? Not sure how big of a deal that is. E.g. in my EntityViewModel that has components in it in my C#/Blend mock-up:
public T AddComponent<T>() where T : ComponentModelBase, new()
{
	foreach (ComponentModelBase component in _Components)
	{
		T casted = component as T;
		if (casted != null)
		{
			return casted;
		}
	}

	T newComponent = new T();

	_Components.Add(newComponent);
	OnPropertyChanged("Item[]");

	return newComponent;
}

Who is online

Users browsing this forum: Ahrefs [Bot], Bing [Bot] and 8 guests