Search code examples
c#wpfmvvmcaliburn.micro

wpf mvvm - Is there a way to programmatically create textbox based on number of properties in model and bind to each of them in?


I have multiple objects with multiple properties which I want to display or hide (or create if needed) based on the property value.

For example: Model:

     public class InputModel
{
    public string Name { get; set; }
    public string Id { get; set; }
    public string Type { get; set; }
    public bool Required { get; set; }
    public int Dpi { get; set; }
    public string Data { get; set; }
    public List<ColorModel> Filter { get; set; }
    public List<RenderModel> Render { get; set; }
    public List<LayoutModel> Layout { get; set; }
}

View:

      <ItemsControl x:Name="InputsList"
                  Grid.Row="2" Grid.Column="1" Grid.ColumnSpan="2" Height="400">
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <WrapPanel/>
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <StackPanel HorizontalAlignment="Center" Background="Beige" Margin="10">
                            
                            <TextBlock Text="{Binding Name, StringFormat='Name: {0}'}"/>
                            <TextBlock Text="{Binding Id, StringFormat='Id: {0}'}"/>
                            <TextBlock Text="{Binding Type, StringFormat='Type: {0}'}"/>
                        <CheckBox Grid.Column="1" Content="Required" Margin="0,5,0,0"
                                  IsChecked="{Binding ElementName=Required, Path=CheckBoxIsChecked}"/>
                            <TextBox Grid.Column="0" Grid.Row="4"
                                   Text="{Binding Dpi, StringFormat='Dpi: {0}'}"/>
                            <TextBlock Grid.Column="0" Grid.Row="5"
                                   Text="Render:"/>
                            
                    </StackPanel>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>

And my ViewModel:

public class InputsListViewModel : Screen
{
    private IRootModel _rootModel;

    public InputsListViewModel(IRootModel rootModel)
    {
        _rootModel = rootModel;
    }

    private BindingList<InputModel> _inputs;

    public BindingList<InputModel> InputsList
    {
        get
        {
            _inputs = new (_rootModel.Inputs);
            return _inputs;
        }
        set
        {
            _inputs = value;
            NotifyOfPropertyChange(() => InputsList);
        }
    }

    private BindingList<RenderModel> _renders;

    public BindingList<RenderModel> RenderList
    {
        get 
        {
            _renders = new(_rootModel.Render);
            return _renders;
        }
        set 
        {
            _renders = value;
            NotifyOfPropertyChange(() => RenderList);
        }
    }

    private BindingList<LayoutModel> _layouts;

    public BindingList<LayoutModel> LayoutList
    {
        get
        {
            _layouts = new (_rootModel.Layout);
            return _layouts;
        }
        set
        {
            _layouts = value;
            NotifyOfPropertyChange(() => LayoutList);
        }
    }
}

I don't need to display all of the properties in here, just the ones that I need at a given time. And I want to be able to add any of the properties that are missing, if I need to.

I am a beginner and so far I only know how to manually create the fields that I need in view and bind to them.

Thanks!


Solution

  • you can hide whole items with ItemContainerStyle or individual Properties as shown in 2 different ways

    <ItemsControl ItemsSource="{Binding MyItems}">
        <ItemsControl.ItemContainerStyle>
            <Style>
                <Setter Property="Visibility" Value="{Binding ShowInUi, Converter={StaticResource BooleanToVisibilityConverter}}" />
            </Style>
        </ItemsControl.ItemContainerStyle>
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <StackPanel>
                    <TextBlock Text="{Binding Name, StringFormat='Name: {0}'}"/>
                    <TextBlock Text="{Binding Id, StringFormat='Id: {0}'}"/>
                    <TextBlock Text="{Binding Type, StringFormat='Type: {0}'}"/>
                        
                    <!--possibility 1-->
                    <TextBlock x:Name="AdditionalInfoText" Text="{Binding AdditionalInfo}"/>
                    <!--possibility 2-->
                    <TextBlock Text="{Binding SuperDuperValue}" Visibility="{Binding IsSuperDuperItem, Converter={StaticResource BooleanToVisibilityConverter}}"/>
    
                </StackPanel>
                <DataTemplate.Triggers>
                    <DataTrigger Binding="{Binding AdditionalInfo}" Value="{x:Null}">
                        <Setter TargetName="AdditionalInfoText" Property="Visibility" Value="Collapsed"/>
                    </DataTrigger>
                </DataTemplate.Triggers>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
    

    For all textblocks in an area

    <Style TargetType="TextBlock">
        <Style.Triggers>
            <Trigger Property="Text" Value="{x:Null}">
                <Setter Property="Visibility" Value="Collapsed"/>
            </Trigger>
            <DataTrigger Binding="{Binding Text.Length, RelativeSource={RelativeSource Self}}" Value="0">
                <Setter Property="Visibility" Value="Collapsed"/>
            </DataTrigger>
        </Style.Triggers>
    </Style>