Search code examples
wpfitemscontrol

In WPF,How to add radio button , checkbox, comboBox based on the type which is configured in the database?


Totally i am new to WPF, I need to solve the problem. Could anybody give me a sample xaml code without using code-behind. Based on the questType (Check,radio, combo) Control need to be created based on the ObserverableCollection.

public class Order
{
  public int OrderCode {get;set;}
  public string Description {get;set;}
  public ObserverableCollection<question> Questions{get;set;}
}
public class question
{
   public string questType {get;set;}
   public string Question {get;set;}
   public ObserverableCollection<Answer> Answers {get;set;}
}
public class Answer
{
    public string Ans{get; set;}
}

Based on the questType (Check,radio, combo) Control need to be created based on the ObserverableCollection. example: 1001 Pencil Gender? oMale oFemale oOther []Checkbox1 []Checkbox2 1002 Pen Fasting? oYes oNo


Solution

  • Here is how I would do it:

    Code behind:

    using System;
    using System.Collections.ObjectModel;
    using System.Linq;
    using System.Text;
    using System.Windows;
    
    namespace Ans
    {
    
        public class Order
        {
            public int OrderCode { get; set; }
            public string Description { get; set; }
            public ObservableCollection<Question> Questions { get; set; }
        }
        public class Question
        {
            public string questType { get; set; }
            public string Label { get; set; }
            public ObservableCollection<Answer> Answers { get; set; }
        }
        public class Answer
        {
            public string Ans { get; set; }
            public bool IsSelected { get; set; }
        }
    
        /// <summary>
        /// Interaction logic for MainWindow.xaml
        /// </summary>
        public partial class MainWindow : Window
        {
    
            #region Order
    
            /// <summary>
            /// Order Dependency Property
            /// </summary>
            public static readonly DependencyProperty OrderProperty =
                DependencyProperty.Register("Order", typeof(Order), typeof(MainWindow),
                    new FrameworkPropertyMetadata((Order)null));
    
            /// <summary>
            /// Gets or sets the Order property. This dependency property 
            /// indicates ....
            /// </summary>
            public Order Order
            {
                get { return (Order)GetValue(OrderProperty); }
                set { SetValue(OrderProperty, value); }
            }
    
            #endregion
    
    
    
            public MainWindow()
            {
                InitializeComponent();
    
                Order = new Order()
                {
                    Questions = new ObservableCollection<Question>()
                    {
                        new Question()
                        {
                            questType = "Combo",
                            Label = "Combo",
                            Answers = new ObservableCollection<Answer>()
                            {
                                new Answer(){Ans = "Female"},
                                new Answer(){Ans = "Male"}
                            }
                        },
                        new Question()
                        {
                            questType = "Check",
                            Label = "Multi",
                            Answers = new ObservableCollection<Answer>()
                            {
                                new Answer(){Ans = "Female"},
                                new Answer(){Ans = "Male"}
                            }
                        },
                        new Question()
                        {
                            questType = "Radio",
                            Label = "Radio",
                            Answers = new ObservableCollection<Answer>()
                            {
                                new Answer(){Ans = "Female"},
                                new Answer(){Ans = "Male"}
                            }
                        }
    
    
    
                    }
                };
    
                DataContext = this;
            }
    
            private void Button_Click(object sender, RoutedEventArgs e)
            {
                foreach(Question q in Order.Questions)
                {
                    Console.WriteLine( q.Label + " : " + string.Join(", " , q.Answers.Where(a=>a.IsSelected).Select(a=>a.Ans)) );
                }
            }
        }
    }
    

    XAML:

    <Window x:Class="Ans.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:Ans"
            mc:Ignorable="d"
            Title="MainWindow" Height="450" Width="800">
    
        <Window.Resources>
            <DataTemplate x:Key="ComboQuestion">
                <ComboBox ItemsSource="{Binding Answers}">
                    <ComboBox.ItemTemplate>
                        <DataTemplate>
                            <TextBlock Text="{Binding Ans}"/>
                        </DataTemplate>
                    </ComboBox.ItemTemplate>
                    <ComboBox.ItemContainerStyle>
                        <Style TargetType="{x:Type ComboBoxItem}">
                            <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}"/>
                        </Style>
                    </ComboBox.ItemContainerStyle>
                </ComboBox>
            </DataTemplate>
            <DataTemplate x:Key="CheckQuestion">
                <ItemsControl ItemsSource="{Binding Answers}">
                    <ItemsControl.ItemTemplate>
                        <DataTemplate>
                            <CheckBox Content="{Binding Ans}" IsChecked="{Binding IsSelected, Mode=TwoWay}"/>
                        </DataTemplate>
                    </ItemsControl.ItemTemplate>
                </ItemsControl>
            </DataTemplate>
            <DataTemplate x:Key="RadioQuestion">
                <ItemsControl ItemsSource="{Binding Answers}">
                    <ItemsControl.ItemTemplate>
                        <DataTemplate>
                            <RadioButton Content="{Binding Ans}" IsChecked="{Binding IsSelected, Mode=TwoWay}" GroupName="{Binding DataContext.Label, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
                        </DataTemplate>
                    </ItemsControl.ItemTemplate>
                </ItemsControl>
            </DataTemplate>
        </Window.Resources>
        <Grid>
    
            <ItemsControl ItemsSource="{Binding Order.Questions}" Grid.IsSharedSizeScope="True">
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <Grid>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="Auto" SharedSizeGroup="Label"/>
                                <ColumnDefinition Width="*"/>
                            </Grid.ColumnDefinitions>
    
                            <TextBlock Text="{Binding Label}"/>
    
                            <ContentControl x:Name="ccQuestion" Grid.Column="1" Content="{Binding}" Margin="10"/>
                        </Grid>
                        <DataTemplate.Triggers>
                            <DataTrigger Binding="{Binding questType}" Value="Combo">
                                <Setter TargetName="ccQuestion" Property="ContentTemplate" Value="{StaticResource ComboQuestion}"/>
                            </DataTrigger>
                            <DataTrigger Binding="{Binding questType}" Value="Check">
                                <Setter TargetName="ccQuestion" Property="ContentTemplate" Value="{StaticResource CheckQuestion}"/>
                            </DataTrigger>
                            <DataTrigger Binding="{Binding questType}" Value="Radio">
                                <Setter TargetName="ccQuestion" Property="ContentTemplate" Value="{StaticResource RadioQuestion}"/>
                            </DataTrigger>
                        </DataTemplate.Triggers>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>
    
    
            <Button Content="Order" Click="Button_Click" VerticalAlignment="Bottom"/>
        </Grid>
    </Window>
    

    The only thing I've added to your model is IsSelected property that allows to know if this answer was selected.

    The other important thing is the Radios. Their GroupName property defines the scope. So if no GroupName is set then when clicking on a radio in one question it will unselect radio in another question. I used Question label in my solution however it only works if labels are unique.

    Another point is that data triggers are OK if you have 3-5 question types and if thay are only based on the questionType. However for more complex scenarios you can look for ItemTemplateSelector. It allows to write C# code that will select the template based on each item in ItemsControl.