Traits
Contents
UBIK® Traits (Client)
UBIK® Traits provide the ability to define a dynamic list of lightweight properties for individual instances (UBIK® objects). Unlike traditional UBIK® Properties, which are rigidly bound to a MetaClass, instances of the same entity can contain a different list of traits.
This feature is particularly useful for data that only becomes known at runtime or varies significantly from instance to instance.
| This feature must be used wisely, too many traits could impact the performance. Traits do not replace properties. |
ViewModel Architecture & Data Access
To work with traits in the UI (XAML), the ContentViewModel provides several dedicated accessors and collections.
1. ValueAccessors (Recommended for Read/Write)
The ValueAccessors acts as a combined dynamic value resolver. It is the preferred way to read or edit values in the UI because of its built-in fallback logic:
- Trait Lookup: First attempts to resolve the value via a trait with the specified name.
- Property Fallback: If no trait is found, it queries a regular UBIK® Property with that name.
- Default: If neither exists, it returns
null.
2. TraitByName (Traits Only)
TraitByName is a dedicated collection (ObservableTraitDictionary) that exclusively loads and initializes traits. Unlike the ValueAccessors, this does not fall back to regular properties.
- Usage: When you explicitly want to query only traits (e.g., read-only display in the UI).
3. Values (Raw Data)
This is a combined getter that returns the raw value directly from the dictionary, instead of providing a complex TraitViewModel or PropertyViewModel.
- Usage: For pure display purposes or evaluations.
Commands (Set & Create Values)
SetValueCommand
This command on the ContentViewModel orchestrates value assignments and handles the creation of new traits if necessary.
- If the trait exists: The value will be overwritten.
- If a property exists: The property's value will be overwritten (if it is not locked/read-only).
- If neither exists: A new
TraitViewModelis created and added to the UBIK® content.
XAML Customizing / UI Integration
To display traits in custom XAML templates, make them editable, or create new ones, standard UBIK® controls like EvalExpression or SfListViewExt are used.
1.Displaying all Traits in a List View (read-only)
If you simply want to display all traits of an object, you can bind directly to Content.Traits. Here it is combined with the Syncfusion ListView (SfListViewExt):
| Bindings persist only on DataModel level which means changes on ViewModel level are only visible after saving. |
<controls:SfListViewExt ItemsSource="{Binding Content.Traits}">
<controls:SfListViewExt.ItemTemplate>
<DataTemplate>
<Grid ColumnDefinitions="0.4*, 0.6*">
<Label Grid.Column="0" Text="{Binding Name}" />
<Label Grid.Column="1" Text="{Binding Value}" />
</Grid>
</DataTemplate>
</controls:SfListViewExt.ItemTemplate>
</controls:SfListViewExt>
2. Editing a single Trait or Property via ValueAccessors
To dynamically query and edit a specific trait, an EvalExpression with the ValueAccessors Context should be used. The TwoWay binding ensures that changes are written directly back to the ViewModel.
<controls:EvalExpression x:Name="EvalS2" Context="{Binding ValueAccessors}" Expression="Context[P0]">
<controls:EvalExpressionParameter Name="P0" Value="{Binding Path=Text, Source={x:Reference S2NameInput}}" />
</controls:EvalExpression>
<Entry x:Name="S2NameInput" />
<Entry x:Name="S2ValueInput" Text="{Binding Result.Value, Source={x:Reference EvalS2}, Mode=TwoWay}" />
3. Deleting a Trait
Deleting a trait does not require a dedicated command. Instead an event behavior can be used to directly invoke the Delete method on the evaluator's result..
<Button Text="Delete">
<Button.Behaviors>
<behaviors:EventHandlerBehavior EventName="Clicked">
<behaviors:InvokeMethodAction TargetObject="{Binding Result, Source={x:Reference EvalS2}}" MethodName="Delete" />
</behaviors:EventHandlerBehavior>
</Button.Behaviors>
</Button>
4. Creating or overwriting a Trait via SetValueCommand
To create traits entirely from scratch (or to explicitly overwrite an existing value), the SetValueCommand of the ContentViewModel should be used. A KeyValueList must be passed to this Command.
<Entry x:Name="S4NameInput" Placeholder="Name of the (new) trait..." />
<Entry x:Name="S4ValueInput" Grid.Column="0" Placeholder="Enter value to set..." />
<Button Grid.Column="1" Command="{Binding SetValueCommand}" Text="Set">
<Button.CommandParameter>
<classes:KeyValueList>
<classes:KeyValueParameter Key="PropertyName" Value="{Binding Text, Source={x:Reference S4NameInput}}" />
<classes:KeyValueParameter Key="PropertyValue" Value="{Binding Text, Source={x:Reference S4ValueInput}}" />
</classes:KeyValueList>
</Button.CommandParameter>
</Button>
</Grid>
5. Edit Specific Datatypes (e.g., DatePicker)
Traits store actual underlying datatypes (such as DateTime). This means you can bind regular UI controls like a DatePicker directly to the result of the EvalExpression.
<controls:EvalExpressionParameter Name="P0" Value="{Binding Path=Text, Source={x:Reference DateTraitNameInput}}" />
</controls:EvalExpression>
<Entry x:Name="DateTraitNameInput" Text="TRAIT_DATETIME" />
<DatePicker x:Name="DateTestPicker" Date="{Binding Result.Value, Source={x:Reference EvalDateTime}}" Format="dd.MM.yyyy" />
<Button Command="{Binding SetValueCommand}" Text="Set">
<Button.CommandParameter>
<classes:KeyValueList>
<classes:KeyValueParameter Key="PropertyName" Value="{Binding Text, Source={x:Reference DateTraitNameInput}}" />
<classes:KeyValueParameter Key="PropertyValue" Value="{Binding Date, Source={x:Reference DateTestPicker}}" />
</classes:KeyValueList>
</Button.CommandParameter>
</Button>
