- Eli Iannuzzo
- Posts: 5
- Joined:
Dynamic Menu3D options menu
We've been having trouble creating an options menu in our project with the following criteria.
- Options list is generated from data.
- Options can have different behaviors when interacted with, so we need a way of passing in an "interaction type" to each option, and based on that type it would use a different template & bind functionality to the view model. e.g.
- -A label only option that can either do nothing or respond to a click event.
- -A slider option that responds to specified inputs & has event bound to the view model for processing updates to the slider position.
- -An array of entries that we go through using specified inputs (similar to the OptionSelector in the Menu3D example)
- -An option containing an editable text box, again firing off an event bound to the view model when text is updated.
-
sfernandez
Site Admin
- Posts: 2997
- Joined:
Re: Dynamic Menu3D options menu
I think using typed DataTemplates here can help. You can have a list of heterogeneous options (sharing a base class), and different DataTemplates for each type:
The key here is, instead of explicitly setting the ItemTemplate on the ItemsControl, you let the resource lookup to automatically pick the appropriate DataTemplate based on the type of the item.
The key here is, instead of explicitly setting the ItemTemplate on the ItemsControl, you let the resource lookup to automatically pick the appropriate DataTemplate based on the type of the item.
- Eli Iannuzzo
- Posts: 5
- Joined:
Re: Dynamic Menu3D options menu
Could you give an example of doing this using an ObservableCollection as the ItemSource?
-
sfernandez
Site Admin
- Posts: 2997
- Joined:
Re: Dynamic Menu3D options menu
The example will be pretty similar, but instead of having the items inside ItemsControl, they will be provided by the collection in the ItemsSource:
At any time you will fill the ViewModel's collection of options, and set the view model as DataContext of some control like this:
Hope this helps.
Code: Select all
class Option: public BaseComponent, public INotifyPropertyChanged
{
public:
Option(const char* label): _label(label) { }
const char* GetLabel() const { return _label.Str(); }
PropertyChangedEventHandler& PropertyChanged() override { return _changed; }
NS_IMPLEMENT_INTERFACE_FIXUP
protected:
void OnPropertyChanged(const char* name)
{
_changed(this, PropertyChangedEventArgs(Symbol(name)));
}
private:
String _label;
PropertyChangedEventHandler _changed;
NS_IMPLEMENT_INLINE_REFLECTION(Option, BaseComponent)
{
NsImpl<INotifyPropertyChanged>();
NsProp("Label", &Option::GetLabel);
}
};
class BoolOption: public Option
{
public:
BoolOption(const char* label): Option(label) { }
bool GetBool() const { return _bool; }
void SetBool(bool value)
{
if (_bool != value)
{
_bool = value;
OnPropertyChanged("Bool");
}
}
private:
bool _bool;
NS_IMPLEMENT_INLINE_REFLECTION(TextOption, Option)
{
NsProp("Bool", &BoolOption::GetBool, &BoolOption::SetBool);
}
}
class TextOption: public Option
{
public:
TextOption(const char* label): Option(label) { }
const char* GetText() const { return _text.Str(); }
void SetText(const char* value)
{
if (_text != value)
{
_text = value;
OnPropertyChanged("Text");
}
}
private:
String _text;
NS_IMPLEMENT_INLINE_REFLECTION(TextOption, Option)
{
NsProp("Text", &TextOption::GetText, &TextOption::SetText);
}
}
//...
typedef ObservableCollection<Option> OptionCollection;
class ViewModel: public BaseComponent
{
public:
ViewModel(): _options(MakePtr<OptionCollection>()) { }
OptionCollection* GetOptions() const { return _options; }
private:
Ptr<OptionCollection> _options;
NS_IMPLEMENT_INLINE_REFLECTION(ViewModel, BaseComponent)
{
NsProp("Options", &ViewModel::GetOptions);
}
};
Code: Select all
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<UserControl.Resources>
<ControlTemplate x:Key="OptionTemplate" TargetType="HeaderedContentControl">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2*" MaxWidth="200"/>
<ColumnDefinition Width="5*/>
<Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{TemplateBinding Header}" TextTrimming="CharacterEllipsis" VerticalAlignment="Center"/>
<ContentPresenter Grid.Column="1" VerticalAlignment="Center"/>
</Grid>
</ControlTemplate>
<DataTemplate DataType="{x:Type BoolOption}">
<HeaderedContentControl Template="{StaticResource OptionTemplate}" Header="{Binding Label}">
<CheckBox Grid.Column="1" IsChecked="{Binding Bool}"/>
</HeaderedContentControl>
</DataTemplate>
<DataTemplate DataType="{x:Type TextOption}">
<HeaderedContentControl Template="{StaticResource OptionTemplate}" Header="{Binding Label}">
<TextBox Text="{Binding Text}"/>
</HeaderedContentControl>
</DataTemplate>
<DataTemplate DataType="{x:Type RangeOption}">
<HeaderedContentControl Template="{StaticResource OptionTemplate}" Header="{Binding Label}">
<Slider Minimum="{Binding Minimum}" Maximum="{Binding Maximum}" Value="{Binding Value}"/>
</HeaderedContentControl>
</DataTemplate>
<!-- ... -->
</UserControl.Resources>
<ItemsControl ItemsSource="{Binding Options}"/>
</UserControl>
- Eli Iannuzzo
- Posts: 5
- Joined:
Re: Dynamic Menu3D options menu
Those option classes defined in the ViewModel.h don't seem to be accessible/valid in xaml, how can I expose them?
"The type reference cannot find a public type named 'BoolOption' ..."
"The name 'BoolOption' does not exist in the namespace ..."
<DataTemplate DataType="{x:Type BoolOption}">
"The type reference cannot find a public type named 'BoolOption' ..."
<DataTemplate DataType="{x:Type local:BoolOption}">
"The name 'BoolOption' does not exist in the namespace ..."
-
sfernandez
Site Admin
- Posts: 2997
- Joined:
Re: Dynamic Menu3D options menu
In order to make those classes available to the xaml parser they need to be at least registered in the reflection. So they need to have reflection marks with a TypeId, and you can force the types to be registered in reflection by calling TypeOf<T>() for those classes when you register your component types:
Code: Select all
NS_IMPLEMENT_REFLECTION(BoolOption)
{
NsMeta<TypeId>("Test.BoolOption");
...
}
Code: Select all
void RegisterComponents()
{
TypeOf<BoolOption>();
...
NsRegisterComponent<MyControl>();
...
}
Code: Select all
<UserControl x:Class="Test.MyControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Test">
<UserControl.Resources>
<DataTemplate DataType="{x:Type local:BoolOption}">
...
</DataTemplate>
</UserControl.Resources>
...
</UserControl>
Who is online
Users browsing this forum: Ahrefs [Bot] and 43 guests